|
|
@ -221,6 +221,208 @@ Context2d::restorePath() { |
|
|
|
cairo_append_path(_context, _path); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill and apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::fill(bool preserve) { |
|
|
|
setSourceRGBA(state->fill); |
|
|
|
if (preserve) { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_fill_preserve) |
|
|
|
: cairo_fill_preserve(_context); |
|
|
|
} else { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_fill) |
|
|
|
: cairo_fill(_context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Stroke and apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::stroke(bool preserve) { |
|
|
|
setSourceRGBA(state->stroke); |
|
|
|
if (preserve) { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_stroke_preserve) |
|
|
|
: cairo_stroke_preserve(_context); |
|
|
|
} else { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_stroke) |
|
|
|
: cairo_stroke(_context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply shadow with the given draw fn. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadow(void (fn)(cairo_t *cr)) { |
|
|
|
cairo_path_t *path = cairo_copy_path_flat(_context); |
|
|
|
cairo_save(_context); |
|
|
|
|
|
|
|
// Offset
|
|
|
|
cairo_translate( |
|
|
|
_context |
|
|
|
, state->shadowOffsetX |
|
|
|
, state->shadowOffsetY); |
|
|
|
|
|
|
|
// Apply shadow
|
|
|
|
cairo_push_group(_context); |
|
|
|
cairo_new_path(_context); |
|
|
|
cairo_append_path(_context, path); |
|
|
|
setSourceRGBA(state->shadow); |
|
|
|
fn(_context); |
|
|
|
|
|
|
|
// No need to invoke blur if shadowBlur is 0
|
|
|
|
if (state->shadowBlur) { |
|
|
|
blur(cairo_get_group_target(_context), state->shadowBlur); |
|
|
|
} |
|
|
|
|
|
|
|
// Paint the shadow
|
|
|
|
cairo_pop_group_to_source(_context); |
|
|
|
cairo_paint(_context); |
|
|
|
|
|
|
|
// Restore state
|
|
|
|
cairo_restore(_context); |
|
|
|
cairo_new_path(_context); |
|
|
|
cairo_append_path(_context, path); |
|
|
|
fn(_context); |
|
|
|
|
|
|
|
cairo_path_destroy(path); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Set source RGBA. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::setSourceRGBA(rgba_t color) { |
|
|
|
cairo_set_source_rgba( |
|
|
|
_context |
|
|
|
, color.r |
|
|
|
, color.g |
|
|
|
, color.b |
|
|
|
, color.a * state->globalAlpha); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Start shadow context. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadowStart() { |
|
|
|
savePath(); |
|
|
|
cairo_save(_context); |
|
|
|
cairo_translate( |
|
|
|
_context |
|
|
|
, state->shadowOffsetX |
|
|
|
, state->shadowOffsetY); |
|
|
|
cairo_push_group(_context); |
|
|
|
setSourceRGBA(state->shadow); |
|
|
|
restorePath(); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadowApply() { |
|
|
|
savePath(); |
|
|
|
if (state->shadowBlur) { |
|
|
|
blur(cairo_get_group_target(_context), state->shadowBlur); |
|
|
|
} |
|
|
|
cairo_pop_group_to_source(_context); |
|
|
|
cairo_paint(_context); |
|
|
|
cairo_restore(_context); |
|
|
|
restorePath(); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the context has a drawable shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
bool |
|
|
|
Context2d::hasShadow() { |
|
|
|
return state->shadow.a |
|
|
|
&& (state->shadowBlur || state->shadowOffsetX || state->shadowOffsetX); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Blur the given surface with the given radius. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::blur(cairo_surface_t *surface, int radius) { |
|
|
|
// Steve Hanov, 2009
|
|
|
|
// Released into the public domain.
|
|
|
|
|
|
|
|
// get width, height
|
|
|
|
int width = cairo_image_surface_get_width( surface ); |
|
|
|
int height = cairo_image_surface_get_height( surface ); |
|
|
|
unsigned char* dst = (unsigned char*)malloc(width*height*4); |
|
|
|
unsigned* precalc = |
|
|
|
(unsigned*)malloc(width*height*sizeof(unsigned)); |
|
|
|
unsigned char* src = cairo_image_surface_get_data( surface ); |
|
|
|
double mul=1.f/((radius*2)*(radius*2)); |
|
|
|
int channel; |
|
|
|
|
|
|
|
// The number of times to perform the averaging. According to wikipedia,
|
|
|
|
// three iterations is good enough to pass for a gaussian.
|
|
|
|
const int MAX_ITERATIONS = 3; |
|
|
|
int iteration; |
|
|
|
|
|
|
|
memcpy( dst, src, width*height*4 ); |
|
|
|
|
|
|
|
for ( iteration = 0; iteration < MAX_ITERATIONS; iteration++ ) { |
|
|
|
for( channel = 0; channel < 4; channel++ ) { |
|
|
|
int x,y; |
|
|
|
|
|
|
|
// precomputation step.
|
|
|
|
unsigned char* pix = src; |
|
|
|
unsigned* pre = precalc; |
|
|
|
|
|
|
|
pix += channel; |
|
|
|
for (y=0;y<height;y++) { |
|
|
|
for (x=0;x<width;x++) { |
|
|
|
int tot=pix[0]; |
|
|
|
if (x>0) tot+=pre[-1]; |
|
|
|
if (y>0) tot+=pre[-width]; |
|
|
|
if (x>0 && y>0) tot-=pre[-width-1]; |
|
|
|
*pre++=tot; |
|
|
|
pix += 4; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// blur step.
|
|
|
|
pix = dst + (int)radius * width * 4 + (int)radius * 4 + channel; |
|
|
|
for (y=radius;y<height-radius;y++) { |
|
|
|
for (x=radius;x<width-radius;x++) { |
|
|
|
int l = x < radius ? 0 : x - radius; |
|
|
|
int t = y < radius ? 0 : y - radius; |
|
|
|
int r = x + radius >= width ? width - 1 : x + radius; |
|
|
|
int b = y + radius >= height ? height - 1 : y + radius; |
|
|
|
int tot = precalc[r+b*width] + precalc[l+t*width] - |
|
|
|
precalc[l+b*width] - precalc[r+t*width]; |
|
|
|
*pix=(unsigned char)(tot*mul); |
|
|
|
pix += 4; |
|
|
|
} |
|
|
|
pix += (int)radius * 2 * 4; |
|
|
|
} |
|
|
|
} |
|
|
|
memcpy( src, dst, width*height*4 ); |
|
|
|
} |
|
|
|
|
|
|
|
free( dst ); |
|
|
|
free( precalc ); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Get global alpha. |
|
|
|
*/ |
|
|
@ -1141,205 +1343,3 @@ Context2d::Arc(const Arguments &args) { |
|
|
|
|
|
|
|
return Undefined(); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill and apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::fill(bool preserve) { |
|
|
|
setSourceRGBA(state->fill); |
|
|
|
if (preserve) { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_fill_preserve) |
|
|
|
: cairo_fill_preserve(_context); |
|
|
|
} else { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_fill) |
|
|
|
: cairo_fill(_context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Stroke and apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::stroke(bool preserve) { |
|
|
|
setSourceRGBA(state->stroke); |
|
|
|
if (preserve) { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_stroke_preserve) |
|
|
|
: cairo_stroke_preserve(_context); |
|
|
|
} else { |
|
|
|
hasShadow() |
|
|
|
? shadow(cairo_stroke) |
|
|
|
: cairo_stroke(_context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply shadow with the given draw fn. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadow(void (fn)(cairo_t *cr)) { |
|
|
|
cairo_path_t *path = cairo_copy_path_flat(_context); |
|
|
|
cairo_save(_context); |
|
|
|
|
|
|
|
// Offset
|
|
|
|
cairo_translate( |
|
|
|
_context |
|
|
|
, state->shadowOffsetX |
|
|
|
, state->shadowOffsetY); |
|
|
|
|
|
|
|
// Apply shadow
|
|
|
|
cairo_push_group(_context); |
|
|
|
cairo_new_path(_context); |
|
|
|
cairo_append_path(_context, path); |
|
|
|
setSourceRGBA(state->shadow); |
|
|
|
fn(_context); |
|
|
|
|
|
|
|
// No need to invoke blur if shadowBlur is 0
|
|
|
|
if (state->shadowBlur) { |
|
|
|
blur(cairo_get_group_target(_context), state->shadowBlur); |
|
|
|
} |
|
|
|
|
|
|
|
// Paint the shadow
|
|
|
|
cairo_pop_group_to_source(_context); |
|
|
|
cairo_paint(_context); |
|
|
|
|
|
|
|
// Restore state
|
|
|
|
cairo_restore(_context); |
|
|
|
cairo_new_path(_context); |
|
|
|
cairo_append_path(_context, path); |
|
|
|
fn(_context); |
|
|
|
|
|
|
|
cairo_path_destroy(path); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Set source RGBA. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::setSourceRGBA(rgba_t color) { |
|
|
|
cairo_set_source_rgba( |
|
|
|
_context |
|
|
|
, color.r |
|
|
|
, color.g |
|
|
|
, color.b |
|
|
|
, color.a * state->globalAlpha); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Start shadow context. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadowStart() { |
|
|
|
savePath(); |
|
|
|
cairo_save(_context); |
|
|
|
cairo_translate( |
|
|
|
_context |
|
|
|
, state->shadowOffsetX |
|
|
|
, state->shadowOffsetY); |
|
|
|
cairo_push_group(_context); |
|
|
|
setSourceRGBA(state->shadow); |
|
|
|
restorePath(); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::shadowApply() { |
|
|
|
savePath(); |
|
|
|
if (state->shadowBlur) { |
|
|
|
blur(cairo_get_group_target(_context), state->shadowBlur); |
|
|
|
} |
|
|
|
cairo_pop_group_to_source(_context); |
|
|
|
cairo_paint(_context); |
|
|
|
cairo_restore(_context); |
|
|
|
restorePath(); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the context has a drawable shadow. |
|
|
|
*/ |
|
|
|
|
|
|
|
bool |
|
|
|
Context2d::hasShadow() { |
|
|
|
return state->shadow.a |
|
|
|
&& (state->shadowBlur || state->shadowOffsetX || state->shadowOffsetX); |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Blur the given surface with the given radius. |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
Context2d::blur(cairo_surface_t *surface, int radius) { |
|
|
|
// Steve Hanov, 2009
|
|
|
|
// Released into the public domain.
|
|
|
|
|
|
|
|
// get width, height
|
|
|
|
int width = cairo_image_surface_get_width( surface ); |
|
|
|
int height = cairo_image_surface_get_height( surface ); |
|
|
|
unsigned char* dst = (unsigned char*)malloc(width*height*4); |
|
|
|
unsigned* precalc = |
|
|
|
(unsigned*)malloc(width*height*sizeof(unsigned)); |
|
|
|
unsigned char* src = cairo_image_surface_get_data( surface ); |
|
|
|
double mul=1.f/((radius*2)*(radius*2)); |
|
|
|
int channel; |
|
|
|
|
|
|
|
// The number of times to perform the averaging. According to wikipedia,
|
|
|
|
// three iterations is good enough to pass for a gaussian.
|
|
|
|
const int MAX_ITERATIONS = 3; |
|
|
|
int iteration; |
|
|
|
|
|
|
|
memcpy( dst, src, width*height*4 ); |
|
|
|
|
|
|
|
for ( iteration = 0; iteration < MAX_ITERATIONS; iteration++ ) { |
|
|
|
for( channel = 0; channel < 4; channel++ ) { |
|
|
|
int x,y; |
|
|
|
|
|
|
|
// precomputation step.
|
|
|
|
unsigned char* pix = src; |
|
|
|
unsigned* pre = precalc; |
|
|
|
|
|
|
|
pix += channel; |
|
|
|
for (y=0;y<height;y++) { |
|
|
|
for (x=0;x<width;x++) { |
|
|
|
int tot=pix[0]; |
|
|
|
if (x>0) tot+=pre[-1]; |
|
|
|
if (y>0) tot+=pre[-width]; |
|
|
|
if (x>0 && y>0) tot-=pre[-width-1]; |
|
|
|
*pre++=tot; |
|
|
|
pix += 4; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// blur step.
|
|
|
|
pix = dst + (int)radius * width * 4 + (int)radius * 4 + channel; |
|
|
|
for (y=radius;y<height-radius;y++) { |
|
|
|
for (x=radius;x<width-radius;x++) { |
|
|
|
int l = x < radius ? 0 : x - radius; |
|
|
|
int t = y < radius ? 0 : y - radius; |
|
|
|
int r = x + radius >= width ? width - 1 : x + radius; |
|
|
|
int b = y + radius >= height ? height - 1 : y + radius; |
|
|
|
int tot = precalc[r+b*width] + precalc[l+t*width] - |
|
|
|
precalc[l+b*width] - precalc[r+t*width]; |
|
|
|
*pix=(unsigned char)(tot*mul); |
|
|
|
pix += 4; |
|
|
|
} |
|
|
|
pix += (int)radius * 2 * 4; |
|
|
|
} |
|
|
|
} |
|
|
|
memcpy( src, dst, width*height*4 ); |
|
|
|
} |
|
|
|
|
|
|
|
free( dst ); |
|
|
|
free( precalc ); |
|
|
|
} |
|
|
|