view libclient-js/malikania/js-color.cpp @ 37:702e0a2b9e5e

Misc: make JavaScript different libraries, Closes T3
author David Demelier <markand@malikania.fr>
date Thu, 18 Aug 2016 11:07:34 +0200
parents libclient/malikania/js-color.cpp@9af360f34c7d
children
line wrap: on
line source

/*
 * js-color.cpp -- color description (JavaScript binding)
 *
 * Copyright (c) 2013-2016 Malikania Authors
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <cassert>

#include "js-color.hpp"
#include "util.hpp"

namespace malikania {

namespace {

std::uint8_t clampComponent(duk_context *ctx, int value, bool required)
{
    if (value < 0 || value > 255) {
        if (required)
            duk_error(ctx, DUK_ERR_RANGE_ERROR, "%d is out of range (0, 255)", value);
        else
            value = util::clamp(value, 0, 255);
    }

    return static_cast<std::uint8_t>(value);
}

Color parseString(duk_context *ctx, duk_idx_t index, bool required)
{
    assert(duk_is_string(ctx, index));

    Color color;

    try {
        color = Color(duk_get_string(ctx, index));
    } catch (const std::exception &ex) {
        if (required)
            duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
    }

    return color;
}

Color parseObject(duk_context *ctx, duk_idx_t index, bool required)
{
    assert(duk_is_object(ctx, index));

    auto require = [&] (const auto prop) -> std::uint8_t {
        if (required && !duk_has_prop_string(ctx, index, prop))
            duk_error(ctx, DUK_ERR_ERROR, "missing %s property in color description", prop);

        duk_get_prop_string(ctx, index, prop);
        auto comp = duk_get_int(ctx, -1);
        duk_pop(ctx);

        return clampComponent(ctx, comp, required);
    };

    // Alpha is optional.
    duk_get_prop_string(ctx, index, "alpha");
    auto alpha = duk_is_number(ctx, -1) ? duk_to_int(ctx, -1) : 255;
    duk_pop(ctx);

    return Color(
        require("red"),
        require("green"),
        require("blue"),
        clampComponent(ctx, alpha, required)
    );
}

Color parse(duk_context *ctx, duk_idx_t index, bool required, Color color = {})
{
    switch (duk_get_type(ctx, index)) {
    case DUK_TYPE_STRING:
        color = parseString(ctx, index, required);
        break;
    case DUK_TYPE_OBJECT:
        color = parseObject(ctx, index, required);
        break;
    default:
        if (required)
            duk_error(ctx, DUK_ERR_TYPE_ERROR, "color (string, object) expected");

        break;
    }

    return color;
}

duk_ret_t constructor(duk_context *ctx)
{
    Color color;

    /*
     * The constructor allows an additional signature that takes 4 number
     * arguments, otherwise use the literal parsing functions.
     */
    if (duk_get_top(ctx) >= 3) {
        // Alpha is optional.
        auto alpha = duk_is_number(ctx, 3) ? duk_to_int(ctx, 3) : 255;

        color = Color(
            clampComponent(ctx, duk_require_int(ctx, 0), true),
            clampComponent(ctx, duk_require_int(ctx, 1), true),
            clampComponent(ctx, duk_require_int(ctx, 2), true),
            clampComponent(ctx, alpha, true)
        );
    } else if (duk_get_top(ctx) == 1)
        color = parse(ctx, 0, true);

    duk_ret_t ret;

    // Allow both constructor and non constructor calls.
    if (duk_is_constructor_call(ctx)) {
        duk_push_this(ctx);
        dukx_put_color(ctx, color);
        duk_pop(ctx);
        ret = 0;
    } else {
        dukx_push_color(ctx, color);
        ret = 1;
    }

    return ret;
}

} //! namespace

Color dukx_get_color(duk_context *ctx, duk_idx_t index)
{
    return parse(ctx, index, false);
}

Color dukx_require_color(duk_context *ctx, duk_idx_t index)
{
    return parse(ctx, index, true);
}

Color dukx_optional_color(duk_context *ctx, duk_idx_t index, Color def)
{
    return parse(ctx, index, false, std::move(def));
}

void dukx_push_color(duk_context *ctx, const Color &color)
{
    StackAssert sa(ctx, 1);

    duk_push_object(ctx);
    dukx_put_color(ctx, color);
}

void dukx_put_color(duk_context *ctx, const Color &color)
{
    assert(duk_is_object(ctx, -1));

    StackAssert sa(ctx, 0);

    duk_push_uint(ctx, color.red());
    duk_put_prop_string(ctx, -2, "red");
    duk_push_uint(ctx, color.green());
    duk_put_prop_string(ctx, -2, "green");
    duk_push_uint(ctx, color.blue());
    duk_put_prop_string(ctx, -2, "blue");
    duk_push_uint(ctx, color.alpha());
    duk_put_prop_string(ctx, -2, "alpha");
}

void dukx_load_color(duk_context *ctx)
{
    StackAssert sa(ctx, 0);

    duk_get_global_string(ctx, "Malikania");
    duk_push_c_function(ctx, constructor, DUK_VARARGS);
    duk_put_prop_string(ctx, -2, "Color");
    duk_pop(ctx);
}

} // !malikania