summaryrefslogtreecommitdiffstats
path: root/common/canvas_base.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/canvas_base.c')
-rw-r--r--common/canvas_base.c1617
1 files changed, 1617 insertions, 0 deletions
diff --git a/common/canvas_base.c b/common/canvas_base.c
new file mode 100644
index 00000000..a5519ffc
--- /dev/null
+++ b/common/canvas_base.c
@@ -0,0 +1,1617 @@
+/*
+ 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 <stdarg.h>
+#include <cairo.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+
+#include "draw.h"
+#include "quic.h"
+#include "lz.h"
+#include "canvas_base.h"
+#include "canvas_utils.h"
+#include "rect.h"
+
+#include "mutex.h"
+
+#ifndef CANVAS_ERROR
+#define CANVAS_ERROR(format, ...) { \
+ printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \
+ abort(); \
+}
+#endif
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+#define access_test(cancas, ptr, size) \
+ if ((unsigned long)(ptr) < (cancas)->base || \
+ (unsigned long)(ptr) + (size) > (cancas)->max) { \
+ CANVAS_ERROR("access violation 0x%lx %lu", (unsigned long)ptr, (unsigned long)(size)); \
+ }
+#else
+#define access_test(cancas, base, size)
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+#ifndef WARN
+#define WARN(x) printf("warning: %s\n", x)
+#endif
+
+#ifndef DBG
+#define DBG(level, format, ...) printf("%s: debug: " format "\n", __FUNCTION__, ## __VA_ARGS__);
+#endif
+
+#ifndef ALIGN
+#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
+#endif
+
+#ifdef WIN32
+typedef struct __declspec (align(1)) LZImage {
+#else
+typedef struct __attribute__ ((__packed__)) LZImage {
+#endif
+ ImageDescriptor descriptor;
+ union {
+ LZ_RGBData lz_rgb;
+ LZ_PLTData lz_plt;
+ };
+} LZImage;
+
+static const cairo_user_data_key_t invers_data_type = {0};
+
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+/* should be defined and initialized once in application.cpp */
+extern mutex_t cairo_surface_user_data_mutex;
+#endif
+
+static inline double fix_to_double(FIXED28_4 fixed)
+{
+ return (double)(fixed & 0x0f) / 0x0f + (fixed >> 4);
+}
+
+static inline uint32_t canvas_16bpp_to_32bpp(uint32_t color)
+{
+ uint32_t ret;
+
+ ret = ((color & 0x001f) << 3) | ((color & 0x001c) >> 2);
+ ret |= ((color & 0x03e0) << 6) | ((color & 0x0380) << 1);
+ ret |= ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
+
+ return ret;
+}
+
+static inline int test_bit(void* addr, int bit)
+{
+ return !!(((uint32_t*)addr)[bit >> 5] & (1 << (bit & 0x1f)));
+}
+
+static inline int test_bit_be(void* addr, int bit)
+{
+ return !!(((uint8_t*)addr)[bit >> 3] & (0x80 >> (bit & 0x07)));
+}
+
+#ifdef WIN32
+static HDC create_compatible_dc()
+{
+ HDC dc = CreateCompatibleDC(NULL);
+ if (!dc) {
+ CANVAS_ERROR("create compatible DC failed");
+ }
+ return dc;
+}
+
+#endif
+
+typedef struct LzData {
+ LzUsrContext usr;
+ LzContext *lz;
+ LzDecodeUsrData decode_data;
+ jmp_buf jmp_env;
+ char message_buf[512];
+} LzData;
+
+typedef struct GlzData {
+ void *decoder_opaque;
+ glz_decode_fn_t decode;
+ LzDecodeUsrData decode_data;
+} GlzData;
+
+typedef struct QuicData {
+ QuicUsrContext usr;
+ QuicContext *quic;
+ jmp_buf jmp_env;
+#ifndef CAIRO_CANVAS_NO_CHUNKS
+ ADDRESS next;
+ ADDRESS address_delta;
+#endif
+ char message_buf[512];
+} QuicData;
+
+typedef struct CanvasBase {
+ uint32_t color_shift;
+ uint32_t color_mask;
+ QuicData quic_data;
+ ADDRESS address_delta;
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+ unsigned long base;
+ unsigned long max;
+#endif
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+ void *bits_cache_opaque;
+ bits_cache_put_fn_t bits_cache_put;
+ bits_cache_get_fn_t bits_cache_get;
+#endif
+#ifdef CAIRO_CANVAS_CACHE
+ 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;
+#endif
+#ifdef WIN32
+ HDC dc;
+#endif
+
+ LzData lz_data;
+ GlzData glz_data;
+} CanvasBase;
+
+
+#ifndef CAIRO_CANVAS_NO_CHUNKS
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#endif
+
+typedef struct ATTR_PACKED DataChunk {
+ UINT32 size;
+ ADDRESS prev;
+ ADDRESS next;
+ UINT8 data[0];
+} DataChunk;
+
+#undef ATTR_PACKED
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#endif
+
+
+static inline void canvas_localize_palette(CanvasBase *canvas, Palette *palette)
+{
+ if (canvas->color_shift == 5) {
+ UINT32 *now = palette->ents;
+ UINT32 *end = now + palette->num_ents;
+ for (; now < end; now++) {
+ *now = canvas_16bpp_to_32bpp(*now);
+ }
+ }
+}
+
+//#define DEBUG_DUMP_COMPRESS
+#ifdef DEBUG_DUMP_COMPRESS
+static void dump_surface(cairo_surface_t *surface, int cache);
+#endif
+static cairo_surface_t *canvas_get_quic(CanvasBase *canvas, QUICImage *image, int invers)
+{
+ cairo_surface_t *surface = NULL;
+ QuicData *quic_data = &canvas->quic_data;
+ QuicImageType type;
+ uint8_t *dest;
+ int stride;
+ int width;
+ int height;
+ int alpha;
+#ifndef CAIRO_CANVAS_NO_CHUNKS
+ DataChunk **tmp;
+ DataChunk *chunk;
+#endif
+
+ if (setjmp(quic_data->jmp_env)) {
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("quic error, %s", quic_data->message_buf);
+ }
+
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+ if (quic_decode_begin(quic_data->quic, (uint32_t *)image->quic.data,
+ image->quic.data_size >> 2, &type, &width, &height) == QUIC_ERROR) {
+ CANVAS_ERROR("quic decode begin failed");
+ }
+#else
+ tmp = (DataChunk **)image->quic.data;
+ chunk = *tmp;
+ quic_data->next = chunk->next;
+ quic_data->address_delta = canvas->address_delta;
+ if (quic_decode_begin(quic_data->quic, (uint32_t *)chunk->data, chunk->size >> 2,
+ &type, &width, &height) == QUIC_ERROR) {
+ CANVAS_ERROR("quic decode begin failed");
+ }
+#endif
+
+ switch (type) {
+ case QUIC_IMAGE_TYPE_RGBA:
+ alpha = 1;
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGB24:
+ case QUIC_IMAGE_TYPE_RGB16:
+ alpha = 0;
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ case QUIC_IMAGE_TYPE_GRAY:
+ default:
+ CANVAS_ERROR("unexpected image type");
+ }
+
+ ASSERT((uint32_t)width == image->descriptor.width);
+ ASSERT((uint32_t)height == image->descriptor.height);
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+ width, height, FALSE);
+
+ 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);
+ stride = cairo_image_surface_get_stride(surface);
+ if (quic_decode(quic_data->quic, alpha ? QUIC_IMAGE_TYPE_RGBA : QUIC_IMAGE_TYPE_RGB32,
+ dest, stride) == QUIC_ERROR) {
+ CANVAS_ERROR("quic decode failed");
+ }
+
+ if (invers) {
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)dest;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0x00ffffff;
+ }
+ }
+ }
+
+#ifdef DEBUG_DUMP_COMPRESS
+ dump_surface(surface, 0);
+#endif
+ return surface;
+}
+
+static inline void canvas_copy_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) {
+ memcpy(dest, src, width << 2);
+ }
+}
+
+static inline void canvas_copy_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_copy_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);
+ }
+ }
+}
+
+static inline void canvas_copy_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++)];
+ }
+ }
+}
+
+static inline void canvas_copy_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];
+ *(dest_line++) = palette->ents[*(now++) & 0x0f];
+ }
+ if (width & 1) {
+ *(dest_line) = palette->ents[(*src >> 4) & 0x0f];
+ }
+ }
+}
+
+static inline void canvas_copy_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;
+ } else {
+ *(dest_line++) = back_color;
+ }
+ }
+ }
+}
+
+static cairo_surface_t *canvas_bitmap_to_surface(CanvasBase *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, src, bitmap->y * src_stride);
+
+ cairo_surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ (bitmap->format == BITMAP_FMT_RGBA) ? CAIRO_FORMAT_ARGB32 :
+ CAIRO_FORMAT_RGB24,
+ bitmap->x, bitmap->y, FALSE);
+ 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 * ((int)bitmap->y - 1);
+ dest_stride = -dest_stride;
+ }
+
+ switch (bitmap->format) {
+ case BITMAP_FMT_32BIT:
+ case BITMAP_FMT_RGBA:
+ canvas_copy_32bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_24BIT:
+ canvas_copy_24bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_16BIT:
+ canvas_copy_16bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
+ break;
+ case BITMAP_FMT_8BIT:
+ canvas_copy_8bpp(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ case BITMAP_FMT_4BIT_BE:
+ canvas_copy_4bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ case BITMAP_FMT_1BIT_BE:
+ canvas_copy_1bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
+ break;
+ }
+ return cairo_surface;
+}
+
+#ifdef CAIRO_CANVAS_CACHE
+
+static inline Palette *canvas_get_palett(CanvasBase *canvas, ADDRESS base_palette, uint8_t flags)
+{
+ Palette *palette;
+ if (!base_palette) {
+ return NULL;
+ }
+
+ if (flags & BITMAP_PAL_FROM_CACHE) {
+ palette = canvas->palette_cache_get(canvas->palette_cache_opaque, base_palette);
+ } else if (flags & BITMAP_PAL_CACHE_ME) {
+ palette = (Palette *)GET_ADDRESS(base_palette);
+ access_test(canvas, palette, sizeof(Palette));
+ access_test(canvas, palette, sizeof(Palette) + palette->num_ents * sizeof(uint32_t));
+ canvas_localize_palette(canvas, palette);
+ canvas->palette_cache_put(canvas->palette_cache_opaque, palette);
+ } else {
+ palette = (Palette *)GET_ADDRESS(base_palette);
+ canvas_localize_palette(canvas, palette);
+ }
+ return palette;
+}
+
+static cairo_surface_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int invers)
+{
+ LzData *lz_data = &canvas->lz_data;
+ uint8_t *comp_buf = NULL;
+ int comp_size;
+ uint8_t *decomp_buf = NULL;
+ uint8_t *src;
+ LzImageType type;
+ Palette *palette;
+ int alpha;
+ int n_comp_pixels;
+ int width;
+ int height;
+ int top_down;
+ int stride;
+
+ if (setjmp(lz_data->jmp_env)) {
+ if (decomp_buf) {
+ free(decomp_buf);
+ }
+ CANVAS_ERROR("lz error, %s", lz_data->message_buf);
+ }
+
+ if (image->descriptor.type == IMAGE_TYPE_LZ_RGB) {
+ comp_buf = image->lz_rgb.data;
+ comp_size = image->lz_rgb.data_size;
+ palette = NULL;
+ } else if (image->descriptor.type == IMAGE_TYPE_LZ_PLT) {
+ comp_buf = image->lz_plt.data;
+ comp_size = image->lz_plt.data_size;
+ palette = canvas_get_palett(canvas, image->lz_plt.palette, image->lz_plt.flags);
+ } else {
+ CANVAS_ERROR("unexpected image type");
+ }
+
+ lz_decode_begin(lz_data->lz, comp_buf, comp_size, &type,
+ &width, &height, &n_comp_pixels, &top_down, palette);
+
+ switch (type) {
+ case LZ_IMAGE_TYPE_RGBA:
+ alpha = 1;
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ case LZ_IMAGE_TYPE_RGB24:
+ case LZ_IMAGE_TYPE_RGB16:
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ case LZ_IMAGE_TYPE_PLT8:
+ alpha = 0;
+ break;
+ default:
+ CANVAS_ERROR("unexpected LZ image type");
+ }
+
+ ASSERT(width == image->descriptor.width);
+ ASSERT(height == image->descriptor.height);
+
+ ASSERT((image->descriptor.type == IMAGE_TYPE_LZ_PLT) || (n_comp_pixels == width * height));
+#ifdef WIN32
+ lz_data->decode_data.dc = canvas->dc;
+#endif
+
+
+ alloc_lz_image_surface(&lz_data->decode_data, alpha ? LZ_IMAGE_TYPE_RGBA : LZ_IMAGE_TYPE_RGB32,
+ width, height, n_comp_pixels, top_down);
+
+ src = cairo_image_surface_get_data(lz_data->decode_data.out_surface);
+
+ stride = (n_comp_pixels / height) * 4;
+ if (!top_down) {
+ stride = -stride;
+ decomp_buf = src + stride * (height - 1);
+ } else {
+ decomp_buf = src;
+ }
+
+ lz_decode(lz_data->lz, alpha ? LZ_IMAGE_TYPE_RGBA : LZ_IMAGE_TYPE_RGB32, decomp_buf);
+
+ if (invers) {
+ uint8_t *line = src;
+ uint8_t *end = src + height * stride;
+ for (; line != end; line += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)line;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0x00ffffff;
+ }
+ }
+ }
+
+ return lz_data->decode_data.out_surface;
+}
+
+// don't handle plts since bitmaps with plt can be decoded globaly to RGB32 (because
+// same byte sequence can be transformed to different RGB pixels by different plts)
+static cairo_surface_t *canvas_get_glz(CanvasBase *canvas, LZImage *image)
+{
+ ASSERT(image->descriptor.type == IMAGE_TYPE_GLZ_RGB);
+#ifdef WIN32
+ canvas->glz_data.decode_data.dc = canvas->dc;
+#endif
+ canvas->glz_data.decode(canvas->glz_data.decoder_opaque, image->lz_rgb.data, NULL,
+ &canvas->glz_data.decode_data);
+ /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */
+ return (canvas->glz_data.decode_data.out_surface);
+}
+
+//#define DEBUG_DUMP_BITMAP
+
+#ifdef DEBUG_DUMP_BITMAP
+static void dump_bitmap(Bitmap *bitmap, Palette *palette)
+{
+ uint8_t* data = (uint8_t *)GET_ADDRESS(bitmap->data);
+ static uint32_t file_id = 0;
+ uint32_t i, j;
+ char file_str[200];
+ uint32_t id = ++file_id;
+
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%u.%ubpp", id, bitmap->format);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.%ubpp", id, bitmap->format);
+#endif
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+
+ fprintf(f, "%d\n", bitmap->format); // 1_LE,1_BE,....
+ fprintf(f, "%d %d\n", bitmap->x, bitmap->y); // width and height
+ fprintf(f, "%d\n", palette->num_ents); // #plt entries
+ for (i = 0; i < palette->num_ents; i++) {
+ fwrite(&(palette->ents[i]), 4, 1, f);
+ }
+ fprintf(f, "\n");
+
+ for (i = 0; i < bitmap->y; i++, data += bitmap->stride) {
+ uint8_t *now = data;
+ for (j = 0; j < bitmap->x; j++) {
+ fwrite(now, 1, 1, f);
+ now++;
+ }
+ }
+}
+
+#endif
+
+static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, Bitmap *bitmap)
+{
+ cairo_surface_t* surface;
+ Palette *palette;
+
+ palette = canvas_get_palett(canvas, bitmap->palette, bitmap->flags);
+#ifdef DEBUG_DUMP_BITMAP
+ if (palette) {
+ dump_bitmap(bitmap, palette);
+ }
+#endif
+
+ surface = canvas_bitmap_to_surface(canvas, bitmap, palette);
+
+ if (palette && (bitmap->flags & BITMAP_PAL_FROM_CACHE)) {
+ canvas->palette_cache_release(palette);
+ }
+
+ return surface;
+}
+
+#else
+
+
+static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, Bitmap *bitmap)
+{
+ Palette *palette;
+
+ if (!bitmap->palette) {
+ return canvas_bitmap_to_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, local_palette);
+ surface = canvas_bitmap_to_surface(canvas, bitmap, local_palette);
+ free(local_palette);
+ return surface;
+ } else {
+ return canvas_bitmap_to_surface(canvas, bitmap, palette);
+ }
+}
+
+#endif
+
+
+
+// caution: defining DEBUG_DUMP_SURFACE will dump both cached & non-cached
+// images to disk. it will reduce performance dramatically & eat
+// disk space rapidly. use it only for debugging.
+//#define DEBUG_DUMP_SURFACE
+
+#if defined(DEBUG_DUMP_SURFACE) || defined(DEBUG_DUMP_COMPRESS)
+
+static void dump_surface(cairo_surface_t *surface, int cache)
+{
+ static uint32_t file_id = 0;
+ int i, j;
+ char file_str[200];
+ cairo_format_t format = cairo_image_surface_get_format(surface);
+
+ if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) {
+ return;
+ }
+
+ uint8_t *data = cairo_image_surface_get_data(surface);
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ int stride = cairo_image_surface_get_stride(surface);
+
+ uint32_t id = ++file_id;
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%d\\%u.ppm", cache, id);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.ppm", id);
+#endif
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+ fprintf(f, "P6\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "#spicec dump\n");
+ fprintf(f, "255\n");
+ for (i = 0; i < height; i++, data += stride) {
+ uint8_t *now = data;
+ for (j = 0; j < width; j++) {
+ fwrite(&now[2], 1, 1, f);
+ fwrite(&now[1], 1, 1, f);
+ fwrite(&now[0], 1, 1, f);
+ now += 4;
+ }
+ }
+ fclose(f);
+}
+
+#endif
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+
+static void __release_surface(void *inv_surf)
+{
+ cairo_surface_destroy((cairo_surface_t *)inv_surf);
+}
+
+//#define DEBUG_LZ
+
+static cairo_surface_t *canvas_get_image(CanvasBase *canvas, ADDRESS addr)
+{
+ ImageDescriptor *descriptor = (ImageDescriptor *)GET_ADDRESS(addr);
+ cairo_surface_t *surface;
+ access_test(canvas, descriptor, sizeof(ImageDescriptor));
+#ifdef DEBUG_LZ
+ LOG_DEBUG("canvas_get_image image type: " << (int)descriptor->type);
+#endif
+
+ switch (descriptor->type) {
+ case IMAGE_TYPE_QUIC: {
+ QUICImage *image = (QUICImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(QUICImage));
+ surface = canvas_get_quic(canvas, image, 0);
+ break;
+ }
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+ case IMAGE_TYPE_LZ_PLT: {
+ access_test(canvas, descriptor, sizeof(LZ_PLTImage));
+ LZImage *image = (LZImage *)descriptor;
+ surface = canvas_get_lz(canvas, image, 0);
+ break;
+ }
+ case IMAGE_TYPE_LZ_RGB: {
+ access_test(canvas, descriptor, sizeof(LZ_RGBImage));
+ LZImage *image = (LZImage *)descriptor;
+ surface = canvas_get_lz(canvas, image, 0);
+ break;
+ }
+#endif
+#ifdef USE_GLZ
+ case IMAGE_TYPE_GLZ_RGB: {
+ access_test(canvas, descriptor, sizeof(LZ_RGBImage));
+ LZImage *image = (LZImage *)descriptor;
+ surface = canvas_get_glz(canvas, image);
+ break;
+ }
+#endif
+ case IMAGE_TYPE_FROM_CACHE:
+ return canvas->bits_cache_get(canvas->bits_cache_opaque, descriptor->id);
+ case IMAGE_TYPE_BITMAP: {
+ BitmapImage *bitmap = (BitmapImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(BitmapImage));
+ surface = canvas_get_bits(canvas, &bitmap->bitmap);
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+
+ if (descriptor->flags & IMAGE_CACHE_ME) {
+ canvas->bits_cache_put(canvas->bits_cache_opaque, descriptor->id, surface);
+#ifdef DEBUG_DUMP_SURFACE
+ dump_surface(surface, 1);
+#endif
+ } else if (descriptor->type != IMAGE_TYPE_FROM_CACHE) {
+#ifdef DEBUG_DUMP_SURFACE
+ dump_surface(surface, 0);
+#endif
+ }
+ return surface;
+}
+
+#else
+
+static cairo_surface_t *canvas_get_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, 0);
+ }
+ case IMAGE_TYPE_BITMAP: {
+ BitmapImage *bitmap = (BitmapImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(BitmapImage));
+ return canvas_get_bits(canvas, &bitmap->bitmap);
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+}
+
+#endif
+
+static inline uint8_t revers_bits(uint8_t byte)
+{
+ uint8_t ret = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ int shift = 7 - i * 2;
+ ret |= (byte & (1 << i)) << shift;
+ ret |= (byte & (0x80 >> i)) >> shift;
+ }
+ return ret;
+}
+
+static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, Bitmap* bitmap, int invers)
+{
+ cairo_surface_t *surface;
+ uint8_t *src_line;
+ uint8_t *end_line;
+ uint8_t *dest_line;
+ int src_stride;
+ int line_size;
+ int dest_stride;
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ CAIRO_FORMAT_A1, bitmap->x, bitmap->y, TRUE);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+
+ src_line = (uint8_t *)GET_ADDRESS(bitmap->data);
+ src_stride = bitmap->stride;
+ end_line = src_line + (bitmap->y * src_stride);
+ access_test(canvas, src_line, end_line - src_line);
+ line_size = ALIGN(bitmap->x, 8) >> 3;
+
+ dest_stride = cairo_image_surface_get_stride(surface);
+ dest_line = cairo_image_surface_get_data(surface);
+#if defined(GL_CANVAS)
+ if ((bitmap->flags & BITMAP_TOP_DOWN)) {
+#else
+ if (!(bitmap->flags & BITMAP_TOP_DOWN)) {
+#endif
+ ASSERT(bitmap->y > 0);
+ dest_line += dest_stride * ((int)bitmap->y - 1);
+ dest_stride = -dest_stride;
+ }
+
+ if (invers) {
+ switch (bitmap->format) {
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case BITMAP_FMT_1BIT_BE:
+#else
+ case BITMAP_FMT_1BIT_LE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+ while (now < end) {
+ *(dest++) = ~*(now++);
+ }
+ }
+ break;
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case BITMAP_FMT_1BIT_LE:
+#else
+ case BITMAP_FMT_1BIT_BE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+
+ while (now < end) {
+ *(dest++) = ~revers_bits(*(now++));
+ }
+ }
+ break;
+ default:
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("invalid bitmap format");
+ }
+ } else {
+ switch (bitmap->format) {
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case BITMAP_FMT_1BIT_BE:
+#else
+ case BITMAP_FMT_1BIT_LE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ memcpy(dest_line, src_line, line_size);
+ }
+ break;
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case BITMAP_FMT_1BIT_LE:
+#else
+ case BITMAP_FMT_1BIT_BE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+
+ while (now < end) {
+ *(dest++) = revers_bits(*(now++));
+ }
+ }
+ break;
+ default:
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("invalid bitmap format");
+ }
+ }
+ return surface;
+}
+
+static inline cairo_surface_t *canvas_A1_invers(cairo_surface_t *src_surf)
+{
+ int width = cairo_image_surface_get_width(src_surf);
+ int height = cairo_image_surface_get_height(src_surf);
+
+ cairo_surface_t * invers = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
+ if (cairo_surface_status(invers) == CAIRO_STATUS_SUCCESS) {
+ uint8_t *src_line = cairo_image_surface_get_data(src_surf);
+ int src_stride = cairo_image_surface_get_stride(src_surf);
+ uint8_t *end_line = src_line + (height * src_stride);
+ int line_size = ALIGN(width, 8) >> 3;
+ uint8_t *dest_line = cairo_image_surface_get_data(invers);
+ int dest_stride = cairo_image_surface_get_stride(invers);
+
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+ while (now < end) {
+ *(dest++) = ~*(now++);
+ }
+ }
+ }
+ return invers;
+}
+
+static cairo_surface_t *canvas_surf_to_invers(cairo_surface_t *surf)
+{
+ int width = cairo_image_surface_get_width(surf);
+ int height = cairo_image_surface_get_height(surf);
+ uint8_t *dest_line;
+ uint8_t *dest_line_end;
+ uint8_t *src_line;
+ int dest_stride;
+ int src_stride;
+
+ ASSERT(cairo_image_surface_get_format(surf) == CAIRO_FORMAT_RGB24);
+ cairo_surface_t *invers = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+
+ if (cairo_surface_status(invers) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(invers)));
+ }
+
+ dest_line = cairo_image_surface_get_data(invers);
+ dest_stride = cairo_image_surface_get_stride(invers);
+ dest_line_end = dest_line + dest_stride * height;
+ src_line = cairo_image_surface_get_data(surf);
+ src_stride = cairo_image_surface_get_stride(surf);
+
+ for (; dest_line != dest_line_end; dest_line += dest_stride, src_line += src_stride) {
+ uint32_t *src = (uint32_t *)src_line;
+ uint32_t *dest = (uint32_t *)dest_line;
+ uint32_t *end = dest + width;
+ while (dest < end) {
+ *(dest++) = ~*(src++) & 0x00ffffff;
+ }
+ }
+ return invers;
+}
+
+/*
+* Return the inversed surface and assigns it to the user data of the given surface.
+* The routine also handles the reference count of the inversed surface. It you don't use
+* the returned reference, you must call cairo_surface_destroy.
+* Thread safe with respect to the user data.
+*/
+static inline cairo_surface_t* canvas_handle_inverse_user_data(cairo_surface_t* surface)
+{
+ cairo_surface_t *inv_surf = NULL;
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+ MUTEX_LOCK(cairo_surface_user_data_mutex);
+#endif
+ inv_surf = (cairo_surface_t *)cairo_surface_get_user_data(surface, &invers_data_type);
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+ MUTEX_UNLOCK(cairo_surface_user_data_mutex);
+#endif
+ if (!inv_surf) {
+ if (cairo_image_surface_get_format(surface) == CAIRO_FORMAT_A1) {
+ inv_surf = canvas_A1_invers(surface);
+ } else {
+ inv_surf = canvas_surf_to_invers(surface);
+ }
+
+ if (cairo_surface_status(inv_surf) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(inv_surf);
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+ MUTEX_LOCK(cairo_surface_user_data_mutex);
+
+ // checking if other thread has already assigned the user data
+ if (!cairo_surface_get_user_data(surface, &invers_data_type)) {
+#endif
+ if (cairo_surface_set_user_data(surface, &invers_data_type, inv_surf,
+ __release_surface) == CAIRO_STATUS_SUCCESS) {
+ cairo_surface_reference(inv_surf);
+ }
+#ifdef CAIRO_CANVAS_CACH_IS_SHARED
+ }
+ MUTEX_UNLOCK(cairo_surface_user_data_mutex);
+#endif
+ } else {
+ cairo_surface_reference(inv_surf);
+ }
+
+ return inv_surf;
+}
+
+static cairo_surface_t *canvas_get_mask(CanvasBase *canvas, QMask *mask)
+{
+ ImageDescriptor *descriptor;
+ cairo_surface_t *surface;
+ int need_invers;
+ int is_invers;
+ int cache_me;
+
+ if (!mask->bitmap) {
+ return NULL;
+ }
+
+ descriptor = (ImageDescriptor *)GET_ADDRESS(mask->bitmap);
+ access_test(canvas, descriptor, sizeof(ImageDescriptor));
+ need_invers = mask->flags & MASK_INVERS;
+
+#ifdef CAIRO_CANVAS_CACHE
+ cache_me = descriptor->flags & IMAGE_CACHE_ME;
+#else
+ cache_me = 0;
+#endif
+
+ switch (descriptor->type) {
+ case IMAGE_TYPE_BITMAP: {
+ BitmapImage *bitmap = (BitmapImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(BitmapImage));
+ is_invers = need_invers && !cache_me;
+ surface = canvas_get_bitmap_mask(canvas, &bitmap->bitmap, is_invers);
+ break;
+ }
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+ case IMAGE_TYPE_FROM_CACHE:
+ surface = canvas->bits_cache_get(canvas->bits_cache_opaque, descriptor->id);
+ is_invers = 0;
+ break;
+#endif
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+ if (cache_me) {
+ canvas->bits_cache_put(canvas->bits_cache_opaque, descriptor->id, surface);
+ }
+
+ if (need_invers && !is_invers) { // surface is in cache
+ cairo_surface_t *inv_surf;
+
+ inv_surf = canvas_handle_inverse_user_data(surface);
+
+ cairo_surface_destroy(surface);
+ surface = inv_surf;
+ }
+#endif
+ return surface;
+}
+
+static inline RasterGlyph *canvas_next_raster_glyph(const RasterGlyph *glyph, int bpp)
+{
+ return (RasterGlyph *)((uint8_t *)(glyph + 1) +
+ (ALIGN(glyph->width * bpp, 8) * glyph->height >> 3));
+}
+
+static inline void canvas_raster_glyph_box(const RasterGlyph *glyph, Rect *r)
+{
+ ASSERT(r);
+ r->top = glyph->render_pos.y + glyph->glyph_origin.y;
+ r->bottom = r->top + glyph->height;
+ r->left = glyph->render_pos.x + glyph->glyph_origin.x;
+ r->right = r->left + glyph->width;
+}
+
+#ifdef GL_CANVAS
+static inline void __canvas_put_bits(uint8_t *dest, int offset, uint8_t val, int n)
+{
+ uint8_t mask;
+ int now;
+
+ dest = dest + (offset >> 3);
+ offset &= 0x07;
+ now = MIN(8 - offset, n);
+
+ mask = ~((1 << (8 - now)) - 1);
+ mask >>= offset;
+ *dest = ((val >> offset) & mask) | *dest;
+
+ if ((n = n - now)) {
+ mask = ~((1 << (8 - n)) - 1);
+ dest++;
+ *dest = ((val << now) & mask) | *dest;
+ }
+}
+
+#else
+static inline void __canvas_put_bits(uint8_t *dest, int offset, uint8_t val, int n)
+{
+ uint8_t mask;
+ int now;
+
+ dest = dest + (offset >> 3);
+ offset &= 0x07;
+
+ now = MIN(8 - offset, n);
+
+ mask = (1 << now) - 1;
+ mask <<= offset;
+ val = revers_bits(val);
+ *dest = ((val << offset) & mask) | *dest;
+
+ if ((n = n - now)) {
+ mask = (1 << n) - 1;
+ dest++;
+ *dest = ((val >> now) & mask) | *dest;
+ }
+}
+
+#endif
+
+static inline void canvas_put_bits(uint8_t *dest, int dest_offset, uint8_t *src, int n)
+{
+ while (n) {
+ int now = MIN(n, 8);
+
+ n -= now;
+ __canvas_put_bits(dest, dest_offset, *src, now);
+ dest_offset += now;
+ src++;
+ }
+}
+
+static void canvas_put_glyph_bits(RasterGlyph *glyph, int bpp, uint8_t *dest, int dest_stride,
+ Rect *bounds)
+{
+ Rect glyph_box;
+ uint8_t *src;
+ int lines;
+ int width;
+
+ //todo: support STRING_RASTER_TOP_DOWN
+ canvas_raster_glyph_box(glyph, &glyph_box);
+ ASSERT(glyph_box.top >= bounds->top && glyph_box.bottom <= bounds->bottom);
+ ASSERT(glyph_box.left >= bounds->left && glyph_box.right <= bounds->right);
+ rect_offset(&glyph_box, -bounds->left, -bounds->top);
+
+ dest += glyph_box.top * dest_stride;
+ src = glyph->data;
+ lines = glyph_box.bottom - glyph_box.top;
+ width = glyph_box.right - glyph_box.left;
+ switch (bpp) {
+ case 1: {
+ int src_stride = ALIGN(width, 8) >> 3;
+ int i;
+
+ src += src_stride * (lines);
+ for (i = 0; i < lines; i++) {
+ src -= src_stride;
+ canvas_put_bits(dest, glyph_box.left, src, width);
+ dest += dest_stride;
+ }
+ break;
+ }
+ case 4: {
+ uint8_t *end;
+ int src_stride = ALIGN(width * 4, 8) >> 3;
+
+ src += src_stride * lines;
+ dest += glyph_box.left;
+ end = dest + dest_stride * lines;
+ for (; dest != end; dest += dest_stride) {
+ int i = 0;
+ uint8_t *now;
+
+ src -= src_stride;
+ now = src;
+ while (i < (width & ~1)) {
+ dest[i] = MAX(dest[i], *now & 0xf0);
+ dest[i + 1] = MAX(dest[i + 1], *now << 4);
+ i += 2;
+ now++;
+ }
+ if (i < width) {
+ dest[i] = MAX(dest[i], *now & 0xf0);
+ now++;
+ }
+ }
+ break;
+ }
+ case 8: {
+ uint8_t *end;
+ src += width * lines;
+ dest += glyph_box.left;
+ end = dest + dest_stride * lines;
+ for (; dest != end; dest += dest_stride, src -= width) {
+ int i;
+
+ for (i = 0; i < width; i++) {
+ dest[i] = MAX(dest[i], src[i]);
+ }
+ }
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid bpp");
+ }
+}
+
+static cairo_surface_t *canvas_get_str_mask(CanvasBase *canvas, String *str, int bpp, Point *pos)
+{
+ RasterGlyph *glyph = (RasterGlyph *)str->data;
+ RasterGlyph *next_glyph;
+ Rect bounds;
+ cairo_surface_t *str_mask;
+ uint8_t *dest;
+ int dest_stride;
+ int i;
+
+ ASSERT(str->length > 0);
+
+ access_test(canvas, glyph, sizeof(RasterGlyph));
+ next_glyph = canvas_next_raster_glyph(glyph, bpp);
+ access_test(canvas, glyph, (uint8_t*)next_glyph - (uint8_t*)glyph);
+ canvas_raster_glyph_box(glyph, &bounds);
+
+ for (i = 1; i < str->length; i++) {
+ Rect glyph_box;
+
+ glyph = next_glyph;
+ access_test(canvas, glyph, sizeof(RasterGlyph));
+ next_glyph = canvas_next_raster_glyph(glyph, bpp);
+ access_test(canvas, glyph, (uint8_t*)next_glyph - (uint8_t*)glyph);
+ canvas_raster_glyph_box(glyph, &glyph_box);
+ rect_union(&bounds, &glyph_box);
+ }
+
+ str_mask = cairo_image_surface_create((bpp == 1) ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8,
+ bounds.right - bounds.left,
+ bounds.bottom - bounds.top);
+ if (cairo_surface_status(str_mask) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(str_mask)));
+ }
+ dest = cairo_image_surface_get_data(str_mask);
+ dest_stride = cairo_image_surface_get_stride(str_mask);
+ glyph = (RasterGlyph *)str->data;
+ for (i = 0; i < str->length; i++) {
+#if defined(GL_CANVAS)
+ canvas_put_glyph_bits(glyph, bpp, dest + (bounds.bottom - bounds.top - 1) * dest_stride,
+ -dest_stride, &bounds);
+#else
+ canvas_put_glyph_bits(glyph, bpp, dest, dest_stride, &bounds);
+#endif
+ glyph = canvas_next_raster_glyph(glyph, bpp);
+ }
+
+ pos->x = bounds.left;
+ pos->y = bounds.top;
+ return str_mask;
+}
+
+static inline VectotGlyph *canvas_next_vector_glyph(const VectotGlyph *glyph)
+{
+ return (VectotGlyph *)((uint8_t *)(glyph + 1) + glyph->data_size);
+}
+
+static cairo_surface_t *canvas_scale_surface(cairo_surface_t *src, const Rect *src_area, int width,
+ int hight, int scale_mode)
+{
+ cairo_t *cairo;
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ double sx, sy;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, hight);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+
+ cairo = cairo_create(surface);
+ if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s", cairo_status_to_string(cairo_status(cairo)));
+ }
+
+ pattern = cairo_pattern_create_for_surface(src);
+ if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create pattern failed, %s",
+ cairo_status_to_string(cairo_pattern_status(pattern)));
+ }
+
+ sx = (double)(src_area->right - src_area->left) / width;
+ sy = (double)(src_area->bottom - src_area->top) / hight;
+
+ cairo_matrix_init_translate(&matrix, src_area->left, src_area->top);
+ cairo_matrix_scale(&matrix, sx, sy);
+
+ cairo_pattern_set_matrix(pattern, &matrix);
+ ASSERT(scale_mode == IMAGE_SCALE_INTERPOLATE || scale_mode == IMAGE_SCALE_NEAREST);
+ cairo_pattern_set_filter(pattern, (scale_mode == IMAGE_SCALE_NEAREST) ?
+ CAIRO_FILTER_NEAREST : CAIRO_FILTER_GOOD);
+
+ cairo_set_source(cairo, pattern);
+ cairo_pattern_destroy(pattern);
+ cairo_paint(cairo);
+ cairo_destroy(cairo);
+ return surface;
+}
+
+static void quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
+{
+ QuicData *usr_data = (QuicData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static void quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
+{
+ QuicData *usr_data = (QuicData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+}
+
+static void *quic_usr_malloc(QuicUsrContext *usr, int size)
+{
+ return malloc(size);
+}
+
+static void quic_usr_free(QuicUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+
+static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
+{
+ return 0;
+}
+
+static void lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
+{
+ LzData *usr_data = (LzData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+}
+
+static void lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
+{
+ LzData *usr_data = (LzData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static void *lz_usr_malloc(LzUsrContext *usr, int size)
+{
+ return malloc(size);
+}
+
+static void lz_usr_free(LzUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
+{
+ return 0;
+}
+
+static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
+{
+ return 0;
+}
+
+#else
+
+static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
+{
+ QuicData *quic_data = (QuicData *)usr;
+ DataChunk *chunk;
+
+ if (!quic_data->next) {
+ return 0;
+ }
+ chunk = (DataChunk *)GET_ADDRESS(quic_data->next + quic_data->address_delta);
+ quic_data->next = chunk->next;
+ *io_ptr = (uint32_t *)chunk->data;
+ return chunk->size >> 2;
+}
+
+#endif
+
+static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
+{
+ return 0;
+}
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+static void __canvas_set_access_params(CanvasBase *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max)
+{
+ canvas->address_delta = delta;
+ canvas->base = base;
+ canvas->max = max;
+}
+
+#else
+static void __canvas_set_access_params(CanvasBase *canvas, ADDRESS delta)
+{
+ canvas->address_delta = delta;
+}
+
+#endif
+
+static void canvas_base_destroy(CanvasBase *canvas)
+{
+ quic_destroy(canvas->quic_data.quic);
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+ lz_destroy(canvas->lz_data.lz);
+#endif
+#ifdef GDI_CANVAS
+ DeleteDC(canvas->dc);
+#endif
+}
+
+#ifdef CAIRO_CANVAS_CACHE
+static int canvas_base_init(CanvasBase *canvas, int depth,
+ 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)
+static int canvas_base_init(CanvasBase *canvas, int depth,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put,
+ bits_cache_get_fn_t bits_cache_get
+#else
+static int canvas_base_init(CanvasBase *canvas, int depth
+#endif
+#ifdef USE_GLZ
+ , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+#endif
+ )
+{
+ canvas->quic_data.usr.error = quic_usr_error;
+ canvas->quic_data.usr.warn = quic_usr_warn;
+ canvas->quic_data.usr.info = quic_usr_warn;
+ canvas->quic_data.usr.malloc = quic_usr_malloc;
+ canvas->quic_data.usr.free = quic_usr_free;
+ canvas->quic_data.usr.more_space = quic_usr_more_space;
+ canvas->quic_data.usr.more_lines = quic_usr_more_lines;
+ if (!(canvas->quic_data.quic = quic_create(&canvas->quic_data.usr))) {
+ return 0;
+ }
+#ifdef CAIRO_CANVAS_NO_CHUNKS
+ canvas->lz_data.usr.error = lz_usr_error;
+ canvas->lz_data.usr.warn = lz_usr_warn;
+ canvas->lz_data.usr.info = lz_usr_warn;
+ canvas->lz_data.usr.malloc = lz_usr_malloc;
+ canvas->lz_data.usr.free = lz_usr_free;
+ canvas->lz_data.usr.more_space = lz_usr_more_space;
+ canvas->lz_data.usr.more_lines = lz_usr_more_lines;
+ if (!(canvas->lz_data.lz = lz_create(&canvas->lz_data.usr))) {
+ return 0;
+ }
+#endif
+#ifdef USE_GLZ
+ canvas->glz_data.decoder_opaque = glz_decoder_opaque;
+ canvas->glz_data.decode = glz_decode;
+#endif
+
+ if (depth == 16) {
+ canvas->color_shift = 5;
+ canvas->color_mask = 0x1f;
+ } else {
+ canvas->color_shift = 8;
+ canvas->color_mask = 0xff;
+ }
+
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+ canvas->bits_cache_opaque = bits_cache_opaque;
+ canvas->bits_cache_put = bits_cache_put;
+ canvas->bits_cache_get = bits_cache_get;
+#endif
+#ifdef CAIRO_CANVAS_CACHE
+ canvas->palette_cache_opaque = palette_cache_opaque;
+ canvas->palette_cache_put = palette_cache_put;
+ canvas->palette_cache_get = palette_cache_get;
+ canvas->palette_cache_release = palette_cache_release;
+#endif
+
+#ifdef WIN32
+ canvas->dc = NULL;
+#endif
+
+#ifdef GDI_CANVAS
+ canvas->dc = create_compatible_dc();
+ if (!canvas->dc) {
+ lz_destroy(canvas->lz_data.lz);
+ return 0;
+ }
+#endif
+ return 1;
+}
+