From: Søren Sandmann Pedersen <ssp@xxxxxxxxxx> Most desktop applications on Linux rely on the X11 Render extensions for image compositing, which is poorly supported in SPICE. This patch implements support for a compositing command that supports the image compositing features of Render. The pixman library that SPICE is using already contains an implementation of Render, so this patch mainly consists of adding a new composite method to the canvas interface, and then implementing that method with the pixman_image_composite32() function. --- common/Makefile.am | 2 +- common/canvas_base.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++-- common/canvas_base.h | 3 +- common/draw.h | 20 ++++++++ common/messages.h | 5 ++ common/sw_canvas.c | 20 +++++++- spice-protocol | 2 +- spice.proto | 56 ++++++++++++++++++++- 8 files changed, 231 insertions(+), 11 deletions(-) diff --git a/common/Makefile.am b/common/Makefile.am index 5f2c6e5..0e335b1 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -115,7 +115,7 @@ generated_client_marshallers1.c: $(top_srcdir)/spice1.proto $(MARSHALLERS_DEPS) generated_server_demarshallers.c: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS) $(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-demarshallers --server --include messages.h $< $@ >/dev/null -STRUCTS = -M String -M Rect -M Point -M DisplayBase -M Fill -M Opaque -M Copy -M Blend -M Blackness -M Whiteness -M Invers -M Rop3 -M Stroke -M Text -M Transparent -M AlphaBlend +STRUCTS = -M String -M Rect -M Point -M DisplayBase -M Fill -M Opaque -M Copy -M Blend -M Blackness -M Whiteness -M Invers -M Rop3 -M Stroke -M Text -M Transparent -M AlphaBlend -M Composite generated_server_marshallers.c: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS) $(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers $(STRUCTS) --server --include messages.h $< $@ >/dev/null diff --git a/common/canvas_base.c b/common/canvas_base.c index c60c5cf..0ec8526 100644 --- a/common/canvas_base.c +++ b/common/canvas_base.c @@ -1275,13 +1275,25 @@ static void canvas_touch_image(CanvasBase *canvas, SpiceImage *image) static pixman_image_t* canvas_get_image_from_self(SpiceCanvas *canvas, int x, int y, - int32_t width, int32_t height) + int32_t width, int32_t height, + int force_opaque) { CanvasBase *canvas_base = (CanvasBase *)canvas; pixman_image_t *surface; uint8_t *dest; int dest_stride; SpiceRect area; + pixman_format_code_t format; + + format = spice_surface_format_to_pixman (canvas_base->format); + if (force_opaque) + { + /* Set alpha bits of the format to 0 */ + format = (pixman_format_code_t)(((uint32_t)format) & ~(0xf << 12)); + + spice_return_val_if_fail ( + pixman_format_supported_destination (format), NULL); + } surface = pixman_image_create_bits(spice_surface_format_to_pixman (canvas_base->format), width, height, NULL, 0); @@ -1923,7 +1935,7 @@ static void canvas_mask_pixman(CanvasBase *canvas, surface_canvas = canvas_get_surface_mask(canvas, mask->bitmap); if (surface_canvas) { needs_invert = mask->flags & SPICE_MASK_FLAGS_INVERS; - image = surface_canvas->ops->get_image(surface_canvas); + image = surface_canvas->ops->get_image(surface_canvas, FALSE); } else { needs_invert = FALSE; image = canvas_get_mask(canvas, @@ -3178,10 +3190,10 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, width = bbox->right - bbox->left; heigth = bbox->bottom - bbox->top; - d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth); + d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth, FALSE); surface_canvas = canvas_get_surface(canvas, rop3->src_bitmap); if (surface_canvas) { - s = surface_canvas->ops->get_image(surface_canvas); + s = surface_canvas->ops->get_image(surface_canvas, FALSE); } else { s = canvas_get_image(canvas, rop3->src_bitmap, FALSE); } @@ -3208,7 +3220,7 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, _surface_canvas = canvas_get_surface(canvas, rop3->brush.u.pattern.pat); if (_surface_canvas) { - p = _surface_canvas->ops->get_image(_surface_canvas); + p = _surface_canvas->ops->get_image(_surface_canvas, FALSE); } else { p = canvas_get_image(canvas, rop3->brush.u.pattern.pat, FALSE); } @@ -3232,6 +3244,117 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, pixman_region32_fini(&dest_region); } +static void transform_to_pixman_transform(SpiceTransform *transform, + pixman_transform_t *p) +{ + p->matrix[0][0] = transform->t00; + p->matrix[0][1] = transform->t01; + p->matrix[0][2] = transform->t02; + p->matrix[1][0] = transform->t10; + p->matrix[1][1] = transform->t11; + p->matrix[1][2] = transform->t12; + p->matrix[2][0] = 0; + p->matrix[2][1] = 0; + p->matrix[2][2] = pixman_fixed_1; +} + +#define MASK(lo, hi) \ + (((1U << (hi)) - 1) - (((1U << (lo))) - 1)) + +#define EXTRACT(v, lo, hi) \ + ((v & MASK(lo, hi)) >> lo) + +static void canvas_draw_composite(SpiceCanvas *spice_canvas, SpiceRect *bbox, + SpiceClip *clip, SpiceComposite *composite) +{ + CanvasBase *canvas = (CanvasBase *)spice_canvas; + SpiceCanvas *surface_canvas; + pixman_region32_t dest_region; + pixman_image_t *d; + pixman_image_t *s; + pixman_image_t *m; + pixman_repeat_t src_repeat; + pixman_filter_t src_filter; + pixman_op_t op; + pixman_transform_t transform; + int width, height; + + pixman_region32_init_rect(&dest_region, + bbox->left, bbox->top, + bbox->right - bbox->left, + bbox->bottom - bbox->top); + + canvas_clip_pixman(canvas, &dest_region, clip); + + width = bbox->right - bbox->left; + height = bbox->bottom - bbox->top; + + /* Dest */ + d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, height, + (composite->flags & SPICE_COMPOSITE_DEST_OPAQUE)); + + /* Src */ + surface_canvas = canvas_get_surface(canvas, composite->src_bitmap); + if (surface_canvas) { + s = surface_canvas->ops->get_image(surface_canvas, + (composite->flags & SPICE_COMPOSITE_SOURCE_OPAQUE)); + } else { + s = canvas_get_image(canvas, composite->src_bitmap, FALSE); + } + if (composite->flags & SPICE_COMPOSITE_HAS_SRC_TRANSFORM) + { + transform_to_pixman_transform (&composite->src_transform, &transform); + pixman_image_set_transform (s, &transform); + } + src_filter = (pixman_filter_t) EXTRACT (composite->flags, 8, 11); + src_repeat = (pixman_repeat_t) EXTRACT (composite->flags, 14, 16); + pixman_image_set_filter (s, src_filter, NULL, 0); + pixman_image_set_repeat (s, src_repeat); + + /* Mask */ + m = NULL; + if (composite->flags & SPICE_COMPOSITE_HAS_MASK) { + pixman_filter_t mask_filter = (pixman_filter_t) EXTRACT (composite->flags, 11, 14); + pixman_repeat_t mask_repeat = (pixman_repeat_t) EXTRACT (composite->flags, 16, 18); + pixman_bool_t component_alpha = EXTRACT (composite->flags, 18, 19); + + surface_canvas = canvas_get_surface(canvas, composite->mask_bitmap); + if (surface_canvas) { + m = surface_canvas->ops->get_image(surface_canvas, FALSE); + } else { + m = canvas_get_image(canvas, composite->mask_bitmap, FALSE); + } + + if (composite->flags & SPICE_COMPOSITE_HAS_MASK_TRANSFORM) { + transform_to_pixman_transform (&composite->mask_transform, &transform); + pixman_image_set_transform (m, &transform); + } + + pixman_image_set_repeat (m, mask_repeat); + pixman_image_set_filter (m, mask_filter, NULL, 0); + pixman_image_set_component_alpha (m, component_alpha); + } + + op = (pixman_op_t) EXTRACT (composite->flags, 0, 8); + + pixman_image_composite32 (op, s, m, d, + composite->src_origin.x, composite->src_origin.y, + composite->mask_origin.x, composite->mask_origin.y, + 0, 0, width, height); + + pixman_image_unref(s); + if (m) + pixman_image_unref(m); + + spice_canvas->ops->blit_image(spice_canvas, &dest_region, d, + bbox->left, + bbox->top); + + pixman_image_unref(d); + + pixman_region32_fini(&dest_region); +} + static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos) { CanvasBase *canvas = (CanvasBase *)spice_canvas; @@ -3319,6 +3442,7 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops) ops->draw_alpha_blend = canvas_draw_alpha_blend; ops->draw_stroke = canvas_draw_stroke; ops->draw_rop3 = canvas_draw_rop3; + ops->draw_composite = canvas_draw_composite; ops->group_start = canvas_base_group_start; ops->group_end = canvas_base_group_end; } diff --git a/common/canvas_base.h b/common/canvas_base.h index bdf12a1..637cdc1 100644 --- a/common/canvas_base.h +++ b/common/canvas_base.h @@ -133,6 +133,7 @@ typedef struct { void (*draw_text)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text); void (*draw_stroke)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke); void (*draw_rop3)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3); + void (*draw_composite)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceComposite *composite); void (*draw_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend); void (*draw_blackness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness); void (*draw_whiteness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness); @@ -306,7 +307,7 @@ typedef struct { void (*copy_region)(SpiceCanvas *canvas, pixman_region32_t *dest_region, int dx, int dy); - pixman_image_t *(*get_image)(SpiceCanvas *canvas); + pixman_image_t *(*get_image)(SpiceCanvas *canvas, int force_opaque); } SpiceCanvasOps; void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn); diff --git a/common/draw.h b/common/draw.h index 8fad0e5..3704358 100644 --- a/common/draw.h +++ b/common/draw.h @@ -223,6 +223,26 @@ typedef struct SpiceRop3 { SpiceQMask mask; } SpiceRop3; +/* Given in 16.16 fixed point */ +typedef struct SpiceTransform { + uint32_t t00; + uint32_t t01; + uint32_t t02; + uint32_t t10; + uint32_t t11; + uint32_t t12; +} SpiceTransform; + +typedef struct SpiceComposite { + uint32_t flags; + SpiceImage *src_bitmap; + SpiceImage *mask_bitmap; + SpiceTransform src_transform; + SpiceTransform mask_transform; + SpicePoint16 src_origin; + SpicePoint16 mask_origin; +} SpiceComposite; + typedef struct SpiceBlackness { SpiceQMask mask; } SpiceBlackness, SpiceInvers, SpiceWhiteness; diff --git a/common/messages.h b/common/messages.h index 2b6d68a..b5f3368 100644 --- a/common/messages.h +++ b/common/messages.h @@ -253,6 +253,11 @@ typedef struct SpiceMsgDisplayDrawAlphaBlend { SpiceAlphaBlend data; } SpiceMsgDisplayDrawAlphaBlend; +typedef struct SpiceMsgDisplayDrawComposite { + SpiceMsgDisplayBase base; + SpiceComposite data; +} SpiceMsgDisplayDrawComposite; + typedef struct SpiceMsgDisplayCopyBits { SpiceMsgDisplayBase base; SpicePoint src_pos; diff --git a/common/sw_canvas.c b/common/sw_canvas.c index 4b10383..f947dde 100644 --- a/common/sw_canvas.c +++ b/common/sw_canvas.c @@ -85,11 +85,27 @@ static pixman_image_t *canvas_get_pixman_brush(SwCanvas *canvas, return NULL; } -static pixman_image_t *get_image(SpiceCanvas *canvas) +static pixman_image_t *get_image(SpiceCanvas *canvas, int force_opaque) { SwCanvas *sw_canvas = (SwCanvas *)canvas; + pixman_format_code_t format; - pixman_image_ref(sw_canvas->image); + spice_pixman_image_get_format (sw_canvas->image, &format); + if (force_opaque && PIXMAN_FORMAT_A (format) != 0) { + uint32_t *data; + int stride; + int width, height; + + /* Remove alpha bits from format */ + format = (pixman_format_code_t)(((uint32_t)format) & ~(0xf << 12)); + data = pixman_image_get_data (sw_canvas->image); + stride = pixman_image_get_stride (sw_canvas->image); + width = pixman_image_get_width (sw_canvas->image); + height = pixman_image_get_height (sw_canvas->image); + return pixman_image_create_bits (format, width, height, data, stride); + } else { + pixman_image_ref(sw_canvas->image); + } return sw_canvas->image; } diff --git a/spice-protocol b/spice-protocol index 6ef7050..fa3de2f 160000 --- a/spice-protocol +++ b/spice-protocol @@ -1 +1 @@ -Subproject commit 6ef7050b0725dbe385eed290e73a2603af7d3fbc +Subproject commit fa3de2fbf29c5f8819503643b25cba4598d6f68a diff --git a/spice.proto b/spice.proto index 1fdead9..29d6a8b 100644 --- a/spice.proto +++ b/spice.proto @@ -26,6 +26,15 @@ struct Rect { int32 right; }; +struct Transform { + uint32 t00; + uint32 t01; + uint32 t02; + uint32 t10; + uint32 t11; + uint32 t12; +}; + enum32 link_err { OK, ERROR, @@ -52,6 +61,28 @@ flags32 migrate_flags { NEED_DATA_TRANSFER } @prefix(SPICE_MIGRATE_); +flags32 composite_flags { + OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7, + SRC_FILTER0, SRC_FILTER1, SRC_FILTER2, + MASK_FILTER0, MASK_FITLER1, MASK_FILTER2, + + SRC_REPEAT0, SRC_REPEAT1, + MASK_REPEAT0, MASK_REPEAT1, + COMPONENT_ALPHA, + + HAS_MASK, + HAS_SRC_TRANSFORM, + HAS_MASK_TRANSFORM, + + /* These are used to override the formats given in the images. For + * example, if the mask image has format a8r8g8b8, but MASK_OPAQUE + * is set, the image should be treated as if it were x8r8g8b8 + */ + SOURCE_OPAQUE, + MASK_OPAQUE, + DEST_OPAQUE, +} @prefix(SPICE_COMPOSITE_); + enum32 notify_severity { INFO, WARN, @@ -321,7 +352,8 @@ enum8 bitmap_fmt { 16BIT, /* 0555 mode */ 24BIT /* 3 byte, brg */, 32BIT /* 4 byte, xrgb in little endian format */, - RGBA /* 4 byte, argb in little endian format */ + RGBA /* 4 byte, argb in little endian format */, + 8BIT_A /* 1 byte, alpha */ }; flags8 bitmap_flags { @@ -814,6 +846,28 @@ channel DisplayChannel : BaseChannel { Head heads[count] @end; } monitors_config; + message { + DisplayBase base; + struct Composite { + composite_flags flags; + Image *src_bitmap; + switch (flags) { + case HAS_MASK: + Image *mask_bitmap; + } a @anon; + switch (flags) { + case HAS_SRC_TRANSFORM: + Transform src_transform; + } b @anon; + switch (flags) { + case HAS_MASK_TRANSFORM: + Transform mask_transform; + } c @anon; + Point16 src_origin; + Point16 mask_origin; + } data; + } draw_composite; + client: message { uint8 pixmap_cache_id; -- 1.7.11.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel