diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient-js/malikania/js-color.cpp	Thu Aug 18 11:07:34 2016 +0200
@@ -0,0 +1,191 @@
+/*
+ * 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