cairo-region.cpp 11.1 KB
Newer Older
1
/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* Copyright 2014 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <config.h>

25
#include "gi/foreign.h"
26
#include "gjs/jsapi-class.h"
27
#include "gjs/jsapi-util-args.h"
28
#include "gjs/jsapi-wrapper.h"
29 30 31 32 33 34 35 36 37 38 39

#include <cairo.h>
#include <cairo-gobject.h>
#include "cairo-private.h"

typedef struct {
    JSContext *context;
    JSObject *object;
    cairo_region_t *region;
} GjsCairoRegion;

40 41
static JSObject *gjs_cairo_region_get_proto(JSContext *);

42 43 44
GJS_DEFINE_PROTO_WITH_GTYPE("Region", cairo_region,
                            CAIRO_GOBJECT_TYPE_REGION,
                            JSCLASS_BACKGROUND_FINALIZE)
45 46 47
GJS_DEFINE_PRIV_FROM_JS(GjsCairoRegion, gjs_cairo_region_class);

static cairo_region_t *
48 49 50
get_region(JSContext       *context,
           JS::HandleObject obj)
{
51 52 53 54 55 56 57
    GjsCairoRegion *priv = priv_from_js(context, obj);
    if (priv == NULL)
        return NULL;
    else
        return priv->region;
}

58
static bool
59 60
fill_rectangle(JSContext             *context,
               JS::HandleObject       obj,
61 62
               cairo_rectangle_int_t *rect);

63 64 65
#define PRELUDE                                                       \
    GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoRegion, priv); \
    cairo_region_t *this_region = priv ? priv->region : NULL;
66 67 68 69 70

#define RETURN_STATUS                                           \
    return gjs_cairo_check_status(context, cairo_region_status(this_region), "region");

#define REGION_DEFINE_REGION_FUNC(method)                       \
71
    static bool                                                 \
72 73
    method##_func(JSContext *context,                           \
                  unsigned argc,                                \
74
                  JS::Value *vp)                                \
75 76
    {                                                           \
        PRELUDE;                                                \
77
        JS::RootedObject other_obj(context);                    \
78
        cairo_region_t *other_region;                           \
79 80
        if (!gjs_parse_call_args(context, #method, argv, "o",   \
                                 "other_region", &other_obj))   \
81
            return false;                                       \
82 83 84 85
                                                                \
        other_region = get_region(context, other_obj);          \
                                                                \
        cairo_region_##method(this_region, other_region);       \
86
            argv.rval().setUndefined();                         \
87 88 89 90
            RETURN_STATUS;                                      \
    }

#define REGION_DEFINE_RECT_FUNC(method)                         \
91
    static bool                                                 \
92 93
    method##_rectangle_func(JSContext *context,                 \
                            unsigned argc,                      \
94
                            JS::Value *vp)                      \
95 96
    {                                                           \
        PRELUDE;                                                \
97
        JS::RootedObject rect_obj(context);                     \
98
        cairo_rectangle_int_t rect;                             \
99 100
        if (!gjs_parse_call_args(context, #method, argv, "o",   \
                                 "rect", &rect_obj))            \
101
            return false;                                       \
102 103
                                                                \
        if (!fill_rectangle(context, rect_obj, &rect))          \
104
            return false;                                       \
105 106
                                                                \
        cairo_region_##method##_rectangle(this_region, &rect);  \
107
            argv.rval().setUndefined();                         \
108 109 110 111 112 113 114 115 116 117 118 119 120
            RETURN_STATUS;                                      \
    }

REGION_DEFINE_REGION_FUNC(union)
REGION_DEFINE_REGION_FUNC(subtract)
REGION_DEFINE_REGION_FUNC(intersect)
REGION_DEFINE_REGION_FUNC(xor)

REGION_DEFINE_RECT_FUNC(union)
REGION_DEFINE_RECT_FUNC(subtract)
REGION_DEFINE_RECT_FUNC(intersect)
REGION_DEFINE_RECT_FUNC(xor)

121
static bool
122 123
fill_rectangle(JSContext             *context,
               JS::HandleObject       obj,
124 125
               cairo_rectangle_int_t *rect)
{
126
    JS::RootedValue val(context);
127

128
    if (!gjs_object_get_property(context, obj, GJS_STRING_X, &val))
129
        return false;
130
    if (!JS::ToInt32(context, val, &rect->x))
131
        return false;
132

133
    if (!gjs_object_get_property(context, obj, GJS_STRING_Y, &val))
134
        return false;
135
    if (!JS::ToInt32(context, val, &rect->y))
136
        return false;
137

138
    if (!gjs_object_get_property(context, obj, GJS_STRING_WIDTH, &val))
139
        return false;
140
    if (!JS::ToInt32(context, val, &rect->width))
141
        return false;
142

143
    if (!gjs_object_get_property(context, obj, GJS_STRING_HEIGHT, &val))
144
        return false;
145
    if (!JS::ToInt32(context, val, &rect->height))
146
        return false;
147

148
    return true;
149 150 151 152 153 154
}

static JSObject *
make_rectangle(JSContext *context,
               cairo_rectangle_int_t *rect)
{
155
    JS::RootedObject rect_obj(context, JS_NewPlainObject(context));
156
    JS::RootedValue val(context);
157

158
    val = JS::Int32Value(rect->x);
159
    JS_SetProperty(context, rect_obj, "x", val);
160

161
    val = JS::Int32Value(rect->y);
162
    JS_SetProperty(context, rect_obj, "y", val);
163

164
    val = JS::Int32Value(rect->width);
165
    JS_SetProperty(context, rect_obj, "width", val);
166

167
    val = JS::Int32Value(rect->height);
168
    JS_SetProperty(context, rect_obj, "height", val);
169 170 171 172

    return rect_obj;
}

