Skip to content

Commit

Permalink
feat: Context.filter & more (#10)
Browse files Browse the repository at this point in the history
* feat: Context.filter

* cleanup

* more properties support

* more stuff
  • Loading branch information
DjDeveloperr authored Oct 24, 2022
1 parent 18907b9 commit 06053db
Show file tree
Hide file tree
Showing 15 changed files with 607 additions and 80 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![License](https://img.shields.io/github/license/DjDeveloperr/skia_canvas)](https://github.com/DjDeveloperr/skia_canvas/blob/master/LICENSE)
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/DjDeveloperr)

Fast JavaScript Canvas backed by Skia using Deno FFI.
Fast JavaScript Canvas using Skia

## Example

Expand Down
6 changes: 5 additions & 1 deletion mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export * from "./src/canvas.ts";
export * from "./src/context2d.ts";
export * from "./src/path2d.ts";
export { Path2D } from "./src/path2d.ts";
export type { RoundRectRadii } from "./src/path2d.ts";
export * from "./src/image.ts";
export * from "./src/font.ts";
export * from "./src/dommatrix.ts";
export * from "./src/gradient.ts";
export * from "./src/pattern.ts";
5 changes: 5 additions & 0 deletions native/include/canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include "include/core/SkSurface.h"
#include "include/core/SkData.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkImageFilter.h"
#include "include/common.hpp"
#include "include/effects/SkImageFilters.h"

typedef struct sk_canvas {
SkSurface* surface;
Expand All @@ -30,6 +32,9 @@ typedef struct sk_context_state {
TextBaseline textBaseline;
TextDirection direction;
Font* font;
sk_sp<SkImageFilter> filter;
float letterSpacing;
float wordSpacing;
} sk_context_state;

typedef struct sk_context {
Expand Down
74 changes: 46 additions & 28 deletions native/include/context2d.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,16 @@ extern "C" {
);

SKIA_EXPORT float sk_context_get_line_width(sk_context* context);
SKIA_EXPORT void sk_context_set_line_width(sk_context* context, float width);
SKIA_EXPORT int sk_context_get_line_cap(sk_context* context);
SKIA_EXPORT void sk_context_set_line_cap(sk_context* context, int cap);
SKIA_EXPORT int sk_context_get_line_join(sk_context* context);
SKIA_EXPORT void sk_context_set_line_join(sk_context* context, int join);
SKIA_EXPORT void sk_context_set_line_width(sk_context* context, float width);
SKIA_EXPORT int sk_context_get_line_cap(sk_context* context);
SKIA_EXPORT void sk_context_set_line_cap(sk_context* context, int cap);
SKIA_EXPORT int sk_context_get_line_join(sk_context* context);
SKIA_EXPORT void sk_context_set_line_join(sk_context* context, int join);
SKIA_EXPORT float sk_context_get_miter_limit(sk_context* context);
SKIA_EXPORT void sk_context_set_miter_limit(sk_context* context, float limit);
SKIA_EXPORT void sk_context_set_line_dash(sk_context* context, float* dash, int count);
SKIA_EXPORT void sk_context_set_miter_limit(sk_context* context, float limit);
SKIA_EXPORT void sk_context_set_line_dash(sk_context* context, float* dash, int count);
SKIA_EXPORT float sk_context_get_line_dash_offset(sk_context* context);
SKIA_EXPORT void sk_context_set_line_dash_offset(sk_context* context, float offset);
SKIA_EXPORT void sk_context_set_line_dash_offset(sk_context* context, float offset);

SKIA_EXPORT void sk_context_set_font(
sk_context* context,
Expand All @@ -73,27 +73,33 @@ extern "C" {
int variant,
int stretch
);
SKIA_EXPORT int sk_context_get_text_align(sk_context* context);
SKIA_EXPORT void sk_context_set_text_align(sk_context* context, int align);
SKIA_EXPORT int sk_context_get_text_baseline(sk_context* context);
SKIA_EXPORT void sk_context_set_text_baseline(sk_context* context, int baseline);
SKIA_EXPORT int sk_context_get_text_direction(sk_context* context);
SKIA_EXPORT void sk_context_set_text_direction(sk_context* context, int direction);

SKIA_EXPORT int sk_context_set_fill_style(sk_context* context, char* style);
SKIA_EXPORT int sk_context_get_text_align(sk_context* context);
SKIA_EXPORT void sk_context_set_text_align(sk_context* context, int align);
SKIA_EXPORT int sk_context_get_text_baseline(sk_context* context);
SKIA_EXPORT void sk_context_set_text_baseline(sk_context* context, int baseline);
SKIA_EXPORT int sk_context_get_text_direction(sk_context* context);
SKIA_EXPORT void sk_context_set_text_direction(sk_context* context, int direction);
SKIA_EXPORT float sk_context_get_letter_spacing(sk_context* context);
SKIA_EXPORT void sk_context_set_letter_spacing(sk_context* context, float spacing);
SKIA_EXPORT void sk_context_set_font_stretch(sk_context* context, int stretch);
SKIA_EXPORT void sk_context_set_font_variant_caps(sk_context* context, int caps);
SKIA_EXPORT float sk_context_get_word_spacing(sk_context* context);
SKIA_EXPORT void sk_context_set_word_spacing(sk_context* context, float spacing);

SKIA_EXPORT int sk_context_set_fill_style(sk_context* context, char* style);
SKIA_EXPORT void sk_context_set_fill_style_gradient(sk_context* context, sk_gradient* gradient);
SKIA_EXPORT void sk_context_set_fill_style_pattern(sk_context* context, sk_pattern* pattern);
SKIA_EXPORT int sk_context_set_stroke_style(sk_context* context, char* style);
SKIA_EXPORT int sk_context_set_stroke_style(sk_context* context, char* style);
SKIA_EXPORT void sk_context_set_stroke_style_gradient(sk_context* context, sk_gradient* gradient);
SKIA_EXPORT void sk_context_set_stroke_style_pattern(sk_context* context, sk_pattern* pattern);

SKIA_EXPORT float sk_context_get_shadow_blur(sk_context* context);
SKIA_EXPORT void sk_context_set_shadow_blur(sk_context* context, float blur);
SKIA_EXPORT int sk_context_set_shadow_color(sk_context* context, char* style);
SKIA_EXPORT void sk_context_set_shadow_blur(sk_context* context, float blur);
SKIA_EXPORT int sk_context_set_shadow_color(sk_context* context, char* style);
SKIA_EXPORT float sk_context_get_shadow_offset_x(sk_context* context);
SKIA_EXPORT void sk_context_set_shadow_offset_x(sk_context* context, float x);
SKIA_EXPORT void sk_context_set_shadow_offset_x(sk_context* context, float x);
SKIA_EXPORT float sk_context_get_shadow_offset_y(sk_context* context);
SKIA_EXPORT void sk_context_set_shadow_offset_y(sk_context* context, float y);
SKIA_EXPORT void sk_context_set_shadow_offset_y(sk_context* context, float y);

SKIA_EXPORT void sk_context_begin_path(sk_context* context);
SKIA_EXPORT void sk_context_close_path(sk_context* context);
Expand All @@ -110,8 +116,8 @@ extern "C" {
SKIA_EXPORT void sk_context_fill(sk_context* context, SkPath* path, unsigned char rule);
SKIA_EXPORT void sk_context_stroke(sk_context* context, SkPath* path);
SKIA_EXPORT void sk_context_clip(sk_context* context, SkPath* path, unsigned char rule);
SKIA_EXPORT int sk_context_is_point_in_path(sk_context* context, float x, float y, SkPath* path, int rule);
SKIA_EXPORT int sk_context_is_point_in_stroke(sk_context* context, float x, float y, SkPath* path);
SKIA_EXPORT int sk_context_is_point_in_path(sk_context* context, float x, float y, SkPath* path, int rule);
SKIA_EXPORT int sk_context_is_point_in_stroke(sk_context* context, float x, float y, SkPath* path);

SKIA_EXPORT void sk_context_get_transform(sk_context* context, float* m);
SKIA_EXPORT void sk_context_rotate(sk_context* context, float angle);
Expand All @@ -122,9 +128,9 @@ extern "C" {
SKIA_EXPORT void sk_context_reset_transform(sk_context* context);

SKIA_EXPORT float sk_context_get_global_alpha(sk_context* context);
SKIA_EXPORT void sk_context_set_global_alpha(sk_context* context, float alpha);
SKIA_EXPORT int sk_context_get_global_composite_operation(sk_context* context);
SKIA_EXPORT void sk_context_set_global_composite_operation(sk_context* context, unsigned char op);
SKIA_EXPORT void sk_context_set_global_alpha(sk_context* context, float alpha);
SKIA_EXPORT int sk_context_get_global_composite_operation(sk_context* context);
SKIA_EXPORT void sk_context_set_global_composite_operation(sk_context* context, unsigned char op);

SKIA_EXPORT void sk_context_draw_image(
sk_context* context,
Expand All @@ -143,13 +149,25 @@ extern "C" {
SKIA_EXPORT void sk_context_put_image_data(sk_context* context, int width, int height, uint8_t *pixels, int row_bytes, float x, float y);
SKIA_EXPORT void sk_context_put_image_data_dirty(sk_context* context, int width, int height, uint8_t *pixels, int row_bytes, int length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height, uint8_t cs);

SKIA_EXPORT int sk_context_get_image_smoothing_enabled(sk_context* context);
SKIA_EXPORT int sk_context_get_image_smoothing_enabled(sk_context* context);
SKIA_EXPORT void sk_context_set_image_smoothing_enabled(sk_context* context, int enabled);
SKIA_EXPORT int sk_context_get_image_smoothing_quality(sk_context* context);
SKIA_EXPORT int sk_context_get_image_smoothing_quality(sk_context* context);
SKIA_EXPORT void sk_context_set_image_smoothing_quality(sk_context* context, int quality);

SKIA_EXPORT void sk_context_save(sk_context* context);
SKIA_EXPORT void sk_context_restore(sk_context* context);

SKIA_EXPORT void sk_context_filter_reset(sk_context* context);
SKIA_EXPORT void sk_context_filter_blur(sk_context* context, float blur);
SKIA_EXPORT void sk_context_filter_brightness(sk_context* context, float brightness);
SKIA_EXPORT void sk_context_filter_contrast(sk_context* context, float contrast);
SKIA_EXPORT int sk_context_filter_drop_shadow(sk_context* context, float dx, float dy, float blur, char* style);
SKIA_EXPORT void sk_context_filter_grayscale(sk_context* context, float grayscale);
SKIA_EXPORT void sk_context_filter_hue_rotate(sk_context* context, float angle);
SKIA_EXPORT void sk_context_filter_invert(sk_context* context, float invert);
SKIA_EXPORT void sk_context_filter_opacity(sk_context* context, float opacity);
SKIA_EXPORT void sk_context_filter_saturated(sk_context* context, float saturate);
SKIA_EXPORT void sk_context_filter_sepia(sk_context* context, float sepia);

SKIA_EXPORT void sk_context_destroy(sk_context* context);
}
170 changes: 161 additions & 9 deletions native/src/context2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ sk_context_state* create_default_state() {
state->direction = kLTR;
state->font = new Font();
state->font->size = 10;
state->font->family = strdup("DejaVu Sans");
state->font->family = strdup("sans-serif");
state->font->weight = 400;
state->font->style = FontStyle::kNormalStyle;
state->font->variant = FontVariant::kNormalVariant;
state->font->stretch = FontStretch::kNormal;
state->filter = sk_sp((SkImageFilter*)(nullptr));
state->letterSpacing = 0;
state->wordSpacing = 0;
return state;
}

Expand Down Expand Up @@ -66,12 +69,16 @@ sk_context_state* clone_context_state(sk_context_state* state) {
new_state->font->style = state->font->style;
new_state->font->variant = state->font->variant;
new_state->font->stretch = state->font->stretch;
new_state->filter = state->filter;
new_state->letterSpacing = state->letterSpacing;
new_state->wordSpacing = state->wordSpacing;
return new_state;
}

void free_context_state(sk_context_state* state) {
if (state->fillStyle.shader) state->fillStyle.shader.~sk_sp();
if (state->strokeStyle.shader) state->strokeStyle.shader.~sk_sp();
if (state->filter.get() != nullptr) state->filter.~sk_sp();
delete state->paint;
delete state->transform;
free(state->font);
Expand All @@ -98,6 +105,9 @@ SkPaint* sk_context_fill_paint(sk_context_state* state) {
);
paint->setPathEffect(effect);
}
if (state->filter.get() != nullptr) {
paint->setImageFilter(state->filter);
}
return paint;
}

Expand All @@ -119,6 +129,9 @@ SkPaint* sk_context_stroke_paint(sk_context_state* state) {
);
paint->setPathEffect(effect);
}
if (state->filter.get() != nullptr) {
paint->setImageFilter(state->filter);
}
return paint;
}

Expand Down Expand Up @@ -271,7 +284,8 @@ extern "C" {
tstyle.setTextBaseline(skia::textlayout::TextBaseline::kAlphabetic);
tstyle.setFontFamilies(familyVec);
tstyle.setFontSize(context->state->font->size);
tstyle.setWordSpacing(0);
tstyle.setWordSpacing(context->state->wordSpacing);
tstyle.setLetterSpacing(context->state->letterSpacing);
tstyle.setHeight(1);

auto fstyle = SkFontStyle(
Expand Down Expand Up @@ -593,12 +607,39 @@ extern "C" {
context->state->direction = TextDirection(direction);
}

// TODO: Context.letterSpacing
// TODO: Context.fontKerning
// TODO: Context.fontStretch
// TODO: Context.fontVariantCaps
// TODO: Context.textRendering
// TODO: Context.wordSpacing
// Context.letterSpacing getter
float sk_context_get_letter_spacing(sk_context* context) {
return context->state->letterSpacing;
}

// Context.letterSpacing setter
void sk_context_set_letter_spacing(sk_context* context, float spacing) {
context->state->letterSpacing = spacing;
}

// Context.fontKerning() stubbed in JS side

// Context.fontStretch setter
void sk_context_set_font_stretch(sk_context* context, int stretch) {
context->state->font->stretch = FontStretch(stretch);
}

// Context.fontVariantCaps setter
void sk_context_set_font_variant_caps(sk_context* context, int caps) {
context->state->font->variant = FontVariant(caps);
}

// Context.textRendering() stubbed in JS side

// Context.wordSpacing getter
float sk_context_get_word_spacing(sk_context* context) {
return context->state->wordSpacing;
}

// Context.wordSpacing setter
void sk_context_set_word_spacing(sk_context* context, float spacing) {
context->state->wordSpacing = spacing;
}

/// Fill and stroke styles

Expand Down Expand Up @@ -1116,7 +1157,118 @@ extern "C" {

/// Filters

// TODO: Context.filter
void sk_context_filter_reset(sk_context* context) {
context->state->filter = sk_sp((SkImageFilter*) nullptr);
}

void sk_context_filter_blur(sk_context* context, float blur) {
context->state->filter = SkImageFilters::Blur(blur, blur, SkTileMode::kClamp, context->state->filter);
}

void sk_context_filter_brightness(sk_context* context, float brightness) {
const auto color_matrix = SkColorMatrix(
brightness, 0.0, 0.0, 0.0, 0.0,
0.0, brightness, 0.0, 0.0, 0.0,
0.0, 0.0, brightness, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_contrast(sk_context* context, float contrast) {
contrast = std::max(contrast, 0.0f);
uint8_t table[256] = {0};
for (int i = 0; i < 256; i++) {
table[i] = (uint8_t) std::min(255.0f, std::max(0.0f, (i - 127) * contrast + 127));
}
auto color_filter = SkColorFilters::TableARGB(table, table, table, table);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

int sk_context_filter_drop_shadow(sk_context* context, float dx, float dy, float blur, char* style) {
if (dx == 0 && dy == 0 && blur == 0) {
return 1; // no-op
}
auto color = CSSColorParser::parse(std::string(style));
if (color) {
auto val = color.value();
uint8_t a = (uint8_t) (val.a * 255), r = val.r, g = val.g, b = val.b;
if (a == 0) {
return 1; // no-op
}
float sigma = blur / 2.0f;
context->state->filter = SkImageFilters::DropShadow(dx, dy, sigma, sigma, SkColorSetARGB(a, r, g, b), context->state->filter);
return 1;
}
return 0;
}

void sk_context_filter_grayscale(sk_context* context, float grayscale) {
grayscale = 1.0f - std::max(std::min(grayscale, 1.0f), 0.0f);
const auto color_matrix = SkColorMatrix(
0.2126 + 0.7874 * (grayscale), 0.7152 - 0.7152 * (grayscale), 0.0722 - 0.0722 * (grayscale), 0.0, 0.0,
0.2126 - 0.2126 * (grayscale), 0.7152 + 0.2848 * (grayscale), 0.0722 - 0.0722 * (grayscale), 0.0, 0.0,
0.2126 - 0.2126 * (grayscale), 0.7152 - 0.7152 * (grayscale), 0.0722 + 0.9278 * (grayscale), 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_hue_rotate(sk_context* context, float angle) {
angle = angle * M_PI / 180.0f;
float acos = std::cos(angle);
float asin = std::sin(angle);
const auto color_matrix = SkColorMatrix(
0.213 + 0.787 * acos - 0.213 * asin, 0.715 - 0.715 * acos - 0.715 * asin, 0.072 - 0.072 * acos + 0.928 * asin, 0.0, 0.0,
0.213 - 0.213 * acos + 0.143 * asin, 0.715 + 0.285 * acos + 0.140 * asin, 0.072 - 0.072 * acos - 0.283 * asin, 0.0, 0.0,
0.213 - 0.213 * acos - 0.787 * asin, 0.715 - 0.715 * acos + 0.715 * asin, 0.072 + 0.928 * acos + 0.072 * asin, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_invert(sk_context* context, float invert) {
invert = std::max(std::min(invert, 1.0f), 0.0f);
const auto color_matrix = SkColorMatrix(
-1.0 * invert + 1.0, 0.0, 0.0, 0.0, 255.0 * invert,
0.0, -1.0 * invert + 1.0, 0.0, 0.0, 255.0 * invert,
0.0, 0.0, -1.0 * invert + 1.0, 0.0, 255.0 * invert,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_opacity(sk_context* context, float opacity) {
const auto color_matrix = SkColorMatrix(
1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, opacity, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_saturated(sk_context* context, float saturate) {
saturate = std::max(saturate, 0.0f);
const auto color_matrix = SkColorMatrix(
0.213 + 0.787 * saturate, 0.715 - 0.715 * saturate, 0.072 - 0.072 * saturate, 0.0, 0.0,
0.213 - 0.213 * saturate, 0.715 + 0.285 * saturate, 0.072 - 0.072 * saturate, 0.0, 0.0,
0.213 - 0.213 * saturate, 0.715 - 0.715 * saturate, 0.072 + 0.928 * saturate, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

void sk_context_filter_sepia(sk_context* context, float sepia) {
sepia = std::max(std::min(sepia, 1.0f), 0.0f);
const auto color_matrix = SkColorMatrix(
0.393 + 0.607 * (sepia), 0.769 - 0.769 * (sepia), 0.189 - 0.189 * (sepia), 0.0, 0.0,
0.349 - 0.349 * (sepia), 0.686 + 0.314 * (sepia), 0.168 - 0.168 * (sepia), 0.0, 0.0,
0.272 - 0.272 * (sepia), 0.534 - 0.534 * (sepia), 0.131 + 0.869 * (sepia), 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0);
auto color_filter = SkColorFilters::Matrix(color_matrix);
context->state->filter = SkImageFilters::ColorFilter(color_filter, context->state->filter);
}

/// CONTEXT_FINALIZER callback
void sk_context_destroy(sk_context* context) {
Expand Down
Loading

0 comments on commit 06053db

Please sign in to comment.