Skip to content

Commit

Permalink
Add Canvas globalCompositeOperation
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Nov 29, 2023
1 parent 0bfc46b commit 50e4168
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-eggs-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'nxjs-runtime': patch
---

Add Canvas `globalCompositeOperation`
9 changes: 9 additions & 0 deletions packages/runtime/src/canvas/canvas-rendering-context-2d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
CanvasLineCap,
CanvasLineJoin,
CanvasImageSource,
GlobalCompositeOperation,
} from '../types';
import type { RGBA } from '../internal';
import type { SwitchClass } from '../switch';
Expand Down Expand Up @@ -195,6 +196,14 @@ export class CanvasRenderingContext2D {
*/
declare globalAlpha: number;

/**
* Specifies the type of compositing operation to apply when drawing new shapes.
*
* @default "source-over"
* @see https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
*/
declare globalCompositeOperation: GlobalCompositeOperation;

/**
* Determines the shape used to draw the end points of lines.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
CanvasLineCap,
CanvasLineJoin,
CanvasImageSource,
GlobalCompositeOperation,
} from '../types';
import type { RGBA } from '../internal';
import type { SwitchClass } from '../switch';
Expand Down Expand Up @@ -166,6 +167,14 @@ export class OffscreenCanvasRenderingContext2D {
*/
declare globalAlpha: number;

/**
* Specifies the type of compositing operation to apply when drawing new shapes.
*
* @default "source-over"
* @see https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
*/
declare globalCompositeOperation: GlobalCompositeOperation;

/**
* Determines the shape used to draw the end points of lines.
*
Expand Down
28 changes: 28 additions & 0 deletions packages/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,34 @@ export type CanvasFillRule = 'evenodd' | 'nonzero';
export type CanvasImageSource = Image | Screen | OffscreenCanvas;
export type CanvasLineCap = 'butt' | 'round' | 'square';
export type CanvasLineJoin = 'bevel' | 'miter' | 'round';
export type GlobalCompositeOperation =
| 'color'
| 'color-burn'
| 'color-dodge'
| 'copy'
| 'darken'
| 'destination-atop'
| 'destination-in'
| 'destination-out'
| 'destination-over'
| 'difference'
| 'exclusion'
| 'hard-light'
| 'hue'
| 'lighten'
| 'lighter'
| 'luminosity'
| 'multiply'
| 'overlay'
| 'saturate'
| 'saturation'
| 'screen'
| 'soft-light'
| 'source-atop'
| 'source-in'
| 'source-out'
| 'source-over'
| 'xor';

export type FontDisplay = 'auto' | 'block' | 'fallback' | 'optional' | 'swap';
export type FontFaceLoadStatus = 'error' | 'loaded' | 'loading' | 'unloaded';
Expand Down
244 changes: 244 additions & 0 deletions source/canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,249 @@ static JSValue nx_canvas_context_2d_set_global_alpha(JSContext *ctx, JSValueCons
return JS_UNDEFINED;
}

static JSValue nx_canvas_context_2d_get_global_composite_operation(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
CANVAS_CONTEXT_THIS;
const char *op;
switch (cairo_get_operator(context->ctx))
{
// composite modes:
case CAIRO_OPERATOR_CLEAR:
op = "clear";
break;
case CAIRO_OPERATOR_SOURCE:
op = "copy";
break;
case CAIRO_OPERATOR_DEST:
op = "destination";
break;
case CAIRO_OPERATOR_OVER:
op = "source-over";
break;
case CAIRO_OPERATOR_DEST_OVER:
op = "destination-over";
break;
case CAIRO_OPERATOR_IN:
op = "source-in";
break;
case CAIRO_OPERATOR_DEST_IN:
op = "destination-in";
break;
case CAIRO_OPERATOR_OUT:
op = "source-out";
break;
case CAIRO_OPERATOR_DEST_OUT:
op = "destination-out";
break;
case CAIRO_OPERATOR_ATOP:
op = "source-atop";
break;
case CAIRO_OPERATOR_DEST_ATOP:
op = "destination-atop";
break;
case CAIRO_OPERATOR_XOR:
op = "xor";
break;
case CAIRO_OPERATOR_ADD:
op = "lighter";
break;
// blend modes:
// Note: "source-over" and "normal" are synonyms. Chrome and FF both report
// "source-over" after setting gCO to "normal".
// case CAIRO_OPERATOR_OVER: op = "normal";
case CAIRO_OPERATOR_MULTIPLY:
op = "multiply";
break;
case CAIRO_OPERATOR_SCREEN:
op = "screen";
break;
case CAIRO_OPERATOR_OVERLAY:
op = "overlay";
break;
case CAIRO_OPERATOR_DARKEN:
op = "darken";
break;
case CAIRO_OPERATOR_LIGHTEN:
op = "lighten";
break;
case CAIRO_OPERATOR_COLOR_DODGE:
op = "color-dodge";
break;
case CAIRO_OPERATOR_COLOR_BURN:
op = "color-burn";
break;
case CAIRO_OPERATOR_HARD_LIGHT:
op = "hard-light";
break;
case CAIRO_OPERATOR_SOFT_LIGHT:
op = "soft-light";
break;
case CAIRO_OPERATOR_DIFFERENCE:
op = "difference";
break;
case CAIRO_OPERATOR_EXCLUSION:
op = "exclusion";
break;
case CAIRO_OPERATOR_HSL_HUE:
op = "hue";
break;
case CAIRO_OPERATOR_HSL_SATURATION:
op = "saturation";
break;
case CAIRO_OPERATOR_HSL_COLOR:
op = "color";
break;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
op = "luminosity";
break;
// non-standard:
case CAIRO_OPERATOR_SATURATE:
op = "saturate";
break;
default:
op = "source-over";
}

return JS_NewString(ctx, op);
}

static JSValue nx_canvas_context_2d_set_global_composite_operation(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
CANVAS_CONTEXT_THIS;
int op = -1;
const char *str = JS_ToCString(ctx, argv[0]);
if (!str)
return JS_EXCEPTION;
if (strcmp(str, "clear") == 0)
{
// composite modes:
op = CAIRO_OPERATOR_CLEAR;
}
else if (strcmp(str, "copy") == 0)
{
op = CAIRO_OPERATOR_SOURCE;
}
else if (strcmp(str, "destination") == 0)
{
op = CAIRO_OPERATOR_DEST; // this seems to have been omitted from the spec
}
else if (strcmp(str, "source-over") == 0)
{
op = CAIRO_OPERATOR_OVER;
}
else if (strcmp(str, "destination-over") == 0)
{
op = CAIRO_OPERATOR_DEST_OVER;
}
else if (strcmp(str, "source-in") == 0)
{
op = CAIRO_OPERATOR_IN;
}
else if (strcmp(str, "destination-in") == 0)
{
op = CAIRO_OPERATOR_DEST_IN;
}
else if (strcmp(str, "source-out") == 0)
{
op = CAIRO_OPERATOR_OUT;
}
else if (strcmp(str, "destination-out") == 0)
{
op = CAIRO_OPERATOR_DEST_OUT;
}
else if (strcmp(str, "source-atop") == 0)
{
op = CAIRO_OPERATOR_ATOP;
}
else if (strcmp(str, "destination-atop") == 0)
{
op = CAIRO_OPERATOR_DEST_ATOP;
}
else if (strcmp(str, "xor") == 0)
{
op = CAIRO_OPERATOR_XOR;
}
else if (strcmp(str, "lighter") == 0)
{
op = CAIRO_OPERATOR_ADD;
}
else if (strcmp(str, "normal") == 0)
{
// blend modes:
op = CAIRO_OPERATOR_OVER;
}
else if (strcmp(str, "multiply") == 0)
{
op = CAIRO_OPERATOR_MULTIPLY;
}
else if (strcmp(str, "screen") == 0)
{
op = CAIRO_OPERATOR_SCREEN;
}
else if (strcmp(str, "overlay") == 0)
{
op = CAIRO_OPERATOR_OVERLAY;
}
else if (strcmp(str, "darken") == 0)
{
op = CAIRO_OPERATOR_DARKEN;
}
else if (strcmp(str, "lighten") == 0)
{
op = CAIRO_OPERATOR_LIGHTEN;
}
else if (strcmp(str, "color-dodge") == 0)
{
op = CAIRO_OPERATOR_COLOR_DODGE;
}
else if (strcmp(str, "color-burn") == 0)
{
op = CAIRO_OPERATOR_COLOR_BURN;
}
else if (strcmp(str, "hard-light") == 0)
{
op = CAIRO_OPERATOR_HARD_LIGHT;
}
else if (strcmp(str, "soft-light") == 0)
{
op = CAIRO_OPERATOR_SOFT_LIGHT;
}
else if (strcmp(str, "difference") == 0)
{
op = CAIRO_OPERATOR_DIFFERENCE;
}
else if (strcmp(str, "exclusion") == 0)
{
op = CAIRO_OPERATOR_EXCLUSION;
}
else if (strcmp(str, "hue") == 0)
{
op = CAIRO_OPERATOR_HSL_HUE;
}
else if (strcmp(str, "saturation") == 0)
{
op = CAIRO_OPERATOR_HSL_SATURATION;
}
else if (strcmp(str, "color") == 0)
{
op = CAIRO_OPERATOR_HSL_COLOR;
}
else if (strcmp(str, "luminosity") == 0)
{
op = CAIRO_OPERATOR_HSL_LUMINOSITY;
}
else if (strcmp(str, "saturate") == 0)
{
// non-standard:
op = CAIRO_OPERATOR_SATURATE;
}
if (op != -1)
{
cairo_set_operator(context->ctx, op);
}
return JS_UNDEFINED;
}

static JSValue nx_canvas_context_2d_get_image_data(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
CANVAS_CONTEXT;
Expand Down Expand Up @@ -1452,6 +1695,7 @@ static JSValue nx_canvas_context_2d_init_class(JSContext *ctx, JSValueConst this
JSAtom atom;
JSValue proto = JS_GetPropertyStr(ctx, argv[0], "prototype");
NX_DEF_GETSET(proto, "globalAlpha", nx_canvas_context_2d_get_global_alpha, nx_canvas_context_2d_set_global_alpha);
NX_DEF_GETSET(proto, "globalCompositeOperation", nx_canvas_context_2d_get_global_composite_operation, nx_canvas_context_2d_set_global_composite_operation);
NX_DEF_GETSET(proto, "lineCap", nx_canvas_context_2d_get_line_cap, nx_canvas_context_2d_set_line_cap);
NX_DEF_GETSET(proto, "lineDashOffset", nx_canvas_context_2d_get_line_dash_offset, nx_canvas_context_2d_set_line_dash_offset);
NX_DEF_GETSET(proto, "lineJoin", nx_canvas_context_2d_get_line_join, nx_canvas_context_2d_set_line_join);
Expand Down

0 comments on commit 50e4168

Please sign in to comment.