view libclient-js/malikania/js_color.cpp @ 63:96ba0c5cf893

Misc: update duktape.hpp to new style
author David Demelier <markand@malikania.fr>
date Fri, 16 Dec 2016 16:11:24 +0100
parents fabbe1759cec
children 858621081b95
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 mlk {

namespace {

std::uint8_t clamp_component(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 parse_string(duk_context* ctx, duk_idx_t index, bool required)
{
    assert(duk_is_string(ctx, index));

    color ret;

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

    return ret;
}

color parse_object(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 clamp_component(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"),
        clamp_component(ctx, alpha, required)
    );
}

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

        break;
    }

    return ret;
}

duk_ret_t constructor(duk_context* ctx)
{
    color obj;

    /*
     * 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;

        obj = color(
            clamp_component(ctx, duk_require_int(ctx, 0), true),
            clamp_component(ctx, duk_require_int(ctx, 1), true),
            clamp_component(ctx, duk_require_int(ctx, 2), true),
            clamp_component(ctx, alpha, true)
        );
    } else if (duk_get_top(ctx) == 1) {
        obj = 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, obj);
        duk_pop(ctx);
        ret = 0;
    } else {
        dukx_push_color(ctx, obj);
        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)
{
    dukx_stack_assert 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));

    dukx_stack_assert 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)
{
    dukx_stack_assert 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);
}

} // !mlk