173
static bool
174 175
num_rectangles_func(JSContext *context,
                    unsigned argc,
176
                    JS::Value *vp)
177 178 179 180
{
    PRELUDE;
    int n_rects;

181
    if (!gjs_parse_call_args(context, "num_rectangles", argv, ""))
182
        return false;
183 184

    n_rects = cairo_region_num_rectangles(this_region);
185
    argv.rval().setInt32(n_rects);
186 187 188
    RETURN_STATUS;
}

189
static bool
190 191
get_rectangle_func(JSContext *context,
                   unsigned argc,
192
                   JS::Value *vp)
193 194 195 196 197 198
{
    PRELUDE;
    int i;
    JSObject *rect_obj;
    cairo_rectangle_int_t rect;

199 200
    if (!gjs_parse_call_args(context, "get_rectangle", argv, "i",
                             "rect", &i))
201
        return false;
202 203 204 205

    cairo_region_get_rectangle(this_region, i, &rect);
    rect_obj = make_rectangle(context, &rect);

206
    argv.rval().setObjectOrNull(rect_obj);
207 208 209 210
    RETURN_STATUS;
}

JSPropertySpec gjs_cairo_region_proto_props[] = {
211
    JS_PS_END
212 213 214
};

JSFunctionSpec gjs_cairo_region_proto_funcs[] = {
215 216 217 218 219 220 221 222 223 224 225 226 227
    JS_FN("union", union_func, 0, 0),
    JS_FN("subtract", subtract_func, 0, 0),
    JS_FN("intersect", intersect_func, 0, 0),
    JS_FN("xor", xor_func, 0, 0),

    JS_FN("unionRectangle", union_rectangle_func, 0, 0),
    JS_FN("subtractRectangle", subtract_rectangle_func, 0, 0),
    JS_FN("intersectRectangle", intersect_rectangle_func, 0, 0),
    JS_FN("xorRectangle", xor_rectangle_func, 0, 0),

    JS_FN("numRectangles", num_rectangles_func, 0, 0),
    JS_FN("getRectangle", get_rectangle_func, 0, 0),
    JS_FS_END};
228

229 230
JSFunctionSpec gjs_cairo_region_static_funcs[] = { JS_FS_END };

231
static void
232 233 234
_gjs_cairo_region_construct_internal(JSContext       *context,
                                     JS::HandleObject obj,
                                     cairo_region_t  *region)
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
{
    GjsCairoRegion *priv;

    priv = g_slice_new0(GjsCairoRegion);

    g_assert(priv_from_js(context, obj) == NULL);
    JS_SetPrivate(obj, priv);

    priv->context = context;
    priv->object = obj;
    priv->region = cairo_region_reference(region);
}

GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_region)
{
    GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_region)
    cairo_region_t *region;

    GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_region);

255
    if (!gjs_parse_call_args(context, "Region", argv, ""))
256
        return false;
257 258 259 260 261 262 263 264

    region = cairo_region_create();

    _gjs_cairo_region_construct_internal(context, object, region);
    cairo_region_destroy(region);

    GJS_NATIVE_CONSTRUCTOR_FINISH(cairo_region);

265
    return true;
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
}

static void
gjs_cairo_region_finalize(JSFreeOp *fop,
                          JSObject *obj)
{
    GjsCairoRegion *priv;
    priv = (GjsCairoRegion*) JS_GetPrivate(obj);
    if (priv == NULL)
        return;

    cairo_region_destroy(priv->region);
    g_slice_free(GjsCairoRegion, priv);
}

static JSObject *
gjs_cairo_region_from_region(JSContext *context,
                             cairo_region_t *region)
{
285
    JS::RootedObject proto(context, gjs_cairo_region_get_proto(context));
286
    JS::RootedObject object(context,
287
        JS_NewObjectWithGivenProto(context, &gjs_cairo_region_class, proto));
288 289 290 291 292 293 294 295
    if (!object)
        return NULL;

    _gjs_cairo_region_construct_internal(context, object, region);

    return object;
}

296
static bool
297
region_to_g_argument(JSContext      *context,
298
                     JS::Value       value,
299 300 301
                     const char     *arg_name,
                     GjsArgumentType argument_type,
                     GITransfer      transfer,
302
                     bool            may_be_null,
303 304
                     GArgument      *arg)
{
305
    JS::RootedObject obj(context, &value.toObject());
306 307 308 309
    cairo_region_t *region;

    region = get_region(context, obj);
    if (!region)
310
        return false;
311 312 313 314
    if (transfer == GI_TRANSFER_EVERYTHING)
        cairo_region_destroy(region);

    arg->v_pointer = region;
315
    return true;
316 317
}

318
static bool
319 320 321
region_from_g_argument(JSContext             *context,
                       JS::MutableHandleValue value_p,
                       GIArgument            *arg)
322 323 324 325 326
{
    JSObject *obj;

    obj = gjs_cairo_region_from_region(context, (cairo_region_t*)arg->v_pointer);
    if (!obj)
327
        return false;
328

329
    value_p.setObject(*obj);
330
    return true;
331 332
}

333
static bool
334 335 336 337 338
region_release_argument(JSContext  *context,
                        GITransfer  transfer,
                        GArgument  *arg)
{
    cairo_region_destroy((cairo_region_t*)arg->v_pointer);
339
    return true;
340 341 342 343 344 345 346 347 348 349 350 351 352
}

static GjsForeignInfo foreign_info = {
    region_to_g_argument,
    region_from_g_argument,
    region_release_argument
};

void
gjs_cairo_region_init(JSContext *context)
{
    gjs_struct_foreign_register("cairo", "Region", &foreign_info);
}