Skip to content

Commit

Permalink
fix repeat-x/y support in createPattern()
Browse files Browse the repository at this point in the history
  • Loading branch information
calvinsomething authored and zbjornson committed Jul 27, 2022
1 parent f8d4949 commit c6a1546
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
* `rgba(r,g,b)` with no alpha should parse as opaque, not transparent. ([#2029](https://github.com/Automattic/node-canvas/issues/2029))
* Typo in `PngConfig.filters` types. ([#2072](https://github.com/Automattic/node-canvas/issues/2072))
* `createPattern()` always used "repeat" mode; now supports "repeat-x" and "repeat-y". ([#2066](https://github.com/Automattic/node-canvas/issues/2066))

2.9.3
==================
Expand Down
32 changes: 31 additions & 1 deletion src/CanvasRenderingContext2d.cc
Expand Up @@ -367,6 +367,7 @@ Context2d::setFillRule(v8::Local<v8::Value> value) {
void
Context2d::fill(bool preserve) {
cairo_pattern_t *new_pattern;
bool needsRestore = false;
if (state->fillPattern) {
if (state->globalAlpha < 1) {
new_pattern = create_transparent_pattern(state->fillPattern, state->globalAlpha);
Expand All @@ -381,10 +382,36 @@ Context2d::fill(bool preserve) {
cairo_set_source(_context, state->fillPattern);
}
repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->fillPattern);
if (NO_REPEAT == repeat) {
if (repeat == NO_REPEAT) {
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE);
} else if (repeat == REPEAT) {
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
} else {
cairo_save(_context);
cairo_path_t *savedPath = cairo_copy_path(_context);
cairo_surface_t *patternSurface = nullptr;
cairo_pattern_get_surface(cairo_get_source(_context), &patternSurface);

double width, height;
if (repeat == REPEAT_X) {
double x1, x2;
cairo_path_extents(_context, &x1, nullptr, &x2, nullptr);
width = x2 - x1;
height = cairo_image_surface_get_height(patternSurface);
} else {
double y1, y2;
cairo_path_extents(_context, nullptr, &y1, nullptr, &y2);
width = cairo_image_surface_get_width(patternSurface);
height = y2 - y1;
}

cairo_new_path(_context);
cairo_rectangle(_context, 0, 0, width, height);
cairo_clip(_context);
cairo_append_path(_context, savedPath);
cairo_path_destroy(savedPath);
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
needsRestore = true;
}
} else if (state->fillGradient) {
if (state->globalAlpha < 1) {
Expand Down Expand Up @@ -412,6 +439,9 @@ Context2d::fill(bool preserve) {
? shadow(cairo_fill)
: cairo_fill(_context);
}
if (needsRestore) {
cairo_restore(_context);
}
}

/*
Expand Down
18 changes: 18 additions & 0 deletions test/public/tests.js
Expand Up @@ -471,6 +471,24 @@ tests['createPattern() with globalAlpha'] = function (ctx, done) {
img.src = imageSrc('face.jpeg')
}

tests['createPattern() repeat-x and repeat-y'] = function (ctx, done) {
const img = new Image()
img.onload = function () {
ctx.scale(0.1, 0.1)
ctx.lineStyle = 'black'
ctx.lineWidth = 10
ctx.fillStyle = ctx.createPattern(img, 'repeat-x')
ctx.fillRect(0, 0, 900, 900)
ctx.strokeRect(0, 0, 900, 900)
ctx.translate(1000, 1000)
ctx.fillStyle = ctx.createPattern(img, 'repeat-y')
ctx.fillRect(0, 0, 900, 900)
ctx.strokeRect(0, 0, 900, 900)
done()
}
img.src = imageSrc('face.jpeg')
}

tests['createPattern() no-repeat'] = function (ctx, done) {
const img = new Image()
img.onload = function () {
Expand Down

0 comments on commit c6a1546

Please sign in to comment.