summaryrefslogtreecommitdiff
path: root/common/cairo_canvas.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/cairo_canvas.c')
-rw-r--r--common/cairo_canvas.c1664
1 files changed, 1664 insertions, 0 deletions
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
new file mode 100644
index 00000000..732df919
--- /dev/null
+++ b/common/cairo_canvas.c
@@ -0,0 +1,1664 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "cairo_canvas.h"
+#include "canvas_base.c"
+#include "rop3.h"
+#include "rect.h"
+#include "region.h"
+
+struct CairoCanvas {
+ CanvasBase base;
+ cairo_t *cairo;
+ uint32_t *private_data;
+ int private_data_size;
+};
+
+static void canvas_set_path(CairoCanvas *canvas, void *addr)
+{
+ cairo_t *cairo = canvas->cairo;
+ uint32_t* data_size = (uint32_t*)addr;
+ access_test(&canvas->base, data_size, sizeof(uint32_t));
+ uint32_t more = *data_size;
+
+ PathSeg* seg = (PathSeg*)(data_size + 1);
+
+ do {
+ access_test(&canvas->base, seg, sizeof(PathSeg));
+
+ uint32_t flags = seg->flags;
+ PointFix* point = (PointFix*)seg->data;
+ PointFix* end_point = point + seg->count;
+ access_test(&canvas->base, point, (unsigned long)end_point - (unsigned long)point);
+ ASSERT(point < end_point);
+ more -= ((unsigned long)end_point - (unsigned long)seg);
+ seg = (PathSeg*)end_point;
+
+ if (flags & PATH_BEGIN) {
+ cairo_new_sub_path(cairo);
+ cairo_move_to(cairo, fix_to_double(point->x), fix_to_double(point->y));
+ point++;
+ }
+
+ if (flags & PATH_BEZIER) {
+ ASSERT((point - end_point) % 3 == 0);
+ for (; point + 2 < end_point; point += 3) {
+ cairo_curve_to(cairo,
+ fix_to_double(point[0].x), fix_to_double(point[0].y),
+ fix_to_double(point[1].x), fix_to_double(point[1].y),
+ fix_to_double(point[2].x), fix_to_double(point[2].y));
+ }
+ } else {
+ for (; point < end_point; point++) {
+ cairo_line_to(cairo, fix_to_double(point->x), fix_to_double(point->y));
+ }
+ }
+ if (flags & PATH_END) {
+ if (flags & PATH_CLOSE) {
+ cairo_close_path(cairo);
+ }
+ cairo_new_sub_path(cairo);
+ }
+ } while (more);
+}
+
+static void canvas_clip(CairoCanvas *canvas, Clip *clip)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ switch (clip->type) {
+ case CLIP_TYPE_NONE:
+ break;
+ case CLIP_TYPE_RECTS: {
+ uint32_t *n = (uint32_t *)GET_ADDRESS(clip->data);
+ access_test(&canvas->base, n, sizeof(uint32_t));
+
+ Rect *now = (Rect *)(n + 1);
+ Rect *end = now + *n;
+ access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
+
+ for (; now < end; now++) {
+ cairo_rectangle(cairo, now->left, now->top, now->right - now->left,
+ now->bottom - now->top);
+ }
+ cairo_clip(cairo);
+ break;
+ }
+ case CLIP_TYPE_PATH:
+ canvas_set_path(canvas, GET_ADDRESS(clip->data));
+ cairo_clip(cairo);
+ break;
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static inline cairo_line_cap_t canvas_line_cap_to_cairo(int end_style)
+{
+ switch (end_style) {
+ case LINE_CAP_ROUND:
+ return CAIRO_LINE_CAP_ROUND;
+ case LINE_CAP_SQUARE:
+ return CAIRO_LINE_CAP_SQUARE;
+ case LINE_CAP_BUTT:
+ return CAIRO_LINE_CAP_BUTT;
+ default:
+ CANVAS_ERROR("bad end style %d", end_style);
+ }
+}
+
+static inline cairo_line_join_t canvas_line_join_to_cairo(int join_style)
+{
+ switch (join_style) {
+ case LINE_JOIN_ROUND:
+ return CAIRO_LINE_JOIN_ROUND;
+ case LINE_JOIN_BEVEL:
+ return CAIRO_LINE_JOIN_BEVEL;
+ case LINE_JOIN_MITER:
+ return CAIRO_LINE_JOIN_MITER;
+ default:
+ CANVAS_ERROR("bad join style %d", join_style);
+ }
+}
+
+static void canvas_set_line_attr_no_dash(CairoCanvas *canvas, LineAttr *attr)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ cairo_set_line_width(cairo, fix_to_double(attr->width));
+ cairo_set_line_cap(cairo, canvas_line_cap_to_cairo(attr->end_style));
+ cairo_set_line_join(cairo, canvas_line_join_to_cairo(attr->join_style));
+ cairo_set_miter_limit(cairo, fix_to_double(attr->miter_limit));
+ cairo_set_dash(cairo, NULL, 0, 0);
+}
+
+static void canvas_set_dash(CairoCanvas *canvas, UINT8 nseg, ADDRESS addr, int start_is_gap)
+{
+ FIXED28_4* style = (FIXED28_4*)GET_ADDRESS(addr);
+ double offset = 0;
+ double *local_style;
+ int i;
+
+ access_test(&canvas->base, style, nseg * sizeof(*style));
+
+ if (nseg == 0) {
+ CANVAS_ERROR("bad nseg");
+ }
+ local_style = (double *)malloc(nseg * sizeof(*local_style));
+
+ if (start_is_gap) {
+ offset = fix_to_double(*style);
+ local_style[nseg - 1] = fix_to_double(*style);
+ style++;
+
+ for (i = 0; i < nseg - 1; i++, style++) {
+ local_style[i] = fix_to_double(*style);
+ }
+ } else {
+ for (i = 0; i < nseg; i++, style++) {
+ local_style[i] = fix_to_double(*style);
+ }
+ }
+ cairo_set_dash(canvas->cairo, local_style, nseg, offset);
+ free(local_style);
+}
+
+static inline void canvas_invers_32bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
+ int width, uint8_t *end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t *src_line = (uint32_t *)src;
+ uint32_t *src_line_end = src_line + width;
+ uint32_t *dest_line = (uint32_t *)dest;
+
+ while (src_line < src_line_end) {
+ *(dest_line++) = ~*(src_line++) & 0x00ffffff;
+ }
+ }
+}
+
+static inline void canvas_invers_24bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
+ int width, uint8_t *end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint8_t *src_line = src;
+ uint8_t *src_line_end = src_line + width * 3;
+ uint8_t *dest_line = dest;
+
+ for (; src_line < src_line_end; ++dest_line) {
+ *(dest_line++) = ~*(src_line++);
+ *(dest_line++) = ~*(src_line++);
+ *(dest_line++) = ~*(src_line++);
+ }
+ }
+}
+
+static inline void canvas_invers_16bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
+ int width, uint8_t *end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t *src_line = (uint16_t*)src;
+ uint16_t *src_line_end = src_line + width;
+ uint32_t *dest_line = (uint32_t*)dest;
+
+ for (; src_line < src_line_end; ++dest_line, src_line++) {
+ *dest_line = ~canvas_16bpp_to_32bpp(*src_line) & 0x00ffffff;
+ }
+ }
+}
+
+static inline void canvas_invers_8bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
+ int width, uint8_t *end, Palette *palette)
+{
+ if (!palette) {
+ CANVAS_ERROR("no palette");
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t *dest_line = (uint32_t*)dest;
+ uint8_t *src_line = src;
+ uint8_t *src_line_end = src_line + width;
+
+ while (src_line < src_line_end) {
+ ASSERT(*src_line < palette->num_ents);
+ *(dest_line++) = ~palette->ents[*(src_line++)] & 0x00ffffff;
+ }
+ }
+}
+
+static inline void canvas_invers_4bpp_be(uint8_t* dest, int dest_stride, uint8_t* src,
+ int src_stride, int width, uint8_t* end,
+ Palette *palette)
+{
+ if (!palette) {
+ CANVAS_ERROR("no palette");
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t *dest_line = (uint32_t *)dest;
+ uint8_t *now = src;
+ int i;
+
+ for (i = 0; i < (width >> 1); i++) {
+ ASSERT((*now & 0x0f) < palette->num_ents);
+ ASSERT(((*now >> 4) & 0x0f) < palette->num_ents);
+ *(dest_line++) = ~palette->ents[(*now >> 4) & 0x0f] & 0x00ffffff;
+ *(dest_line++) = ~palette->ents[*(now++) & 0x0f] & 0x00ffffff;
+ }
+ if (width & 1) {
+ *(dest_line) = ~palette->ents[(*src >> 4) & 0x0f] & 0x00ffffff;
+ }
+ }
+}
+
+static inline void canvas_invers_1bpp_be(uint8_t* dest, int dest_stride, uint8_t* src,
+ int src_stride, int width, uint8_t* end,
+ Palette *palette)
+{
+ uint32_t fore_color;
+ uint32_t back_color;
+
+ if (!palette) {
+ CANVAS_ERROR("no palette");
+ }
+
+ fore_color = palette->ents[1];
+ back_color = palette->ents[0];
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t* dest_line = (uint32_t*)dest;
+ int i;
+
+ for (i = 0; i < width; i++) {
+ if (test_bit_be(src, i)) {
+ *(dest_line++) = ~fore_color & 0x00ffffff;
+ } else {
+ *(dest_line++) = ~back_color & 0x00ffffff;
+ }
+ }
+ }
+}
+
+static cairo_surface_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, Bitmap* bitmap,
+ Palette *palette)
+{
+ uint8_t* src = (uint8_t *)GET_ADDRESS(bitmap->data);
+ int src_stride;
+ uint8_t* end;
+ uint8_t* dest;
+ int dest_stride;
+ cairo_surface_t* cairo_surface;
+
+ src_stride = bitmap->stride;
+ end = src + (bitmap->y * src_stride);
+ access_test(&canvas->base, src, bitmap->y * src_stride);
+
+ cairo_surface = cairo_image_surface_create((bitmap->format == BITMAP_FMT_RGBA) ?
+ CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+ bitmap->x, bitmap->y);
+ if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(cairo_surface)));
+ }
+ dest = cairo_image_surface_get_data(cairo_surface);
+ dest_stride = cairo_image_surface_get_stride(cairo_surface);
+
+ if (!(bitmap->flags & BITMAP_TOP_DOWN)) {
+ ASSERT(bitmap->y > 0);
+ dest += dest_stride * (bitmap->y - 1);
+ dest_stride = -dest_stride;
+ }
+ switch (bitmap->format) {
+ case BITMAP_FMT_32BIT:
+ case BITMAP_FMT_RGBA:
+ canvas_invers_32bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_24BIT:
+ canvas_invers_24bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_16BIT:
+ canvas_invers_16bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_8BIT:
+ canvas_invers_8bpp(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ case BITMAP_FMT_4BIT_BE:
+ canvas_invers_4bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ case BITMAP_FMT_1BIT_BE:
+ canvas_invers_1bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ }
+ return cairo_surface;
+}
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+
+#ifndef CAIRO_CANVAS_CACHE
+static Palette *canvas_get_palette(CairoCanvas *canvas, Bitmap *bitmap)
+{
+ Palette *local_palette;
+ Palette *palette;
+ int size;
+
+ if (!bitmap->palette) {
+ return NULL;
+ }
+
+ palette = (Palette *)GET_ADDRESS(bitmap->palette);
+ if (canvas->base.color_shift != 5) {
+ return palette;
+ }
+
+ size = sizeof(Palette) + (palette->num_ents << 2);
+ local_palette = malloc(size);
+ memcpy(local_palette, palette, size);
+ canvas_localize_palette(&canvas->base, palette);
+
+ return local_palette;
+}
+
+static void free_palette(Bitmap *bitmap, Palette *palette)
+{
+ if (!palette || palette == GET_ADDRESS(bitmap->palette)) {
+ return;
+ }
+ free(palette);
+}
+
+#endif
+
+static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, ADDRESS addr)
+{
+ ImageDescriptor *descriptor = (ImageDescriptor *)GET_ADDRESS(addr);
+ cairo_surface_t *surface;
+ cairo_surface_t *invers = NULL;
+
+ access_test(&canvas->base, descriptor, sizeof(ImageDescriptor));
+
+ int cache_me = descriptor->flags & IMAGE_CACHE_ME;
+
+ switch (descriptor->type) {
+ case IMAGE_TYPE_QUIC: {
+ QUICImage *image = (QUICImage *)descriptor;
+ access_test(&canvas->base, descriptor, sizeof(QUICImage));
+ if (cache_me) {
+ surface = canvas_get_quic(&canvas->base, image, 0);
+ } else {
+ return canvas_get_quic(&canvas->base, image, 1);
+ }
+ break;
+ }
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+ case IMAGE_TYPE_LZ_PLT: {
+ access_test(&canvas->base, descriptor, sizeof(LZ_PLTImage));
+ LZImage *image = (LZImage *)descriptor;
+ if (cache_me) {
+ surface = canvas_get_lz(&canvas->base, image, 0);
+ } else {
+ return canvas_get_lz(&canvas->base, image, 1);
+ }
+ break;
+ }
+ case IMAGE_TYPE_LZ_RGB: {
+ access_test(&canvas->base, descriptor, sizeof(LZ_RGBImage));
+ LZImage *image = (LZImage *)descriptor;
+ if (cache_me) {
+ surface = canvas_get_lz(&canvas->base, image, 0);
+ } else {
+ return canvas_get_lz(&canvas->base, image, 1);
+ }
+ break;
+ }
+#endif
+#ifdef USE_GLZ
+ case IMAGE_TYPE_GLZ_RGB: {
+ access_test(&canvas->base, descriptor, sizeof(LZ_RGBImage));
+ LZImage *image = (LZImage *)descriptor;
+ surface = canvas_get_glz(&canvas->base, image);
+ break;
+ }
+#endif
+ case IMAGE_TYPE_FROM_CACHE:
+ surface = canvas->base.bits_cache_get(canvas->base.bits_cache_opaque, descriptor->id);
+ break;
+ case IMAGE_TYPE_BITMAP: {
+ BitmapImage *bitmap = (BitmapImage *)descriptor;
+ access_test(&canvas->base, descriptor, sizeof(BitmapImage));
+ if (cache_me) {
+ surface = canvas_get_bits(&canvas->base, &bitmap->bitmap);
+ } else {
+#ifdef CAIRO_CANVAS_CACHE
+ return canvas_bitmap_to_invers_surface(canvas, &bitmap->bitmap,
+ canvas_get_palett(&canvas->base,
+ bitmap->bitmap.palette,
+ bitmap->bitmap.flags));
+#else
+ Palette *palette = canvas_get_palette(canvas, &bitmap->bitmap);
+ surface = canvas_bitmap_to_invers_surface(canvas, &bitmap->bitmap, palette);
+ free_palette(&bitmap->bitmap, palette);
+ return surface;
+#endif
+ }
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+
+ if (cache_me) {
+ canvas->base.bits_cache_put(canvas->base.bits_cache_opaque, descriptor->id, surface);
+ }
+
+ invers = canvas_handle_inverse_user_data(surface);
+ cairo_surface_destroy(surface);
+ return invers;
+}
+
+#else
+
+static cairo_surface_t *canvas_get_invers(CairoCanvas *canvas, Bitmap *bitmap)
+{
+ Palette *palette;
+
+ if (!bitmap->palette) {
+ return canvas_bitmap_to_invers_surface(canvas, bitmap, NULL);
+ }
+ palette = (Palette *)GET_ADDRESS(bitmap->palette);
+ if (canvas->color_shift == 5) {
+ int size = sizeof(Palette) + (palette->num_ents << 2);
+ Palette *local_palette = malloc(size);
+ cairo_surface_t* surface;
+
+ memcpy(local_palette, palette, size);
+ canvas_localize_palette(canvas, palette);
+ surface = canvas_bitmap_to_invers_surface(canvas, bitmap, local_palette);
+ free(local_palette);
+ return surface;
+ } else {
+ return canvas_bitmap_to_invers_surface(canvas, bitmap, palette);
+ }
+}
+
+static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, ADDRESS addr)
+{
+ ImageDescriptor *descriptor = (ImageDescriptor *)GET_ADDRESS(addr);
+
+ access_test(canvas, descriptor, sizeof(ImageDescriptor));
+
+ switch (descriptor->type) {
+ case IMAGE_TYPE_QUIC: {
+ QUICImage *image = (QUICImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(QUICImage));
+ return canvas_get_quic(canvas, image, 1);
+ }
+ case IMAGE_TYPE_BITMAP: {
+ BitmapImage *bitmap = (BitmapImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(BitmapImage));
+ return canvas_get_invers(canvas, &bitmap->bitmap);
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+}
+
+#endif
+
+static cairo_surface_t* canvas_surface_from_self(CairoCanvas *canvas, Point *pos,
+ int32_t width, int32_t heigth)
+{
+ cairo_surface_t *surface;
+ cairo_surface_t *src_surface;
+ uint8_t *dest;
+ int dest_stride;
+ uint8_t *src;
+ int src_stride;
+ int i;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, heigth);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+ dest = cairo_image_surface_get_data(surface);
+ dest_stride = cairo_image_surface_get_stride(surface);
+
+ src_surface = cairo_get_target(canvas->cairo);
+ src = cairo_image_surface_get_data(src_surface);
+ src_stride = cairo_image_surface_get_stride(src_surface);
+ src += pos->y * src_stride + (pos->x << 2);
+ for (i = 0; i < heigth; i++, dest += dest_stride, src += src_stride) {
+ memcpy(dest, src, width << 2);
+ }
+ return surface;
+}
+
+static cairo_pattern_t *canvas_get_brush(CairoCanvas *canvas, Brush *brush, uint32_t invers)
+{
+ switch (brush->type) {
+ case BRUSH_TYPE_SOLID: {
+ uint32_t color = (invers) ? ~brush->u.color : brush->u.color;
+ double r, g, b;
+
+ b = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ g = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ r = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ return cairo_pattern_create_rgb(r, g, b);
+ }
+ case BRUSH_TYPE_PATTERN: {
+ cairo_surface_t* surface;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+
+ if (invers) {
+ surface = canvas_get_invers_image(canvas, brush->u.pattern.pat);
+ } else {
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+ }
+ pattern = cairo_pattern_create_for_surface(surface);
+ if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(pattern)));
+ }
+ cairo_surface_destroy(surface);
+ cairo_matrix_init_translate(&matrix, -brush->u.pattern.pos.x, -brush->u.pattern.pos.y);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ return pattern;
+ }
+ case BRUSH_TYPE_NONE:
+ return NULL;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+}
+
+typedef void (*DrawMethod)(void *);
+
+static inline void __canvas_draw_invers(CairoCanvas *canvas, DrawMethod draw_method, void *data)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_set_source_rgb(cairo, 1, 1, 1);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_XOR);
+ draw_method(data);
+}
+
+static inline int canvas_set_ropd_operator(cairo_t *cairo, uint16_t rop_decriptor)
+{
+ cairo_operator_t cairo_op;
+
+ if (rop_decriptor & ROPD_OP_PUT) {
+ cairo_op = CAIRO_OPERATOR_RASTER_COPY;
+ } else if (rop_decriptor & ROPD_OP_XOR) {
+ cairo_op = CAIRO_OPERATOR_RASTER_XOR;
+ } else if (rop_decriptor & ROPD_OP_OR) {
+ cairo_op = CAIRO_OPERATOR_RASTER_OR;
+ } else if (rop_decriptor & ROPD_OP_AND) {
+ cairo_op = CAIRO_OPERATOR_RASTER_AND;
+ } else {
+ return 0;
+ }
+ cairo_set_operator(cairo, cairo_op);
+ return 1;
+}
+
+static void canvas_draw_with_pattern(CairoCanvas *canvas, cairo_pattern_t *pattern,
+ uint16_t rop_decriptor,
+ DrawMethod draw_method, void *data)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ if (rop_decriptor & ROPD_OP_BLACKNESS) {
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ draw_method(data);
+ } else if (rop_decriptor & ROPD_OP_WHITENESS) {
+ cairo_set_source_rgb(cairo, 1, 1, 1);
+ draw_method(data);
+ } else if (rop_decriptor & ROPD_OP_INVERS) {
+ __canvas_draw_invers(canvas, draw_method, data);
+ } else {
+ if (rop_decriptor & ROPD_INVERS_DEST) {
+ __canvas_draw_invers(canvas, draw_method, data);
+ }
+
+ if (canvas_set_ropd_operator(cairo, rop_decriptor)) {
+ ASSERT(pattern);
+ cairo_set_source(cairo, pattern);
+ draw_method(data);
+ }
+
+ if (rop_decriptor & ROPD_INVERS_RES) {
+ __canvas_draw_invers(canvas, draw_method, data);
+ }
+ }
+}
+
+static inline void canvas_draw(CairoCanvas *canvas, Brush *brush, uint16_t rop_decriptor,
+ DrawMethod draw_method, void *data)
+{
+ cairo_pattern_t *pattern = canvas_get_brush(canvas, brush, rop_decriptor & ROPD_INVERS_BRUSH);
+ canvas_draw_with_pattern(canvas, pattern, rop_decriptor, draw_method, data);
+ if (pattern) {
+ cairo_pattern_destroy(pattern);
+ }
+}
+
+static cairo_pattern_t *canvas_get_mask_pattern(CairoCanvas *canvas, QMask *mask, int x, int y)
+{
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+
+ if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+ return NULL;
+ }
+ pattern = cairo_pattern_create_for_surface(surface);
+ if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(pattern)));
+ }
+ cairo_surface_destroy(surface);
+
+ cairo_matrix_init_translate(&matrix, mask->pos.x - x, mask->pos.y - y);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ return pattern;
+}
+
+typedef struct DrawMaskData {
+ cairo_t *cairo;
+ cairo_pattern_t *mask;
+} DrawMaskData;
+
+static void __draw_mask(void *data)
+{
+ cairo_mask(((DrawMaskData *)data)->cairo, ((DrawMaskData *)data)->mask);
+}
+
+void canvas_draw_fill(CairoCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill)
+{
+ DrawMaskData draw_data;
+ draw_data.cairo = canvas->cairo;
+
+ cairo_save(draw_data.cairo);
+ canvas_clip(canvas, clip);
+ if ((draw_data.mask = canvas_get_mask_pattern(canvas, &fill->mask, bbox->left, bbox->top))) {
+ cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(draw_data.cairo);
+ canvas_draw(canvas, &fill->brush, fill->rop_decriptor, __draw_mask, &draw_data);
+ cairo_pattern_destroy(draw_data.mask);
+ } else {
+ cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ canvas_draw(canvas, &fill->brush, fill->rop_decriptor, (DrawMethod)cairo_fill_preserve,
+ draw_data.cairo);
+ cairo_new_path(draw_data.cairo);
+ }
+ cairo_restore(draw_data.cairo);
+}
+
+static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, ADDRESS src_bitmap,
+ const Rect *src, const Rect *dest, int invers,
+ int scale_mode)
+{
+ cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ cairo_matrix_t matrix;
+
+ ASSERT(src_bitmap);
+ ASSERT(scale_mode == IMAGE_SCALE_INTERPOLATE || scale_mode == IMAGE_SCALE_NEAREST);
+ if (invers) {
+ surface = canvas_get_invers_image(canvas, src_bitmap);
+ } else {
+ surface = canvas_get_image(&canvas->base, src_bitmap);
+ }
+
+ pattern = cairo_pattern_create_for_surface(surface);
+ cairo_surface_destroy(surface);
+ if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(pattern)));
+ }
+
+ if (!rect_is_same_size(src, dest)) {
+ double sx, sy;
+
+ sx = (double)(src->right - src->left) / (dest->right - dest->left);
+ sy = (double)(src->bottom - src->top) / (dest->bottom - dest->top);
+
+ cairo_matrix_init_scale(&matrix, sx, sy);
+ cairo_pattern_set_filter(pattern, (scale_mode == IMAGE_SCALE_NEAREST) ?
+ CAIRO_FILTER_NEAREST : CAIRO_FILTER_GOOD);
+
+ cairo_matrix_translate(&matrix, src->left / sx - dest->left, src->top / sy - dest->top);
+ } else {
+ cairo_matrix_init_translate(&matrix, src->left - dest->left, src->top - dest->top);
+ }
+
+ cairo_pattern_set_matrix(pattern, &matrix);
+ return pattern;
+}
+
+void canvas_draw_copy(CairoCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_pattern_t *pattern;
+ cairo_pattern_t *mask;
+
+ cairo_save(cairo);
+ canvas_clip(canvas, clip);
+
+ pattern = canvas_src_image_to_pat(canvas, copy->src_bitmap, &copy->src_area, bbox,
+ copy->rop_decriptor & ROPD_INVERS_SRC, copy->scale_mode);
+ cairo_set_source(cairo, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
+ if ((mask = canvas_get_mask_pattern(canvas, &copy->mask, bbox->left, bbox->top))) {
+ cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(cairo);
+ cairo_mask(cairo, mask);
+ cairo_pattern_destroy(mask);
+ } else {
+ cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_fill(cairo);
+ }
+
+ cairo_restore(cairo);
+}
+
+#ifdef WIN32
+void canvas_put_image(CairoCanvas *canvas, HDC dc, const Rect *dest, const uint8_t *_src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+#else
+void canvas_put_image(CairoCanvas *canvas, const Rect *dest, const uint8_t *_src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+#endif
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_surface_t* surf = NULL;
+ int dest_width;
+ int dest_height;
+ uint8_t *src_data = (uint8_t *)_src_data;
+ uint32_t *data;
+ int nstride;
+
+ cairo_save(cairo);
+
+ if (clip) {
+ const Rect *now = clip->rects;
+ const Rect *end = clip->rects + clip->num_rects;
+ for (; now < end; now++) {
+ cairo_rectangle(cairo, now->left, now->top, now->right - now->left,
+ now->bottom - now->top);
+ }
+ cairo_clip(cairo);
+ }
+
+
+ dest_width = dest->right - dest->left;
+ dest_height = dest->bottom - dest->top;
+
+ if (dest_width != src_width || dest_height != src_height) {
+ int x, y;
+ int x_mul = (uint32_t)((src_width << 16) / dest_width);
+ int y_mul = (uint32_t)((src_height << 16) / dest_height);
+ int new_y;
+ int set_y;
+ int nsrc_stride;
+
+ if (src_stride < 0) {
+ nsrc_stride = -src_stride;
+ src_data = src_data - (src_height - 1) * nsrc_stride;
+ nsrc_stride = nsrc_stride / 4;
+ } else {
+ nsrc_stride = src_stride / 4;
+ }
+ if ((dest_width * dest_height) > canvas->private_data_size) {
+ if (canvas->private_data) {
+ free(canvas->private_data);
+ canvas->private_data = NULL;
+ canvas->private_data_size = 0;
+ }
+ canvas->private_data = (uint32_t *)malloc(4 * dest_width * dest_height);
+ if (!canvas->private_data) {
+ return;
+ }
+ canvas->private_data_size = dest_width * dest_height;
+ }
+ if (!clip) {
+ surf = cairo_get_target(cairo);
+ data = (uint32_t *)cairo_image_surface_get_data(surf);
+ nstride = cairo_image_surface_get_stride(surf) / 4;
+ data += dest->top * nstride + dest->left + (dest_height - 1) * nstride;
+ } else {
+ data = (uint32_t *)canvas->private_data;
+ nstride = dest_width;
+ data += (dest_height - 1) * nstride;
+ }
+
+ for (y = 0; y < dest_height; ++y) {
+ int y_mul_stride = -y * nstride;
+ new_y = ((y * y_mul) >> 16);
+ set_y = (new_y * nsrc_stride);
+ for (x = 0; x < dest_width; ++x) {
+ data[y_mul_stride + x] = ((uint32_t *)src_data)[set_y + ((x * x_mul) >> 16)];
+ }
+ }
+ if (clip) {
+ surf = cairo_image_surface_create_for_data((uint8_t *)canvas->private_data,
+ CAIRO_FORMAT_RGB24, dest_width,
+ dest_height, 4 * dest_width);
+ }
+ } else {
+ surf = cairo_image_surface_create_for_data((uint8_t *)src_data, CAIRO_FORMAT_RGB24,
+ src_width, src_height, src_stride);
+ }
+
+ if (clip || !(dest_width != src_width || dest_height != src_height)) {
+ cairo_set_source_surface(cairo, surf, dest->left, dest->top);
+ cairo_surface_destroy(surf);
+
+ cairo_rectangle(cairo, dest->left, dest->top, dest_width, dest_height);
+ cairo_fill(cairo);
+ }
+ cairo_restore(cairo);
+}
+
+static cairo_surface_t *canvas_surf_to_color_maks_invers(cairo_surface_t *surface,
+ uint32_t trans_color)
+{
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ uint8_t *src_line;
+ uint8_t *end_src_line;
+ int src_stride;
+ uint8_t *dest_line;
+ int dest_stride;
+ cairo_surface_t *mask;
+ int i;
+
+ ASSERT(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32 ||
+ cairo_image_surface_get_format(surface) == CAIRO_FORMAT_RGB24);
+
+ mask = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
+ if (cairo_surface_status(mask) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(mask)));
+ }
+
+ src_line = cairo_image_surface_get_data(surface);
+ src_stride = cairo_image_surface_get_stride(surface);
+ end_src_line = src_line + src_stride * height;
+
+ dest_line = cairo_image_surface_get_data(mask);
+ dest_stride = cairo_image_surface_get_stride(mask);
+
+ for (; src_line != end_src_line; src_line += src_stride, dest_line += dest_stride) {
+ memset(dest_line, 0xff, dest_stride);
+ for (i = 0; i < width; i++) {
+ if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
+ dest_line[i >> 3] &= ~(1 << (i & 0x07));
+ }
+ }
+ }
+
+ return mask;
+}
+
+void canvas_draw_transparent(CairoCanvas *canvas, Rect *bbox, Clip *clip, Transparent* transparent)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_pattern_t *pattern;
+ cairo_pattern_t *mask;
+ cairo_surface_t *surface;
+ cairo_surface_t *mask_surf;
+ cairo_matrix_t matrix;
+
+ cairo_save(cairo);
+ cairo_rectangle(cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(cairo);
+ canvas_clip(canvas, clip);
+
+ pattern = canvas_src_image_to_pat(canvas, transparent->src_bitmap, &transparent->src_area, bbox,
+ 0, IMAGE_SCALE_NEAREST);
+ if (cairo_pattern_get_surface(pattern, &surface) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("surfase from pattern pattern failed");
+ }
+
+ mask_surf = canvas_surf_to_color_maks_invers(surface, transparent->true_color);
+ mask = cairo_pattern_create_for_surface(mask_surf);
+ cairo_surface_destroy(mask_surf);
+ if (cairo_pattern_status(mask) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(pattern)));
+ }
+ cairo_pattern_get_matrix(pattern, &matrix);
+ cairo_pattern_set_matrix(mask, &matrix);
+
+ cairo_set_source(cairo, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
+ cairo_mask(cairo, mask);
+ cairo_pattern_destroy(mask);
+
+ cairo_restore(cairo);
+}
+
+void canvas_draw_alpha_blend(CairoCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_pattern_t *pattern;
+
+ cairo_save(cairo);
+ cairo_rectangle(cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(cairo);
+ canvas_clip(canvas, clip);
+
+ pattern = canvas_src_image_to_pat(canvas, alpha_blend->src_bitmap, &alpha_blend->src_area, bbox,
+ 0, IMAGE_SCALE_INTERPOLATE);
+ cairo_set_source(cairo, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_ATOP);
+ cairo_paint_with_alpha(cairo, (double)alpha_blend->alpha / 0xff);
+
+ cairo_restore(cairo);
+}
+
+void canvas_draw_opaque(CairoCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque)
+{
+ cairo_pattern_t *pattern;
+ DrawMaskData draw_data;
+
+ draw_data.cairo = canvas->cairo;
+
+ cairo_save(draw_data.cairo);
+ canvas_clip(canvas, clip);
+
+ pattern = canvas_src_image_to_pat(canvas, opaque->src_bitmap, &opaque->src_area, bbox,
+ opaque->rop_decriptor & ROPD_INVERS_SRC, opaque->scale_mode);
+ cairo_set_source(draw_data.cairo, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_set_operator(draw_data.cairo, CAIRO_OPERATOR_RASTER_COPY);
+ if ((draw_data.mask = canvas_get_mask_pattern(canvas, &opaque->mask, bbox->left, bbox->top))) {
+ cairo_rectangle(draw_data.cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(draw_data.cairo);
+ cairo_mask(draw_data.cairo, draw_data.mask);
+ canvas_draw(canvas, &opaque->brush, opaque->rop_decriptor, __draw_mask, &draw_data);
+ cairo_pattern_destroy(draw_data.mask);
+ } else {
+ cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_fill_preserve(draw_data.cairo);
+ canvas_draw(canvas, &opaque->brush, opaque->rop_decriptor, (DrawMethod)cairo_fill_preserve,
+ draw_data.cairo);
+ cairo_new_path(draw_data.cairo);
+ }
+
+ cairo_restore(draw_data.cairo);
+}
+
+void canvas_draw_blend(CairoCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend)
+{
+ cairo_pattern_t *pattern;
+ DrawMaskData mask_data;
+
+ mask_data.cairo = canvas->cairo;
+
+ cairo_save(mask_data.cairo);
+ canvas_clip(canvas, clip);
+
+ pattern = canvas_src_image_to_pat(canvas, blend->src_bitmap, &blend->src_area, bbox,
+ blend->rop_decriptor & ROPD_INVERS_SRC, blend->scale_mode);
+
+ if ((mask_data.mask = canvas_get_mask_pattern(canvas, &blend->mask, bbox->left, bbox->top))) {
+ cairo_rectangle(mask_data.cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(mask_data.cairo);
+ canvas_draw_with_pattern(canvas, pattern, blend->rop_decriptor, __draw_mask, &mask_data);
+ cairo_pattern_destroy(mask_data.mask);
+ } else {
+ cairo_rectangle(mask_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ canvas_draw_with_pattern(canvas, pattern, blend->rop_decriptor,
+ (DrawMethod)cairo_fill_preserve,
+ mask_data.cairo);
+ cairo_new_path(mask_data.cairo);
+ }
+ cairo_pattern_destroy(pattern);
+ cairo_restore(mask_data.cairo);
+}
+
+static inline void canvas_fill_common(CairoCanvas *canvas, Rect *bbox, Clip *clip,
+ QMask *qxl_mask)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_pattern_t *mask;
+
+ canvas_clip(canvas, clip);
+ if ((mask = canvas_get_mask_pattern(canvas, qxl_mask, bbox->left, bbox->top))) {
+ cairo_rectangle(cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(cairo);
+ cairo_mask(cairo, mask);
+ cairo_pattern_destroy(mask);
+ } else {
+ cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_fill(cairo);
+ }
+}
+
+void canvas_draw_blackness(CairoCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_save(cairo);
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ canvas_fill_common(canvas, bbox, clip, &blackness->mask);
+ cairo_restore(cairo);
+}
+
+void canvas_draw_whiteness(CairoCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_save(cairo);
+ cairo_set_source_rgb(cairo, 1, 1, 1);
+ canvas_fill_common(canvas, bbox, clip, &whiteness->mask);
+ cairo_restore(cairo);
+}
+
+void canvas_draw_invers(CairoCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_save(cairo);
+ cairo_set_source_rgb(cairo, 1, 1, 1);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_XOR);
+ canvas_fill_common(canvas, bbox, clip, &invers->mask);
+ cairo_restore(cairo);
+}
+
+void canvas_draw_rop3(CairoCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_pattern_t *mask;
+ cairo_surface_t *d;
+ cairo_surface_t *s;
+ Point pos;
+ int width;
+ int heigth;
+ double x_pos;
+ double y_pos;
+ Point src_pos;
+
+ cairo_save(cairo);
+ canvas_clip(canvas, clip);
+ width = bbox->right - bbox->left;
+ heigth = bbox->bottom - bbox->top;
+ x_pos = bbox->left;
+ y_pos = bbox->top;
+ cairo_user_to_device(cairo, &x_pos, &y_pos);
+ pos.x = (INT32)x_pos;
+ pos.y = (INT32)y_pos;
+ d = canvas_surface_from_self(canvas, &pos, width, heigth);
+ s = canvas_get_image(&canvas->base, rop3->src_bitmap);
+
+ if (!rect_is_same_size(bbox, &rop3->src_area)) {
+ cairo_surface_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
+ rop3->scale_mode);
+ cairo_surface_destroy(s);
+ s = scaled_s;
+ src_pos.x = 0;
+ src_pos.y = 0;
+ } else {
+ src_pos.x = rop3->src_area.left;
+ src_pos.y = rop3->src_area.top;
+ }
+ if (cairo_image_surface_get_width(s) - src_pos.x < width ||
+ cairo_image_surface_get_height(s) - src_pos.y < heigth) {
+ CANVAS_ERROR("bad src bitmap size");
+ }
+ if (rop3->brush.type == BRUSH_TYPE_PATTERN) {
+ cairo_surface_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
+ Point pat_pos;
+
+ pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % cairo_image_surface_get_width(p);
+ pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % cairo_image_surface_get_height(p);
+ do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
+ cairo_surface_destroy(p);
+ } else {
+ uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
+ canvas_16bpp_to_32bpp(rop3->brush.u.color);
+ do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
+ }
+ cairo_surface_destroy(s);
+ cairo_set_source_surface(cairo, d, bbox->left, bbox->top);
+ cairo_surface_destroy(d);
+ if ((mask = canvas_get_mask_pattern(canvas, &rop3->mask, bbox->left, bbox->top))) {
+ cairo_rectangle(cairo,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_clip(cairo);
+ cairo_mask(cairo, mask);
+ cairo_pattern_destroy(mask);
+ } else {
+ cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+ cairo_fill(cairo);
+ }
+ cairo_restore(cairo);
+}
+
+#define FAST_COPY_BITS
+
+#ifdef FAST_COPY_BITS
+
+static inline void __canvas_copy_bits_up(uint8_t *data, const int stride,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + src_y * stride + src_x * sizeof(uint32_t);
+ uint8_t *dest = data + dest_y * stride + dest_x * sizeof(uint32_t);
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride, src += stride) {
+ memcpy(dest, src, width * sizeof(uint32_t));
+ }
+}
+
+static inline void __canvas_copy_bits_down(uint8_t *data, const int stride,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + (src_y + height - 1) * stride + src_x * sizeof(uint32_t);
+ uint8_t *end = data + (dest_y - 1) * stride + dest_x * sizeof(uint32_t);
+ uint8_t *dest = end + height * stride;
+
+ for (; dest != end; dest -= stride, src -= stride) {
+ memcpy(dest, src, width * sizeof(uint32_t));
+ }
+}
+
+static inline void __canvas_copy_bits_right(uint8_t *data, const int stride,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + src_y * stride + (src_x + width - 1) * sizeof(uint32_t);
+ uint8_t *dest = data + dest_y * stride + (dest_x + width - 1) * sizeof(uint32_t);
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride, src += stride) {
+ uint32_t *src_pix = (uint32_t *)src;
+ uint32_t *end_pix = src_pix - width;
+ uint32_t *dest_pix = (uint32_t *)dest;
+
+ for (; src_pix > end_pix; src_pix--, dest_pix--) {
+ *dest_pix = *src_pix;
+ }
+ }
+}
+
+static inline void __canvas_copy_rect_bits(uint8_t *data, const int stride, Rect *dest_rect,
+ Point *src_pos)
+{
+ if (dest_rect->top > src_pos->y) {
+ __canvas_copy_bits_down(data, stride, src_pos->x, src_pos->y,
+ dest_rect->right - dest_rect->left,
+ dest_rect->bottom - dest_rect->top,
+ dest_rect->left, dest_rect->top);
+ } else if (dest_rect->top < src_pos->y || dest_rect->left < src_pos->x) {
+ __canvas_copy_bits_up(data, stride, src_pos->x, src_pos->y,
+ dest_rect->right - dest_rect->left,
+ dest_rect->bottom - dest_rect->top,
+ dest_rect->left, dest_rect->top);
+ } else {
+ __canvas_copy_bits_right(data, stride, src_pos->x, src_pos->y,
+ dest_rect->right - dest_rect->left,
+ dest_rect->bottom - dest_rect->top,
+ dest_rect->left, dest_rect->top);
+ }
+}
+
+static inline void canvas_copy_fix_clip_area(const Rect *dest,
+ const Point *src_pos,
+ const Rect *now,
+ Point *ret_pos,
+ Rect *ret_dest)
+{
+ *ret_dest = *now;
+ rect_sect(ret_dest, dest);
+ ret_pos->x = src_pos->x + (ret_dest->left - dest->left);
+ ret_pos->y = src_pos->y + (ret_dest->top - dest->top);
+}
+
+static inline void __canvas_copy_region_bits(uint8_t *data, int stride, Rect *dest_rect,
+ Point *src_pos, QRegion *region)
+{
+ Rect curr_area;
+ Point curr_pos;
+ Rect *now;
+ Rect *end;
+
+ if (dest_rect->top > src_pos->y) {
+ end = region->rects - 1;
+ now = end + region->num_rects;
+ if (dest_rect->left < src_pos->x) {
+ for (; now > end; now--) {
+ Rect *line_end = now;
+ Rect *line_pos;
+
+ while (now - 1 > end && now->top == now[-1].top) {
+ now--;
+ }
+
+ for (line_pos = now; line_pos <= line_end; line_pos++) {
+ canvas_copy_fix_clip_area(dest_rect, src_pos, line_pos, &curr_pos, &curr_area);
+ __canvas_copy_bits_down(data, stride, curr_pos.x, curr_pos.y,
+ curr_area.right - curr_area.left,
+ curr_area.bottom - curr_area.top,
+ curr_area.left, curr_area.top);
+ }
+ }
+ } else {
+ for (; now > end; now--) {
+ canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
+ __canvas_copy_bits_down(data, stride, curr_pos.x, curr_pos.y,
+ curr_area.right - curr_area.left,
+ curr_area.bottom - curr_area.top,
+ curr_area.left, curr_area.top);
+ }
+ }
+ } else if (dest_rect->top < src_pos->y || dest_rect->left < src_pos->x) {
+ now = region->rects;
+ end = now + region->num_rects;
+ if (dest_rect->left > src_pos->x) {
+ for (; now < end; now++) {
+ Rect *line_end = now;
+ Rect *line_pos;
+
+ while (now + 1 < end && now->top == now[1].top) {
+ now++;
+ }
+
+ for (line_pos = now; line_pos >= line_end; line_pos--) {
+ canvas_copy_fix_clip_area(dest_rect, src_pos, line_pos, &curr_pos, &curr_area);
+ __canvas_copy_bits_up(data, stride, curr_pos.x, curr_pos.y,
+ curr_area.right - curr_area.left,
+ curr_area.bottom - curr_area.top,
+ curr_area.left, curr_area.top);
+ }
+ }
+ } else {
+ for (; now < end; now++) {
+ canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
+ __canvas_copy_bits_up(data, stride, curr_pos.x, curr_pos.y,
+ curr_area.right - curr_area.left,
+ curr_area.bottom - curr_area.top,
+ curr_area.left, curr_area.top);
+ }
+ }
+ } else {
+ end = region->rects - 1;
+ now = end + region->num_rects;
+ for (; now > end; now--) {
+ canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
+ __canvas_copy_bits_right(data, stride, curr_pos.x, curr_pos.y,
+ curr_area.right - curr_area.left,
+ curr_area.bottom - curr_area.top,
+ curr_area.left, curr_area.top);
+ }
+ }
+}
+
+#endif
+
+void canvas_copy_bits(CairoCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_surface_t *surface;
+ int32_t width;
+ int32_t heigth;
+
+ cairo_save(cairo);
+#ifdef FAST_COPY_BITS
+ switch (clip->type) {
+ case CLIP_TYPE_NONE: {
+ surface = cairo_get_target(cairo);
+ __canvas_copy_rect_bits(cairo_image_surface_get_data(surface),
+ cairo_image_surface_get_stride(surface),
+ bbox, src_pos);
+ break;
+ }
+ case CLIP_TYPE_RECTS: {
+ surface = cairo_get_target(cairo);
+ uint32_t *n = (uint32_t *)GET_ADDRESS(clip->data);
+ access_test(&canvas->base, n, sizeof(uint32_t));
+
+ Rect *now = (Rect *)(n + 1);
+ Rect *end = now + *n;
+ access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
+ uint8_t *data = cairo_image_surface_get_data(surface);
+ int stride = cairo_image_surface_get_stride(surface);
+
+ //using QRegion in order to sort and remove intersections
+ QRegion region;
+ region_init(&region);
+ for (; now < end; now++) {
+ region_add(&region, now);
+ }
+ __canvas_copy_region_bits(data, stride, bbox, src_pos, &region);
+ region_destroy(&region);
+ break;
+ }
+ default:
+#endif
+ canvas_clip(canvas, clip);
+
+ width = bbox->right - bbox->left;
+ heigth = bbox->bottom - bbox->top;
+ surface = canvas_surface_from_self(canvas, src_pos, width, heigth);
+ cairo_set_source_surface(cairo, surface, bbox->left, bbox->top);
+ cairo_surface_destroy(surface);
+ cairo_rectangle(cairo, bbox->left, bbox->top, width, heigth);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
+ cairo_fill(cairo);
+#ifdef FAST_COPY_BITS
+ }
+
+#endif
+ cairo_restore(cairo);
+}
+
+static void canvas_draw_raster_str(CairoCanvas *canvas, String *str, int bpp,
+ Brush *brush, uint16_t rop_decriptor)
+{
+ cairo_surface_t *str_mask;
+ DrawMaskData draw_data;
+ cairo_matrix_t matrix;
+ Point pos;
+
+ str_mask = canvas_get_str_mask(&canvas->base, str, bpp, &pos);
+ draw_data.cairo = canvas->cairo;
+ draw_data.mask = cairo_pattern_create_for_surface(str_mask);
+ if (cairo_pattern_status(draw_data.mask) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(str_mask);
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(draw_data.mask)));
+ }
+ cairo_matrix_init_translate(&matrix, -pos.x, -pos.y);
+ cairo_pattern_set_matrix(draw_data.mask, &matrix);
+ canvas_draw(canvas, brush, rop_decriptor, __draw_mask, &draw_data);
+ cairo_pattern_destroy(draw_data.mask);
+ cairo_surface_destroy(str_mask);
+}
+
+static void canvas_draw_vector_str(CairoCanvas *canvas, String *str, Brush *brush,
+ uint16_t rop_decriptor)
+{
+ VectotGlyph *glyph = (VectotGlyph *)str->data;
+ int i;
+
+ for (i = 0; i < str->length; i++) {
+ VectotGlyph *next_glyph = canvas_next_vector_glyph(glyph);
+ access_test(&canvas->base, glyph, (uint8_t *)next_glyph - (uint8_t *)glyph);
+ canvas_set_path(canvas, glyph->data);
+ glyph = next_glyph;
+ }
+ canvas_draw(canvas, brush, rop_decriptor, (DrawMethod)cairo_fill_preserve, canvas->cairo);
+ cairo_new_path(canvas->cairo);
+}
+
+void canvas_draw_text(CairoCanvas *canvas, Rect *bbox, Clip *clip, Text *text)
+{
+ cairo_t *cairo = canvas->cairo;
+ String *str;
+
+ cairo_save(cairo);
+ canvas_clip(canvas, clip);
+ if (!rect_is_empty(&text->back_area)) {
+ cairo_rectangle(cairo,
+ text->back_area.left,
+ text->back_area.top,
+ text->back_area.right - text->back_area.left,
+ text->back_area.bottom - text->back_area.top);
+ canvas_draw(canvas, &text->back_brush, text->back_mode,
+ (DrawMethod)cairo_fill_preserve, cairo);
+ cairo_new_path(cairo);
+ }
+ str = (String *)GET_ADDRESS(text->str);
+
+ if (str->flags & STRING_RASTER_A1) {
+ canvas_draw_raster_str(canvas, str, 1, &text->fore_brush, text->fore_mode);
+ } else if (str->flags & STRING_RASTER_A4) {
+ canvas_draw_raster_str(canvas, str, 4, &text->fore_brush, text->fore_mode);
+ } else if (str->flags & STRING_RASTER_A8) {
+ WARN("untested path A8 glyphs, doing nothing");
+ if (0) {
+ canvas_draw_raster_str(canvas, str, 8, &text->fore_brush, text->fore_mode);
+ }
+ } else {
+ WARN("untested path vector glyphs, doing nothing");
+ if (0) {
+ canvas_draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
+ }
+ }
+ cairo_restore(cairo);
+}
+
+void canvas_draw_stroke(CairoCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ cairo_save(cairo);
+ canvas_clip(canvas, clip);
+
+ canvas_set_line_attr_no_dash(canvas, &stroke->attr);
+ canvas_set_path(canvas, GET_ADDRESS(stroke->path));
+ if (stroke->attr.flags & LINE_ATTR_STYLED) {
+ canvas_draw(canvas, &stroke->brush, stroke->back_mode,
+ (DrawMethod)cairo_stroke_preserve, cairo);
+ canvas_set_dash(canvas, stroke->attr.style_nseg, stroke->attr.style,
+ !!(stroke->attr.flags & LINE_ATTR_STARTGAP));
+ }
+ canvas_draw(canvas, &stroke->brush, stroke->fore_mode, (DrawMethod)cairo_stroke_preserve,
+ cairo);
+ cairo_new_path(cairo);
+ cairo_restore(cairo);
+}
+
+void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const Rect *area)
+{
+ cairo_t *cairo = canvas->cairo;
+ cairo_surface_t* surface;
+ uint8_t *src;
+ int src_stride;
+ uint8_t *dest_end;
+
+ ASSERT(canvas && area);
+
+ surface = cairo_get_target(cairo);
+ src_stride = cairo_image_surface_get_stride(surface);
+ src = cairo_image_surface_get_data(surface) + area->top * src_stride +
+ area->left * sizeof(uint32_t);
+ dest_end = dest + (area->bottom - area->top) * dest_stride;
+ for (; dest != dest_end; dest += dest_stride, src += src_stride) {
+ memcpy(dest, src, dest_stride);
+ }
+}
+
+void canvas_group_start(CairoCanvas *canvas, int n_clip_rects, Rect *clip_rects)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ cairo_save(cairo);
+
+ if (n_clip_rects) {
+ Rect *end = clip_rects + n_clip_rects;
+ for (; clip_rects < end; clip_rects++) {
+ cairo_rectangle(cairo,
+ clip_rects->left,
+ clip_rects->top,
+ clip_rects->right - clip_rects->left,
+ clip_rects->bottom - clip_rects->top);
+ }
+ cairo_clip(cairo);
+ }
+}
+
+void canvas_group_end(CairoCanvas *canvas)
+{
+ cairo_restore(canvas->cairo);
+}
+
+void canvas_clear(CairoCanvas *canvas)
+{
+ cairo_t *cairo = canvas->cairo;
+
+ ASSERT(cairo);
+ cairo_save(cairo);
+ cairo_reset_clip(cairo);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cairo);
+ cairo_restore(cairo);
+}
+
+cairo_t *canvas_get_cairo(CairoCanvas *canvas)
+{
+ return canvas->cairo;
+}
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void canvas_set_access_params(CairoCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max)
+{
+ __canvas_set_access_params(&canvas->base, delta, base, max);
+}
+
+#else
+void canvas_set_access_params(CairoCanvas *canvas, ADDRESS delta)
+{
+ __canvas_set_access_params(&canvas->base, delta);
+}
+
+#endif
+
+void canvas_destroy(CairoCanvas *canvas)
+{
+ if (!canvas) {
+ return;
+ }
+ canvas_base_destroy(&canvas->base);
+ if (canvas->private_data) {
+ free(canvas->private_data);
+ }
+ free(canvas);
+}
+
+static int need_init = 1;
+
+#ifdef CAIRO_CANVAS_CACHE
+CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get,
+ void *palette_cache_opaque, palette_cache_put_fn_t palette_cache_put,
+ palette_cache_get_fn_t palette_cache_get,
+ palette_cache_release_fn_t palette_cache_release
+#elif defined(CAIRO_CANVAS_IMAGE_CACHE)
+CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get
+#else
+CairoCanvas *canvas_create(cairo_t *cairo, int bits
+#endif
+#ifdef USE_GLZ
+ , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+#endif
+ )
+{
+ CairoCanvas *canvas;
+ int init_ok;
+
+ if (need_init || !(canvas = (CairoCanvas *)malloc(sizeof(CairoCanvas)))) {
+ return NULL;
+ }
+ memset(canvas, 0, sizeof(CairoCanvas));
+#ifdef CAIRO_CANVAS_CACHE
+ init_ok = canvas_base_init(&canvas->base, bits,
+ bits_cache_opaque,
+ bits_cache_put,
+ bits_cache_get,
+ palette_cache_opaque,
+ palette_cache_put,
+ palette_cache_get,
+ palette_cache_release
+#elif defined(CAIRO_CANVAS_IMAGE_CACHE)
+ init_ok = canvas_base_init(&canvas->base, bits,
+ bits_cache_opaque,
+ bits_cache_put,
+ bits_cache_get
+#else
+ init_ok = canvas_base_init(&canvas->base, bits
+#endif
+#ifdef USE_GLZ
+ ,
+ glz_decoder_opaque,
+ glz_decode
+#endif
+ );
+ canvas->cairo = cairo;
+ canvas->private_data = NULL;
+ canvas->private_data_size = 0;
+ cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
+ return canvas;
+}
+
+void cairo_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+ rop3_init();
+}
+