-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for context globalAlpha for gradients and patterns #1064
Changes from all commits
5875be3
b5f6545
74e9250
e00aa8b
009c632
e3cdd85
931c0d3
ac4dd37
5e9b4e6
93dab0c
a5c2e74
b2be100
5e5614a
c894c8b
e679840
803a24e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -292,6 +292,67 @@ Context2d::restorePath() { | |
cairo_path_destroy(_path); | ||
} | ||
|
||
/* | ||
* Create temporary surface for gradient or pattern transparency | ||
*/ | ||
cairo_pattern_t* | ||
create_transparent_gradient(cairo_pattern_t *source, float alpha) { | ||
double x0; | ||
double y0; | ||
double x1; | ||
double y1; | ||
double r0; | ||
double r1; | ||
int count; | ||
int i; | ||
double offset; | ||
double r; | ||
double g; | ||
double b; | ||
double a; | ||
cairo_pattern_t *newGradient; | ||
cairo_pattern_type_t type = cairo_pattern_get_type(source); | ||
cairo_pattern_get_color_stop_count(source, &count); | ||
if (type == CAIRO_PATTERN_TYPE_LINEAR) { | ||
cairo_pattern_get_linear_points (source, &x0, &y0, &x1, &y1); | ||
newGradient = cairo_pattern_create_linear(x0, y0, x1, y1); | ||
} else if (type == CAIRO_PATTERN_TYPE_RADIAL) { | ||
cairo_pattern_get_radial_circles(source, &x0, &y0, &r0, &x1, &y1, &r1); | ||
newGradient = cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1); | ||
} else { | ||
Nan::ThrowError("Unexpected gradient type"); | ||
return NULL; | ||
} | ||
for ( i = 0; i < count; i++ ) { | ||
cairo_pattern_get_color_stop_rgba(source, i, &offset, &r, &g, &b, &a); | ||
cairo_pattern_add_color_stop_rgba(newGradient, offset, r, g, b, a * alpha); | ||
} | ||
return newGradient; | ||
} | ||
|
||
cairo_pattern_t* | ||
create_transparent_pattern(cairo_pattern_t *source, float alpha) { | ||
cairo_surface_t *surface; | ||
cairo_pattern_get_surface(source, &surface); | ||
int width = cairo_image_surface_get_width(surface); | ||
int height = cairo_image_surface_get_height(surface); | ||
cairo_surface_t *mask_surface = cairo_image_surface_create( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still don't see this being freed... calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i thought that since the pattern is alive and to be used you have to keep the surface. |
||
CAIRO_FORMAT_ARGB32, | ||
width, | ||
height); | ||
cairo_t *mask_context = cairo_create(mask_surface); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You should also check |
||
if (cairo_status(mask_context) != CAIRO_STATUS_SUCCESS) { | ||
Nan::ThrowError("Failed to initialize context"); | ||
return NULL; | ||
} | ||
cairo_set_source(mask_context, source); | ||
cairo_paint_with_alpha(mask_context, alpha); | ||
cairo_destroy(mask_context); | ||
cairo_pattern_t* newPattern = cairo_pattern_create_for_surface(mask_surface); | ||
cairo_surface_destroy(mask_surface); | ||
return newPattern; | ||
} | ||
|
||
/* | ||
* Fill and apply shadow. | ||
*/ | ||
|
@@ -310,22 +371,42 @@ Context2d::setFillRule(v8::Local<v8::Value> value) { | |
|
||
void | ||
Context2d::fill(bool preserve) { | ||
cairo_pattern_t *new_pattern; | ||
if (state->fillPattern) { | ||
cairo_set_source(_context, state->fillPattern); | ||
if (state->globalAlpha < 1) { | ||
new_pattern = create_transparent_pattern(state->fillPattern, state->globalAlpha); | ||
if (new_pattern == NULL) { | ||
// failed to allocate; Nan::ThrowError has already been called, so return from this fn. | ||
return; | ||
} | ||
cairo_set_source(_context, new_pattern); | ||
cairo_pattern_destroy(new_pattern); | ||
} else { | ||
cairo_set_source(_context, state->fillPattern); | ||
} | ||
repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->fillPattern); | ||
if (NO_REPEAT == repeat) { | ||
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE); | ||
} else { | ||
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT); | ||
} | ||
// TODO repeat-x/repeat-y | ||
} else if (state->fillGradient) { | ||
cairo_pattern_set_filter(state->fillGradient, state->patternQuality); | ||
cairo_set_source(_context, state->fillGradient); | ||
if (state->globalAlpha < 1) { | ||
new_pattern = create_transparent_gradient(state->fillGradient, state->globalAlpha); | ||
if (new_pattern == NULL) { | ||
// failed to recognize gradient; Nan::ThrowError has already been called, so return from this fn. | ||
return; | ||
} | ||
cairo_pattern_set_filter(new_pattern, state->patternQuality); | ||
cairo_set_source(_context, new_pattern); | ||
cairo_pattern_destroy(new_pattern); | ||
} else { | ||
cairo_pattern_set_filter(state->fillGradient, state->patternQuality); | ||
cairo_set_source(_context, state->fillGradient); | ||
} | ||
} else { | ||
setSourceRGBA(state->fill); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't comment far down enough, but after the |
||
if (preserve) { | ||
hasShadow() | ||
? shadow(cairo_fill_preserve) | ||
|
@@ -343,17 +424,39 @@ Context2d::fill(bool preserve) { | |
|
||
void | ||
Context2d::stroke(bool preserve) { | ||
cairo_pattern_t *new_pattern; | ||
if (state->strokePattern) { | ||
cairo_set_source(_context, state->strokePattern); | ||
if (state->globalAlpha < 1) { | ||
new_pattern = create_transparent_pattern(state->strokePattern, state->globalAlpha); | ||
if (new_pattern == NULL) { | ||
// failed to allocate; Nan::ThrowError has already been called, so return from this fn. | ||
return; | ||
} | ||
cairo_set_source(_context, new_pattern); | ||
cairo_pattern_destroy(new_pattern); | ||
} else { | ||
cairo_set_source(_context, state->strokePattern); | ||
} | ||
repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->strokePattern); | ||
if (NO_REPEAT == repeat) { | ||
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE); | ||
} else { | ||
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT); | ||
} | ||
} else if (state->strokeGradient) { | ||
cairo_pattern_set_filter(state->strokeGradient, state->patternQuality); | ||
cairo_set_source(_context, state->strokeGradient); | ||
if (state->globalAlpha < 1) { | ||
new_pattern = create_transparent_gradient(state->strokeGradient, state->globalAlpha); | ||
if (new_pattern == NULL) { | ||
// failed to recognize gradient; Nan::ThrowError has already been called, so return from this fn. | ||
return; | ||
} | ||
cairo_pattern_set_filter(new_pattern, state->patternQuality); | ||
cairo_set_source(_context, new_pattern); | ||
cairo_pattern_destroy(new_pattern); | ||
} else { | ||
cairo_pattern_set_filter(state->strokeGradient, state->patternQuality); | ||
cairo_set_source(_context, state->strokeGradient); | ||
} | ||
} else { | ||
setSourceRGBA(state->stroke); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember exactly how this works but it feels like there should be a
return
statement here:Hmm, actually, possibly that needs to be guarded at the call-site as well? I don't see how
Nan::ThrowError
can alter the normal flow of a c++ function, but maybe I'm missing something 🤔There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if i only knew c++... i have C knowledge coming from embedded platforms with proprietary api. I stop there.
How can i test it? i can force a gradient type comparision and see what happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that sounds good 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh by the way using nullptr kills nodejs < 4.