Commit 746380be authored by Caleb Michael Moore's avatar Caleb Michael Moore

adobe blending modes

parent 5e8932a1
......@@ -792,7 +792,9 @@ rsvg_end_filter (RsvgHandle * ctx)
typedef enum
{
normal, multiply, screen, darken, lighten
normal, multiply, screen, darken, lighten, softlight,
hardlight, colordodge, colorburn, overlay, exclusion,
difference
}
RsvgFilterPrimitiveBlendMode;
......@@ -804,46 +806,27 @@ struct _RsvgFilterPrimitiveBlend
GString *in2;
};
static void
rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
RsvgFilterContext * ctx)
static void rsvg_filter_blend(RsvgFilterPrimitiveBlendMode mode, GdkPixbuf *in, GdkPixbuf *in2, GdkPixbuf *output, FPBox boundarys)
{
guchar i;
gint x, y;
gint rowstride, height, width;
FPBox boundarys;
guchar *in_pixels;
guchar *in2_pixels;
guchar *output_pixels;
RsvgFilterPrimitiveBlend *bself;
GdkPixbuf *output;
GdkPixbuf *in;
GdkPixbuf *in2;
bself = (RsvgFilterPrimitiveBlend *) self;
boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
in = rsvg_filter_get_in (self->in, ctx);
in_pixels = gdk_pixbuf_get_pixels (in);
in2 = rsvg_filter_get_in (bself->in2, ctx);
in2_pixels = gdk_pixbuf_get_pixels (in2);
height = gdk_pixbuf_get_height (in);
width = gdk_pixbuf_get_width (in);
rowstride = gdk_pixbuf_get_rowstride (in);
output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
output_pixels = gdk_pixbuf_get_pixels (output);
in_pixels = gdk_pixbuf_get_pixels (in);
in2_pixels = gdk_pixbuf_get_pixels (in2);
for (y = boundarys.y1; y < boundarys.y2; y++)
for (x = boundarys.x1; x < boundarys.x2; x++)
{
double qr, cr, qa, qb, ca, cb;
qa = (double) in_pixels[4 * x + y * rowstride + 3] / 255.0;
qb = (double) in2_pixels[4 * x + y * rowstride + 3] / 255.0;
qr = 1 - (1 - qa) * (1 - qb);
......@@ -852,7 +835,7 @@ rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
{
ca = (double) in_pixels[4 * x + y * rowstride + i] * qa / 255.0;
cb = (double) in2_pixels[4 * x + y * rowstride + i] * qb / 255.0;
switch (bself->mode)
switch (mode)
{
case normal:
cr = (1 - qa) * cb + ca;
......@@ -869,6 +852,42 @@ rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
case lighten:
cr = MAX ((1 - qa) * cb + ca, (1 - qb) * ca + cb);
break;
case softlight:
if (cb < 0.5)
cr = 2 * ca * cb + ca * ca * (1 - 2 * cb);
else
cr = sqrt(ca)*(2*cb-1)+(2*ca)*(1-cb);
break;
case hardlight:
if (cb < 0.5)
cr = 2 * ca * cb;
else
cr = 1 - 2 * (1 - ca) * (1 - cb);
break;
case colordodge:
if (cb == 1)
cr = 1;
else
cr = MIN(ca / (1 - cb), 1);
break;
case colorburn:
if (cb == 0)
cr = 0;
else
cr = MAX(1 - (1 - ca) / cb, 0);
break;
case overlay:
if (ca < 0.5)
cr = 2 * ca * cb;
else
cr = 1 - 2 * (1 - ca) * (1 - cb);
break;
case exclusion:
cr = ca + cb - 2 * ca * cb;
break;
case difference:
cr = abs(ca - cb);
break;
}
cr *= 255.0 / qr;
if (cr > 255)
......@@ -881,6 +900,31 @@ rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
output_pixels[4 * x + y * rowstride + 3] = qr * 255.0;
}
}
static void
rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
RsvgFilterContext * ctx)
{
FPBox boundarys;
RsvgFilterPrimitiveBlend *bself;
GdkPixbuf *output;
GdkPixbuf *in;
GdkPixbuf *in2;
bself = (RsvgFilterPrimitiveBlend *) self;
boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
in = rsvg_filter_get_in (self->in, ctx);
in2 = rsvg_filter_get_in (bself->in2, ctx);
output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, gdk_pixbuf_get_width (in), gdk_pixbuf_get_height (in));
rsvg_filter_blend(bself->mode, in, in2, output, boundarys);
rsvg_filter_store_result (self->result, output, ctx);
g_object_unref (G_OBJECT (in));
......@@ -888,6 +932,61 @@ rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self,
g_object_unref (G_OBJECT (output));
}
void rsvg_filter_adobe_blend(gint modenum, GdkPixbuf *in, GdkPixbuf *bg, GdkPixbuf *output)
{
FPBox boundarys;
RsvgFilterPrimitiveBlendMode mode;
boundarys.x1 = 0;
boundarys.y1 = 0;
boundarys.x2 = gdk_pixbuf_get_width (in);
boundarys.y2 = gdk_pixbuf_get_height (in);
mode = normal;
switch(modenum)
{
case 0:
mode = normal;
break;
case 1:
mode = multiply;
break;
case 2:
mode = screen;
break;
case 3:
mode = darken;
break;
case 4:
mode = lighten;
break;
case 5:
mode = softlight;
break;
case 6:
mode = hardlight;
break;
case 7:
mode = colordodge;
break;
case 8:
mode = colorburn;
break;
case 9:
mode = overlay;
break;
case 10:
mode = exclusion;
break;
case 11:
mode = difference;
break;
}
rsvg_filter_blend(mode, in, bg, output, boundarys);
}
static void
rsvg_filter_primitive_blend_free (RsvgFilterPrimitive * self)
{
......
......@@ -116,6 +116,9 @@ rsvg_start_filter_primitive_specular_lighting (RsvgHandle * ctx, RsvgPropertyBag
void
rsvg_start_filter_primitive_tile (RsvgHandle * ctx, RsvgPropertyBag * atts);
void
rsvg_filter_adobe_blend(gint modenum, GdkPixbuf *in, GdkPixbuf *bg, GdkPixbuf *output);
G_END_DECLS
#endif
......@@ -239,7 +239,7 @@ rsvg_render_bpath (RsvgHandle *ctx, const ArtBpath *bpath)
art_free (affine_bpath);
need_tmpbuf = ((state->fill != NULL) && (state->stroke != NULL) &&
state->opacity != 0xff) || state->filter || state->mask;
state->opacity != 0xff) || rsvg_needs_discrete_layer(state);
if (need_tmpbuf)
rsvg_push_discrete_layer (ctx);
......@@ -439,7 +439,7 @@ rsvg_defs_drawable_use_draw (RsvgDefsDrawable * self, RsvgHandle *ctx,
rsvg_state_reinherit(state, &ctx->state[ctx->n_state - 2]);
}
if (state->opacity != 0xff || state->filter)
if (state->opacity != 0xff || rsvg_needs_discrete_layer(state))
rsvg_push_discrete_layer (ctx);
......@@ -460,7 +460,7 @@ rsvg_defs_drawable_use_draw (RsvgDefsDrawable * self, RsvgHandle *ctx,
ctx->n_state--;
rsvg_state_finalize (&ctx->state[ctx->n_state]);
if (state->opacity != 0xff || state->filter)
if (state->opacity != 0xff || rsvg_needs_discrete_layer(state))
rsvg_pop_discrete_layer (ctx);
}
......
......@@ -66,6 +66,7 @@ rsvg_state_init (RsvgState *state)
art_affine_identity (state->personal_affine);
state->mask = NULL;
state->opacity = 0xff;
state->adobe_blend = 0;
state->fill = rsvg_paint_server_parse (NULL, NULL, "#000", 0);
state->fill_opacity = 0xff;
state->stroke_opacity = 0xff;
......@@ -334,6 +335,35 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str)
}
else if (rsvg_css_param_match (str, "filter"))
state->filter = rsvg_filter_parse(ctx->defs, str + arg_off);
else if (rsvg_css_param_match (str, "adobe-blending-mode"))
{
if (!strcmp (str + arg_off, "normal"))
state->adobe_blend = 0;
else if (!strcmp (str + arg_off, "multiply"))
state->adobe_blend = 1;
else if (!strcmp (str + arg_off, "screen"))
state->adobe_blend = 2;
else if (!strcmp (str + arg_off, "darken"))
state->adobe_blend = 3;
else if (!strcmp (str + arg_off, "lighten"))
state->adobe_blend = 4;
else if (!strcmp (str + arg_off, "softlight"))
state->adobe_blend = 5;
else if (!strcmp (str + arg_off, "hardlight"))
state->adobe_blend = 6;
else if (!strcmp (str + arg_off, "colordodge"))
state->adobe_blend = 7;
else if (!strcmp (str + arg_off, "colorburn"))
state->adobe_blend = 8;
else if (!strcmp (str + arg_off, "overlay"))
state->adobe_blend = 9;
else if (!strcmp (str + arg_off, "exclusion"))
state->adobe_blend = 10;
else if (!strcmp (str + arg_off, "difference"))
state->adobe_blend = 11;
else
state->adobe_blend = 0;
}
else if (rsvg_css_param_match (str, "mask"))
state->mask = rsvg_mask_parse(ctx->defs, str + arg_off);
else if (rsvg_css_param_match (str, "enable-background"))
......@@ -634,6 +664,7 @@ void
rsvg_parse_style_pairs (RsvgHandle *ctx, RsvgState *state,
RsvgPropertyBag *atts)
{
rsvg_lookup_parse_style_pair (ctx, state, "adobe-blending-mode", atts);
rsvg_lookup_parse_style_pair (ctx, state, "color", atts);
rsvg_lookup_parse_style_pair (ctx, state, "display", atts);
rsvg_lookup_parse_style_pair (ctx, state, "enable-background", atts);
......@@ -1226,7 +1257,7 @@ rsvg_push_discrete_layer (RsvgHandle *ctx)
pixbuf = ctx->pixbuf;
if (state->filter == NULL && state->opacity == 0xFF &&
!state->backgroundnew && state->mask == NULL)
!state->backgroundnew && state->mask == NULL && !state->adobe_blend)
return;
state->save_pixbuf = pixbuf;
......@@ -1380,11 +1411,12 @@ rsvg_composite_layer(RsvgHandle *ctx, RsvgState *state, GdkPixbuf *tos, GdkPixbu
GdkPixbuf *intermediate;
GdkPixbuf *in, *out, *insidebg;
int operationsleft;
gint adobe_blend = state->adobe_blend;
intermediate = NULL;
if (state->filter == NULL && state->opacity == 0xFF &&
!state->backgroundnew && state->mask == NULL)
!state->backgroundnew && state->mask == NULL && !state->adobe_blend)
return;
operationsleft = 0;
......@@ -1395,6 +1427,8 @@ rsvg_composite_layer(RsvgHandle *ctx, RsvgState *state, GdkPixbuf *tos, GdkPixbu
operationsleft++;
if (mask != NULL)
operationsleft++;
if (adobe_blend)
operationsleft++;
if (operationsleft > 1)
intermediate = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 1, 8,
......@@ -1408,12 +1442,17 @@ rsvg_composite_layer(RsvgHandle *ctx, RsvgState *state, GdkPixbuf *tos, GdkPixbu
rsvg_use_opacity (ctx, 0xFF, tos, nos);
}
if (filter != NULL || adobe_blend)
{
insidebg = rsvg_compile_bg(ctx, state);
}
else
insidebg = NULL;
if (filter != NULL)
{
out = get_next_out(&operationsleft, in, tos, nos, intermediate);
insidebg = rsvg_compile_bg(ctx, state);
rsvg_filter_render (filter, in, out, insidebg, ctx);
g_object_unref (insidebg);
in = out;
}
if (opacity != 0xFF)
......@@ -1428,6 +1467,17 @@ rsvg_composite_layer(RsvgHandle *ctx, RsvgState *state, GdkPixbuf *tos, GdkPixbu
rsvg_mask_render ((RsvgMask *)mask, in, out, ctx);
in = out;
}
if (adobe_blend)
{
out = get_next_out(&operationsleft, in, tos, nos, intermediate);
rsvg_filter_adobe_blend (adobe_blend, in, insidebg, out);
in = out;
}
if (filter != NULL || adobe_blend)
{
g_object_unref (insidebg);
}
if (intermediate != NULL)
g_object_unref (intermediate);
......@@ -1451,7 +1501,7 @@ rsvg_pop_discrete_layer(RsvgHandle *ctx)
state = rsvg_state_current(ctx);
if (state->filter == NULL && state->opacity == 0xFF &&
!state->backgroundnew && state->mask == NULL)
!state->backgroundnew && state->mask == NULL && !state->adobe_blend)
return;
tos = ctx->pixbuf;
......@@ -1464,6 +1514,12 @@ rsvg_pop_discrete_layer(RsvgHandle *ctx)
ctx->pixbuf = nos;
}
gboolean
rsvg_needs_discrete_layer(RsvgState *state)
{
return state->filter || state->mask || state->adobe_blend || state->backgroundnew;
}
RsvgState *
rsvg_state_current (RsvgHandle *ctx)
{
......
......@@ -72,6 +72,7 @@ struct _RsvgState {
RsvgFilter *filter;
void *mask;
gboolean backgroundnew;
gint adobe_blend;
RsvgPaintServer *stroke;
gboolean has_stroke_server;
......@@ -149,6 +150,8 @@ gdouble rsvg_viewport_percentage (gdouble width, gdouble height);
void
rsvg_pop_discrete_layer(RsvgHandle *ctx);
void rsvg_push_discrete_layer (RsvgHandle *ctx);
gboolean
rsvg_needs_discrete_layer(RsvgState *state);
gboolean rsvg_parse_transform (double dst[6], const char *src);
RsvgState * rsvg_state_parent (RsvgHandle *ctx);
......
......@@ -750,18 +750,6 @@ rsvg_start_style (RsvgHandle *ctx, RsvgPropertyBag *atts)
ctx->handler = &handler->super;
}
/* start defs */
static void
rsvg_defs_handler_free (RsvgSaxHandler *self)
{
g_free (self);
}
static void
rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len)
{
}
static void
rsvg_filter_handler_start (RsvgHandle *ctx, const xmlChar *name,
......@@ -819,119 +807,6 @@ rsvg_filter_handler_start (RsvgHandle *ctx, const xmlChar *name,
rsvg_start_filter_primitive_component_transfer_function(ctx, atts, 'a');
}
static void
rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name,
RsvgPropertyBag *atts)
{
RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self;
RsvgHandle *ctx = z->ctx;
/* push the state stack */
if (ctx->n_state == ctx->n_state_max)
ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1);
if (ctx->n_state)
{
rsvg_state_inherit (&ctx->state[ctx->n_state],
&ctx->state[ctx->n_state - 1]);
}
else
rsvg_state_init (ctx->state);
ctx->n_state++;
/*
* conicalGradient isn't in the SVG spec and I'm not sure exactly what it does. libart definitely
* has no analogue. But it does seem similar enough to a radialGradient that i'd rather get the
* onscreen representation of the colour wrong than not have any colour displayed whatsoever
*/
if (!strcmp ((char *)name, "defs"))
ctx->in_defs++;
else if (!strcmp ((char *)name, "linearGradient"))
rsvg_start_linear_gradient (ctx, atts);
else if (!strcmp ((char *)name, "radialGradient"))
rsvg_start_radial_gradient (ctx, atts, "radialGradient");
else if (!strcmp((char *)name, "conicalGradient"))
rsvg_start_radial_gradient (ctx, atts, "conicalGradient");
else if (!strcmp ((char *)name, "style"))
rsvg_start_style (ctx, atts);
else if (!strcmp ((char *)name, "g"))
rsvg_start_g (ctx, atts);
else if (!strcmp ((char *)name, "symbol"))
{
ctx->in_defs++;
rsvg_start_g (ctx, atts);
}
else if (!strcmp ((char *)name, "use"))
rsvg_start_use (ctx, atts);
else if (!strcmp ((char *)name, "path"))
rsvg_start_path (ctx, atts);
else if (!strcmp ((char *)name, "line"))
rsvg_start_line (ctx, atts);
else if (!strcmp ((char *)name, "rect"))
rsvg_start_rect (ctx, atts);
else if (!strcmp ((char *)name, "circle"))
rsvg_start_circle (ctx, atts);
else if (!strcmp ((char *)name, "ellipse"))
rsvg_start_ellipse (ctx, atts);
else if (!strcmp ((char *)name, "polygon"))
rsvg_start_polygon (ctx, atts);
else if (!strcmp ((char *)name, "polyline"))
rsvg_start_polyline (ctx, atts);
else if (!strcmp ((char *)name, "mask"))
rsvg_start_mask(ctx, atts);
rsvg_filter_handler_start (ctx, name, atts);
}
static void
rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name)
{
RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self;
RsvgHandle *ctx = z->ctx;
if (!strcmp((char *)name, "defs"))
{
if (ctx->handler != NULL)
{
ctx->handler->free (ctx->handler);
ctx->handler = NULL;
}
ctx->in_defs--;
}
if (!strcmp ((char *)name, "g"))
rsvg_end_g (ctx);
else if (!strcmp ((char *)name, "symbol"))
{
ctx->in_defs--;
rsvg_end_g (ctx);
}
else if (!strcmp ((char *)name, "filter"))
rsvg_end_filter (ctx);
else if (!strcmp ((char *)name, "mask"))
rsvg_end_mask(ctx);
/* pop the state stack */
ctx->n_state--;
rsvg_state_finalize (&ctx->state[ctx->n_state]);
}
static void
rsvg_start_defs (RsvgHandle *ctx, RsvgPropertyBag *atts)
{
RsvgSaxHandlerDefs *handler = g_new0 (RsvgSaxHandlerDefs, 1);
handler->super.free = rsvg_defs_handler_free;
handler->super.characters = rsvg_defs_handler_characters;
handler->super.start_element = rsvg_defs_handler_start;
handler->super.end_element = rsvg_defs_handler_end;
handler->ctx = ctx;
ctx->in_defs++;
ctx->handler = &handler->super;
}
/* end defs */
/* start desc */
static void
......@@ -1085,9 +960,9 @@ rsvg_start_title (RsvgHandle *ctx, RsvgPropertyBag *atts)
}
/* end title */
static void
rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
rsvg_start_element (void *data, const xmlChar *name,
const xmlChar ** atts)
{
RsvgHandle *ctx = (RsvgHandle *)data;
......@@ -1125,7 +1000,7 @@ rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
rsvg_start_g (ctx, bag);
}
else if (!strcmp ((char *)name, "defs"))
rsvg_start_defs (ctx, bag);
ctx->in_defs++;
else if (!strcmp ((char *)name, "path"))
rsvg_start_path (ctx, bag);
else if (!strcmp ((char *)name, "line"))
......@@ -1172,6 +1047,19 @@ rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
rsvg_property_bag_free(bag);
}
static void
rsvg_start_elementns (void *data, const xmlChar *name,
const xmlChar * prefix,
const xmlChar * URI,
int nb_namespaces,
const xmlChar ** namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar ** atts)
{
rsvg_start_element (data, name, atts);
}
static void
rsvg_end_element (void *data, const xmlChar *name)
{
......@@ -1213,6 +1101,14 @@ rsvg_end_element (void *data, const xmlChar *name)
}
}
static void
rsvg_end_elementns (void *data, const xmlChar *name,
const xmlChar * prefix,
const xmlChar * URI)
{
rsvg_end_element (data, name);
}
static void
rsvg_characters (void *data, const xmlChar *ch, int len)
{
......@@ -1264,36 +1160,21 @@ rsvg_error_cb (void *data, const char *msg, ...)
va_end (args);
}
static xmlSAXHandler rsvgSAXHandlerStruct = {
NULL, /* internalSubset */
NULL, /* isStandalone */
NULL, /* hasInternalSubset */
NULL, /* hasExternalSubset */
NULL, /* resolveEntity */
rsvg_get_entity, /* getEntity */
rsvg_entity_decl, /* entityDecl */
NULL, /* notationDecl */
NULL, /* attributeDecl */
NULL, /* elementDecl */
NULL, /* unparsedEntityDecl */
NULL, /* setDocumentLocator */
NULL, /* startDocument */
NULL, /* endDocument */
rsvg_start_element, /* startElement */
rsvg_end_element, /* endElement */
NULL, /* reference */
rsvg_characters, /* characters */
NULL, /* ignorableWhitespace */
NULL, /* processingInstruction */
NULL, /* comment */
NULL, /* xmlParserWarning */
rsvg_error_cb, /* xmlParserError */
rsvg_error_cb, /* xmlParserFatalError */
NULL, /* getParameterEntity */
rsvg_characters, /* cdataCallback */
NULL /* externalSubset */
/* initialized */
};
static xmlSAXHandler rsvgSAXHandlerStruct;
static void rsvg_SAX_handler_struct_init()
{
rsvgSAXHandlerStruct.getEntity = rsvg_get_entity;
rsvgSAXHandlerStruct.entityDecl = rsvg_entity_decl;
rsvgSAXHandlerStruct.characters = rsvg_characters;
rsvgSAXHandlerStruct.error = rsvg_error_cb;
rsvgSAXHandlerStruct.error = rsvg_error_cb;
rsvgSAXHandlerStruct.cdataBlock = rsvg_characters;
rsvgSAXHandlerStruct.startElement = rsvg_start_element;
rsvgSAXHandlerStruct.endElement = rsvg_end_element;
rsvgSAXHandlerStruct.startElementNs = rsvg_start_elementns;
rsvgSAXHandlerStruct.endElementNs = rsvg_end_elementns;
}
/**
* rsvg_error_quark
......@@ -1470,6 +1351,7 @@ rsvg_handle_init (RsvgHandle * handle)
handle->css_props = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
rsvg_SAX_handler_struct_init();
handle->ctxt = NULL;
handle->current_defs_group = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment