summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorYaniv Kamay <ykamay@redhat.com>2009-09-19 21:25:46 +0300
committerYaniv Kamay <ykamay@redhat.com>2009-10-14 15:06:41 +0200
commitc1b79eb035fa158fb2ac3bc8e559809611070016 (patch)
tree3348dd749a700dedf87c9b16fe8be77c62928df8 /common
downloadspice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.gz
spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.xz
spice-c1b79eb035fa158fb2ac3bc8e559809611070016.zip
fresh start
Diffstat (limited to 'common')
-rw-r--r--common/.gitignore8
-rw-r--r--common/Makefile.am49
-rw-r--r--common/cairo_canvas.c1664
-rw-r--r--common/cairo_canvas.h86
-rw-r--r--common/canvas_base.c1617
-rw-r--r--common/canvas_base.h40
-rw-r--r--common/canvas_utils.c277
-rw-r--r--common/canvas_utils.h67
-rw-r--r--common/draw.h406
-rw-r--r--common/gdi_canvas.c1757
-rw-r--r--common/gdi_canvas.h76
-rw-r--r--common/gl_canvas.c886
-rw-r--r--common/gl_canvas.h83
-rw-r--r--common/gl_utils.h85
-rw-r--r--common/glc.c1582
-rw-r--r--common/glc.c.save1413
-rw-r--r--common/glc.h158
-rw-r--r--common/ipc_ring.h135
-rw-r--r--common/lookup3.c769
-rw-r--r--common/lookup3.h43
-rw-r--r--common/lz.c720
-rw-r--r--common/lz.h75
-rw-r--r--common/lz_common.h60
-rw-r--r--common/lz_compress_tmpl.c523
-rw-r--r--common/lz_config.h59
-rw-r--r--common/lz_decompress_tmpl.c317
-rw-r--r--common/mutex.h34
-rw-r--r--common/ogl_ctx.c253
-rw-r--r--common/ogl_ctx.h30
-rw-r--r--common/quic.c1709
-rw-r--r--common/quic.h64
-rw-r--r--common/quic_config.h53
-rw-r--r--common/quic_family_tmpl.c114
-rw-r--r--common/quic_rgb_tmpl.c762
-rw-r--r--common/quic_tmpl.c632
-rw-r--r--common/qxl_dev.h332
-rw-r--r--common/rect.h116
-rw-r--r--common/red.h685
-rw-r--r--common/red_error_codes.h50
-rw-r--r--common/reds_stat.h68
-rw-r--r--common/region.c1230
-rw-r--r--common/region.h75
-rw-r--r--common/ring.h132
-rw-r--r--common/rop3.c613
-rw-r--r--common/rop3.h33
-rw-r--r--common/vd_agent.h109
-rw-r--r--common/vdi_dev.h97
-rw-r--r--common/win/my_getopt-1.5/ChangeLog22
-rw-r--r--common/win/my_getopt-1.5/LICENSE22
-rw-r--r--common/win/my_getopt-1.5/Makefile26
-rw-r--r--common/win/my_getopt-1.5/README140
-rw-r--r--common/win/my_getopt-1.5/getopt.3288
-rw-r--r--common/win/my_getopt-1.5/getopt.h56
-rw-r--r--common/win/my_getopt-1.5/getopt.txt330
-rw-r--r--common/win/my_getopt-1.5/main.c387
-rw-r--r--common/win/my_getopt-1.5/my_getopt.c281
-rw-r--r--common/win/my_getopt-1.5/my_getopt.h72
57 files changed, 21770 insertions, 0 deletions
diff --git a/common/.gitignore b/common/.gitignore
new file mode 100644
index 00000000..07491dd4
--- /dev/null
+++ b/common/.gitignore
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.loT
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 00000000..7fcdfe90
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,49 @@
+NULL =
+
+COMMON_SRCS = \
+ cairo_canvas.h \
+ cairo_canvas.c \
+ canvas_base.h \
+ canvas_base.c \
+ canvas_utils.h \
+ canvas_utils.c \
+ draw.h \
+ gl_canvas.h \
+ gl_canvas.c \
+ glc.h \
+ glc.c \
+ gl_utils.h \
+ lookup3.h \
+ lookup3.c \
+ lz_common.h \
+ mutex.h \
+ ogl_ctx.h \
+ ogl_ctx.c \
+ quic.h \
+ quic.c \
+ quic_config.h \
+ qxl_dev.h \
+ rect.h \
+ red_error_codes.h \
+ red.h \
+ reds_stat.h \
+ region.h \
+ region.c \
+ ring.h \
+ rop3.h \
+ rop3.c \
+ lz.c \
+ lz_compress_tmpl.c \
+ lz_config.h \
+ lz_decompress_tmpl.c \
+ lz.h \
+ quic_family_tmpl.c \
+ quic_rgb_tmpl.c \
+ quic_tmpl.c \
+ ipc_ring.h \
+ vd_agent.h \
+ quic_tmpl.c \
+ $(NULL)
+
+EXTRA_DIST = $(COMMON_SRCS)
+
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();
+}
+
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
new file mode 100644
index 00000000..3d9a930d
--- /dev/null
+++ b/common/cairo_canvas.h
@@ -0,0 +1,86 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H__CANVAS
+#define _H__CANVAS
+
+#include <stdint.h>
+
+#include "draw.h"
+#include "cairo.h"
+#include "canvas_base.h"
+#include "region.h"
+
+typedef struct CairoCanvas CairoCanvas;
+
+void canvas_draw_fill(CairoCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill);
+void canvas_draw_copy(CairoCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy);
+void canvas_draw_opaque(CairoCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque);
+void canvas_copy_bits(CairoCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos);
+void canvas_draw_text(CairoCanvas *canvas, Rect *bbox, Clip *clip, Text *text);
+void canvas_draw_stroke(CairoCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke);
+void canvas_draw_rop3(CairoCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3);
+void canvas_draw_blend(CairoCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend);
+void canvas_draw_blackness(CairoCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness);
+void canvas_draw_whiteness(CairoCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness);
+void canvas_draw_invers(CairoCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers);
+void canvas_draw_transparent(CairoCanvas *canvas, Rect *bbox, Clip *clip, Transparent* transparent);
+void canvas_draw_alpha_blend(CairoCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend);
+#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
+void canvas_clear(CairoCanvas *canvas);
+void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const Rect *area);
+void canvas_group_start(CairoCanvas *canvas, int n_clip_rects, Rect *clip_rects);
+void canvas_group_end(CairoCanvas *canvas);
+void canvas_set_addr_delta(CairoCanvas *canvas, ADDRESS delta);
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void canvas_set_access_params(CairoCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max);
+#else
+void canvas_set_access_params(CairoCanvas *canvas, ADDRESS delta);
+#endif
+
+cairo_t *canvas_get_cairo(CairoCanvas *canvas);
+
+#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
+ );
+void canvas_destroy(CairoCanvas *canvas);
+
+void cairo_canvas_init();
+
+#endif
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;
+}
+
diff --git a/common/canvas_base.h b/common/canvas_base.h
new file mode 100644
index 00000000..778244f7
--- /dev/null
+++ b/common/canvas_base.h
@@ -0,0 +1,40 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_CANVAS_BASE
+#define _H_CANVAS_BASE
+
+
+#include "cairo.h"
+#include "lz.h"
+#include "draw.h"
+
+#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
+typedef void (*bits_cache_put_fn_t)(void *bits_cache_opaque, uint64_t id, cairo_surface_t *surface);
+typedef cairo_surface_t *(*bits_cache_get_fn_t)(void *bits_cache_opaque, uint64_t id);
+#endif
+#ifdef CAIRO_CANVAS_CACHE
+typedef void (*palette_cache_put_fn_t)(void *palette_cache_opaque, Palette *palette);
+typedef Palette *(*palette_cache_get_fn_t)(void *palette_cache_opaque, uint64_t id);
+typedef void (*palette_cache_release_fn_t)(Palette *palette);
+#endif
+
+typedef void (*glz_decode_fn_t)(void *glz_decoder_opaque, uint8_t *data,
+ Palette *plt, void *usr_data);
+
+#endif
+
diff --git a/common/canvas_utils.c b/common/canvas_utils.c
new file mode 100644
index 00000000..f6470cae
--- /dev/null
+++ b/common/canvas_utils.c
@@ -0,0 +1,277 @@
+/*
+ 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 "canvas_utils.h"
+
+#ifdef __GNUC__
+#include <stdlib.h>
+#include <stdio.h>
+#endif
+
+#ifdef WIN32
+extern int gdi_handlers;
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+
+#ifndef CANVAS_ERROR
+#define CANVAS_ERROR(format, ...) { \
+ printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \
+ abort(); \
+}
+#endif
+
+#ifndef ALIGN
+#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
+#endif
+
+const cairo_user_data_key_t bitmap_data_type = {0};
+const cairo_user_data_key_t bitmap_withstride_data_type = {0};
+
+#ifdef WIN32
+static void release_bitmap(void *bitmap_cache)
+{
+ DeleteObject((HBITMAP)((BitmapCache *)bitmap_cache)->bitmap);
+ CloseHandle(((BitmapCache *)bitmap_cache)->mutex);
+ free(bitmap_cache);
+ gdi_handlers--;
+}
+
+#endif
+
+static void release_withstride_bitmap(void *data)
+{
+ free(data);
+}
+
+static inline cairo_surface_t *__surface_create_stride(cairo_format_t format, int width, int height,
+ int stride)
+{
+ uint8_t *data;
+ uint8_t *stride_data;
+ cairo_surface_t *surface;
+
+ data = (uint8_t *)malloc(abs(stride) * height);
+ if (stride < 0) {
+ stride_data = data + (-stride) * (height - 1);
+ } else {
+ stride_data = data;
+ }
+
+ surface = cairo_image_surface_create_for_data(stride_data, format, width, height, stride);
+
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ free(data);
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+
+ if (cairo_surface_set_user_data(surface, &bitmap_withstride_data_type, data,
+ release_withstride_bitmap) != CAIRO_STATUS_SUCCESS) {
+ free(data);
+ cairo_surface_destroy(surface);
+ CANVAS_ERROR("set_user_data surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+
+ return surface;
+}
+
+#ifdef WIN32
+cairo_surface_t *surface_create(HDC dc, cairo_format_t format,
+ int width, int height, int top_down)
+#else
+cairo_surface_t * surface_create(cairo_format_t format, int width, int height, int top_down)
+#endif
+{
+#ifdef WIN32
+ /*
+ * Windows xp allow only 10,000 of gdi handlers, considering the fact that
+ * we limit here the number to 5000, we dont use atomic operations to sync
+ * this calculation against the other canvases (in case of multiple
+ * monitors), in worst case there will be little more than 5000 gdi
+ * handlers.
+ */
+ if (dc && gdi_handlers < 5000) {
+ uint8_t *data;
+ uint8_t *src;
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+ int nstride;
+ cairo_surface_t *surface;
+ BitmapCache *bitmap_cache;
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = width;
+
+ bitmap_info.inf.bmiHeader.biHeight = (!top_down) ? height : -height;
+
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ bitmap_info.inf.bmiHeader.biBitCount = 32;
+ nstride = width * 4;
+ break;
+ case CAIRO_FORMAT_A8:
+ bitmap_info.inf.bmiHeader.biBitCount = 8;
+ nstride = ALIGN(width, 4);
+ break;
+ case CAIRO_FORMAT_A1:
+ bitmap_info.inf.bmiHeader.biBitCount = 1;
+ nstride = ALIGN(width, 32) / 8;
+ break;
+ default:
+ CANVAS_ERROR("invalid format");
+ }
+
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ bitmap_cache = (BitmapCache *)malloc(sizeof(*bitmap_cache));
+ if (!bitmap_cache) {
+ CANVAS_ERROR("malloc failed");
+ return NULL;
+ }
+
+ bitmap_cache->mutex = CreateMutex(NULL, 0, NULL);
+ if (!bitmap_cache->mutex) {
+ free(bitmap_cache);
+ CANVAS_ERROR("Unable to CreateMutex");
+ return NULL;
+ }
+
+ bitmap_cache->bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
+ if (!bitmap_cache->bitmap) {
+ CloseHandle(bitmap_cache->mutex);
+ free(bitmap_cache);
+ CANVAS_ERROR("Unable to CreateDIBSection");
+ return NULL;
+ }
+
+ if (top_down) {
+ src = data;
+ } else {
+ src = data + nstride * (height - 1);
+ nstride = -nstride;
+ }
+
+ surface = cairo_image_surface_create_for_data(src, format, width, height, nstride);
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ CloseHandle(bitmap_cache->mutex);
+ DeleteObject((HBITMAP)bitmap_cache->bitmap);
+ free(bitmap_cache);
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+ if (cairo_surface_set_user_data(surface, &bitmap_data_type, bitmap_cache,
+ release_bitmap) != CAIRO_STATUS_SUCCESS) {
+ CloseHandle(bitmap_cache->mutex);
+ cairo_surface_destroy(surface);
+ DeleteObject((HBITMAP)bitmap_cache->bitmap);
+ free(bitmap_cache);
+ CANVAS_ERROR("set_user_data surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(surface)));
+ }
+ gdi_handlers++;
+ return surface;
+ } else {
+#endif
+ if (top_down) {
+ return cairo_image_surface_create(format, width, height);
+ } else {
+ // NOTE: we assume here that the lz decoders always decode to RGB32.
+ int stride = 0;
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ stride = width * 4;
+ break;
+ case CAIRO_FORMAT_A8:
+ stride = ALIGN(width, 4);
+ break;
+ case CAIRO_FORMAT_A1:
+ stride = ALIGN(width, 32) / 8;
+ break;
+ default:
+ CANVAS_ERROR("invalid format");
+ }
+ stride = -stride;
+ return __surface_create_stride(format, width, height, stride);
+ }
+#ifdef WIN32
+}
+
+#endif
+}
+
+#ifdef WIN32
+cairo_surface_t *surface_create_stride(HDC dc, cairo_format_t format, int width, int height,
+ int stride)
+#else
+cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int height,
+ int stride)
+#endif
+{
+#ifdef WIN32
+ if (dc) {
+ if (abs(stride) == (width * 4)) {
+ return surface_create(dc, format, width, height, (stride > 0));
+ }
+ }
+#endif
+
+ return __surface_create_stride(format, width, height, stride);
+}
+
+cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
+ int height, int gross_pixels, int top_down)
+{
+ int stride;
+ int alpha;
+ cairo_surface_t *surface = NULL;
+
+ stride = (gross_pixels / height) * 4;
+
+ if (!top_down) {
+ stride = -stride;
+ }
+
+ if (type == LZ_IMAGE_TYPE_RGB32) {
+ alpha = 0;
+ } else if (type == LZ_IMAGE_TYPE_RGBA) {
+ alpha = 1;
+ } else {
+ CANVAS_ERROR("unexpected image type");
+ }
+ surface = surface_create_stride(
+#ifdef WIN32
+ canvas_data->dc,
+#endif
+ alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height, stride);
+ canvas_data->out_surface = surface;
+ return surface;
+}
+
diff --git a/common/canvas_utils.h b/common/canvas_utils.h
new file mode 100644
index 00000000..8ccc3044
--- /dev/null
+++ b/common/canvas_utils.h
@@ -0,0 +1,67 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_CANVAS_UTILS
+#define _H_CANVAS_UTILS
+
+#ifdef __GNUC__
+#include <stdint.h>
+#else
+#include <stddef.h>
+#include <basetsd.h>
+typedef UINT8 uint8_t;
+#endif //__GNUC__
+
+#include "cairo.h"
+#include "lz.h"
+
+#ifdef WIN32
+typedef struct BitmapCache {
+ HBITMAP bitmap;
+ HANDLE mutex;
+} BitmapCache;
+#endif
+
+extern const cairo_user_data_key_t bitmap_data_type;
+
+#ifdef WIN32
+cairo_surface_t *surface_create(HDC dc, cairo_format_t format,
+ int width, int height, int top_down);
+#else
+cairo_surface_t *surface_create(cairo_format_t format, int width, int height, int top_down);
+#endif
+
+#ifdef WIN32
+cairo_surface_t *surface_create_stride(HDC dc, cairo_format_t format, int width, int height,
+ int stride);
+#else
+cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int height,
+ int stride);
+#endif
+
+
+typedef struct LzDecodeUsrData {
+#ifdef WIN32
+ HDC dc;
+#endif
+ cairo_surface_t *out_surface;
+} LzDecodeUsrData;
+
+
+cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
+ int height, int gross_pixels, int top_down);
+#endif
diff --git a/common/draw.h b/common/draw.h
new file mode 100644
index 00000000..e277b2d9
--- /dev/null
+++ b/common/draw.h
@@ -0,0 +1,406 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _H_DRAW
+#define _H_DRAW
+
+#ifndef _WIN32
+#include <stdint.h>
+#endif
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+typedef uint64_t UINT64;
+typedef uint32_t UINT32;
+typedef uint16_t UINT16;
+typedef uint8_t UINT8;
+
+typedef int16_t INT16;
+typedef int32_t INT32;
+#else
+#include <basetsd.h>
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#pragma warning(disable:4200)
+#endif
+
+#ifdef _WIN32_WCE
+#include <stdint.h>
+typedef uint64_t UINT64;
+typedef uint32_t UINT32;
+typedef uint16_t UINT16;
+typedef uint8_t UINT8;
+
+typedef int16_t INT16;
+typedef int32_t INT32;
+#endif
+
+#define GET_ADDRESS(addr) ((void *)(unsigned long)(addr))
+#define SET_ADDRESS(addr, val) ((addr) = (unsigned long)(val))
+
+typedef INT32 FIXED28_4;
+typedef UINT64 ADDRESS;
+
+enum {
+ PATH_BEGIN = (1 << 0),
+ PATH_END = (1 << 1),
+ PATH_CLOSE = (1 << 3),
+ PATH_BEZIER = (1 << 4),
+};
+
+enum {
+ LINE_ATTR_STARTGAP = (1 << 2),
+ LINE_ATTR_STYLED = (1 << 3),
+};
+
+typedef struct ATTR_PACKED PointFix {
+ FIXED28_4 x;
+ FIXED28_4 y;
+} PointFix;
+
+typedef struct ATTR_PACKED Point {
+ INT32 x;
+ INT32 y;
+} Point;
+
+typedef struct ATTR_PACKED Point16 {
+ INT16 x;
+ INT16 y;
+} Point16;
+
+typedef struct ATTR_PACKED Rect {
+ INT32 top;
+ INT32 left;
+ INT32 bottom;
+ INT32 right;
+} Rect;
+
+typedef struct ATTR_PACKED PathSeg {
+ UINT32 flags;
+ UINT32 count;
+ UINT8 data[0];
+} PathSeg;
+
+enum ClipType {
+ CLIP_TYPE_NONE,
+ CLIP_TYPE_RECTS,
+ CLIP_TYPE_PATH,
+};
+
+typedef struct ATTR_PACKED Clip {
+ UINT32 type;
+ ADDRESS data;
+} Clip;
+
+enum ROPDescriptor {
+ ROPD_INVERS_SRC = (1 << 0),
+ ROPD_INVERS_BRUSH = (1 << 1),
+ ROPD_INVERS_DEST = (1 << 2),
+ ROPD_OP_PUT = (1 << 3),
+ ROPD_OP_OR = (1 << 4),
+ ROPD_OP_AND = (1 << 5),
+ ROPD_OP_XOR = (1 << 6),
+ ROPD_OP_BLACKNESS = (1 << 7),
+ ROPD_OP_WHITENESS = (1 << 8),
+ ROPD_OP_INVERS = (1 << 9),
+ ROPD_INVERS_RES = (1 << 10),
+};
+
+typedef struct ATTR_PACKED Pattern {
+ ADDRESS pat;
+ Point pos;
+} Pattern;
+
+enum {
+ BRUSH_TYPE_NONE,
+ BRUSH_TYPE_SOLID,
+ BRUSH_TYPE_PATTERN,
+};
+
+typedef struct ATTR_PACKED Brush {
+ UINT32 type;
+ union {
+ UINT32 color;
+ Pattern pattern;
+ } u;
+} Brush;
+
+enum {
+ MASK_INVERS = (1 << 0),
+};
+
+typedef struct ATTR_PACKED QMask {
+ UINT8 flags;
+ Point pos;
+ ADDRESS bitmap;
+} QMask;
+
+typedef struct ATTR_PACKED Fill {
+ Brush brush;
+ UINT16 rop_decriptor;
+ QMask mask;
+} Fill;
+
+typedef struct ATTR_PACKED Palette {
+ UINT64 unique;
+ UINT16 num_ents;
+ UINT32 ents[0];
+} Palette;
+
+enum {
+ IMAGE_TYPE_BITMAP,
+ IMAGE_TYPE_QUIC,
+ IMAGE_TYPE_RESERVED,
+ IMAGE_TYPE_LZ_PLT = 100,
+ IMAGE_TYPE_LZ_RGB,
+ IMAGE_TYPE_GLZ_RGB,
+ IMAGE_TYPE_FROM_CACHE,
+};
+
+enum {
+ IMAGE_CACHE_ME = (1 << 0),
+};
+
+typedef struct ATTR_PACKED ImageDescriptor {
+ UINT64 id;
+ UINT8 type;
+ UINT8 flags;
+ UINT32 width;
+ UINT32 height;
+} ImageDescriptor;
+
+enum {
+ BITMAP_FMT_INVALID,
+ BITMAP_FMT_1BIT_LE,
+ BITMAP_FMT_1BIT_BE,
+ BITMAP_FMT_4BIT_LE,
+ BITMAP_FMT_4BIT_BE,
+ BITMAP_FMT_8BIT,
+ BITMAP_FMT_16BIT,
+ BITMAP_FMT_24BIT,
+ BITMAP_FMT_32BIT,
+ BITMAP_FMT_RGBA,
+};
+
+enum {
+ BITMAP_PAL_CACHE_ME = (1 << 0),
+ BITMAP_PAL_FROM_CACHE = (1 << 1),
+ BITMAP_TOP_DOWN = (1 << 2),
+};
+
+typedef struct ATTR_PACKED Bitmap {
+ UINT8 format;
+ UINT8 flags;
+ UINT32 x;
+ UINT32 y;
+ UINT32 stride;
+ ADDRESS palette;
+ ADDRESS data; //data[0] ?
+} Bitmap;
+
+typedef struct ATTR_PACKED BitmapImage {
+ ImageDescriptor descriptor;
+ Bitmap bitmap;
+} BitmapImage;
+
+typedef struct ATTR_PACKED QUICData {
+ UINT32 data_size;
+ UINT8 data[0];
+} QUICData, LZ_RGBData;
+
+typedef struct ATTR_PACKED QUICImage {
+ ImageDescriptor descriptor;
+ QUICData quic;
+} QUICImage;
+
+typedef struct ATTR_PACKED LZ_RGBImage {
+ ImageDescriptor descriptor;
+ LZ_RGBData lz_rgb;
+} LZ_RGBImage;
+
+typedef struct ATTR_PACKED LZ_PLTData {
+ UINT8 flags;
+ UINT32 data_size;
+ ADDRESS palette;
+ UINT8 data[0];
+} LZ_PLTData;
+
+typedef struct ATTR_PACKED LZ_PLTImage {
+ ImageDescriptor descriptor;
+ LZ_PLTData lz_plt;
+} LZ_PLTImage;
+
+enum {
+ IMAGE_SCALE_INTERPOLATE,
+ IMAGE_SCALE_NEAREST,
+};
+
+typedef struct ATTR_PACKED Opaque {
+ ADDRESS src_bitmap;
+ Rect src_area;
+ Brush brush;
+ UINT16 rop_decriptor;
+ UINT8 scale_mode;
+ QMask mask;
+} Opaque;
+
+typedef struct ATTR_PACKED Copy {
+ ADDRESS src_bitmap;
+ Rect src_area;
+ UINT16 rop_decriptor;
+ UINT8 scale_mode;
+ QMask mask;
+} Copy, Blend;
+
+typedef struct ATTR_PACKED Transparent {
+ ADDRESS src_bitmap;
+ Rect src_area;
+ UINT32 src_color;
+ UINT32 true_color;
+} Transparent;
+
+typedef struct ATTR_PACKED AlphaBlnd {
+ UINT8 alpha;
+ ADDRESS src_bitmap;
+ Rect src_area;
+} AlphaBlnd;
+
+typedef struct ATTR_PACKED Rop3 {
+ ADDRESS src_bitmap;
+ Rect src_area;
+ Brush brush;
+ UINT8 rop3;
+ UINT8 scale_mode;
+ QMask mask;
+} Rop3;
+
+typedef struct ATTR_PACKED Blackness {
+ QMask mask;
+} Blackness, Invers, Whiteness;
+
+enum {
+ LINE_STYLED = (1 << 3),
+ LINE_START_WITH_GAP = (1 << 2),
+};
+
+enum {
+ LINE_CAP_ROUND,
+ LINE_CAP_SQUARE,
+ LINE_CAP_BUTT,
+};
+
+enum {
+ LINE_JOIN_ROUND,
+ LINE_JOIN_BEVEL,
+ LINE_JOIN_MITER,
+};
+
+typedef struct ATTR_PACKED LineAttr {
+ UINT8 flags;
+ UINT8 join_style;
+ UINT8 end_style;
+ UINT8 style_nseg;
+ FIXED28_4 width;
+ FIXED28_4 miter_limit;
+ ADDRESS style; //data[0] ?
+} LineAttr;
+
+typedef struct ATTR_PACKED Stroke {
+ ADDRESS path;
+ LineAttr attr;
+ Brush brush;
+ UINT16 fore_mode;
+ UINT16 back_mode;
+} Stroke;
+
+typedef struct ATTR_PACKED RasterGlyph {
+ Point render_pos;
+ Point glyph_origin;
+ UINT16 width;
+ UINT16 height;
+ UINT8 data[0];
+} RasterGlyph;
+
+typedef struct ATTR_PACKED VectotGlyph {
+ Point render_pos;
+ UINT32 data_size;
+ UINT8 data[0]; //PathSeg[]
+} VectotGlyph;
+
+enum {
+ STRING_RASTER_A1 = 1 << 0,
+ STRING_RASTER_A4 = 1 << 1,
+ STRING_RASTER_A8 = 1 << 2,
+ STRING_RASTER_TOP_DOWN = 1 << 3,
+};
+
+typedef struct ATTR_PACKED String {
+ UINT16 length;
+ UINT16 flags;
+ UINT8 data[0];
+} String;
+
+typedef struct ATTR_PACKED Text {
+ ADDRESS str;
+ Rect back_area;
+ Brush fore_brush;
+ Brush back_brush;
+ UINT16 fore_mode;
+ UINT16 back_mode;
+} Text;
+
+enum {
+ CURSOR_TYPE_ALPHA,
+ CURSOR_TYPE_MONO,
+ CURSOR_TYPE_COLOR4,
+ CURSOR_TYPE_COLOR8,
+ CURSOR_TYPE_COLOR16,
+ CURSOR_TYPE_COLOR24,
+ CURSOR_TYPE_COLOR32,
+};
+
+typedef struct ATTR_PACKED CursorHeader {
+ UINT64 unique;
+ UINT16 type;
+ UINT16 width;
+ UINT16 height;
+ UINT16 hot_spot_x;
+ UINT16 hot_spot_y;
+} CursorHeader;
+
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+#endif
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
new file mode 100644
index 00000000..99079f56
--- /dev/null
+++ b/common/gdi_canvas.c
@@ -0,0 +1,1757 @@
+/*
+ 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 <Windows.h>
+#include <Wingdi.h>
+#include "gdi_canvas.h"
+#define GDI_CANVAS
+#include "canvas_base.c"
+#include "rop3.h"
+#include "rect.h"
+#include "region.h"
+#include "threads.h"
+
+struct GdiCanvas {
+ CanvasBase base;
+ HDC dc;
+ Mutex* lock;
+};
+
+
+struct BitmapData {
+ HBITMAP hbitmap;
+ HBITMAP prev_hbitmap;
+ Point pos;
+ uint8_t flags;
+ HDC dc;
+ int cache;
+};
+
+#define _rop3_brush 0xf0
+#define _rop3_src 0xcc
+#define _rop3_dest 0xaa
+
+uint32_t raster_ops[] = {
+ 0x00000042,
+ 0x00010289,
+ 0x00020C89,
+ 0x000300AA,
+ 0x00040C88,
+ 0x000500A9,
+ 0x00060865,
+ 0x000702C5,
+ 0x00080F08,
+ 0x00090245,
+ 0x000A0329,
+ 0x000B0B2A,
+ 0x000C0324,
+ 0x000D0B25,
+ 0x000E08A5,
+ 0x000F0001,
+ 0x00100C85,
+ 0x001100A6,
+ 0x00120868,
+ 0x001302C8,
+ 0x00140869,
+ 0x001502C9,
+ 0x00165CCA,
+ 0x00171D54,
+ 0x00180D59,
+ 0x00191CC8,
+ 0x001A06C5,
+ 0x001B0768,
+ 0x001C06CA,
+ 0x001D0766,
+ 0x001E01A5,
+ 0x001F0385,
+ 0x00200F09,
+ 0x00210248,
+ 0x00220326,
+ 0x00230B24,
+ 0x00240D55,
+ 0x00251CC5,
+ 0x002606C8,
+ 0x00271868,
+ 0x00280369,
+ 0x002916CA,
+ 0x002A0CC9,
+ 0x002B1D58,
+ 0x002C0784,
+ 0x002D060A,
+ 0x002E064A,
+ 0x002F0E2A,
+ 0x0030032A,
+ 0x00310B28,
+ 0x00320688,
+ 0x00330008,
+ 0x003406C4,
+ 0x00351864,
+ 0x003601A8,
+ 0x00370388,
+ 0x0038078A, // PSDPoax
+ 0x00390604, // SPDnox
+ 0x003A0644, // SPDSxox
+ 0x003B0E24, // SPDnoan
+ 0x003C004A, // PSx
+ 0x003D18A4, // SPDSonox
+ 0x003E1B24, // SPDSnaox
+ 0x003F00EA, // PSan
+ 0x00400F0A, // PSDnaa
+ 0x00410249, // DPSxon
+ 0x00420D5D, // SDxPDxa
+ 0x00431CC4, // SPDSanaxn
+ 0x00440328, // SDna SRCERASE
+ 0x00450B29, // DPSnaon
+ 0x004606C6, // DSPDaox
+ 0x0047076A, // PSDPxaxn
+ 0x00480368, // SDPxa
+ 0x004916C5, // PDSPDaoxxn
+ 0x004A0789, // DPSDoax
+ 0x004B0605, // PDSnox
+ 0x004C0CC8, // SDPana
+ 0x004D1954, // SSPxDSxoxn
+ 0x004E0645, // PDSPxox
+ 0x004F0E25, // PDSnoan
+ 0x00500325, // PDna
+ 0x00510B26, // DSPnaon
+ 0x005206C9, // DPSDaox
+ 0x00530764, // SPDSxaxn
+ 0x005408A9, // DPSonon
+ 0x00550009, // Dn DSTINVERT
+ 0x005601A9, // DPSox
+ 0x00570389, // DPSoan
+ 0x00580785, // PDSPoax
+ 0x00590609, // DPSnox
+ 0x005A0049, // DPx PATINVERT
+ 0x005B18A9, // DPSDonox
+ 0x005C0649, // DPSDxox
+ 0x005D0E29, // DPSnoan
+ 0x005E1B29, // DPSDnaox
+ 0x005F00E9, // DPan
+ 0x00600365, // PDSxa
+ 0x006116C6, // DSPDSaoxxn
+ 0x00620786, // DSPDoax
+ 0x00630608, // SDPnox
+ 0x00640788, // SDPSoax
+ 0x00650606, // DSPnox
+ 0x00660046, // DSx SRCINVERT
+ 0x006718A8, // SDPSonox
+ 0x006858A6, // DSPDSonoxxn
+ 0x00690145, // PDSxxn
+ 0x006A01E9, // DPSax
+ 0x006B178A, // PSDPSoaxxn
+ 0x006C01E8, // SDPax
+ 0x006D1785, // PDSPDoaxxn
+ 0x006E1E28, // SDPSnoax
+ 0x006F0C65, // PDSxnan
+ 0x00700CC5, // PDSana
+ 0x00711D5C, // SSDxPDxaxn
+ 0x00720648, // SDPSxox
+ 0x00730E28, // SDPnoan
+ 0x00740646, // DSPDxox
+ 0x00750E26, // DSPnoan
+ 0x00761B28, // SDPSnaox
+ 0x007700E6, // DSan
+ 0x007801E5, // PDSax
+ 0x00791786, // DSPDSoaxxn
+ 0x007A1E29, // DPSDnoax
+ 0x007B0C68, // SDPxnan
+ 0x007C1E24, // SPDSnoax
+ 0x007D0C69, // DPSxnan
+ 0x007E0955, // SPxDSxo
+ 0x007F03C9, // DPSaan
+ 0x008003E9, // DPSaa
+ 0x00810975, // SPxDSxon
+ 0x00820C49, // DPSxna
+ 0x00831E04, // SPDSnoaxn
+ 0x00840C48, // SDPxna
+ 0x00851E05, // PDSPnoaxn
+ 0x008617A6, // DSPDSoaxx
+ 0x008701C5, // PDSaxn
+ 0x008800C6, // DSa SRCAND
+ 0x00891B08, // SDPSnaoxn
+ 0x008A0E06, // DSPnoa
+ 0x008B0666, // DSPDxoxn
+ 0x008C0E08, // SDPnoa
+ 0x008D0668, // SDPSxoxn
+ 0x008E1D7C, // SSDxPDxax
+ 0x008F0CE5, // PDSanan
+ 0x00900C45, // PDSxna
+ 0x00911E08, // SDPSnoaxn
+ 0x009217A9, // DPSDPoaxx
+ 0x009301C4, // SPDaxn
+ 0x009417AA, // PSDPSoaxx
+ 0x009501C9, // DPSaxn
+ 0x00960169, // DPSxx
+ 0x0097588A, // PSDPSonoxx
+ 0x00981888, // SDPSonoxn
+ 0x00990066, // DSxn
+ 0x009A0709, // DPSnax
+ 0x009B07A8, // SDPSoaxn
+ 0x009C0704, // SPDnax
+ 0x009D07A6, // DSPDoaxn
+ 0x009E16E6, // DSPDSaoxx
+ 0x009F0345, // PDSxan
+ 0x00A000C9, // DPa
+ 0x00A11B05, // PDSPnaoxn
+ 0x00A20E09, // DPSnoa
+ 0x00A30669, // DPSDxoxn
+ 0x00A41885, // PDSPonoxn
+ 0x00A50065, // PDxn
+ 0x00A60706, // DSPnax
+ 0x00A707A5, // PDSPoaxn
+ 0x00A803A9, // DPSoa
+ 0x00A90189, // DPSoxn
+ 0x00AA0029, // D
+ 0x00AB0889, // DPSono
+ 0x00AC0744, // SPDSxax
+ 0x00AD06E9, // DPSDaoxn
+ 0x00AE0B06, // DSPnao
+ 0x00AF0229, // DPno
+ 0x00B00E05, // PDSnoa
+ 0x00B10665, // PDSPxoxn
+ 0x00B21974, // SSPxDSxox
+ 0x00B30CE8, // SDPanan
+ 0x00B4070A, // PSDnax
+ 0x00B507A9, // DPSDoaxn
+ 0x00B616E9, // DPSDPaoxx
+ 0x00B70348, // SDPxan
+ 0x00B8074A, // PSDPxax
+ 0x00B906E6, // DSPDaoxn
+ 0x00BA0B09, // DPSnao
+ 0x00BB0226, // DSno MERGEPAINT
+ 0x00BC1CE4, // SPDSanax
+ 0x00BD0D7D, // SDxPDxan
+ 0x00BE0269, // DPSxo
+ 0x00BF08C9, // DPSano
+ 0x00C000CA, // PSa MERGECOPY
+ 0x00C11B04, // SPDSnaoxn
+ 0x00C21884, // SPDSonoxn
+ 0x00C3006A, // PSxn
+ 0x00C40E04, // SPDnoa
+ 0x00C50664, // SPDSxoxn
+ 0x00C60708, // SDPnax
+ 0x00C707AA, // PSDPoaxn
+ 0x00C803A8, // SDPoa
+ 0x00C90184, // SPDoxn
+ 0x00CA0749, // DPSDxax
+ 0x00CB06E4, // SPDSaoxn
+ 0x00CC0020, // S SRCCOPY
+ 0x00CD0888, // SDPono
+ 0x00CE0B08, // SDPnao
+ 0x00CF0224, // SPno
+ 0x00D00E0A, // PSDnoa
+ 0x00D1066A, // PSDPxoxn
+ 0x00D20705, // PDSnax
+ 0x00D307A4, // SPDSoaxn
+ 0x00D41D78, // SSPxPDxax
+ 0x00D50CE9, // DPSanan
+ 0x00D616EA, // PSDPSaoxx
+ 0x00D70349, // DPSxan
+ 0x00D80745, // PDSPxax
+ 0x00D906E8, // SDPSaoxn
+ 0x00DA1CE9, // DPSDanax
+ 0x00DB0D75, // SPxDSxan
+ 0x00DC0B04, // SPDnao
+ 0x00DD0228, // SDno
+ 0x00DE0268, // SDPxo
+ 0x00DF08C8, // SDPano
+ 0x00E003A5, // PDSoa
+ 0x00E10185, // PDSoxn
+ 0x00E20746, // DSPDxax
+ 0x00E306EA, // PSDPaoxn
+ 0x00E40748, // SDPSxax
+ 0x00E506E5, // PDSPaoxn
+ 0x00E61CE8, // SDPSanax
+ 0x00E70D79, // SPxPDxan
+ 0x00E81D74, // SSPxDSxax
+ 0x00E95CE6, // DSPDSanaxxn
+ 0x00EA02E9, // DPSao
+ 0x00EB0849, // DPSxno
+ 0x00EC02E8, // SDPao
+ 0x00ED0848, // SDPxno
+ 0x00EE0086, // DSo SRCPAINT
+ 0x00EF0A08, // SDPnoo
+ 0x00F00021, // P PATCOPY
+ 0x00F10885, // PDSono
+ 0x00F20B05, // PDSnao
+ 0x00F3022A, // PSno
+ 0x00F40B0A, // PSDnao
+ 0x00F50225, // PDno
+ 0x00F60265, // PDSxo
+ 0x00F708C5, // PDSano
+ 0x00F802E5, // PDSao
+ 0x00F90845, // PDSxno
+ 0x00FA0089, // DPo
+ 0x00FB0A09, // DPSnoo PATPAINT
+ 0x00FC008A, // PSo
+ 0x00FD0A0A, // PSDnoo
+ 0x00FE02A9, // DPSoo
+ 0x00FF0062 // 1 WHITENESS
+};
+
+static inline void surface_to_image(cairo_surface_t *surface, GdiImage *image)
+{
+ cairo_format_t format = cairo_image_surface_get_format(surface);
+
+ ASSERT(format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
+ image->width = cairo_image_surface_get_width(surface);
+ image->height = cairo_image_surface_get_height(surface);
+ image->stride = cairo_image_surface_get_stride(surface);
+ image->pixels = cairo_image_surface_get_data(surface);
+}
+
+static void set_path(GdiCanvas *canvas, void *addr)
+{
+ 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) {
+ BeginPath(canvas->dc);
+ if (!MoveToEx(canvas->dc, (int)fix_to_double(point->x), (int)fix_to_double(point->y),
+ NULL)) {
+ CANVAS_ERROR("MoveToEx failed");
+ return;
+ }
+ point++;
+ }
+
+ if (flags & PATH_BEZIER) {
+ ASSERT((point - end_point) % 3 == 0);
+ for (; point + 2 < end_point; point += 3) {
+ POINT points[3];
+
+ points[0].x = (int)fix_to_double(point[0].x);
+ points[0].y = (int)fix_to_double(point[0].y);
+ points[1].x = (int)fix_to_double(point[1].x);
+ points[1].y = (int)fix_to_double(point[1].y);
+ points[2].x = (int)fix_to_double(point[2].x);
+ points[2].y = (int)fix_to_double(point[2].y);
+ if (!PolyBezierTo(canvas->dc, points, 3)) {
+ CANVAS_ERROR("PolyBezierTo failed");
+ return;
+ }
+ }
+ } else {
+ for (; point < end_point; point++) {
+ if (!LineTo(canvas->dc, (int)fix_to_double(point->x),
+ (int)fix_to_double(point->y))) {
+ CANVAS_ERROR("LineTo failed");
+ }
+ }
+ }
+
+ if (flags & PATH_END) {
+
+ if (flags & PATH_CLOSE) {
+ if (!CloseFigure(canvas->dc)) {
+ CANVAS_ERROR("CloseFigure failed");
+ }
+ }
+
+ if (!EndPath(canvas->dc)) {
+ CANVAS_ERROR("EndPath failed");
+ }
+ }
+
+ } while (more);
+}
+
+static void set_scale_mode(GdiCanvas *canvas, uint8_t scale_mode)
+{
+ if (scale_mode == IMAGE_SCALE_INTERPOLATE) {
+ SetStretchBltMode(canvas->dc, HALFTONE);
+ } else if (scale_mode == IMAGE_SCALE_NEAREST) {
+ SetStretchBltMode(canvas->dc, COLORONCOLOR);
+ } else {
+ CANVAS_ERROR("Unknown ScaleMode");
+ }
+}
+
+static void set_clip(GdiCanvas *canvas, Clip *clip)
+{
+ switch (clip->type) {
+ case CLIP_TYPE_NONE:
+ if (SelectClipRgn(canvas->dc, NULL) == ERROR) {
+ CANVAS_ERROR("SelectClipRgn failed");
+ }
+ 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);
+
+ if (now < end) {
+ HRGN main_hrgn;
+
+ main_hrgn = CreateRectRgn(now->left, now->top, now->right, now->bottom);
+ if (!main_hrgn) {
+ return;
+ }
+ now++;
+ for (; now < end; now++) {
+ HRGN combaine_hrgn;
+ combaine_hrgn = CreateRectRgn(now->left, now->top, now->right,
+ now->bottom);
+ if (!combaine_hrgn) {
+ CANVAS_ERROR("Unable to CreateRectRgn");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ if (CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR) == ERROR) {
+ CANVAS_ERROR("Unable to CombineRgn");
+ DeleteObject(combaine_hrgn);
+ return;
+ }
+ DeleteObject(combaine_hrgn);
+ }
+ if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
+ CANVAS_ERROR("Unable to SelectClipRgn");
+ }
+ DeleteObject(main_hrgn);
+ }
+ break;
+ }
+ case CLIP_TYPE_PATH:
+ set_path(canvas, GET_ADDRESS(clip->data));
+ if (SelectClipPath(canvas->dc, RGN_COPY) == ERROR) {
+ CANVAS_ERROR("Unable to SelectClipPath");
+ }
+ break;
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static void copy_bitmap(const uint8_t *src_image, int height, int src_stride,
+ uint8_t *dest_bitmap, int dest_stride)
+{
+ int copy_width = MIN(dest_stride, src_stride);
+ int y = 0;
+
+ ASSERT(dest_stride >= 0 && src_stride >= 0);
+ while (y < height) {
+ memcpy(dest_bitmap, src_image, copy_width);
+ src_image += src_stride;
+ dest_bitmap += dest_stride;
+ y++;
+ }
+}
+
+static void copy_bitmap_alpha(const uint8_t *src_alpha, int height, int width, int src_stride,
+ uint8_t *dest_bitmap, int dest_stride, int alpha_bits_size)
+{
+ int y = 0;
+ uint8_t i_offset;
+ int i_count = 0;
+ int i = 0;
+ int width_div_stride;
+
+ width_div_stride = width / src_stride;
+
+ if (alpha_bits_size == 1) {
+ i_offset = 1;
+ } else {
+ i_offset = 8;
+ }
+
+
+ while (y < height) {
+ int x;
+
+ for (x = 0; x < width; ++x) {
+ uint8_t alphaval;
+ double alpha;
+
+ alphaval = src_alpha[i];
+ alphaval = alphaval >> (i_count * i_offset);
+ alphaval = alphaval &= ((uint8_t)0xff >> (8 - i_offset));
+ alphaval = ((255 * alphaval) / ((uint8_t)0xff >> (8 - i_offset)));
+
+ dest_bitmap[x * 4 + 3] = alphaval;
+ alpha = (double)alphaval / 0xff;
+ dest_bitmap[x * 4 + 2] = (uint8_t)(alpha * dest_bitmap[x * 4 + 2]);
+ dest_bitmap[x * 4 + 1] = (uint8_t)(alpha * dest_bitmap[x * 4 + 1]);
+ dest_bitmap[x * 4] = (uint8_t)(alpha * dest_bitmap[x * 4]);
+
+ i_count++;
+ if (i_count == (8 / i_offset)) {
+ i++;
+ i_count = 0;
+ }
+ }
+
+ dest_bitmap += width * 4;
+ i = 0;
+ src_alpha += src_stride;
+ i_count = 0;
+ y++;
+ }
+}
+
+static uint8_t *create_bitmap(HBITMAP *bitmap, HBITMAP *prev_bitmap, HDC *dc,
+ const uint8_t *bitmap_data, int width, int height,
+ int stride, int bits, int rotate)
+{
+ uint8_t *data;
+ const uint8_t *src_data;
+ uint32_t nstride;
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = width;
+ if (stride < 0) {
+ bitmap_info.inf.bmiHeader.biHeight = height;
+ } else {
+ bitmap_info.inf.bmiHeader.biHeight = -height;
+ }
+
+ if (rotate) {
+ bitmap_info.inf.bmiHeader.biHeight = -bitmap_info.inf.bmiHeader.biHeight;
+ }
+
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ bitmap_info.inf.bmiHeader.biBitCount = bits;
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ *dc = create_compatible_dc();
+ if (!*dc) {
+ CANVAS_ERROR("create_compatible_dc() failed");
+ return NULL;
+ }
+
+ *bitmap = CreateDIBSection(*dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
+ if (!*bitmap) {
+ CANVAS_ERROR("Unable to CreateDIBSection");
+ DeleteDC(*dc);
+ return NULL;
+ }
+ *prev_bitmap = (HBITMAP)SelectObject(*dc, *bitmap);
+
+ if (stride < 0) {
+ src_data = bitmap_data - (height - 1) * -stride;
+ } else {
+ src_data = bitmap_data;
+ }
+
+ switch (bits) {
+ case 1:
+ nstride = ALIGN(width, 32) / 8;
+ break;
+ case 8:
+ nstride = ALIGN(width, 4);
+ break;
+ case 32:
+ nstride = width * 4;
+ break;
+ default:
+ CANVAS_ERROR("invalid bitmap bits size");
+ }
+
+ if (bitmap_data) {
+ if (stride < 0) {
+ copy_bitmap(src_data, height, -stride, data, nstride);
+ } else {
+ copy_bitmap(src_data, height, stride, data, nstride);
+ }
+ }
+
+ return data;
+}
+
+static void release_bitmap(HDC dc, HBITMAP bitmap, HBITMAP prev_bitmap, int cache)
+{
+ bitmap = (HBITMAP)SelectObject(dc, prev_bitmap);
+ if (!cache) {
+ DeleteObject(bitmap);
+ }
+ DeleteDC(dc);
+}
+
+static inline uint8_t get_converted_color(uint8_t color)
+{
+ uint8_t msb;
+
+ msb = color & 0xE0;
+ msb = msb >> 5;
+ color |= msb;
+ return color;
+}
+
+static inline COLORREF get_color_ref(GdiCanvas *canvas, uint32_t color)
+{
+ int shift = canvas->base.color_shift == 8 ? 0 : 3;
+ uint8_t r, g, b;
+
+ b = (color & canvas->base.color_mask);
+ color >>= canvas->base.color_shift;
+ g = (color & canvas->base.color_mask);
+ color >>= canvas->base.color_shift;
+ r = (color & canvas->base.color_mask);
+ if (shift) {
+ r = get_converted_color(r << shift);
+ g = get_converted_color(g << shift);
+ b = get_converted_color(b << shift);
+ }
+ return RGB(r, g, b);
+}
+
+static HBRUSH get_brush(GdiCanvas *canvas, Brush *brush)
+{
+ HBRUSH hbrush;
+
+ switch (brush->type) {
+ case BRUSH_TYPE_SOLID:
+ if (!(hbrush = CreateSolidBrush(get_color_ref(canvas, brush->u.color)))) {
+ CANVAS_ERROR("CreateSolidBrush failed");
+ }
+ return hbrush;
+ case BRUSH_TYPE_PATTERN: {
+ GdiImage image;
+ HBRUSH hbrush;
+ cairo_surface_t *surface;
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+ surface_to_image(surface, &image);
+
+ if (!create_bitmap(&bitmap, &prev_bitmap, &dc, image.pixels, image.width,
+ image.height, image.stride, 32, 0)) {
+ CANVAS_ERROR("create_bitmap failed");
+ return NULL;
+ }
+
+ if (!(hbrush = CreatePatternBrush(bitmap))) {
+ CANVAS_ERROR("CreatePatternBrush failed");
+ }
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+ cairo_surface_destroy(surface);
+ return hbrush;
+ }
+ case BRUSH_TYPE_NONE:
+ return NULL;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ return NULL;
+ }
+}
+
+static HBRUSH set_brush(HDC dc, HBRUSH hbrush, Brush *brush)
+{
+ switch (brush->type) {
+ case BRUSH_TYPE_SOLID: {
+ return (HBRUSH)SelectObject(dc, hbrush);
+ }
+ case BRUSH_TYPE_PATTERN: {
+ HBRUSH prev_hbrush;
+ prev_hbrush = (HBRUSH)SelectObject(dc, hbrush);
+ if (!SetBrushOrgEx(dc, brush->u.pattern.pos.x, brush->u.pattern.pos.y, NULL)) {
+ CANVAS_ERROR("SetBrushOrgEx failed");
+ }
+ return prev_hbrush;
+ }
+ default:
+ CANVAS_ERROR("invalid brush type");
+ return NULL;
+ }
+}
+
+static void unset_brush(HDC dc, HBRUSH prev_hbrush)
+{
+ if (!prev_hbrush) {
+ return;
+ }
+ prev_hbrush = (HBRUSH)SelectObject(dc, prev_hbrush);
+ DeleteObject(prev_hbrush);
+}
+
+uint8_t calc_rop3(uint16_t rop3_bits, int brush)
+{
+ uint8_t rop3 = 0;
+ uint8_t rop3_src = _rop3_src;
+ uint8_t rop3_dest = _rop3_dest;
+ uint8_t rop3_brush = _rop3_brush;
+ uint8_t rop3_src_brush;
+
+ if (rop3_bits & ROPD_INVERS_SRC) {
+ rop3_src = ~rop3_src;
+ }
+ if (rop3_bits & ROPD_INVERS_BRUSH) {
+ rop3_brush = ~rop3_brush;
+ }
+ if (rop3_bits & ROPD_INVERS_DEST) {
+ rop3_dest = ~rop3_dest;
+ }
+
+ if (brush) {
+ rop3_src_brush = rop3_brush;
+ } else {
+ rop3_src_brush = rop3_src;
+ }
+
+ if (rop3_bits & ROPD_OP_PUT) {
+ rop3 = rop3_src_brush;
+ }
+ if (rop3_bits & ROPD_OP_OR) {
+ rop3 = rop3_src_brush | rop3_dest;
+ }
+ if (rop3_bits & ROPD_OP_AND) {
+ rop3 = rop3_src_brush & rop3_dest;
+ }
+ if (rop3_bits & ROPD_OP_XOR) {
+ rop3 = rop3_src_brush ^ rop3_dest;
+ }
+ if (rop3_bits & ROPD_INVERS_RES) {
+ rop3 = ~rop3_dest;
+ }
+
+ if (rop3_bits & ROPD_OP_BLACKNESS || rop3_bits & ROPD_OP_WHITENESS ||
+ rop3_bits & ROPD_OP_INVERS) {
+ CANVAS_ERROR("invalid rop3 type");
+ }
+ return rop3;
+}
+
+uint8_t calc_rop3_src_brush(uint16_t rop3_bits)
+{
+ uint8_t rop3 = 0;
+ uint8_t rop3_src = _rop3_src;
+ uint8_t rop3_brush = _rop3_brush;
+
+ if (rop3_bits & ROPD_INVERS_SRC) {
+ rop3_src = ~rop3_src;
+ }
+ if (rop3_bits & ROPD_INVERS_BRUSH) {
+ rop3_brush = ~rop3_brush;
+ }
+
+ if (rop3_bits & ROPD_OP_OR) {
+ rop3 = rop3_src | rop3_brush;
+ }
+ if (rop3_bits & ROPD_OP_AND) {
+ rop3 = rop3_src & rop3_brush;
+ }
+ if (rop3_bits & ROPD_OP_XOR) {
+ rop3 = rop3_src ^ rop3_brush;
+ }
+
+ return rop3;
+}
+
+static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct QMask *mask)
+{
+ cairo_surface_t *surface;
+ struct BitmapData bitmap;
+ BitmapCache *bitmap_cache;
+
+ bitmap.hbitmap = NULL;
+ if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+ return bitmap;
+ }
+
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ bitmap.dc = create_compatible_dc();
+ bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, bitmap_cache->bitmap);
+ bitmap.hbitmap = bitmap_cache->bitmap;
+ ReleaseMutex(bitmap_cache->mutex);
+ bitmap.cache = 1;
+ } else if (!create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
+ cairo_image_surface_get_data(surface),
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface),
+ cairo_image_surface_get_stride(surface), 1, 0)) {
+ bitmap.hbitmap = NULL;
+ } else {
+ bitmap.cache = 0;
+ }
+
+ bitmap.flags = mask->flags;
+ bitmap.pos = mask->pos;
+
+ return bitmap;
+}
+
+static void gdi_draw_bitmap(HDC dest_dc, const Rect *src, const Rect *dest,
+ HDC src_dc, struct BitmapData *bitmapmask, uint32_t rop3_val)
+{
+ uint32_t rast_oper;
+
+ rast_oper = raster_ops[rop3_val];
+
+ if (!bitmapmask || !bitmapmask->hbitmap) {
+ if ((dest->right - dest->left) == (src->right - src->left) &&
+ (dest->bottom - dest->top) == (src->bottom - src->top)) {
+ if (!BitBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top, rast_oper)) {
+ CANVAS_ERROR("BitBlt failed");
+ }
+ } else {
+ if (!StretchBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, rast_oper)) {
+ CANVAS_ERROR("StretchBlt failed");
+ }
+ }
+ } else {
+ rast_oper = MAKEROP4(rast_oper, raster_ops[_rop3_dest]);
+
+ if (!MaskBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ bitmapmask->hbitmap, bitmapmask->pos.x, bitmapmask->pos.y,
+ rast_oper)) {
+ CANVAS_ERROR("MaskBlt failed");
+ }
+ }
+}
+
+static void gdi_draw_bitmap_redrop(HDC dest_dc, const Rect *src, const Rect *dest,
+ HDC src_dc, struct BitmapData *bitmapmask,
+ uint16_t rop, int brush)
+{
+ uint32_t rop3_val;
+
+ rop3_val = calc_rop3(rop, brush);
+ gdi_draw_bitmap(dest_dc, src, dest, src_dc, bitmapmask, rop3_val);
+}
+
+static void free_mask(struct BitmapData *bitmap)
+{
+ if (bitmap->hbitmap) {
+ release_bitmap(bitmap->dc, bitmap->hbitmap, bitmap->prev_hbitmap, bitmap->cache);
+ }
+}
+
+static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
+ String *str, int n, Rect *dest,
+ Rect *src, Brush *brush)
+{
+ cairo_surface_t *surface;
+ struct BitmapData bitmap;
+ Point pos;
+ int dest_stride;
+ uint8_t *bitmap_data;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+
+ bitmap.hbitmap = (HBITMAP)1;
+ if (!(surface = canvas_get_str_mask(&canvas->base, str, n, &pos))) {
+ CANVAS_ERROR("unable to canvas_get_str_mask");
+ return;
+ }
+
+ bitmap.cache = 0;
+ bitmap_data = create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap,
+ &bitmap.dc, NULL,
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface),
+ cairo_image_surface_get_stride(surface), 32, 0);
+
+ if (!bitmap_data) {
+ return;
+ }
+
+ bitmap.flags = 0;
+ bitmap.pos.x = 0;
+ bitmap.pos.y = 0;
+
+ dest->left = pos.x;
+ dest->top = pos.y;
+ dest->right = pos.x + cairo_image_surface_get_width(surface);
+ dest->bottom = pos.y + cairo_image_surface_get_height(surface);
+ src->left = 0;
+ src->top = 0;
+ src->right = cairo_image_surface_get_width(surface);
+ src->bottom = cairo_image_surface_get_height(surface);
+
+ dest_stride = cairo_image_surface_get_width(surface);
+ switch (n) {
+ case 1:
+ dest_stride = dest_stride / 8;
+ break;
+ case 4:
+ dest_stride = dest_stride / 2;
+ break;
+ case 32:
+ dest_stride = dest_stride * 4;
+ break;
+ default:
+ CANVAS_ERROR("unsupported bitmap bits size");
+ }
+ dest_stride = dest_stride + 3;
+ dest_stride = dest_stride & ~3;
+
+ hbrush = get_brush(canvas, brush);
+ prev_hbrush = set_brush(bitmap.dc, hbrush, brush);
+ gdi_draw_bitmap(bitmap.dc, src, src, bitmap.dc, NULL, _rop3_brush);
+
+ unset_brush(bitmap.dc, prev_hbrush);
+
+ copy_bitmap_alpha(cairo_image_surface_get_data(surface),
+ cairo_image_surface_get_height(surface),
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_stride(surface),
+ bitmap_data, dest_stride, n);
+
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+
+ Lock lock(*canvas->lock);
+ AlphaBlend(canvas->dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, bitmap.dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, bf);
+
+ free_mask(&bitmap);
+}
+
+static void gdi_draw_image(HDC dest_dc, const Rect *src, const Rect *dest,
+ const uint8_t *bitmap_data, int bit_stride, int bit_width,
+ int bit_height, struct BitmapData *bitmapmask, uint16_t rop,
+ int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap(&bitmap, &prev_bitmap, &dc, bitmap_data, bit_width, bit_height,
+ bit_stride, 32, rotate);
+
+ gdi_draw_bitmap_redrop(dest_dc, src, dest, dc, bitmapmask, rop, 0);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+static void gdi_draw_image_rop3(HDC dest_dc, const Rect *src, const Rect *dest,
+ const uint8_t *bitmap_data, int bit_stride, int bit_width,
+ int bit_height, struct BitmapData *bitmapmask, uint8_t rop3,
+ int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap(&bitmap, &prev_bitmap, &dc, bitmap_data, bit_width, bit_height,
+ bit_stride, 32, rotate);
+
+ gdi_draw_bitmap(dest_dc, src, dest, dc, bitmapmask, rop3);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+void gdi_canvas_draw_fill(GdiCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill)
+{
+ HBRUSH prev_hbrush;
+ HBRUSH brush;
+ struct BitmapData bitmapmask;
+
+ if (!(brush = get_brush(canvas, &fill->brush))) {
+ CANVAS_ERROR("no braash");
+ return;
+ }
+ bitmapmask = get_mask_bitmap(canvas, &fill->mask);
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, brush, &fill->brush);
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
+ fill->rop_decriptor, fill->brush.type != BRUSH_TYPE_NONE);
+
+ free_mask(&bitmapmask);
+ unset_brush(canvas->dc, prev_hbrush);
+}
+
+void gdi_canvas_draw_copy(GdiCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ struct BitmapData bitmapmask;
+ BitmapCache *bitmap_cache;
+
+ bitmapmask = get_mask_bitmap(canvas, &copy->mask);
+ surface = canvas_get_image(&canvas->base, copy->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+
+ Lock lock(*canvas->lock);
+ set_scale_mode(canvas, copy->scale_mode);
+ set_clip(canvas, clip);
+
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, dc,
+ &bitmapmask, copy->rop_decriptor, 0);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image(canvas->dc, &copy->src_area, bbox, image.pixels,
+ image.stride, image.width, image.height, &bitmapmask,
+ copy->rop_decriptor, 0);
+ }
+
+ free_mask(&bitmapmask);
+
+ cairo_surface_destroy(surface);
+}
+
+void gdi_canvas_put_image(GdiCanvas *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)
+{
+ Rect src;
+ src.top = 0;
+ src.bottom = src_height;
+ src.left = 0;
+ src.right = src_width;
+
+ Lock lock(*canvas->lock);
+ set_scale_mode(canvas, IMAGE_SCALE_NEAREST);
+ if (clip) {
+ if (clip->num_rects == 0) {
+ return;
+ } else {
+ HRGN main_hrgn;
+ uint32_t i;
+
+ main_hrgn = CreateRectRgn(clip->rects[0].left, clip->rects[0].top, clip->rects[0].right,
+ clip->rects[0].bottom);
+ if (!main_hrgn) {
+ return;
+ }
+
+ for (i = 1; i < clip->num_rects; i++) {
+ HRGN combaine_hrgn;
+
+ combaine_hrgn = CreateRectRgn(clip->rects[i].left, clip->rects[i].top,
+ clip->rects[i].right,
+ clip->rects[i].bottom);
+ if (!combaine_hrgn) {
+ CANVAS_ERROR("CreateRectRgn failed");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ if (!CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR)) {
+ CANVAS_ERROR("CombineRgn failed in put_image");
+ return;
+ }
+ DeleteObject(combaine_hrgn);
+ }
+ if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
+ CANVAS_ERROR("SelectClipRgn failed in put_image");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ DeleteObject(main_hrgn);
+ }
+ } else {
+ SelectClipRgn(canvas->dc, NULL);
+ }
+
+ if (dc) {
+ gdi_draw_bitmap_redrop(canvas->dc, &src, dest, dc,
+ NULL, ROPD_OP_PUT, 0);
+ } else {
+ gdi_draw_image(canvas->dc, &src, dest, src_data,
+ src_stride, src_width, src_height, NULL, ROPD_OP_PUT, 0);
+ }
+}
+
+static void gdi_draw_bitmap_transparent(GdiCanvas *canvas, HDC dest_dc, const Rect *src,
+ const Rect *dest, HDC src_dc, uint32_t color)
+{
+ TransparentBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top,
+ RGB(((uint8_t*)&color)[2], ((uint8_t*)&color)[1], ((uint8_t*)&color)[0]));
+}
+
+static void gdi_draw_image_transparent(GdiCanvas *canvas, HDC dest_dc, const Rect *src,
+ const Rect *dest, const uint8_t *bitmap_data,
+ int bit_stride, int bit_width, int bit_height,
+ uint32_t color, int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap(&bitmap, &prev_bitmap, &dc, bitmap_data, bit_width, bit_height,
+ bit_stride, 32, rotate);
+
+ gdi_draw_bitmap_transparent(canvas, dest_dc, src, dest, dc, color);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+void gdi_canvas_draw_transparent(GdiCanvas *canvas, Rect *bbox, Clip *clip,
+ Transparent* transparent)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ BitmapCache *bitmap_cache;
+
+ surface = canvas_get_image(&canvas->base, transparent->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
+ transparent->true_color);
+
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, image.pixels,
+ image.stride, image.width, image.height,
+ transparent->true_color, 0);
+ }
+
+ cairo_surface_destroy(surface);
+}
+
+static void gdi_draw_bitmap_alpha(HDC dest_dc, const Rect *src, const Rect *dest,
+ HDC src_dc, uint8_t alpha)
+{
+ BLENDFUNCTION bf;
+
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.SourceConstantAlpha = alpha;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+
+ if (!AlphaBlend(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, bf)) {
+ CANVAS_ERROR("AlphaBlend failed");
+ }
+}
+
+static void gdi_draw_image_alpha(HDC dest_dc, const Rect *src, const Rect *dest,
+ const uint8_t *bitmap_data, int bit_stride,
+ int bit_width, int bit_height, uint8_t alpha,
+ int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap(&bitmap, &prev_bitmap, &dc, bitmap_data, bit_width, bit_height,
+ bit_stride, 32, rotate);
+
+ gdi_draw_bitmap_alpha(dest_dc, src, dest, dc, alpha);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ BitmapCache *bitmap_cache;
+
+ surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, image.pixels,
+ image.stride, image.width, image.height,
+ alpha_blend->alpha, 0);
+ }
+
+ cairo_surface_destroy(surface);
+}
+
+void gdi_canvas_draw_opaque(GdiCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ struct BitmapData bitmapmask;
+ BitmapCache *bitmap_cache;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+ uint8_t rop3;
+
+ surface = canvas_get_image(&canvas->base, opaque->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+ bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
+ rop3 = calc_rop3_src_brush(opaque->rop_decriptor);
+ hbrush = get_brush(canvas, &opaque->brush);
+
+
+ Lock lock(*canvas->lock);
+ set_scale_mode(canvas, opaque->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
+
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, image.pixels,
+ image.stride, image.width, image.height, &bitmapmask, rop3, 0);
+ }
+
+ unset_brush(canvas->dc, prev_hbrush);
+
+ free_mask(&bitmapmask);
+
+ cairo_surface_destroy(surface);
+}
+
+void gdi_canvas_draw_blend(GdiCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ struct BitmapData bitmapmask;
+ BitmapCache *bitmap_cache;
+
+ bitmapmask = get_mask_bitmap(canvas, &blend->mask);
+ surface = canvas_get_image(&canvas->base, blend->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+
+ Lock lock(*canvas->lock);
+ set_scale_mode(canvas, blend->scale_mode);
+ set_clip(canvas, clip);
+
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
+ &bitmapmask, blend->rop_decriptor, 0);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image(canvas->dc, &blend->src_area, bbox, image.pixels, image.stride, image.width,
+ image.height, &bitmapmask, blend->rop_decriptor, 0);
+ }
+
+ free_mask(&bitmapmask);
+
+ cairo_surface_destroy(surface);
+}
+
+void gdi_canvas_draw_blackness(GdiCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness)
+{
+ struct BitmapData bitmapmask;
+
+ bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x0);
+
+ free_mask(&bitmapmask);
+}
+
+void gdi_canvas_draw_invers(GdiCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers)
+{
+ struct BitmapData bitmapmask;
+
+ bitmapmask = get_mask_bitmap(canvas, &invers->mask);
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x55);
+
+ free_mask(&bitmapmask);
+}
+
+void gdi_canvas_draw_whiteness(GdiCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness)
+{
+ struct BitmapData bitmapmask;
+
+ bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0xff);
+
+ free_mask(&bitmapmask);
+}
+
+void gdi_canvas_draw_rop3(GdiCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3)
+{
+ cairo_surface_t *surface;
+ GdiImage image;
+ struct BitmapData bitmapmask;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+ BitmapCache *bitmap_cache;
+
+ hbrush = get_brush(canvas, &rop3->brush);
+ surface = canvas_get_image(&canvas->base, rop3->src_bitmap);
+ bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+ bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
+
+ Lock lock(*canvas->lock);
+ set_scale_mode(canvas, rop3->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
+
+ if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+ gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
+ &bitmapmask, rop3->rop3);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(bitmap_cache->mutex);
+ } else {
+ surface_to_image(surface, &image);
+ gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, image.pixels,
+ image.stride, image.width, image.height, &bitmapmask, rop3->rop3, 0);
+ }
+
+ unset_brush(canvas->dc, prev_hbrush);
+ free_mask(&bitmapmask);
+
+ cairo_surface_destroy(surface);
+}
+
+void gdi_canvas_copy_bits(GdiCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos)
+{
+ Lock lock(*canvas->lock);
+
+ set_clip(canvas, clip);
+
+ BitBlt(canvas->dc, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top, canvas->dc, src_pos->x, src_pos->y, SRCCOPY);
+}
+
+void gdi_canvas_draw_text(GdiCanvas *canvas, Rect *bbox, Clip *clip, Text *text)
+{
+ String *str;
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ lock.unlock();
+
+ if (!rect_is_empty(&text->back_area)) {
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+
+ hbrush = get_brush(canvas, &text->back_brush);
+ Lock lock(*canvas->lock);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &text->back_brush);
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
+ text->back_mode, 1);
+ unset_brush(canvas->dc, prev_hbrush);
+ }
+
+ str = (String *)GET_ADDRESS(text->str);
+
+ if (str->flags & STRING_RASTER_A1) {
+ Rect dest;
+ Rect src;
+
+ draw_str_mask_bitmap(canvas, str, 1, &dest, &src, &text->fore_brush);
+ } else if (str->flags & STRING_RASTER_A4) {
+ Rect dest;
+ Rect src;
+
+ draw_str_mask_bitmap(canvas, str, 4, &dest, &src, &text->fore_brush);
+ } else if (str->flags & STRING_RASTER_A8) {
+ WARN("untested path A8 glyphs, doing nothing");
+ if (0) {
+ Rect dest;
+ Rect src;
+
+ draw_str_mask_bitmap(canvas, str, 8, &dest, &src, &text->fore_brush);
+ }
+ } else {
+ WARN("untested path vector glyphs, doing nothing");
+ if (0) {
+ }
+ }
+}
+
+static int get_join_style(uint8_t join_style)
+{
+ switch (join_style) {
+ case LINE_JOIN_ROUND:
+ return PS_JOIN_ROUND;
+ case LINE_JOIN_BEVEL:
+ return PS_JOIN_BEVEL;
+ case LINE_JOIN_MITER:
+ return PS_JOIN_MITER;
+ default:
+ CANVAS_ERROR("bad join style %d", join_style);
+ }
+}
+
+static int get_cap(int end_style)
+{
+ switch (end_style) {
+ case LINE_CAP_ROUND:
+ return PS_ENDCAP_ROUND;
+ case LINE_CAP_SQUARE:
+ return PS_ENDCAP_SQUARE;
+ case LINE_CAP_BUTT:
+ return PS_ENDCAP_FLAT;
+ default:
+ CANVAS_ERROR("bad end style %d", end_style);
+ }
+}
+
+static uint32_t *gdi_get_userstyle(GdiCanvas *canvas, UINT8 nseg, ADDRESS addr, int start_is_gap)
+{
+ FIXED28_4* style = (FIXED28_4*)GET_ADDRESS(addr);
+ double offset = 0;
+ uint32_t *local_style;
+ int i;
+
+ access_test(&canvas->base, style, nseg * sizeof(*style));
+
+ if (nseg == 0) {
+ CANVAS_ERROR("bad nseg");
+ }
+ local_style = (uint32_t *)malloc(nseg * sizeof(*local_style));
+
+ if (start_is_gap) {
+ offset = (uint32_t)fix_to_double(*style);
+ local_style[nseg - 1] = (uint32_t)fix_to_double(*style);
+ style++;
+
+ for (i = 0; i < nseg - 1; i++, style++) {
+ local_style[i] = (uint32_t)fix_to_double(*style);
+ }
+ } else {
+ for (i = 0; i < nseg; i++, style++) {
+ local_style[i] = (uint32_t)fix_to_double(*style);
+ }
+ }
+
+ return local_style;
+}
+
+void gdi_canvas_draw_stroke(GdiCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke)
+{
+ HPEN hpen;
+ HPEN prev_hpen;
+ LOGBRUSH logbrush;
+ int ps_join = 0;
+ int line_cap = 0;
+ uint32_t *user_style = NULL;
+ cairo_surface_t *surface = NULL;
+
+ if (stroke->brush.type == BRUSH_TYPE_PATTERN) {
+ surface = canvas_get_image(&canvas->base, stroke->brush.u.pattern.pat);
+ }
+
+ Lock lock(*canvas->lock);
+ set_clip(canvas, clip);
+
+ switch (stroke->fore_mode) {
+ case ROPD_OP_WHITENESS:
+ SetROP2(canvas->dc, R2_WHITE); //0
+ break;
+ case ROPD_OP_BLACKNESS:
+ SetROP2(canvas->dc, R2_BLACK); //1
+ break;
+ case ROPD_OP_INVERS:
+ SetROP2(canvas->dc, R2_NOT); //Dn
+ break;
+ case ROPD_OP_PUT:
+ SetROP2(canvas->dc, R2_COPYPEN); //P
+ break;
+ case ROPD_OP_OR:
+ SetROP2(canvas->dc, R2_MERGEPEN); //DPo
+ break;
+ case ROPD_OP_XOR:
+ SetROP2(canvas->dc, R2_XORPEN); //DPx
+ break;
+ case ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKPEN); //DPa
+ break;
+ case ROPD_INVERS_BRUSH | ROPD_OP_PUT: //Pn
+ SetROP2(canvas->dc, R2_NOTCOPYPEN);
+ break;
+ case ROPD_OP_XOR | ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTXORPEN); //DPxn
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTMERGEPEN); //DPon
+ break;
+ case ROPD_OP_AND | ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTMASKPEN); //DPan
+ break;
+ case ROPD_INVERS_DEST | ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKPENNOT); //PDna
+ break;
+ case ROPD_INVERS_BRUSH | ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKNOTPEN); //DPna
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_BRUSH:
+ SetROP2(canvas->dc, R2_MERGENOTPEN); //DPno
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_DEST:
+ SetROP2(canvas->dc, R2_MERGEPENNOT); //PDno
+ break;
+ default:
+ SetROP2(canvas->dc, R2_NOP); //D
+ }
+
+
+ if (stroke->brush.type == BRUSH_TYPE_SOLID) {
+ logbrush.lbStyle = BS_SOLID | DIB_RGB_COLORS;
+ logbrush.lbHatch = 0;
+ logbrush.lbColor = get_color_ref(canvas, stroke->brush.u.color);
+ } else if (stroke->brush.type == BRUSH_TYPE_PATTERN) {
+#if 0
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+ GdiImage image;
+#endif
+ //CANVAS_ERROR("untested path stroke brush with pattern");
+#if 0
+ ASSERT(surface)
+ surface_to_image(surface, &image);
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = image.width;
+ if (image.stride < 0) {
+ bitmap_info.inf.bmiHeader.biHeight = image.height;
+ } else {
+ bitmap_info.inf.bmiHeader.biHeight = -image.height;
+ }
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ bitmap_info.inf.bmiHeader.biBitCount = 32;
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ if (image.stride < 0) {
+ logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
+ image.height * -image.stride + sizeof(BITMAPINFO));
+ if (!logbrush.lbHatch) {
+ CANVAS_ERROR("GlobalAlloc failed");
+ }
+ copy_bitmap(image.pixels - (image.height - 1) * -image.stride,
+ image.height, -image.stride,
+ (uint8_t *)logbrush.lbHatch, image.width);
+ } else {
+ logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
+ image.height * image.stride + sizeof(BITMAPINFO));
+ if (!logbrush.lbHatch) {
+ CANVAS_ERROR("GlobalAlloc failed");
+ }
+ copy_bitmap(image.pixels, image.height, image.stride,
+ (uint8_t *)logbrush.lbHatch, image.width);
+ }
+
+ memcpy((void *)logbrush.lbHatch, &bitmap_info.inf, sizeof(BITMAPINFO));
+
+ logbrush.lbStyle = BS_DIBPATTERN | DIB_RGB_COLORS;
+ logbrush.lbColor = 0;
+#endif
+ cairo_surface_destroy(surface);
+ }
+
+#if 0
+ ps_join = get_join_style(stroke->attr.join_style);
+ line_cap = get_cap(stroke->attr.end_style);
+
+ SetMiterLimit(canvas->dc, (FLOAT)fix_to_double(stroke->attr.miter_limit), &old_miter);
+#endif
+
+ if (stroke->attr.flags & LINE_ATTR_STYLED) {
+ user_style = gdi_get_userstyle(canvas, stroke->attr.style_nseg,
+ stroke->attr.style,
+ !!(stroke->attr.flags & LINE_ATTR_STARTGAP));
+ hpen = ExtCreatePen(PS_GEOMETRIC | ps_join | line_cap | PS_USERSTYLE,
+ (uint32_t)fix_to_double(stroke->attr.width),
+ &logbrush, stroke->attr.style_nseg, (DWORD *)user_style);
+ } else {
+ hpen = ExtCreatePen(PS_GEOMETRIC | ps_join | line_cap,
+ (uint32_t)fix_to_double(stroke->attr.width),
+ &logbrush, 0, NULL);
+ }
+ prev_hpen = (HPEN)SelectObject(canvas->dc, hpen);
+
+ set_path(canvas, GET_ADDRESS(stroke->path));
+
+ StrokePath(canvas->dc);
+
+ SelectObject(canvas->dc, prev_hpen);
+ DeleteObject(hpen);
+
+#if 0
+ if (stroke->brush.type == BRUSH_TYPE_PATTERN) {
+ GlobalFree((HGLOBAL)logbrush.lbHatch);
+ }
+#endif
+
+ if (user_style) {
+ free(user_style);
+ }
+}
+
+void gdi_canvas_clear(GdiCanvas *canvas)
+{
+}
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void gdi_canvas_set_access_params(GdiCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max)
+{
+ __canvas_set_access_params(&canvas->base, delta, base, max);
+}
+
+#else
+void gdi_canvas_set_access_params(GdiCanvas *canvas, ADDRESS delta)
+{
+ __gdi_canvas_set_access_params(&canvas->base, delta);
+}
+
+#endif
+
+void gdi_canvas_destroy(GdiCanvas *canvas)
+{
+ if (!canvas) {
+ return;
+ }
+ canvas_base_destroy(&canvas->base);
+ free(canvas);
+}
+
+static int need_init = 1;
+
+#ifdef CAIRO_CANVAS_CACHE
+GdiCanvas *gdi_canvas_create(HDC dc, Mutex* lock, 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)
+GdiCanvas *gdi_canvas_create(HDC dc, int bits,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get
+#else
+GdiCanvas *gdi_canvas_create(HDC dc, int bits
+#endif
+#ifdef USE_GLZ
+ , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+#endif
+ )
+{
+ GdiCanvas *canvas;
+ int init_ok;
+
+ if (need_init || !(canvas = (GdiCanvas *)malloc(sizeof(GdiCanvas)))) {
+ return NULL;
+ }
+ memset(canvas, 0, sizeof(GdiCanvas));
+#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 = gdi_canvas_base_init(&canvas->base, bits,
+ bits_cache_opaque,
+ bits_cache_put,
+ bits_cache_get
+#else
+ init_ok = gdi_canvas_base_init(&canvas->base, bits
+#endif
+#ifdef USE_GLZ
+ ,
+ glz_decoder_opaque,
+ glz_decode
+#endif
+ );
+ canvas->dc = dc;
+ canvas->lock = lock;
+ return canvas;
+}
+
+void gdi_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+ rop3_init();
+}
+
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
new file mode 100644
index 00000000..13d7ef3d
--- /dev/null
+++ b/common/gdi_canvas.h
@@ -0,0 +1,76 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H__GDI_CANVAS
+#define _H__GDI_CANVAS
+
+#include <stdint.h>
+
+#include "draw.h"
+#include "cairo.h"
+#include "canvas_base.h"
+#include "region.h"
+
+typedef struct GdiCanvas GdiCanvas;
+
+typedef struct {
+ int width;
+ int height;
+ int stride;
+ uint8_t *pixels;
+} GdiImage;
+
+void gdi_canvas_draw_fill(GdiCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill);
+void gdi_canvas_draw_copy(GdiCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy);
+void gdi_canvas_draw_opaque(GdiCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque);
+void gdi_canvas_copy_bits(GdiCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos);
+void gdi_canvas_draw_text(GdiCanvas *canvas, Rect *bbox, Clip *clip, Text *text);
+void gdi_canvas_draw_stroke(GdiCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke);
+void gdi_canvas_draw_rop3(GdiCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3);
+void gdi_canvas_draw_blend(GdiCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend);
+void gdi_canvas_draw_blackness(GdiCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness);
+void gdi_canvas_draw_whiteness(GdiCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness);
+void gdi_canvas_draw_invers(GdiCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers);
+void gdi_canvas_draw_transparent(GdiCanvas *canvas, Rect *bbox, Clip *clip,
+ Transparent* transparent);
+void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend);
+void gdi_canvas_put_image(GdiCanvas *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);
+void gdi_canvas_clear(GdiCanvas *canvas);
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void gdi_canvas_set_access_params(GdiCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max);
+#else
+void gdi_canvas_set_access_params(GdiCanvas *canvas, ADDRESS delta);
+#endif
+
+
+GdiCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, 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,
+ void *glz_decoder_opaque,
+ glz_decode_fn_t glz_decode);
+
+void gdi_canvas_destroy(GdiCanvas *canvas);
+
+void gdi_canvas_init();
+
+#endif
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
new file mode 100644
index 00000000..aac93c92
--- /dev/null
+++ b/common/gl_canvas.c
@@ -0,0 +1,886 @@
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gl_canvas.h"
+#include "quic.h"
+#include "rop3.h"
+#include "region.h"
+
+#define GL_CANVAS
+#include "canvas_base.c"
+
+struct GLCanvas {
+ CanvasBase base;
+ GLCCtx glc;
+ void *usr_data;
+ void *private_data;
+ int private_data_size;
+};
+
+static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int stride, int height)
+{
+ uint8_t *ret_data = (uint8_t *)data;
+ uint8_t *dest;
+ uint8_t *src;
+ int i;
+
+ if (!canvas->private_data) {
+ canvas->private_data = malloc(stride * height);
+ if (!canvas->private_data) {
+ return ret_data;
+ }
+ canvas->private_data_size = stride * height;
+ }
+
+ if (canvas->private_data_size < (stride * height)) {
+ free(canvas->private_data);
+ canvas->private_data = malloc(stride * height);
+ if (!canvas->private_data) {
+ return ret_data;
+ }
+ canvas->private_data_size = stride * height;
+ }
+
+ dest = (uint8_t *)canvas->private_data;
+ src = (uint8_t *)data + (height - 1) * stride;
+
+ for (i = 0; i < height; ++i) {
+ memcpy(dest, src, stride);
+ dest += stride;
+ src -= stride;
+ }
+ return (uint8_t *)canvas->private_data;
+}
+
+static cairo_surface_t *canvas_surf_to_trans_surf(GLCImage *image,
+ uint32_t trans_color)
+{
+ int width = image->width;
+ int height = image->height;
+ uint8_t *src_line;
+ uint8_t *end_src_line;
+ int src_stride;
+ uint8_t *dest_line;
+ int dest_stride;
+ cairo_surface_t *ret;
+ int i;
+
+ ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ if (cairo_surface_status(ret) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(ret)));
+ }
+
+ src_line = image->pixels;
+ src_stride = image->stride;
+ end_src_line = src_line + src_stride * height;
+
+ dest_line = cairo_image_surface_get_data(ret);
+ dest_stride = cairo_image_surface_get_stride(ret);
+
+ for (; src_line < end_src_line; src_line += src_stride, dest_line += dest_stride) {
+ for (i = 0; i < width; i++) {
+ if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
+ ((uint32_t*)dest_line)[i] = 0;
+ } else {
+ ((uint32_t*)dest_line)[i] = (((uint32_t*)src_line)[i]) | 0xff000000;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static GLCPath get_path(GLCanvas *canvas, void *addr)
+{
+ GLCPath path = glc_path_create(canvas->glc);
+ 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) {
+ glc_path_move_to(path, 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) {
+ glc_path_curve_to(path,
+ 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++) {
+ glc_path_line_to(path, fix_to_double(point->x), fix_to_double(point->y));
+ }
+ }
+ if (flags & PATH_END) {
+ if (flags & PATH_CLOSE) {
+ glc_path_close(path);
+ }
+ }
+ } while (more);
+
+ return path;
+}
+
+#define SET_GLC_RECT(dest, src) { \
+ (dest)->x = (src)->left; \
+ (dest)->y = (src)->top; \
+ (dest)->width = (src)->right - (src)->left; \
+ (dest)->height = (src)->bottom - (src)->top; \
+}
+
+static void set_clip(GLCanvas *canvas, Rect *bbox, Clip *clip)
+{
+ GLCRect rect;
+ glc_clip_reset(canvas->glc);
+
+ 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);
+
+ if (*n == 0) {
+ rect.x = rect.y = 0;
+ rect.width = rect.height = 0;
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ break;
+ } else {
+ SET_GLC_RECT(&rect, now);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ }
+
+ for (now++; now < end; now++) {
+ SET_GLC_RECT(&rect, now);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
+ }
+ break;
+ }
+ case CLIP_TYPE_PATH: {
+ GLCPath path = get_path(canvas, GET_ADDRESS(clip->data));
+ glc_clip_path(canvas->glc, path, GLC_CLIP_OP_SET);
+ glc_path_destroy(path);
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static void set_mask(GLCanvas *canvas, QMask *mask, int x, int y)
+{
+ cairo_surface_t *surface;
+
+ if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ return;
+ }
+
+
+ glc_set_mask(canvas->glc, x - mask->pos.x, y - mask->pos.y,
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface),
+ cairo_image_surface_get_stride(surface),
+ cairo_image_surface_get_data(surface), GLC_MASK_A);
+}
+
+static inline void surface_to_image(GLCanvas *canvas, cairo_surface_t *surface, GLCImage *image,
+ int ignore_stride)
+{
+ cairo_format_t format = cairo_image_surface_get_format(surface);
+
+ ASSERT(format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
+ image->format = (format == CAIRO_FORMAT_RGB24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
+ image->width = cairo_image_surface_get_width(surface);
+ image->height = cairo_image_surface_get_height(surface);
+ image->stride = cairo_image_surface_get_stride(surface);
+ image->pixels = cairo_image_surface_get_data(surface);
+ image->pallet = NULL;
+ if (ignore_stride) {
+ return;
+ }
+ if (image->stride < 0) {
+ image->stride = -image->stride;
+ image->pixels = image->pixels - (image->height - 1) * image->stride;
+ } else {
+ image->pixels = copy_opposite_image(canvas, image->pixels, image->stride, image->height);
+ }
+}
+
+static void set_brush(GLCanvas *canvas, Brush *brush)
+{
+ switch (brush->type) {
+ case BRUSH_TYPE_SOLID: {
+ uint32_t 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;
+ glc_set_rgb(canvas->glc, r, g, b);
+ break;
+ }
+ case BRUSH_TYPE_PATTERN: {
+ GLCImage image;
+ GLCPattern pattern;
+ cairo_surface_t *surface;
+
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+ surface_to_image(canvas, surface, &image, 0);
+
+ pattern = glc_pattern_create(canvas->glc, -brush->u.pattern.pos.x,
+ -brush->u.pattern.pos.y, &image);
+
+ glc_set_pattern(canvas->glc, pattern);
+ glc_pattern_destroy(pattern);
+ }
+ case BRUSH_TYPE_NONE:
+ return;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+}
+
+static void set_op(GLCanvas *canvas, UINT16 rop_decriptor)
+{
+ GLCOp op;
+
+ switch (rop_decriptor) {
+ case ROPD_OP_PUT:
+ op = GLC_OP_COPY;
+ break;
+ case ROPD_OP_XOR:
+ op = GLC_OP_XOR;
+ break;
+ case ROPD_OP_BLACKNESS:
+ op = GLC_OP_CLEAR;
+ break;
+ case ROPD_OP_WHITENESS:
+ op = GLC_OP_SET;
+ break;
+ case ROPD_OP_PUT | ROPD_INVERS_BRUSH:
+ case ROPD_OP_PUT | ROPD_INVERS_SRC:
+ op = GLC_OP_COPY_INVERTED;
+ break;
+ case ROPD_OP_INVERS:
+ op = GLC_OP_INVERT;
+ break;
+ case ROPD_OP_AND:
+ op = GLC_OP_AND;
+ break;
+ case ROPD_OP_AND | ROPD_INVERS_RES:
+ op = GLC_OP_NAND;
+ break;
+ case ROPD_OP_OR:
+ op = GLC_OP_OR;
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_RES:
+ op = GLC_OP_NOR;
+ break;
+ case ROPD_OP_XOR | ROPD_INVERS_RES:
+ op = GLC_OP_EQUIV;
+ break;
+ case ROPD_OP_AND | ROPD_INVERS_DEST:
+ op = GLC_OP_AND_REVERSE;
+ break;
+ case ROPD_OP_AND | ROPD_INVERS_BRUSH:
+ case ROPD_OP_AND | ROPD_INVERS_SRC:
+ op = GLC_OP_AND_INVERTED;
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_DEST:
+ op = GLC_OP_OR_REVERSE;
+ break;
+ case ROPD_OP_OR | ROPD_INVERS_BRUSH:
+ case ROPD_OP_OR | ROPD_INVERS_SRC:
+ op = GLC_OP_OR_INVERTED;
+ break;
+ default:
+ WARN("GLC_OP_NOOP");
+ op = GLC_OP_NOOP;
+ }
+ glc_set_op(canvas->glc, op);
+}
+
+void gl_canvas_draw_fill(GLCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill)
+{
+ GLCRect rect;
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &fill->mask, bbox->left, bbox->top);
+ set_brush(canvas, &fill->brush);
+ set_op(canvas, fill->rop_decriptor);
+ SET_GLC_RECT(&rect, bbox);
+
+ glc_fill_rect(canvas->glc, &rect);
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_draw_copy(GLCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy)
+{
+ cairo_surface_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &copy->mask, bbox->left, bbox->top);
+ set_op(canvas, copy->rop_decriptor);
+
+ //todo: optimize get_imag (use ogl conversion + remove unnecessary copy of 32bpp)
+ surface = canvas_get_image(&canvas->base, copy->src_bitmap);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &copy->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ cairo_surface_destroy(surface);
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_draw_opaque(GLCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque)
+{
+ cairo_surface_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCRect fill_rect;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &opaque->mask, bbox->left, bbox->top);
+
+ glc_set_op(canvas->glc, (opaque->rop_decriptor & ROPD_INVERS_SRC) ? GLC_OP_COPY_INVERTED :
+ GLC_OP_COPY);
+ surface = canvas_get_image(&canvas->base, opaque->src_bitmap);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &opaque->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+ cairo_surface_destroy(surface);
+
+ set_brush(canvas, &opaque->brush);
+ set_op(canvas, opaque->rop_decriptor & ~ROPD_INVERS_SRC);
+ SET_GLC_RECT(&fill_rect, bbox);
+ glc_fill_rect(canvas->glc, &fill_rect);
+
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_draw_alpha_blend(GLCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd *alpha_blend)
+{
+ cairo_surface_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &alpha_blend->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, (double)alpha_blend->alpha / 0xff);
+
+ cairo_surface_destroy(surface);
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_draw_blend(GLCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend)
+{
+ cairo_surface_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &blend->mask, bbox->left, bbox->top);
+ set_op(canvas, blend->rop_decriptor);
+
+ surface = canvas_get_image(&canvas->base, blend->src_bitmap);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &blend->src_area);
+ surface_to_image(canvas, surface, &image, 0);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ cairo_surface_destroy(surface);
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_draw_transparent(GLCanvas *canvas, Rect *bbox, Clip *clip, Transparent *transparent)
+{
+ cairo_surface_t *surface;
+ cairo_surface_t *trans_surf;
+ GLCImage image;
+ GLCRecti src;
+ GLCRecti dest;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ surface = canvas_get_image(&canvas->base, transparent->src_bitmap);
+ surface_to_image(canvas, surface, &image, 0);
+
+ trans_surf = canvas_surf_to_trans_surf(&image, transparent->true_color);
+ cairo_surface_destroy(surface);
+
+ surface_to_image(canvas, trans_surf, &image, 1);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &transparent->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ cairo_surface_destroy(trans_surf);
+ glc_flush(canvas->glc);
+}
+
+static inline void fill_common(GLCanvas *canvas, Rect *bbox, Clip *clip, QMask * mask, GLCOp op)
+{
+ GLCRect rect;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, mask, bbox->left, bbox->top);
+ glc_set_op(canvas->glc, op);
+ SET_GLC_RECT(&rect, bbox);
+ glc_fill_rect(canvas->glc, &rect);
+}
+
+void gl_canvas_draw_whiteness(GLCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness)
+{
+ fill_common(canvas, bbox, clip, &whiteness->mask, GLC_OP_SET);
+}
+
+void gl_canvas_draw_blackness(GLCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness)
+{
+ fill_common(canvas, bbox, clip, &blackness->mask, GLC_OP_CLEAR);
+}
+
+void gl_canvas_draw_invers(GLCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers)
+{
+ fill_common(canvas, bbox, clip, &invers->mask, GLC_OP_INVERT);
+}
+
+void gl_canvas_draw_rop3(GLCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3)
+{
+ cairo_surface_t *d;
+ cairo_surface_t *s;
+ GLCImage image;
+ Point src_pos;
+ uint8_t *data_opp;
+ int src_stride;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &rop3->mask, bbox->left, bbox->top);
+
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ image.format = GLC_IMAGE_RGB32;
+ image.width = bbox->right - bbox->left;
+ image.height = bbox->bottom - bbox->top;
+
+ image.pallet = NULL;
+
+ d = cairo_image_surface_create(CAIRO_FORMAT_RGB24, image.width, image.height);
+ if (cairo_surface_status(d) != CAIRO_STATUS_SUCCESS) {
+ CANVAS_ERROR("create surface failed, %s",
+ cairo_status_to_string(cairo_surface_status(d)));
+ }
+ image.pixels = cairo_image_surface_get_data(d);
+ image.stride = cairo_image_surface_get_stride(d);
+
+ glc_read_pixels(canvas->glc, bbox->left, bbox->top, &image);
+ data_opp = copy_opposite_image(canvas, image.pixels,
+ cairo_image_surface_get_stride(d),
+ cairo_image_surface_get_height(d));
+ memcpy(image.pixels, data_opp,
+ cairo_image_surface_get_stride(d) * cairo_image_surface_get_height(d));
+
+ s = canvas_get_image(&canvas->base, rop3->src_bitmap);
+ src_stride = cairo_image_surface_get_stride(s);
+ if (src_stride > 0) {
+ data_opp = copy_opposite_image(canvas, cairo_image_surface_get_data(s),
+ src_stride, cairo_image_surface_get_height(s));
+ memcpy(cairo_image_surface_get_data(s), data_opp,
+ src_stride * cairo_image_surface_get_height(s));
+ }
+
+ if (!rect_is_same_size(bbox, &rop3->src_area)) {
+ cairo_surface_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
+ image.height, 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 < image.width ||
+ cairo_image_surface_get_height(s) - src_pos.y < image.height) {
+ 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);
+
+ //for now (bottom-top)
+ if (pat_pos.y < 0) {
+ pat_pos.y = cairo_image_surface_get_height(p) + pat_pos.y;
+ }
+ pat_pos.y = (image.height + pat_pos.y) % cairo_image_surface_get_height(p);
+ pat_pos.y = cairo_image_surface_get_height(p) - pat_pos.y;
+
+ 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);
+
+ GLCRecti dest;
+ GLCRecti src;
+ dest.x = bbox->left;
+ dest.y = bbox->top;
+
+ image.pixels = copy_opposite_image(canvas, image.pixels, cairo_image_surface_get_stride(d),
+ cairo_image_surface_get_height(d));
+
+ src.x = src.y = 0;
+ dest.width = src.width = image.width;
+ dest.height = src.height = image.height;
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+ cairo_surface_destroy(d);
+}
+
+void gl_canvas_draw_stroke(GLCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke)
+{
+ GLCPath path;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ set_op(canvas, stroke->fore_mode);
+ set_brush(canvas, &stroke->brush);
+
+ if (stroke->attr.flags & LINE_ATTR_STYLED) {
+ WARN("LINE_ATTR_STYLED");
+ }
+ glc_set_line_width(canvas->glc, fix_to_double(stroke->attr.width));
+
+ path = get_path(canvas, GET_ADDRESS(stroke->path));
+ glc_stroke_path(canvas->glc, path);
+ glc_path_destroy(path);
+}
+
+void gl_canvas_draw_text(GLCanvas *canvas, Rect *bbox, Clip *clip, Text *text)
+{
+ GLCRect rect;
+ String *str;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+
+ if (!rect_is_empty(&text->back_area)) {
+ set_brush(canvas, &text->back_brush);
+ set_op(canvas, text->back_mode);
+ SET_GLC_RECT(&rect, bbox);
+ glc_fill_rect(canvas->glc, &rect);
+ }
+
+ str = (String *)GET_ADDRESS(text->str);
+ set_brush(canvas, &text->fore_brush);
+ set_op(canvas, text->fore_mode);
+ if (str->flags & STRING_RASTER_A1) {
+ Point pos;
+ cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
+ _glc_fill_mask(canvas->glc, pos.x, pos.y,
+ cairo_image_surface_get_width(mask),
+ cairo_image_surface_get_height(mask),
+ cairo_image_surface_get_stride(mask),
+ cairo_image_surface_get_data(mask));
+ cairo_surface_destroy(mask);
+ } else if (str->flags & STRING_RASTER_A4) {
+ Point pos;
+ cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
+ glc_fill_alpha(canvas->glc, pos.x, pos.y,
+ cairo_image_surface_get_width(mask),
+ cairo_image_surface_get_height(mask),
+ cairo_image_surface_get_stride(mask),
+ cairo_image_surface_get_data(mask));
+
+ cairo_surface_destroy(mask);
+ } else if (str->flags & STRING_RASTER_A8) {
+ WARN("untested path A8 glyphs, doing nothing");
+ if (0) {
+ Point pos;
+ cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
+ glc_fill_alpha(canvas->glc, pos.x, pos.y,
+ cairo_image_surface_get_width(mask),
+ cairo_image_surface_get_height(mask),
+ cairo_image_surface_get_stride(mask),
+ cairo_image_surface_get_data(mask));
+ cairo_surface_destroy(mask);
+ }
+ } else {
+ WARN("untested path vector glyphs, doing nothing");
+ if (0) {
+ //draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
+ }
+ }
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_clear(GLCanvas *canvas)
+{
+ glc_clear(canvas->glc);
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_copy_pixels(GLCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos)
+{
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+ glc_copy_pixels(canvas->glc, bbox->left, bbox->top, src_pos->x, src_pos->y,
+ bbox->right - bbox->left, bbox->bottom - bbox->top);
+}
+
+void gl_canvas_read_pixels(GLCanvas *canvas, uint8_t *dest, int dest_stride, const Rect *area)
+{
+ GLCImage image;
+
+ ASSERT(dest_stride > 0);
+ image.format = GLC_IMAGE_RGB32;
+ image.height = area->bottom - area->top;
+ image.width = area->right - area->left;
+ image.pixels = dest;
+ image.stride = dest_stride;
+ glc_read_pixels(canvas->glc, area->left, area->top, &image);
+}
+
+void gl_canvas_set_top_mask(GLCanvas *canvas, int num_rect, const Rect *rects)
+{
+ GLCRect *glc_rects = (GLCRect *)malloc(num_rect * sizeof(GLCRect));
+ GLCRect *now = glc_rects;
+ GLCRect *end = glc_rects + num_rect;
+
+ for (; now < end; now++, rects++) {
+ SET_GLC_RECT(now, rects);
+ }
+ glc_mask_rects(canvas->glc, num_rect, glc_rects, GLC_MASK_B);
+
+ free(glc_rects);
+}
+
+void gl_canvas_put_image(GLCanvas *canvas, const Rect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+{
+ GLCRecti src;
+ GLCRecti gldest;
+ GLCImage image;
+ uint32_t i;
+
+ ASSERT(src_stride <= 0)
+ glc_clip_reset(canvas->glc);
+
+ if (clip) {
+ GLCRect rect;
+ if (clip->num_rects == 0) {
+ rect.x = rect.y = rect.width = rect.height = 0;
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ } else {
+ SET_GLC_RECT(&rect, clip->rects);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ for (i = 1; i < clip->num_rects; i++) {
+ SET_GLC_RECT(&rect, clip->rects + i);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
+ }
+ }
+ }
+
+ SET_GLC_RECT(&gldest, dest);
+ src.x = src.y = 0;
+ src.width = src_width;
+ src.height = src_height;
+
+ image.format = GLC_IMAGE_RGB32;
+ image.width = src_width;
+ image.height = src_height;
+ src_stride = -src_stride;
+ image.stride = src_stride;
+ image.pixels = (uint8_t *)src_data - (src_height - 1) * src_stride;
+ image.pallet = NULL;
+ glc_draw_image(canvas->glc, &gldest, &src, &image, 0, 1);
+
+ glc_flush(canvas->glc);
+}
+
+void gl_canvas_clear_top_mask(GLCanvas *canvas)
+{
+ glc_clear_mask(canvas->glc, GLC_MASK_B);
+}
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void gl_canvas_set_access_params(GLCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max)
+{
+ __canvas_set_access_params(&canvas->base, delta, base, max);
+}
+
+#else
+void gl_canvas_set_access_params(GLCanvas *canvas, ADDRESS delta)
+{
+ __canvas_set_access_params(&canvas->base, delta);
+}
+
+#endif
+
+void *gl_canvas_get_usr_data(GLCanvas *canvas)
+{
+ return canvas->usr_data;
+}
+
+static int need_init = 1;
+
+#ifdef CAIRO_CANVAS_CACHE
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, 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)
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put,
+ bits_cache_get_fn_t bits_cache_get
+#else
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
+#endif
+#ifdef USE_GLZ
+ , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+#endif
+ )
+{
+ GLCanvas *canvas;
+ int init_ok;
+
+ if (need_init || !(canvas = (GLCanvas *)malloc(sizeof(GLCanvas)))) {
+ return NULL;
+ }
+ memset(canvas, 0, sizeof(GLCanvas));
+
+ if (!(canvas->glc = glc_create(width, height))) {
+ goto error_1;
+ }
+ canvas->usr_data = usr_data;
+ canvas->private_data = NULL;
+#ifdef CAIRO_CANVAS_CACHE
+ init_ok = canvas_base_init(&canvas->base, depth,
+ 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, depth,
+ bits_cache_opaque,
+ bits_cache_put,
+ bits_cache_get
+#else
+ init_ok = canvas_base_init(&canvas->base, depth
+#endif
+#ifdef USE_GLZ
+ ,
+ glz_decoder_opaque,
+ glz_decode
+#endif
+ );
+ if (!init_ok) {
+ goto error_2;
+ }
+
+ return canvas;
+
+error_2:
+ glc_destroy(canvas->glc, 0);
+error_1:
+ free(canvas);
+
+ return NULL;
+}
+
+void gl_canvas_destroy(GLCanvas *canvas, int textures_lost)
+{
+ if (!canvas) {
+ return;
+ }
+ canvas_base_destroy(&canvas->base);
+ glc_destroy(canvas->glc, textures_lost);
+ if (canvas->private_data) {
+ free(canvas->private_data);
+ }
+ free(canvas);
+}
+
+void gl_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+ rop3_init();
+}
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
new file mode 100644
index 00000000..b9a4eb89
--- /dev/null
+++ b/common/gl_canvas.h
@@ -0,0 +1,83 @@
+/*
+ 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 "glc.h"
+#include "draw.h"
+#include "canvas_base.h"
+#include "region.h"
+
+typedef struct GLCanvas GLCanvas;
+
+void gl_canvas_draw_fill(GLCanvas *canvas, Rect *bbox, Clip *clip, Fill *fill);
+void gl_canvas_draw_copy(GLCanvas *canvas, Rect *bbox, Clip *clip, Copy *copy);
+void gl_canvas_draw_opaque(GLCanvas *canvas, Rect *bbox, Clip *clip, Opaque *opaque);
+void gl_canvas_draw_blend(GLCanvas *canvas, Rect *bbox, Clip *clip, Blend *blend);
+void gl_canvas_draw_alpha_blend(GLCanvas *canvas, Rect *bbox, Clip *clip, AlphaBlnd *alpha_blend);
+void gl_canvas_draw_transparent(GLCanvas *canvas, Rect *bbox, Clip *clip, Transparent *transparent);
+void gl_canvas_draw_whiteness(GLCanvas *canvas, Rect *bbox, Clip *clip, Whiteness *whiteness);
+void gl_canvas_draw_blackness(GLCanvas *canvas, Rect *bbox, Clip *clip, Blackness *blackness);
+void gl_canvas_draw_invers(GLCanvas *canvas, Rect *bbox, Clip *clip, Invers *invers);
+void gl_canvas_draw_rop3(GLCanvas *canvas, Rect *bbox, Clip *clip, Rop3 *rop3);
+void gl_canvas_draw_stroke(GLCanvas *canvas, Rect *bbox, Clip *clip, Stroke *stroke);
+void gl_canvas_draw_text(GLCanvas *canvas, Rect *bbox, Clip *clip, Text *text);
+
+void gl_canvas_copy_pixels(GLCanvas *canvas, Rect *bbox, Clip *clip, Point *src_pos);
+void gl_canvas_read_pixels(GLCanvas *canvas, uint8_t *dest, int dest_stride, const Rect *area);
+
+void gl_canvas_put_image(GLCanvas *canvas, const Rect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip);
+
+void gl_canvas_clear(GLCanvas *canvas);
+
+void gl_canvas_set_top_mask(GLCanvas *canvas, int num_rect, const Rect *rects);
+void gl_canvas_clear_top_mask(GLCanvas *canvas);
+
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+void gl_canvas_set_access_params(GLCanvas *canvas, ADDRESS delta, unsigned long base,
+ unsigned long max);
+#else
+void gl_canvas_set_access_params(GLCanvas *canvas, ADDRESS delta);
+#endif
+
+void *gl_canvas_get_usr_data(GLCanvas *canvas);
+
+#ifdef CAIRO_CANVAS_CACHE
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, 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)
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
+ void *bits_cache_opaque,
+ bits_cache_put_fn_t bits_cache_put,
+ bits_cache_get_fn_t bits_cache_get
+#else
+GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
+#endif
+#ifdef USE_GLZ
+ , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+#endif
+ );
+void gl_canvas_destroy(GLCanvas *, int);
+
+void gl_canvas_init();
+
diff --git a/common/gl_utils.h b/common/gl_utils.h
new file mode 100644
index 00000000..41266775
--- /dev/null
+++ b/common/gl_utils.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GL_UTILS_H
+#define GL_UTILS_H
+
+#ifdef RED_DEBUG
+#define GLC_ERROR_TEST_FLUSH { \
+ GLenum gl_err; glFlush(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
+ gluErrorString(gl_err)); \
+ abort(); \
+ } \
+}
+
+#define GLC_ERROR_TEST_FINISH { \
+ GLenum gl_err; glFinish(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
+ gluErrorString(gl_err)); \
+ abort(); \
+ } \
+}
+#else
+#define GLC_ERROR_TEST_FLUSH ;
+
+#define GLC_ERROR_TEST_FINISH ;
+#endif
+
+#ifdef WIN32
+static inline int find_msb(uint32_t val)
+{
+ uint32_t r;
+ __asm {
+ bsr eax, val
+ jnz found
+ mov eax, -1
+
+found:
+ mov r, eax
+ }
+ return r + 1;
+}
+
+#else
+static inline int find_msb(unsigned int val)
+{
+ int ret;
+
+ asm ("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:"
+ : "=r"(ret) : "r"(val));
+ return ret + 1;
+}
+
+#endif
+
+static inline int gl_get_to_power_two(unsigned int val)
+{
+ if ((val & (val - 1)) == 0) {
+ return val;
+ }
+ return 1 << find_msb(val);
+}
+
+#endif
diff --git a/common/glc.c b/common/glc.c
new file mode 100644
index 00000000..5300ebdc
--- /dev/null
+++ b/common/glc.c
@@ -0,0 +1,1582 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#define GL_GLEXT_PROTOTYPES
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glext.h>
+
+#ifdef WIN32
+#include "glext.h"
+#include "wglext.h"
+#endif
+
+#include "glc.h"
+#include "gl_utils.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define ASSERT(x) if (!(x)) {printf("%s: assert failed %s\n", __FUNCTION__, #x); abort();}
+
+#define WARN_ONCE(x) { \
+ static int warn = TRUE; \
+ if (warn) { \
+ printf x; \
+ warn = FALSE; \
+ } \
+}
+
+#define TESS_VERTEX_ALLOC_BUNCH 20
+
+typedef struct InternaCtx InternaCtx;
+typedef struct InternalPat {
+ InternaCtx *owner;
+ int refs;
+ GLuint texture;
+ int x_orign;
+ int y_orign;
+ int width;
+ int height;
+} InternalPat;
+
+typedef struct Pathpath {
+ int start_point;
+ int num_segments;
+} Path;
+
+enum {
+ GLC_PATH_SEG_LINES,
+ GLC_PATH_SEG_BEIZER,
+};
+
+//todo: flatten cache
+typedef struct PathSegment {
+ int type;
+ int count;
+} PathSegment;
+
+typedef struct PathPoint {
+ double x;
+ double y;
+ double z;
+} PathPoint;
+
+typedef GLdouble Vertex[3];
+
+typedef struct InternalPath {
+ InternaCtx *owner;
+
+ Path *paths;
+ int paths_size;
+ int paths_pos;
+
+ PathSegment *segments;
+ int segments_size;
+ int segments_pos;
+
+ PathPoint *points;
+ int points_size;
+ int points_pos;
+
+ Path *current_path;
+ PathSegment *current_segment;
+} InternalPath;
+
+typedef struct TassVertex TassVertex;
+struct TassVertex {
+ PathPoint point;
+ TassVertex *list_link;
+ TassVertex *next;
+};
+
+typedef struct TassVertexBuf TassVertexBuf;
+struct TassVertexBuf {
+ TassVertexBuf *next;
+ TassVertex vertexs[0];
+};
+
+#define USE_LINE_ANTIALIAS 0
+
+typedef struct LineDash {
+ double *dashes;
+ int num_dashes;
+ double offset;
+ int cur_dash;
+ double dash_pos;
+} LineDash;
+
+enum {
+ GLC_STROKE_NONACTIVE,
+ GLC_STROKE_FIRST,
+ GLC_STROKE_ACTIVE,
+};
+
+typedef struct PathStroke {
+ double x;
+ double y;
+ int state;
+} PathStroke;
+
+struct InternaCtx {
+ int draw_mode;
+ int stencil_refs;
+ int stencil_mask;
+ int width;
+ int height;
+ GLfloat line_width;
+ LineDash line_dash;
+ PathStroke path_stroke;
+ InternalPat *pat;
+ int max_texture_size;
+ GLUtesselator* tesselator;
+ TassVertex *free_tess_vertex;
+ TassVertex *used_tess_vertex;
+ TassVertexBuf *vertex_bufs;
+ int private_tex_width;
+ int private_tex_height;
+ GLuint private_tex;
+#ifdef WIN32
+ PFNGLBLENDEQUATIONPROC glBlendEquation;
+#endif
+};
+
+#define Y(y) -(y)
+#define VERTEX2(x, y) glVertex2d(x, Y(y))
+
+static void fill_rect(InternaCtx *ctx, void *rect);
+static void fill_path(InternaCtx *ctx, void *path);
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+static void set_pat(InternaCtx *ctx, InternalPat *pat);
+
+static inline void *zmalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline void set_raster_pos(InternaCtx *ctx, int x, int y)
+{
+ if (x >= 0 && y >= 0 && x < ctx->width && y < ctx->height) {
+ glRasterPos2i(x, Y(y));
+ return;
+ }
+ glRasterPos2i(0, 0);
+ glBitmap(0, 0, 0, 0, (GLfloat)x, (GLfloat)Y(y), NULL);
+}
+
+static TassVertex *alloc_tess_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+
+ if (!ctx->free_tess_vertex) {
+ TassVertexBuf *buf;
+ int i;
+
+ if (!(buf = (TassVertexBuf *)malloc(sizeof(TassVertexBuf) +
+ sizeof(TassVertex) * TESS_VERTEX_ALLOC_BUNCH))) {
+ //warn
+ return NULL;
+ }
+ buf->next = ctx->vertex_bufs;
+ ctx->vertex_bufs = buf;
+ for (i = 0; i < TESS_VERTEX_ALLOC_BUNCH; i++) {
+ buf->vertexs[i].point.z = 0;
+ buf->vertexs[i].next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = &buf->vertexs[i];
+ }
+ }
+
+ vertex = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex->next;
+ vertex->next = ctx->used_tess_vertex;
+ ctx->used_tess_vertex = vertex;
+ return vertex;
+}
+
+static void reset_tass_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+ while ((vertex = ctx->used_tess_vertex)) {
+ ctx->used_tess_vertex = vertex->next;
+ vertex->next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex;
+ }
+}
+
+static void free_tass_vertex_bufs(InternaCtx *ctx)
+{
+ TassVertexBuf *buf;
+
+ ctx->used_tess_vertex = NULL;
+ ctx->free_tess_vertex = NULL;
+ while ((buf = ctx->vertex_bufs)) {
+ ctx->vertex_bufs = buf->next;
+ free(buf);
+ }
+}
+
+//naiev bezier flattener
+static TassVertex *bezier_flattener(InternaCtx *ctx, PathPoint *points)
+{
+ double ax, bx, cx;
+ double ay, by, cy;
+ const int num_points = 30;
+ double dt;
+ int i;
+
+ TassVertex *vertex_list = NULL;
+ TassVertex *curr_vertex;
+
+ for (i = 0; i < num_points - 2; i++) {
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex(ctx))) {
+ //warn
+ return NULL;
+ }
+ vertex->list_link = vertex_list;
+ vertex_list = vertex;
+ }
+
+ curr_vertex = vertex_list;
+
+ cx = 3.0 * (points[1].x - points[0].x);
+ bx = 3.0 * (points[2].x - points[1].x) - cx;
+ ax = points[3].x - points[0].x - cx - bx;
+
+ cy = 3.0 * (points[1].y - points[0].y);
+ by = 3.0 * (points[2].y - points[1].y) - cy;
+ ay = points[3].y - points[0].y - cy - by;
+
+ dt = 1.0 / (num_points - 1);
+
+ for (i = 1; i < num_points - 1; i++, curr_vertex = curr_vertex->list_link) {
+ double tSquared, tCubed;
+ double t;
+ t = i * dt;
+
+ tSquared = t * t;
+ tCubed = tSquared * t;
+
+ curr_vertex->point.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + points[0].x;
+ curr_vertex->point.y = (ay * tCubed) + (by * tSquared) + (cy * t) + points[0].y;
+ }
+
+ return vertex_list;
+}
+
+#define MORE_X(path, Type, name) { \
+ Type *name; \
+ \
+ if (!(name = (Type *)zmalloc(sizeof(*name) * path->name##_size * 2))) { \
+ return FALSE; \
+ } \
+ memcpy(name, path->name, sizeof(*name) * path->name##_size); \
+ free(path->name); \
+ path->name = name; \
+ path->name##_size *= 2; \
+ return TRUE; \
+}
+
+static int more_points(InternalPath *path)
+{
+ MORE_X(path, PathPoint, points);
+}
+
+static int more_segments(InternalPath *path)
+{
+ MORE_X(path, PathSegment, segments);
+}
+
+static int more_paths(InternalPath *path)
+{
+ MORE_X(path, Path, paths);
+}
+
+static inline void put_point(InternalPath *path, double x, double y)
+{
+ path->points[path->points_pos].x = x;
+ path->points[path->points_pos].y = Y(y + 0.5);
+ path->points[path->points_pos++].z = 0;
+}
+
+void glc_path_move_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (internal->current_segment) {
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+ if (internal->points_pos == internal->points_size && !more_points(internal)) {
+ //warn
+ return;
+ }
+ internal->points_pos++;
+ }
+ internal->points[internal->points_pos - 1].x = x;
+ internal->points[internal->points_pos - 1].y = Y(y + 0.5);
+ internal->points[internal->points_pos - 1].z = 0;
+}
+
+static int add_segment_common(InternalPath *internal, int type, int num_points)
+{
+ if (internal->points_size - internal->points_pos < num_points && !more_points(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->current_segment) {
+ if (internal->current_segment->type == type) {
+ internal->current_segment->count++;
+ return TRUE;
+ }
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ internal->current_path->num_segments++;
+ return TRUE;
+ }
+
+ if (internal->paths_pos == internal->paths_size && !more_paths(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ internal->current_path = &internal->paths[internal->paths_pos++];
+ internal->current_path->start_point = internal->points_pos - 1;
+ internal->current_path->num_segments = 1;
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ return TRUE;
+}
+
+void glc_path_line_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_LINES, 1)) {
+ return;
+ }
+ put_point(internal, x, y);
+}
+
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_BEIZER, 3)) {
+ return;
+ }
+ put_point(internal, p1_x, p1_y);
+ put_point(internal, p2_x, p2_y);
+ put_point(internal, p3_x, p3_y);
+}
+
+void glc_path_close(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ if (!internal->current_path) {
+ return;
+ }
+ PathPoint *end_point = &internal->points[internal->current_path->start_point];
+ glc_path_line_to(path, end_point->x, Y(end_point->y));
+ glc_path_move_to(path, end_point->x, Y(end_point->y));
+}
+
+void glc_path_cleare(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ internal->paths_pos = internal->segments_pos = 0;
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+
+ internal->points[0].x = 0;
+ internal->points[0].y = 0;
+ internal->points_pos = 1;
+}
+
+GLCPath glc_path_create(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path;
+
+ ASSERT(ctx);
+ if (!(path = (InternalPath *)zmalloc(sizeof(*path)))) {
+ return NULL;
+ }
+
+ path->paths = (Path *)malloc(sizeof(*path->paths) * (path->paths_size = 2));
+ if (!path->paths) {
+ goto error_1;
+ }
+
+ path->segments = (PathSegment *)malloc(sizeof(*path->segments) * (path->segments_size = 4));
+ if (!path->segments) {
+ goto error_2;
+ }
+
+ path->points = (PathPoint *)malloc(sizeof(*path->points) * (path->points_size = 20));
+ if (!path->points) {
+ goto error_3;
+ }
+
+ path->owner = ctx;
+ path->points_pos = 1;
+ return path;
+
+error_3:
+ free(path->segments);
+
+error_2:
+ free(path->paths);
+
+error_1:
+ free(path);
+
+ return NULL;
+}
+
+void glc_path_destroy(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ if (!path) {
+ return;
+ }
+
+ free(internal->points);
+ free(internal->segments);
+ free(internal->paths);
+ free(internal);
+}
+
+static inline void unref_pat(InternalPat *pat)
+{
+ if (!pat) {
+ return;
+ }
+ ASSERT(pat->refs > 0);
+ if (--pat->refs == 0) {
+ glFinish();
+ glDeleteTextures(1, &pat->texture);
+ free(pat);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static inline InternalPat *ref_pat(InternalPat *pat)
+{
+ pat->refs++;
+ return pat;
+}
+
+static void scale(uint32_t *dest, uint32_t dest_width, uint32_t dest_height,
+ uint32_t *src, uint32_t src_width, uint32_t src_height, int src_stride)
+{
+ double x_scale = (double)src_width / dest_width;
+ double y_scale = (double)src_height / dest_height;
+ uint32_t i;
+ uint32_t j;
+ int prev_row = -1;
+
+ for (i = 0; i < dest_height; i++) {
+ int row = (int)(y_scale * i);
+ if (row == prev_row) {
+ memcpy(dest, dest - dest_width, dest_width * sizeof(uint32_t));
+ dest += dest_width;
+ continue;
+ }
+ for (j = 0; j < dest_width; j++) {
+ int col = (int)(x_scale * j);
+ *(dest++) = *(src + col);
+ }
+ prev_row = row;
+ src = (uint32_t *)((uint8_t *)src + src_stride);
+ }
+}
+
+static inline void init_pattern(InternalPat *pat, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = pat->owner;
+ uint32_t *tmp_pixmap = NULL;
+ int width;
+ int height;
+ int width2;
+ int height2;
+
+ const int pix_bytes = 4;
+
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+
+ width = image->width;
+ height = image->height;
+ width2 = gl_get_to_power_two(width);
+ height2 = gl_get_to_power_two(height);
+
+ ASSERT(width > 0 && height > 0);
+ ASSERT(width > 0 && width <= pat->owner->max_texture_size);
+ ASSERT(height > 0 && height <= pat->owner->max_texture_size);
+
+ if (width2 != width || height2 != height) {
+ if (!(tmp_pixmap = (uint32_t *)malloc(width2 * height2 * sizeof(uint32_t)))) {
+ //warn
+ return;
+ }
+ scale(tmp_pixmap, width2, height2, (uint32_t *)image->pixels, width, height, image->stride);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (tmp_pixmap) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width2);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width2, height2, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ tmp_pixmap);
+ free(tmp_pixmap);
+ } else {
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ image->pixels);
+ }
+
+ GLC_ERROR_TEST_FLUSH;
+ pat->x_orign = x_orign % width;
+ pat->y_orign = y_orign % height;
+ pat->width = width;
+ pat->height = height;
+
+ if (ctx->pat == pat) {
+ set_pat(pat->owner, pat);
+ } else if (ctx->pat) {
+ glBindTexture(GL_TEXTURE_2D, ctx->pat->texture);
+ }
+}
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat;
+
+ ASSERT(ctx && image);
+
+ if (!(pat = (InternalPat *)zmalloc(sizeof(*pat)))) {
+ return NULL;
+ }
+ pat->refs = 1;
+ pat->owner = ctx;
+ glGenTextures(1, &pat->texture);
+ init_pattern(pat, x_orign, y_orign, image);
+ return pat;
+}
+
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternalPat *pat = (InternalPat *)pattern;
+ ASSERT(pat && pat->owner);
+
+ glFinish();
+ init_pattern(pat, x_orign, y_orign, image);
+}
+
+void glc_pattern_destroy(GLCPattern pat)
+{
+ unref_pat((InternalPat *)pat);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void set_pat(InternaCtx *ctx, InternalPat *pat)
+{
+ pat = ref_pat(pat);
+ unref_pat(ctx->pat);
+ ctx->pat = pat;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / pat->width, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / (GLfloat)pat->height, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)pat->x_orign / pat->width, (float)Y(pat->y_orign) / pat->height, 0);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat = (InternalPat *)pattern;
+
+ ASSERT(ctx && pat && pat->owner == ctx);
+ set_pat(ctx, pat);
+}
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+
+ glDisable(GL_TEXTURE_2D);
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ glColor4d(red, green, blue, 1);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_op(GLCCtx glc, GLCOp op)
+{
+ if (op == GL_COPY) {
+ glDisable(GL_COLOR_LOGIC_OP);
+ return;
+ }
+ glLogicOp(op);
+ glEnable(GL_COLOR_LOGIC_OP);
+}
+
+void glc_set_line_width(GLCCtx glc, double width)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ ctx->line_width = (GLfloat)width;
+ if (ctx->line_width > 0) {
+ glLineWidth(ctx->line_width);
+ } else {
+ ctx->line_width = 0;
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_line_dash(GLCCtx glc, const double *dashes, int num_dashes, double offset)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (dashes && num_dashes >= 0 && offset >= 0) {
+ ctx->line_dash.dashes = (double *)malloc(sizeof(double) * num_dashes);
+ if (!ctx->line_dash.dashes) {
+ // FIXME: error
+ return;
+ }
+ memcpy(ctx->line_dash.dashes, dashes, sizeof(double) * num_dashes);
+ ctx->line_dash.num_dashes = num_dashes;
+ ctx->line_dash.offset = offset;
+ ctx->line_dash.cur_dash = offset ? -1 : 0;
+ ctx->line_dash.dash_pos = 0;
+ } else {
+ free(ctx->line_dash.dashes);
+ memset(&ctx->line_dash, 0, sizeof(ctx->line_dash));
+ }
+}
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode fill_mode)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ int mode;
+ switch (fill_mode) {
+ case GLC_FILL_MODE_WINDING_ODD:
+ mode = GLU_TESS_WINDING_ODD;
+ break;
+ case GLC_FILL_MODE_WINDING_NONZERO:
+ mode = GLU_TESS_WINDING_NONZERO;
+ break;
+ default:
+ //warn
+ return;
+ }
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, mode);
+}
+
+static inline void add_stencil_client(InternaCtx *ctx)
+{
+ if (!ctx->stencil_refs) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ ctx->stencil_refs++;
+}
+
+static inline void remove_stencil_client(InternaCtx *ctx)
+{
+ ctx->stencil_refs--;
+ if (!ctx->stencil_refs) {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx && bitmap);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ GLCRect *end;
+ ASSERT(ctx && rects);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ end = rects + num_rect;
+ for (; rects < end; rects++) {
+ fill_rect(ctx, rects);
+ }
+}
+
+void glc_clear_mask(GLCCtx glc, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if ((ctx->stencil_mask & mask)) {
+ ctx->stencil_mask &= ~mask;
+ remove_stencil_client(ctx);
+ }
+}
+
+void glc_clip_reset(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ if (!(ctx->stencil_mask & 0x03)) {
+ return;
+ }
+ remove_stencil_client(ctx);
+ ctx->stencil_mask &= ~0x03;
+ glStencilMask(0x03);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void clip_common(InternaCtx *ctx, GLCClipOp op, void (*fill_func)(InternaCtx *, void *),
+ void *data)
+{
+ int stencil_val;
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ glDisable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+
+ if (op == GLC_CLIP_OP_SET) {
+ glc_clip_reset(ctx);
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= 0x01;
+ } else if (!(ctx->stencil_mask & 0x03)) {
+ GLCRect area;
+ if (op == GLC_CLIP_OP_OR) {
+ return;
+ }
+ area.x = area.y = 0;
+ area.width = ctx->width;
+ area.height = ctx->height;
+ clip_common(ctx, GLC_CLIP_OP_SET, fill_rect, &area);
+ }
+ glStencilMask(0x03);
+ switch (op) {
+ case GLC_CLIP_OP_SET:
+ case GLC_CLIP_OP_OR:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_ALWAYS, stencil_val, stencil_val);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_func(ctx, data);
+ break;
+ case GLC_CLIP_OP_AND: {
+ int clear_mask;
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ if (stencil_val == 0x01) {
+ glStencilOp(GL_ZERO, GL_INCR, GL_INCR);
+ stencil_val = 0x02;
+ clear_mask = 0x01;
+ } else {
+ glStencilOp(GL_ZERO, GL_DECR, GL_DECR);
+ stencil_val = 0x01;
+ clear_mask = 0x02;
+ }
+ fill_func(ctx, data);
+
+ glStencilMask(clear_mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ ctx->stencil_mask = (ctx->stencil_mask & ~clear_mask) | stencil_val;
+ break;
+ }
+ case GLC_CLIP_OP_EXCLUDE:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ fill_func(ctx, data);
+ break;
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && rect);
+ clip_common(ctx, op, fill_rect, (void *)rect);
+}
+
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path);
+ clip_common(ctx, op, fill_path, path);
+}
+
+typedef struct FillMaskInfo {
+ int x_dest;
+ int y_dest;
+ int width;
+ int height;
+ int stride;
+ const uint8_t *bitmap;
+} FillMaskInfo;
+
+static void __fill_mask(InternaCtx *ctx, void *data)
+{
+ FillMaskInfo *info = (FillMaskInfo *)data;
+ fill_mask(ctx, info->x_dest, info->y_dest, info->width, info->height, info->stride,
+ info->bitmap);
+}
+
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ FillMaskInfo mask_info;
+
+ ASSERT(ctx && bitmap);
+ mask_info.x_dest = x_dest;
+ mask_info.y_dest = y_dest;
+ mask_info.width = width;
+ mask_info.height = height;
+ mask_info.stride = stride;
+ mask_info.bitmap = bitmap;
+ clip_common(ctx, op, __fill_mask, &mask_info);
+}
+
+static inline void start_draw(InternaCtx *ctx)
+{
+ if (ctx->draw_mode) {
+ return;
+ }
+ ctx->draw_mode = TRUE;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilFunc(GL_EQUAL, ctx->stencil_mask, ctx->stencil_mask);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void fill_rect(InternaCtx *ctx, void *r)
+{
+ GLCRect *rect = (GLCRect *)r;
+ glRectd(rect->x, Y(rect->y), rect->x + rect->width, Y(rect->y + rect->height));
+ /*glBegin(GL_POLYGON);
+ VERTEX2(rect->x, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height);
+ VERTEX2 (rect->x , rect->y + rect->height);
+ glEnd();*/
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect *r = (GLCRect *)rect; // to avoid bugs in gcc older than 4.3
+
+ ASSERT(ctx);
+ start_draw(ctx);
+ fill_rect(ctx, (void *)r);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void fill_path(InternaCtx *ctx, void *p)
+{
+ InternalPath *path = (InternalPath *)p;
+
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ reset_tass_vertex(ctx);
+ gluTessBeginPolygon(ctx->tesselator, ctx);
+ for (; current_path < end_path; current_path++) {
+ gluTessBeginContour(ctx->tesselator);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point, current_point);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)&vertex->point,
+ (GLdouble *)&vertex->point);
+ vertex = vertex->list_link;
+ }
+ gluTessVertex(ctx->tesselator, (GLdouble *)&current_point[2],
+ (GLdouble *)&current_point[2]);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point,
+ (GLdouble *)current_point);
+ }
+ }
+ }
+ gluTessEndContour(ctx->tesselator);
+ }
+ gluTessEndPolygon(ctx->tesselator);
+}
+
+void glc_fill_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path_ref);
+ start_draw(ctx);
+ fill_path(ctx, path_ref);
+}
+
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap)
+{
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8);
+ glBitmap(width, height, 0, 0, 0, 0, bitmap);
+}
+
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && bitmap);
+ start_draw(ctx);
+ if (ctx->pat) {
+ WARN_ONCE(("%s: unimplemented fill mask with pattern\n", __FUNCTION__));
+ }
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect r;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ glPixelZoom(1, 1);
+ glDrawPixels(width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_mask);
+
+ r.x = x_dest;
+ r.y = y_dest;
+ r.width = width;
+ r.height = height;
+
+ //todo: support color/texture alpah vals (GL_MODULATE)
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ fill_rect(ctx, &r);
+ glDisable(GL_BLEND);
+}
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (ctx->line_width == 0) {
+ return;
+ }
+
+ start_draw(ctx);
+
+ glBegin(GL_LINES);
+ VERTEX2(rect->x, rect->y + 0.5);
+ VERTEX2(rect->x + rect->width, rect->y + 0.5);
+ VERTEX2(rect->x + rect->width - 0.5, rect->y);
+ VERTEX2(rect->x + rect->width - 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + rect->width, rect->y + rect->height - 0.5);
+ VERTEX2(rect->x, rect->y + rect->height - 0.5);
+ VERTEX2(rect->x + 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + 0.5, rect->y);
+ glEnd();
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void glc_stroke_line(double x1, double y1, double x2, double y2, double width)
+{
+ double ax, ay, bx, by, cx, cy, dx, dy;
+ double norm, tx;
+
+ if (width == 1 || y1 == y2 || x1 == x2) {
+ glBegin(GL_LINES);
+ glVertex2d(x1, y1);
+ glVertex2d(x2, y2);
+ glEnd();
+ return;
+ }
+ norm = (x1 - x2) / (y2 - y1);
+ tx = width / (2 * sqrt(1 + norm * norm));
+ ax = x1 + tx;
+ ay = y1 + norm * (ax - x1);
+ bx = x2 + tx;
+ by = y2 + norm * (bx - x2);
+ cx = x2 - tx;
+ cy = y2 + norm * (cx - x2);
+ dx = x1 - tx;
+ dy = y1 + norm * (dx - x1);
+ glBegin(GL_POLYGON);
+ glVertex2d(ax, ay);
+ glVertex2d(bx, by);
+ glVertex2d(cx, cy);
+ glVertex2d(dx, dy);
+ glEnd();
+}
+
+static double glc_stroke_line_dash(double x1, double y1, double x2, double y2,
+ double width, LineDash *dash)
+{
+ double ax, ay, bx, by;
+ double mx, my, len;
+ double dash_len, total = 0;
+
+ len = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
+ if (!dash->dashes || !dash->num_dashes) {
+ glc_stroke_line(x1, y1, x2, y2, width);
+ return len;
+ }
+ mx = (x2 - x1) / len;
+ my = (y2 - y1) / len;
+ ax = x1;
+ ay = y1;
+ while (total < len) {
+ if (dash->cur_dash >= 0) {
+ dash_len = dash->dashes[dash->cur_dash % dash->num_dashes] - dash->dash_pos;
+ } else {
+ dash_len = dash->offset - dash->dash_pos;
+ }
+ total += dash_len;
+ if (total < len) {
+ bx = x1 + mx * total;
+ by = y1 + my * total;
+ dash->dash_pos = 0;
+ } else {
+ bx = x2;
+ by = y2;
+ dash->dash_pos = dash->dashes[dash->cur_dash % dash->num_dashes] - (total - len);
+ }
+ if (dash->cur_dash % 2 == 0) {
+ glc_stroke_line(ax, ay, bx, by, width);
+ }
+ if (dash->dash_pos == 0) {
+ dash->cur_dash = (dash->cur_dash + 1) % (2 * dash->num_dashes);
+ }
+ ax = bx;
+ ay = by;
+ }
+ return len;
+}
+
+static void glc_vertex2d(InternaCtx *ctx, double x, double y)
+{
+ double len;
+ if (ctx->path_stroke.state == GLC_STROKE_ACTIVE) {
+ len = glc_stroke_line_dash(ctx->path_stroke.x, ctx->path_stroke.y, x, y,
+ ctx->line_width, &ctx->line_dash);
+ ctx->path_stroke.x = x;
+ ctx->path_stroke.y = y;
+ } else if (ctx->path_stroke.state == GLC_STROKE_FIRST) {
+ ctx->path_stroke.x = x;
+ ctx->path_stroke.y = y;
+ ctx->path_stroke.state = GLC_STROKE_ACTIVE;
+ } else {
+ ASSERT(ctx->path_stroke.state == GLC_STROKE_NONACTIVE);
+ //error
+ }
+}
+
+static void glc_begin_path(InternaCtx *ctx)
+{
+ ctx->path_stroke.state = GLC_STROKE_FIRST;
+ ctx->line_dash.cur_dash = ctx->line_dash.offset ? -1 : 0;
+ ctx->line_dash.dash_pos = 0;
+}
+
+static void glc_end_path(InternaCtx *ctx)
+{
+ ctx->path_stroke.state = GLC_STROKE_NONACTIVE;
+}
+
+void glc_stroke_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path = (InternalPath *)path_ref;
+
+ ASSERT(ctx && path);
+ if (ctx->line_width == 0) {
+ return;
+ }
+ start_draw(ctx);
+
+ reset_tass_vertex(ctx);
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ for (; current_path < end_path; current_path++) {
+ glc_begin_path(ctx);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ glc_vertex2d(ctx, current_point->x, current_point->y);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ glc_vertex2d(ctx, vertex->point.x, vertex->point.y);
+ vertex = vertex->list_link;
+ }
+ glc_vertex2d(ctx, current_point[2].x, current_point[2].y);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ glc_vertex2d(ctx, current_point->x, current_point->y);
+ }
+ }
+ }
+ glc_end_path(ctx);
+ }
+}
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint8_t *pixels;
+ const int pix_bytes = 4;
+
+ ASSERT(ctx && image);
+ ASSERT(src->width > 0 && src->height > 0);
+
+ ASSERT(image->format == GLC_IMAGE_RGB32 || image->format == GLC_IMAGE_ARGB32); //for now
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, dest->x, dest->y + dest->height);
+
+ if (dest->width == src->width && src->height == dest->height) {
+ glPixelZoom(1, 1);
+ } else {
+ glPixelZoom((float)dest->width / src->width, (float)dest->height / src->height);
+ }
+
+ pixels = image->pixels + src->x * 4 + (image->height - (src->y + src->height)) * image->stride;
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glPixelTransferf(GL_ALPHA_SCALE, (GLfloat)alpha);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glDrawPixels(src->width, src->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glDisable(GL_BLEND);
+ }
+
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ int recreate = 0;
+
+ ASSERT(ctx);
+#ifdef USE_COPY_PIXELS
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelZoom(1, 1);
+ glCopyPixels(x_src, ctx->height - (y_src + height), width, height, GL_COLOR);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+#else
+ int width2 = gl_get_to_power_two(width);
+ int height2 = gl_get_to_power_two(height);
+
+ start_draw(ctx);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (width2 > ctx->private_tex_width) {
+ ctx->private_tex_width = width2;
+ recreate = 1;
+ }
+ if (height2 > ctx->private_tex_height) {
+ ctx->private_tex_height = height2;
+ recreate = 1;
+ }
+ if (recreate) {
+ glDeleteTextures(1, &ctx->private_tex);
+ glGenTextures(1, &ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ ctx->private_tex_width = gl_get_to_power_two(width);
+ ctx->private_tex_height = gl_get_to_power_two(height);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, ctx->private_tex_width,
+ ctx->private_tex_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ }
+ ASSERT(ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x_src, ctx->height - (y_src + height),
+ width2, height2, 0);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / width2, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / height2, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)-x_dest / width2, (float)-Y(y_dest + height) / height2, 0);
+
+ glRecti(x_dest, Y(y_dest), x_dest + width, Y(y_dest + height));
+ glFlush();
+ if (!ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ } else {
+ set_pat(ctx, ctx->pat);
+ }
+#endif
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && image);
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+ ASSERT((image->stride % 4) == 0); //for now
+ glPixelStorei(GL_PACK_ROW_LENGTH, image->stride / 4);
+ glReadPixels(x, ctx->height - (y + image->height), image->width, image->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, image->pixels);
+}
+
+void glc_clear(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void glc_flush(GLCCtx glc)
+{
+ glFlush();
+
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void tessellation_combine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
+ GLdouble **data_out, void *usr_data)
+{
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex((InternaCtx *)usr_data))) {
+ *data_out = NULL;
+ return;
+ }
+ vertex->point.x = coords[0];
+ vertex->point.y = coords[1];
+ //vertex->point.z = coords[2];
+ *data_out = (GLdouble *)&vertex->point;
+}
+
+static void tessellation_error(GLenum errorCode)
+{
+ printf("%s: %s\n", __FUNCTION__, gluErrorString(errorCode));
+}
+
+#ifdef WIN32
+#define TESS_CALL_BACK_TYPE void(CALLBACK *)()
+#else
+#define TESS_CALL_BACK_TYPE void(*)()
+#endif
+
+static int init(InternaCtx *ctx, int width, int height)
+{
+#ifdef WIN32
+ if (!(ctx->glBlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"))) {
+ return FALSE;
+ }
+#endif
+ ctx->width = width;
+ ctx->height = height;
+ ctx->line_width = 1;
+
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+
+ if (!(ctx->tesselator = gluNewTess())) {
+ return FALSE;
+ }
+
+ glGenTextures(1, &ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, gl_get_to_power_two(width),
+ gl_get_to_power_two(height), 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ ctx->private_tex_width = gl_get_to_power_two(width);
+ ctx->private_tex_height = gl_get_to_power_two(height);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, 0, height, -1, 1);
+
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(ctx->tesselator, GLU_BEGIN, (TESS_CALL_BACK_TYPE)glBegin);
+ gluTessCallback(ctx->tesselator, GLU_VERTEX, (TESS_CALL_BACK_TYPE)glVertex3dv);
+ gluTessCallback(ctx->tesselator, GLU_END, (TESS_CALL_BACK_TYPE)glEnd);
+ gluTessCallback(ctx->tesselator, GLU_TESS_COMBINE_DATA,
+ (TESS_CALL_BACK_TYPE)tessellation_combine);
+ gluTessCallback(ctx->tesselator, GLU_TESS_ERROR, (TESS_CALL_BACK_TYPE)tessellation_error);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, (GLfloat)height, 0);
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelTransferf(GL_ALPHA_BIAS, 0);
+#ifdef WIN32
+ ctx->glBlendEquation(GL_FUNC_ADD);
+#else
+ glBlendEquation(GL_FUNC_ADD);
+#endif
+
+ glStencilMask(0xff);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return TRUE;
+}
+
+GLCCtx glc_create(int width, int height)
+{
+ InternaCtx *ctx;
+
+ ASSERT(sizeof(PathPoint) == sizeof(Vertex));
+
+ if (!(ctx = (InternaCtx *)zmalloc(sizeof(*ctx)))) {
+ return NULL;
+ }
+
+ if (!init(ctx, width, height)) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+/*
+ * In glx video mode change the textures will be destroyed, therefore
+ * if we will try to glDeleteTextures() them we might get seagfault.
+ * (this why we use the textures_lost parameter)
+ */
+void glc_destroy(GLCCtx glc, int textures_lost)
+{
+ InternaCtx *ctx;
+
+ if (!(ctx = (InternaCtx *)glc)) {
+ return;
+ }
+
+ if (!textures_lost) {
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ if (ctx->private_tex) {
+ glDeleteTextures(1, &ctx->private_tex);
+ }
+ }
+
+ free_tass_vertex_bufs(ctx);
+ free(ctx->line_dash.dashes);
+ free(ctx);
+ GLC_ERROR_TEST_FINISH;
+}
+
+/*
+ todo:
+ 1. test double vs float in gl calls
+ 2. int vs flat raster position
+ 3. pixels stride vs bytes stride
+ 4. improve non power of two.
+ glGetString(GL_EXTENSIONS);
+ ARB_texture_non_power_of_two
+ ARB_texture_rectangle
+ GL_TEXTURE_RECTANGLE_ARB
+ 5. scale
+ 6. origin
+ 7. fonts
+ 8. support more image formats
+ 9. use GLCImage in mask ops?
+*/
+
diff --git a/common/glc.c.save b/common/glc.c.save
new file mode 100644
index 00000000..19581105
--- /dev/null
+++ b/common/glc.c.save
@@ -0,0 +1,1413 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#ifdef WIN32
+#include "glext.h"
+#include "wglext.h"
+#endif
+
+#include "glc.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define ASSERT(x) if (!(x)) {printf("%s: assert failed %s\n", __FUNCTION__, #x); for (;;);}
+
+#define GLC_ERROR_TETS { \
+ GLenum gl_err; glFlush(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, gluErrorString(gl_err)); \
+ for(;;); \
+ } \
+}
+
+#define WARN_ONCE(x) { \
+ static int warn = TRUE; \
+ if (warn) { \
+ printf x; \
+ warn = FALSE; \
+ } \
+}
+
+#define TESS_VERTEX_ALLOC_BUNCH 20
+
+typedef struct InternaCtx InternaCtx;
+typedef struct InternalPat {
+ InternaCtx *owner;
+ int refs;
+ GLuint texture;
+ int x_orign;
+ int y_orign;
+ int width;
+ int height;
+} InternalPat;
+
+typedef struct Pathpath {
+ int start_point;
+ int num_segments;
+} Path;
+
+enum {
+ GLC_PATH_SEG_LINES,
+ GLC_PATH_SEG_BEIZER,
+};
+
+//todo: flatten cache
+typedef struct PathSegment {
+ int type;
+ int count;
+} PathSegment;
+
+typedef struct PathPoint {
+ double x;
+ double y;
+ double z;
+} PathPoint;
+
+typedef GLdouble Vertex[3];
+
+typedef struct InternalPath {
+ InternaCtx *owner;
+
+ Path *paths;
+ int paths_size;
+ int paths_pos;
+
+ PathSegment *segments;
+ int segments_size;
+ int segments_pos;
+
+ PathPoint *points;
+ int points_size;
+ int points_pos;
+
+ Path *current_path;
+ PathSegment *current_segment;
+
+} InternalPath;
+
+typedef struct TassVertex TassVertex;
+struct TassVertex {
+ PathPoint point;
+ TassVertex *list_link;
+ TassVertex *next;
+};
+
+typedef struct TassVertexBuf TassVertexBuf;
+struct TassVertexBuf {
+ TassVertexBuf *next;
+ TassVertex vertexs[0];
+};
+
+struct InternaCtx {
+ int draw_mode;
+ int stencil_refs;
+ int stencil_mask;
+ int width;
+ int height;
+ GLfloat line_width;
+ InternalPat *pat;
+ int max_texture_size;
+ GLUtesselator* tesselator;
+ TassVertex *free_tess_vertex;
+ TassVertex *used_tess_vertex;
+ TassVertexBuf *vertex_bufs;
+#ifdef WIN32
+ PFNGLBLENDEQUATIONPROC glBlendEquation;
+#endif
+};
+
+#define Y(y) -(y)
+#define VERTEX2(x, y) glVertex2d(x, Y(y))
+
+static void fill_rect(InternaCtx *ctx, void *rect);
+static void fill_path(InternaCtx *ctx, void *path);
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+static void set_pat(InternaCtx *ctx, InternalPat *pat);
+
+static inline void *zmalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline void set_raster_pos(InternaCtx *ctx, int x, int y)
+{
+ if (x >= 0 && y >= 0 && x < ctx->width && y < ctx->height) {
+ glRasterPos2i(x, Y(y));
+ return;
+ }
+ glRasterPos2i(0, 0);
+ glBitmap(0, 0, 0, 0, (GLfloat)x, (GLfloat)Y(y), NULL);
+}
+
+static TassVertex *alloc_tess_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+
+ if (!ctx->free_tess_vertex) {
+ TassVertexBuf *buf;
+ int i;
+
+ if (!(buf = (TassVertexBuf *)malloc(sizeof(TassVertexBuf) +
+ sizeof(TassVertex) * TESS_VERTEX_ALLOC_BUNCH))) {
+ //warn
+ return NULL;
+ }
+ buf->next = ctx->vertex_bufs;
+ ctx->vertex_bufs = buf;
+ for (i = 0; i < TESS_VERTEX_ALLOC_BUNCH; i++) {
+ buf->vertexs[i].point.z = 0;
+ buf->vertexs[i].next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = &buf->vertexs[i];
+ }
+ }
+
+ vertex = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex->next;
+ vertex->next = ctx->used_tess_vertex;
+ ctx->used_tess_vertex = vertex;
+ return vertex;
+}
+
+static void reset_tass_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+ while ((vertex = ctx->used_tess_vertex)) {
+ ctx->used_tess_vertex = vertex->next;
+ vertex->next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex;
+ }
+}
+
+static void free_tass_vertex_bufs(InternaCtx *ctx)
+{
+ TassVertexBuf *buf;
+
+ ctx->used_tess_vertex = NULL;
+ ctx->free_tess_vertex = NULL;
+ while ((buf = ctx->vertex_bufs)) {
+ ctx->vertex_bufs = buf->next;
+ free(buf);
+ }
+}
+
+//naiev bezier flattener
+static TassVertex *bezier_flattener(InternaCtx *ctx, PathPoint *points)
+{
+ double ax, bx, cx;
+ double ay, by, cy;
+ const int num_points = 30;
+ double dt;
+ int i;
+
+ TassVertex *vertex_list = NULL;
+ TassVertex *curr_vertex;
+
+ for (i = 0; i < num_points - 2; i++) {
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex(ctx))) {
+ //warn
+ return NULL;
+ }
+ vertex->list_link = vertex_list;
+ vertex_list = vertex;
+ }
+
+ curr_vertex = vertex_list;
+
+ cx = 3.0 * (points[1].x - points[0].x);
+ bx = 3.0 * (points[2].x - points[1].x) - cx;
+ ax = points[3].x - points[0].x - cx - bx;
+
+ cy = 3.0 * (points[1].y - points[0].y);
+ by = 3.0 * (points[2].y - points[1].y) - cy;
+ ay = points[3].y - points[0].y - cy - by;
+
+ dt = 1.0 / ( num_points - 1 );
+
+ for( i = 1; i < num_points - 1; i++, curr_vertex = curr_vertex->list_link) {
+ double tSquared, tCubed;
+ double t;
+ t = i * dt;
+
+ tSquared = t * t;
+ tCubed = tSquared * t;
+
+ curr_vertex->point.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + points[0].x;
+ curr_vertex->point.y = (ay * tCubed) + (by * tSquared) + (cy * t) + points[0].y;
+ }
+
+ return vertex_list;
+}
+
+#define MORE_X(path, Type, name) {\
+ Type *name;\
+ \
+ if (!(name = (Type *)zmalloc(sizeof(*name) * path->name##_size * 2))) {\
+ return FALSE;\
+ }\
+ memcpy(name, path->name, sizeof(*name) * path->name##_size);\
+ free(path->name);\
+ path->name = name;\
+ path->name##_size *= 2;\
+ return TRUE;\
+}
+
+static int more_points(InternalPath *path)
+{
+ MORE_X(path, PathPoint, points);
+}
+
+static int more_segments(InternalPath *path)
+{
+ MORE_X(path, PathSegment, segments);
+}
+
+static int more_paths(InternalPath *path)
+{
+ MORE_X(path, Path, paths);
+}
+
+static inline void put_point(InternalPath *path, double x, double y)
+{
+ path->points[path->points_pos].x = x;
+ path->points[path->points_pos++].y = Y(y + 0.5);
+ path->points[path->points_pos].z = 0;
+}
+
+void glc_path_move_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (internal->current_segment) {
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+ if (internal->points_pos == internal->points_size && !more_points(internal)) {
+ //warn
+ return;
+ }
+ internal->points_pos++;
+ }
+ internal->points[internal->points_pos - 1].x = x;
+ internal->points[internal->points_pos - 1].y = Y(y + 0.5);
+ internal->points[internal->points_pos - 1].z = 0;
+}
+
+static int add_segment_common(InternalPath *internal, int type, int num_points)
+{
+ if (internal->points_size - internal->points_pos < num_points && !more_points(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->current_segment) {
+ if (internal->current_segment->type == type) {
+ internal->current_segment->count++;
+ return TRUE;
+ }
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ internal->current_path->num_segments++;
+ return TRUE;
+ }
+
+ if (internal->paths_pos == internal->paths_size && !more_paths(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ internal->current_path = &internal->paths[internal->paths_pos++];
+ internal->current_path->start_point = internal->points_pos - 1;
+ internal->current_path->num_segments = 1;
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ return TRUE;
+}
+
+void glc_path_line_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_LINES, 1)) {
+ return;
+ }
+ put_point(internal, x, y);
+}
+
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_BEIZER, 3)) {
+ return;
+ }
+ put_point(internal, p1_x, p1_y);
+ put_point(internal, p2_x, p2_y);
+ put_point(internal, p3_x, p3_y);
+}
+
+void glc_path_close(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ if (!internal->current_path) {
+ return;
+ }
+ PathPoint *end_point = &internal->points[internal->current_path->start_point];
+ glc_path_line_to(path, end_point->x, Y(end_point->y));
+ glc_path_move_to(path, end_point->x, Y(end_point->y));
+}
+
+void glc_path_cleare(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ internal->paths_pos = internal->segments_pos = 0;
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+
+ internal->points[0].x = 0;
+ internal->points[0].y = 0;
+ internal->points_pos = 1;
+}
+
+GLCPath glc_path_create(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path;
+
+ ASSERT(ctx);
+ if (!(path = (InternalPath *)zmalloc(sizeof(*path)))) {
+ return NULL;
+ }
+
+ path->paths = (Path *)malloc(sizeof(*path->paths) * (path->paths_size = 2));
+ if (!path->paths) {
+ goto error_1;
+ }
+
+ path->segments = (PathSegment *)malloc(sizeof(*path->segments) * (path->segments_size = 4));
+ if (!path->segments) {
+ goto error_2;
+ }
+
+ path->points = (PathPoint *)malloc(sizeof(*path->points) * (path->points_size = 20));
+ if (!path->points) {
+ goto error_3;
+ }
+
+ path->owner = ctx;
+ path->points_pos = 1;
+ return path;
+
+error_3:
+ free(path->segments);
+
+error_2:
+ free(path->paths);
+
+error_1:
+ free(path);
+
+ return NULL;
+}
+
+void glc_path_destroy(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ if (!path) {
+ return;
+ }
+
+ free(internal->points);
+ free(internal->segments);
+ free(internal->paths);
+ free(internal);
+}
+
+static inline void unref_pat(InternalPat *pat)
+{
+ if (!pat) {
+ return;
+ }
+ ASSERT(pat->refs > 0);
+ if (--pat->refs == 0) {
+ glFinish();
+ glDeleteTextures(1, &pat->texture);
+ free(pat);
+ }
+ GLC_ERROR_TETS;
+}
+
+static inline InternalPat *ref_pat(InternalPat *pat)
+{
+ pat->refs++;
+ return pat;
+}
+
+#ifdef WIN32
+static inline int find_msb(uint32_t val)
+{
+ uint32_t r;
+ __asm {
+ bsr eax, val
+ jnz found
+ mov eax, -1
+
+ found:
+ mov r, eax
+ }
+ return r + 1;
+}
+#else
+static inline int find_msb(uint32_t val)
+{
+ int ret;
+
+ asm("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:"
+ : "=r"(ret) : "r"(val));
+ return ret + 1;
+}
+#endif
+
+static int to_pwoer_two(uint32_t val)
+{
+ if ((val & (val - 1)) == 0) {
+ return val;
+ }
+ return 1 << find_msb(val);
+}
+
+static void scale(uint32_t *dest, uint32_t dest_width, uint32_t dest_height,
+ uint32_t *src, uint32_t src_width, uint32_t src_height, int src_stride)
+{
+ double x_scale = (double)src_width / dest_width;
+ double y_scale = (double)src_height / dest_height;
+ uint32_t i;
+ uint32_t j;
+ int prev_row = -1;
+
+ for (i = 0; i < dest_height; i++) {
+ int row = (int)(y_scale * i);
+ if (row == prev_row) {
+ memcpy(dest, dest - dest_width, dest_width * sizeof(uint32_t));
+ dest += dest_width;
+ continue;
+ }
+ for (j = 0; j < dest_width; j++) {
+ int col = (int)(x_scale * j);
+ *(dest++) = *(src + col);
+ }
+ prev_row = row;
+ src = (uint32_t *)((uint8_t *)src + src_stride);
+ }
+}
+
+static inline void init_pattern(InternalPat *pat, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = pat->owner;
+ uint32_t *tmp_pixmap = NULL;
+ int width;
+ int height;
+ int width2;
+ int height2;
+
+ const int pix_bytes = 4;
+
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+
+ width = image->width;
+ height = image->height;
+ width2 = to_pwoer_two(width);
+ height2 = to_pwoer_two(height);
+
+ ASSERT(width > 0 && height > 0);
+ ASSERT(width > 0 && width <= pat->owner->max_texture_size);
+ ASSERT(height > 0 && height <= pat->owner->max_texture_size);
+
+ if (width2 != width || height2 != height) {
+ if (!(tmp_pixmap = (uint32_t *)malloc(width2 * height2 * sizeof(uint32_t)))) {
+ //warn
+ return;
+ }
+ scale(tmp_pixmap, width2, height2, (uint32_t *)image->pixels, width, height, image->stride);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (tmp_pixmap) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width2);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width2, height2, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ tmp_pixmap);
+ free(tmp_pixmap);
+ } else {
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ image->pixels);
+ }
+
+ GLC_ERROR_TETS;
+ pat->x_orign = x_orign % width;
+ pat->y_orign = y_orign % height;
+ pat->width = width;
+ pat->height = height;
+
+ if (ctx->pat == pat) {
+ set_pat(pat->owner, pat);
+ } else if (ctx->pat) {
+ glBindTexture(GL_TEXTURE_2D, ctx->pat->texture);
+ }
+}
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat;
+
+ ASSERT(ctx && image);
+
+ if (!(pat = (InternalPat *)zmalloc(sizeof(*pat)))) {
+ return NULL;
+ }
+ pat->refs = 1;
+ pat->owner = ctx;
+ glGenTextures(1, &pat->texture);
+ init_pattern(pat, x_orign, y_orign, image);
+ return pat;
+}
+
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternalPat *pat = (InternalPat *)pattern;
+ ASSERT(pat && pat->owner);
+
+ glFinish();
+ init_pattern(pat, x_orign, y_orign, image);
+}
+
+void glc_pattern_destroy(GLCPattern pat)
+{
+ unref_pat((InternalPat *)pat);
+ GLC_ERROR_TETS;
+}
+
+static void set_pat(InternaCtx *ctx, InternalPat *pat)
+{
+ pat = ref_pat(pat);
+ unref_pat(ctx->pat);
+ ctx->pat = pat;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / pat->width, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / (GLfloat)pat->height, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)pat->x_orign / pat->width, (float)Y(pat->y_orign) / pat->height, 0);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat = (InternalPat *)pattern;
+
+ ASSERT(ctx && pat && pat->owner == ctx);
+ set_pat(ctx, pat);
+}
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+
+ glDisable(GL_TEXTURE_2D);
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ glColor4d(red, green, blue, 1);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_op(GLCCtx glc, GLCOp op)
+{
+ if (op == GL_COPY) {
+ glDisable(GL_COLOR_LOGIC_OP);
+ return;
+ }
+ glLogicOp(op);
+ glEnable(GL_COLOR_LOGIC_OP);
+}
+
+void glc_set_line_width(GLCCtx glc, double width)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ ctx->line_width = (GLfloat)width;
+ if (ctx->line_width > 0) {
+ glLineWidth(ctx->line_width);
+ } else {
+ ctx->line_width = 0;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode fill_mode)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ int mode;
+ switch (fill_mode) {
+ case GLC_FILL_MODE_WINDING_ODD:
+ mode = GLU_TESS_WINDING_ODD;
+ break;
+ case GLC_FILL_MODE_WINDING_NONZERO:
+ mode = GLU_TESS_WINDING_NONZERO;
+ break;
+ default:
+ //warn
+ return;
+ }
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, mode);
+}
+
+static inline void add_stencil_client(InternaCtx *ctx)
+{
+ if (!ctx->stencil_refs) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ ctx->stencil_refs++;
+}
+
+static inline void remove_stencil_client(InternaCtx *ctx)
+{
+ ctx->stencil_refs--;
+ if (!ctx->stencil_refs) {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx && bitmap);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ GLCRect *end;
+ ASSERT(ctx && rects);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ end = rects + num_rect;
+ for (; rects < end; rects++) {
+ fill_rect(ctx, rects);
+ }
+}
+
+void glc_clear_mask(GLCCtx glc, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if ((ctx->stencil_mask & mask)) {
+ ctx->stencil_mask &= ~mask;
+ remove_stencil_client(ctx);
+ }
+}
+
+void glc_clip_reset(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ if (!(ctx->stencil_mask & 0x03)) {
+ return;
+ }
+ remove_stencil_client(ctx);
+ ctx->stencil_mask &= ~0x03;
+ glStencilMask(0x03);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ GLC_ERROR_TETS;
+}
+
+static void clip_common(InternaCtx *ctx, GLCClipOp op, void (*fill_func)(InternaCtx *, void *),
+ void *data)
+{
+ int stencil_val;
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ glDisable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+
+ if (op == GLC_CLIP_OP_SET) {
+ glc_clip_reset(ctx);
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= 0x01;
+ } else if (!(ctx->stencil_mask & 0x03)) {
+ GLCRect area;
+ if (op == GLC_CLIP_OP_OR) {
+ return;
+ }
+ area.x = area.y = 0;
+ area.width= ctx->width;
+ area.height = ctx->height;
+ clip_common(ctx, GLC_CLIP_OP_SET, fill_rect, &area);
+ }
+ glStencilMask(0x03);
+ switch (op) {
+ case GLC_CLIP_OP_SET:
+ case GLC_CLIP_OP_OR:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_ALWAYS, stencil_val, stencil_val);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_func(ctx, data);
+ break;
+ case GLC_CLIP_OP_AND: {
+ int clear_mask;
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ if (stencil_val == 0x01) {
+ glStencilOp(GL_ZERO, GL_INCR, GL_INCR);
+ stencil_val = 0x02;
+ clear_mask = 0x01;
+ } else {
+ glStencilOp(GL_ZERO, GL_DECR, GL_DECR);
+ stencil_val = 0x01;
+ clear_mask = 0x02;
+ }
+ fill_func(ctx, data);
+
+ glStencilMask(clear_mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ ctx->stencil_mask = (ctx->stencil_mask & ~clear_mask) |stencil_val;
+ break;
+ }
+ case GLC_CLIP_OP_EXCLUDE:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ fill_func(ctx, data);
+ break;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && rect);
+ clip_common(ctx, op, fill_rect, (void *)rect);
+}
+
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path);
+ clip_common(ctx, op, fill_path, path);
+}
+
+typedef struct FillMaskInfo {
+ int x_dest;
+ int y_dest;
+ int width;
+ int height;
+ int stride;
+ const uint8_t *bitmap;
+} FillMaskInfo;
+
+static void __fill_mask(InternaCtx *ctx, void *data)
+{
+ FillMaskInfo *info = (FillMaskInfo *)data;
+ fill_mask(ctx, info->x_dest, info->y_dest, info->width, info->height, info->stride,
+ info->bitmap);
+}
+
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ FillMaskInfo mask_info;
+
+ ASSERT(ctx && bitmap);
+ mask_info.x_dest = x_dest;
+ mask_info.y_dest = y_dest;
+ mask_info.width = width;
+ mask_info.height = height;
+ mask_info.stride = stride;
+ mask_info.bitmap = bitmap;
+ clip_common(ctx, op, __fill_mask, &mask_info);
+}
+
+static inline void start_draw(InternaCtx *ctx)
+{
+ if (ctx->draw_mode) {
+ return;
+ }
+ ctx->draw_mode = TRUE;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilFunc(GL_EQUAL, ctx->stencil_mask, ctx->stencil_mask);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+static void fill_rect(InternaCtx *ctx, void *r)
+{
+ GLCRect *rect = (GLCRect *)r;
+ glRectd(rect->x, Y(rect->y), rect->x + rect->width, Y(rect->y + rect->height));
+ /*glBegin(GL_POLYGON);
+ VERTEX2(rect->x, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height);
+ VERTEX2 (rect->x , rect->y + rect->height);
+ glEnd();*/
+ GLC_ERROR_TETS;
+}
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+ fill_rect(ctx, (void *)rect);
+ GLC_ERROR_TETS;
+}
+
+static void fill_path(InternaCtx *ctx, void *p)
+{
+ InternalPath *path = (InternalPath *)p;
+
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ reset_tass_vertex(ctx);
+ gluTessBeginPolygon(ctx->tesselator, ctx);
+ for (; current_path < end_path; current_path++) {
+ gluTessBeginContour(ctx->tesselator);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point, current_point);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)&vertex->point,
+ (GLdouble *)&vertex->point);
+ vertex = vertex->list_link;
+ }
+ gluTessVertex(ctx->tesselator, (GLdouble *)&current_point[2],
+ (GLdouble *)&current_point[2]);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point ,
+ (GLdouble *)current_point);
+ }
+ }
+ }
+ gluTessEndContour(ctx->tesselator);
+ }
+ gluTessEndPolygon(ctx->tesselator);
+}
+
+void glc_fill_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path_ref);
+ start_draw(ctx);
+ fill_path(ctx, path_ref);
+}
+
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap)
+{
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8);
+ glBitmap(width, height, 0, 0, 0, 0, bitmap);
+}
+
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && bitmap);
+ start_draw(ctx);
+ if (ctx->pat) {
+ WARN_ONCE(("%s: unimplemented fill mask with pattern\n", __FUNCTION__));
+ }
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect r;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ glPixelZoom(1, 1);
+ glDrawPixels(width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_mask);
+
+ r.x = x_dest;
+ r.y = y_dest;
+ r.width = width;
+ r.height = height;
+
+ //todo: support color/texture alpah vals (GL_MODULATE)
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ fill_rect(ctx, &r);
+ glDisable(GL_BLEND);
+}
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (ctx->line_width == 0) {
+ return;
+ }
+
+ start_draw(ctx);
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x , rect->y + 0.5);
+ VERTEX2 (rect->x + rect->width, rect->y + 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y + rect->height);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height - 0.5);
+ VERTEX2 (rect->x, rect->y + rect->height - 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2(rect->x + 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + 0.5 , rect->y);
+ glEnd();
+ GLC_ERROR_TETS;
+}
+
+void glc_stroke_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path = (InternalPath *)path_ref;
+
+ ASSERT(ctx && path);
+ if (ctx->line_width == 0) {
+ return;
+ }
+ start_draw(ctx);
+
+ reset_tass_vertex(ctx);
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ for (; current_path < end_path; current_path++) {
+ glBegin(GL_LINE_STRIP);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ glVertex2d(current_point->x , current_point->y);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ glVertex2d(vertex->point.x, vertex->point.y);
+ vertex = vertex->list_link;
+ }
+ glVertex2d(current_point[2].x , current_point[2].y);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ glVertex2d(current_point->x , current_point->y);
+ }
+ }
+ }
+ glEnd();
+ }
+}
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint8_t *pixels;
+ const int pix_bytes = 4;
+
+ ASSERT(ctx && image);
+ ASSERT(src->width > 0 && src->height > 0);
+
+ ASSERT(image->format == GLC_IMAGE_RGB32 || image->format == GLC_IMAGE_ARGB32); //for now
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, dest->x, dest->y + dest->height);
+
+ if (dest->width == src->width && src->height == dest->height) {
+ glPixelZoom(1, 1);
+ } else {
+ glPixelZoom((float)dest->width / src->width, (float)dest->height / src->height);
+ }
+
+ pixels = image->pixels + src->x * 4 + (image->height - (src->y + src->height)) * image->stride;
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glPixelTransferf(GL_ALPHA_SCALE, (GLfloat)alpha);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glDrawPixels(src->width, src->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glDisable(GL_BLEND);
+ }
+
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ ASSERT(ctx);
+#ifdef USE_COPY_PIXELS
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelZoom(1, 1);
+ glCopyPixels(x_src, ctx->height - (y_src + height), width, height, GL_COLOR);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+#else
+ GLuint texture;
+ int width2 = to_pwoer_two(width);
+ int height2 = to_pwoer_two(height);
+
+ start_draw(ctx);
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x_src, ctx->height - (y_src + height),
+ width2, height2, 0);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / width2, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / height2, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)-x_dest / width2, (float)-Y(y_dest + height) / height2, 0);
+
+ glRecti(x_dest, Y(y_dest), x_dest + width, Y(y_dest + height));
+ glFinish();
+ glDeleteTextures(1, &texture);
+ if (!ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ } else {
+ set_pat(ctx, ctx->pat);
+ }
+#endif
+ GLC_ERROR_TETS;
+}
+
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && image);
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+ ASSERT((image->stride % 4) == 0); //for now
+ glPixelStorei(GL_PACK_ROW_LENGTH, image->stride / 4);
+ glReadPixels(x, ctx->height - (y + image->height), image->width, image->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, image->pixels);
+}
+
+void glc_clear(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void glc_flush(GLCCtx glc)
+{
+ glFlush();
+
+ GLC_ERROR_TETS;
+}
+
+static void tessellation_combine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
+ GLdouble **data_out, void *usr_data)
+{
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex((InternaCtx *)usr_data))) {
+ *data_out = NULL;
+ return;
+ }
+ vertex->point.x = coords[0];
+ vertex->point.y = coords[1];
+ //vertex->point.z = coords[2];
+ *data_out = (GLdouble *)&vertex->point;
+}
+
+static void tessellation_error(GLenum errorCode)
+{
+ printf ("%s: %s\n", __FUNCTION__, gluErrorString(errorCode));
+}
+
+#ifdef WIN32
+#define TESS_CALL_BACK_TYPE void (CALLBACK *)()
+#else
+#define TESS_CALL_BACK_TYPE void (*)()
+#endif
+
+static int init(InternaCtx *ctx, int width, int height)
+{
+#ifdef WIN32
+ if (!(ctx->glBlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"))) {
+ return FALSE;
+ }
+#endif
+ ctx->width = width;
+ ctx->height = height;
+ ctx->line_width = 1;
+
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+
+ if (!(ctx->tesselator = gluNewTess())) {
+ return FALSE;
+ }
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, 0, height, -1, 1);
+
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(ctx->tesselator, GLU_BEGIN, (TESS_CALL_BACK_TYPE)glBegin);
+ gluTessCallback(ctx->tesselator, GLU_VERTEX, (TESS_CALL_BACK_TYPE)glVertex3dv);
+ gluTessCallback(ctx->tesselator, GLU_END, (TESS_CALL_BACK_TYPE)glEnd);
+ gluTessCallback(ctx->tesselator, GLU_TESS_COMBINE_DATA, (TESS_CALL_BACK_TYPE)tessellation_combine);
+ gluTessCallback(ctx->tesselator, GLU_TESS_ERROR, (TESS_CALL_BACK_TYPE)tessellation_error);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, (GLfloat)height, 0);
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelTransferf(GL_ALPHA_BIAS, 0);
+#ifdef WIN32
+ ctx->glBlendEquation(GL_FUNC_ADD);
+#else
+ glBlendEquation(GL_FUNC_ADD);
+#endif
+
+ glStencilMask(0xff);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return TRUE;
+}
+
+GLCCtx glc_create(int width, int height)
+{
+ InternaCtx *ctx;
+
+ ASSERT(sizeof(PathPoint) == sizeof(Vertex));
+
+ if (!(ctx = (InternaCtx *)zmalloc(sizeof(*ctx)))) {
+ return NULL;
+ }
+
+ if (!init(ctx, width, height)) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void glc_destroy(GLCCtx glc)
+{
+ InternaCtx *ctx;
+
+ if (!(ctx = (InternaCtx *)glc)) {
+ return;
+ }
+
+ unref_pat(ctx->pat);
+ free_tass_vertex_bufs(ctx);
+ free(ctx);
+}
+
+/*
+ todo:
+ 1. test double vs float in gl calls
+ 2. int vs flat raster position
+ 3. pixels stride vs bytes stride
+ 4. improve non power of two.
+ glGetString(GL_EXTENSIONS);
+ ARB_texture_non_power_of_two
+ ARB_texture_rectangle
+ GL_TEXTURE_RECTANGLE_ARB
+ 5. scale
+ 6. origin
+ 7. fonts
+ 8. support more image formats
+ 9. use GLCImage in mask ops?
+*/
+
diff --git a/common/glc.h b/common/glc.h
new file mode 100644
index 00000000..6213c375
--- /dev/null
+++ b/common/glc.h
@@ -0,0 +1,158 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef _H_GL_CANVASE
+#define _H_GL_CANVASE
+
+#include <stdint.h>
+
+typedef void * GLCCtx;
+typedef void * GLCPattern;
+typedef void * GLCPath;
+
+typedef struct GLCRect {
+ double x;
+ double y;
+ double width;
+ double height;
+} GLCRect;
+
+typedef struct GLCRecti {
+ int x;
+ int y;
+ int width;
+ int height;
+} GLCRecti;
+
+typedef enum {
+ GLC_IMAGE_RGB32,
+ GLC_IMAGE_ARGB32,
+} GLCImageFormat;
+
+typedef struct GLCPImage {
+ GLCImageFormat format;
+ int width;
+ int height;
+ int stride;
+ uint8_t *pixels;
+ uint32_t *pallet;
+} GLCImage;
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image);
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image);
+void glc_pattern_destroy(GLCPattern pattern);
+
+void glc_path_move_to(GLCPath path, double x, double y);
+void glc_path_line_to(GLCPath path, double x, double y);
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y);
+void glc_path_rel_move_to(GLCPath path, double x, double y);
+void glc_path_rel_line_to(GLCPath path, double x, double y);
+void glc_path_rel_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y);
+void glc_path_close(GLCPath path);
+
+void glc_path_cleare(GLCPath);
+GLCPath glc_path_create(GLCCtx glc);
+void glc_path_destroy(GLCPath path);
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue);
+void glc_set_rgba(GLCCtx glc, double red, double green, double blue, double alpha);
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern);
+
+typedef enum {
+ GLC_OP_CLEAR = 0x1500,
+ GLC_OP_SET = 0x150F,
+ GLC_OP_COPY = 0x1503,
+ GLC_OP_COPY_INVERTED = 0x150C,
+ GLC_OP_NOOP = 0x1505,
+ GLC_OP_INVERT = 0x150A,
+ GLC_OP_AND = 0x1501,
+ GLC_OP_NAND = 0x150E,
+ GLC_OP_OR = 0x1507,
+ GLC_OP_NOR = 0x1508,
+ GLC_OP_XOR = 0x1506,
+ GLC_OP_EQUIV = 0x1509,
+ GLC_OP_AND_REVERSE = 0x1502,
+ GLC_OP_AND_INVERTED = 0x1504,
+ GLC_OP_OR_REVERSE = 0x150B,
+ GLC_OP_OR_INVERTED = 0x150D,
+} GLCOp;
+
+void glc_set_op(GLCCtx glc, GLCOp op);
+void glc_set_alpha_factor(GLCCtx glc, double alpah);
+
+typedef enum {
+ GLC_FILL_MODE_WINDING_ODD,
+ GLC_FILL_MODE_WINDING_NONZERO,
+} GLCFillMode;
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode mode);
+void glc_set_line_width(GLCCtx glc, double width);
+void glc_set_line_end_cap(GLCCtx glc, int style);
+void glc_set_line_join(GLCCtx glc, int style);
+void glc_set_miter_limit(GLCCtx glc, int limit);
+void glc_set_line_dash(GLCCtx glc, const double *dashes, int num_dashes, double offset);
+
+typedef enum {
+ GLC_MASK_A,
+ GLC_MASK_B,
+} GLCMaskID;
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id);
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id);
+void glc_clear_mask(GLCCtx glc, GLCMaskID id);
+
+typedef enum {
+ GLC_CLIP_OP_SET,
+ GLC_CLIP_OP_OR,
+ GLC_CLIP_OP_AND,
+ GLC_CLIP_OP_EXCLUDE,
+} GLCClipOp;
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op);
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op);
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap, GLCClipOp op);
+void glc_clip_reset(GLCCtx glc);
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect);
+void glc_fill_path(GLCCtx glc, GLCPath path);
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask);
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect);
+void glc_stroke_path(GLCCtx glc, GLCPath path);
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha);
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height);
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image);
+
+void glc_flush(GLCCtx glc);
+void glc_clear(GLCCtx glc);
+GLCCtx glc_create(int width, int height);
+void glc_destroy(GLCCtx glc, int textures_lost);
+
+#endif
diff --git a/common/ipc_ring.h b/common/ipc_ring.h
new file mode 100644
index 00000000..1d9b3e1e
--- /dev/null
+++ b/common/ipc_ring.h
@@ -0,0 +1,135 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _H_RING_
+#define _H_RING_
+
+
+#define MSB_MASK4(x) \
+ (((x) & 0x8) ? 0x8 : \
+ ((x) & 0x4) ? 0x4 : \
+ ((x) & 0x2) ? 0x2 : \
+ ((x) & 0x1) ? 0x1 : 0)
+
+#define MSB_MASK8(x) \
+ (((x) & 0xf0) ? MSB_MASK4((x) >> 4) << 4 : MSB_MASK4(x))
+
+#define MSB_MASK16(x) \
+ (((x) & 0xff00) ? MSB_MASK8((x) >> 8) << 8 : MSB_MASK8(x))
+
+#define MSB_MASK(x) \
+ (((x) & 0xffff0000) ? MSB_MASK16((x) >> 16) << 16 : MSB_MASK16(x))
+
+#define POWER2_ALIGN(x) MSB_MASK((x) * 2 - 1)
+
+
+#define _TOSHIFT_4(x) \
+ (((x) & 0x8) ? 3 : \
+ ((x) & 0x4) ? 2 : \
+ ((x) & 0x2) ? 1 : 0)
+
+#define _TOSHIFT_8(x) \
+ (((x) & 0xf0) ? _TOSHIFT_4((x) >> 4) + 4 : _TOSHIFT_4(x))
+
+#define _TOSHIFT_16(x) \
+ (((x) & 0xff00) ? _TOSHIFT_8((x) >> 8) + 8 : _TOSHIFT_8(x))
+
+#define PAWER2_TO_SHIFT(x) \
+ (((x) & 0xffff0000) ? _TOSHIFT_16((x) >> 16) + 16 : _TOSHIFT_16(x))
+
+
+
+#define RING_DECLARE(name, el_type, size) \
+typedef struct ATTR_PACKED name##_ring_el { \
+ union { \
+ el_type el; \
+ UINT8 data[POWER2_ALIGN(sizeof(el_type))]; \
+ } ; \
+} name##_ring_el; \
+ \
+typedef struct ATTR_PACKED name { \
+ UINT32 num_items; \
+ UINT32 prod; \
+ UINT32 notify_on_prod; \
+ UINT32 cons; \
+ UINT32 notify_on_cons; \
+ name##_ring_el items[POWER2_ALIGN(size)]; \
+} name;
+
+
+#define RING_INIT(r) \
+ (r)->num_items = sizeof((r)->items) >> \
+ PAWER2_TO_SHIFT(sizeof((r)->items[0])); \
+ (r)->prod = (r)->cons = 0; \
+ (r)->notify_on_prod = 1; \
+ (r)->notify_on_cons = 0;
+
+
+#define RING_INDEX_MASK(r) ((r)->num_items - 1)
+
+#define RING_IS_PACKED(r) (sizeof((r)->items[0]) == sizeof((r)->items[0]).el)
+
+#define RING_IS_EMPTY(r) ((r)->cons == (r)->prod)
+
+#define RING_IS_FULL(r) (((r)->prod - (r)->cons) == (r)->num_items)
+
+#define RING_PROD_ITEM(r) (&(r)->items[(r)->prod & RING_INDEX_MASK(r)].el)
+
+#define RING_PROD_WAIT(r, wait) \
+ if (((wait) = RING_IS_FULL(r))) { \
+ (r)->notify_on_cons = (r)->cons + 1; \
+ mb(); \
+ (wait) = RING_IS_FULL(r); \
+ }
+
+#define RING_PUSH(r, notify) \
+ (r)->prod++; \
+ mb(); \
+ (notify) = (r)->prod == (r)->notify_on_prod;
+
+
+#define RING_CONS_ITEM(r) (&(r)->items[(r)->cons & RING_INDEX_MASK(r)].el)
+
+#define RING_CONS_WAIT(r, wait) \
+ if (((wait) = RING_IS_EMPTY(r))) { \
+ (r)->notify_on_prod = (r)->prod + 1; \
+ mb(); \
+ (wait) = RING_IS_EMPTY(r); \
+ }
+
+#define RING_POP(r, notify) \
+ (r)->cons++; \
+ mb(); \
+ (notify) = (r)->cons == (r)->notify_on_cons;
+
+
+
+#endif
diff --git a/common/lookup3.c b/common/lookup3.c
new file mode 100644
index 00000000..f23461ad
--- /dev/null
+++ b/common/lookup3.c
@@ -0,0 +1,769 @@
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+
+#include <stdio.h> /* defines printf for tests */
+#include <time.h> /* defines time_t for timings in the test */
+#include "lookup3.h"
+#ifdef linux
+# include <endian.h> /* attempt to define endianness */
+#endif
+
+/*
+ * My best guess at if you are big-endian or little-endian. This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN) || \
+ (defined(i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN) || \
+ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines. To be useful, it requires
+ -- that the key be an array of uint32_t's, and
+ -- that the length be the number of uint32_t's in the key
+
+ The function hashword() is identical to hashlittle() on little-endian
+ machines, and identical to hashbig() on big-endian machines,
+ except that the length has to be measured in uint32_ts rather than in
+ bytes. hashlittle() is more complicated than hashword() only because
+ hashlittle() has to dance around fitting the key bytes into registers.
+--------------------------------------------------------------------
+*/
+uint32_t hashword(
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t initval) /* the previous hash, or an arbitrary value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ return c;
+}
+
+
+/*
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values. pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds. If you pass in (*pb)==0, the output
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+void hashword2 (
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t *pc, /* IN: seed OUT: primary hash value */
+uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
+ c += *pb;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ *pc=c; *pb=b;
+}
+
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+#ifdef VALGRIND
+ const uint8_t *k8;
+#endif
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+/*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+void hashlittle2(
+ const void *key, /* the key to hash */
+ size_t length, /* length of the key */
+ uint32_t *pc, /* IN: primary initval, OUT: primary hash */
+ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
+ c += *pb;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+#ifdef VALGRIND
+ const uint8_t *k8;
+#endif
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+ }
+
+ final(a,b,c);
+ *pc=c; *pb=b;
+}
+
+
+
+/*
+ * hashbig():
+ * This is the same as hashword() on big-endian machines. It is different
+ * from hashlittle() on all machines. hashbig() takes advantage of
+ * big-endian byte ordering.
+ */
+uint32_t hashbig( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c;
+ union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+#ifdef VALGRIND
+ const uint8_t *k8;
+#endif
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff00; break;
+ case 2 : a+=k[0]&0xffff0000; break;
+ case 1 : a+=k[0]&0xff000000; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
+ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
+ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
+ case 1 : a+=((uint32_t)k8[0])<<24; break;
+ case 0 : return c;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[11];
+ case 11: c+=((uint32_t)k[10])<<8;
+ case 10: c+=((uint32_t)k[9])<<16;
+ case 9 : c+=((uint32_t)k[8])<<24;
+ case 8 : b+=k[7];
+ case 7 : b+=((uint32_t)k[6])<<8;
+ case 6 : b+=((uint32_t)k[5])<<16;
+ case 5 : b+=((uint32_t)k[4])<<24;
+ case 4 : a+=k[3];
+ case 3 : a+=((uint32_t)k[2])<<8;
+ case 2 : a+=((uint32_t)k[1])<<16;
+ case 1 : a+=((uint32_t)k[0])<<24;
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
diff --git a/common/lookup3.h b/common/lookup3.h
new file mode 100644
index 00000000..a2fbdeab
--- /dev/null
+++ b/common/lookup3.h
@@ -0,0 +1,43 @@
+/*
+ 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/>.
+*/
+
+#ifndef __LOOKUP3_H
+#define __LOOKUP3_H
+
+#ifdef __GNUC__
+
+#include <stdint.h>
+
+#else
+
+#ifdef QXLDD
+#include <windef.h>
+#include "os_dep.h"
+#else
+#include <stddef.h>
+#include <basetsd.h>
+#endif
+
+typedef UINT32 uint32_t;
+typedef UINT16 uint16_t;
+typedef UINT8 uint8_t;
+
+#endif
+
+uint32_t hashlittle(const void *key, size_t length, uint32_t initval);
+
+#endif
diff --git a/common/lz.c b/common/lz.c
new file mode 100644
index 00000000..eac44af6
--- /dev/null
+++ b/common/lz.c
@@ -0,0 +1,720 @@
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This program is licensed to you under the GNU General Public License,
+ version 2 or (at your option) any later version published by the Free
+ Software Foundation. See the file COPYING for details.
+
+ There is NO WARRANTY for this software, not even the implied
+ warranties of MERCHANTABILITY, NONINFRINGEMENT, or FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+*/
+
+#include "lz.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+
+#define ASSERT(usr, x) \
+ if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
+
+#else
+
+#define ASSERT(usr, x)
+
+#endif
+
+#define HASH_LOG 13
+#define HASH_SIZE (1 << HASH_LOG)
+#define HASH_MASK (HASH_SIZE - 1)
+
+
+typedef struct LzImageSegment LzImageSegment;
+struct LzImageSegment {
+ uint8_t *lines;
+ uint8_t *lines_end;
+ unsigned int size_delta; // total size of the previous segments in units of
+ // pixels for rgb and bytes for plt.
+ LzImageSegment *next;
+};
+
+// TODO: pack?
+typedef struct HashEntry {
+ LzImageSegment *image_seg;
+ uint8_t *ref;
+} HashEntry;
+
+typedef struct Encoder {
+ LzUsrContext *usr;
+
+ LzImageType type;
+ const Palette *palette; // for decoding images with palettes to rgb
+ int stride; // stride is in bytes. For rgb must be equal to
+ // width*bytes_per_pix.
+ // For palettes stride can be bigger than width/pixels_per_byte by 1 only if
+ // width%pixels_per_byte != 0.
+ int height;
+ int width; // the original width (in pixels)
+
+ LzImageSegment *head_image_segs;
+ LzImageSegment *tail_image_segs;
+ LzImageSegment *free_image_segs;
+
+ // the dicitionary hash table is composed (1) a pointer to the segment the word was found in
+ // (2) a pointer to the first byte in the segment that matches the word
+ HashEntry htab[HASH_SIZE];
+
+ uint8_t *io_start;
+ uint8_t *io_now;
+ uint8_t *io_end;
+ size_t io_bytes_count;
+
+ uint8_t *io_last_copy; // pointer to the last byte in which copy count was written
+} Encoder;
+
+/****************************************************/
+/* functions for managing the pool of image segments*/
+/****************************************************/
+static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder);
+static void lz_reset_image_seg(Encoder *encoder);
+static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
+ unsigned int num_first_lines);
+
+
+// return a free image segement if one exists. Make allocation if needed. adds it to the
+// tail of the image segments lists
+static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder)
+{
+ LzImageSegment *ret;
+
+ if (encoder->free_image_segs) {
+ ret = encoder->free_image_segs;
+ encoder->free_image_segs = ret->next;
+ } else {
+ if (!(ret = (LzImageSegment *)encoder->usr->malloc(encoder->usr, sizeof(*ret)))) {
+ return NULL;
+ }
+ }
+
+ ret->next = NULL;
+ if (encoder->tail_image_segs) {
+ encoder->tail_image_segs->next = ret;
+ }
+ encoder->tail_image_segs = ret;
+
+ if (!encoder->head_image_segs) {
+ encoder->head_image_segs = ret;
+ }
+
+ return ret;
+}
+
+// adding seg to the head of free segments (lz_reset_image_seg removes it from used ones)
+static INLINE void __lz_free_image_seg(Encoder *encoder, LzImageSegment *seg)
+{
+ seg->next = encoder->free_image_segs;
+ encoder->free_image_segs = seg;
+}
+
+// moves all the used image segments to the free pool
+static void lz_reset_image_seg(Encoder *encoder)
+{
+ while (encoder->head_image_segs) {
+ LzImageSegment *seg = encoder->head_image_segs;
+ encoder->head_image_segs = seg->next;
+ __lz_free_image_seg(encoder, seg);
+ }
+ encoder->tail_image_segs = NULL;
+}
+
+static void lz_dealloc_free_segments(Encoder *encoder)
+{
+ while (encoder->free_image_segs) {
+ LzImageSegment *seg = encoder->free_image_segs;
+ encoder->free_image_segs = seg->next;
+ encoder->usr->free(encoder->usr, seg);
+ }
+}
+
+// return FALSE when operation fails (due to failure in allocation)
+static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
+ unsigned int num_first_lines)
+{
+ LzImageSegment *image_seg;
+ uint32_t size_delta = 0;
+ unsigned int num_lines = num_first_lines;
+ uint8_t* lines = first_lines;
+ int row;
+
+ ASSERT(encoder->usr, !encoder->head_image_segs);
+
+ image_seg = lz_alloc_image_seg(encoder);
+ if (!image_seg) {
+ goto error_1;
+ }
+
+ image_seg->lines = lines;
+ image_seg->lines_end = lines + num_lines * encoder->stride;
+ image_seg->size_delta = size_delta;
+
+ size_delta += num_lines * encoder->stride / RGB_BYTES_PER_PIXEL[encoder->type];
+
+ for (row = num_first_lines; row < encoder->height; row += num_lines) {
+ num_lines = encoder->usr->more_lines(encoder->usr, &lines);
+ if (num_lines <= 0) {
+ encoder->usr->error(encoder->usr, "more lines failed\n");
+ }
+ image_seg = lz_alloc_image_seg(encoder);
+
+ if (!image_seg) {
+ goto error_1;
+ }
+
+ image_seg->lines = lines;
+ image_seg->lines_end = lines + num_lines * encoder->stride;
+ image_seg->size_delta = size_delta;
+
+ size_delta += num_lines * encoder->stride / RGB_BYTES_PER_PIXEL[encoder->type];
+ }
+
+ return TRUE;
+error_1:
+ lz_reset_image_seg(encoder);
+ return FALSE;
+}
+
+/**************************************************************************
+* Handling encoding and decoding of a byte
+***************************************************************************/
+static INLINE int more_io_bytes(Encoder *encoder)
+{
+ uint8_t *io_ptr;
+ int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
+ encoder->io_bytes_count += num_io_bytes;
+ encoder->io_now = io_ptr;
+ encoder->io_end = encoder->io_now + num_io_bytes;
+ return num_io_bytes;
+}
+
+static INLINE void encode(Encoder *encoder, uint8_t byte)
+{
+ if (encoder->io_now == encoder->io_end) {
+ if (more_io_bytes(encoder) <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, encoder->io_now);
+ }
+
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ *(encoder->io_now++) = byte;
+}
+
+static INLINE void encode_32(Encoder *encoder, unsigned int word)
+{
+ encode(encoder, (uint8_t)(word >> 24));
+ encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
+ encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
+ encode(encoder, (uint8_t)(word & 0x0000ff));
+}
+
+static INLINE void encode_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+ encode(encoder, copy_count);
+ encoder->io_last_copy = encoder->io_now - 1; // io_now cannot be the first byte of the buffer
+}
+
+static INLINE void update_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+ ASSERT(encoder->usr, encoder->io_last_copy);
+ *(encoder->io_last_copy) = copy_count;
+}
+
+static INLINE void encode_level(Encoder *encoder, uint8_t level_code)
+{
+ *(encoder->io_start) |= level_code;
+}
+
+// decrease the io ptr by 1
+static INLINE void compress_output_prev(Encoder *encoder)
+{
+ // io_now cannot be the first byte of the buffer
+ encoder->io_now--;
+ // the function should be called only when copy count is written unnecessarily by lz_compress
+ ASSERT(encoder->usr, encoder->io_now == encoder->io_last_copy)
+}
+
+static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
+{
+ ASSERT(encoder->usr, io_ptr <= io_ptr_end);
+ encoder->io_bytes_count = io_ptr_end - io_ptr;
+ encoder->io_start = io_ptr;
+ encoder->io_now = io_ptr;
+ encoder->io_end = io_ptr_end;
+ encoder->io_last_copy = NULL;
+
+ return TRUE;
+}
+
+static INLINE uint8_t decode(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ int num_io_bytes = more_io_bytes(encoder);
+ if (num_io_bytes <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, encoder->io_now);
+ }
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ return *(encoder->io_now++);
+}
+
+static INLINE uint32_t decode_32(Encoder *encoder)
+{
+ uint32_t word = 0;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ return word;
+}
+
+static INLINE int is_io_to_decode_end(Encoder *encoder)
+{
+ if (encoder->io_now != encoder->io_end) {
+ return FALSE;
+ } else {
+ int num_io_bytes = more_io_bytes(encoder); //disable inline optimizations
+ return (num_io_bytes <= 0);
+ }
+}
+
+/*******************************************************************
+* intialization and finalization of lz
+********************************************************************/
+static int init_encoder(Encoder *encoder, LzUsrContext *usr)
+{
+ encoder->usr = usr;
+ encoder->free_image_segs = NULL;
+ encoder->head_image_segs = NULL;
+ encoder->tail_image_segs = NULL;
+ return TRUE;
+}
+
+LzContext *lz_create(LzUsrContext *usr)
+{
+ Encoder *encoder;
+
+ if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
+ !usr->free || !usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
+ return NULL;
+ }
+
+ if (!init_encoder(encoder, usr)) {
+ usr->free(usr, encoder);
+ return NULL;
+ }
+ return (LzContext *)encoder;
+}
+
+void lz_destroy(LzContext *lz)
+{
+ Encoder *encoder = (Encoder *)lz;
+
+ if (!lz) {
+ return;
+ }
+
+ if (encoder->head_image_segs) {
+ encoder->usr->error(encoder->usr, "%s: used_image_segements not empty\n", __FUNCTION__);
+ lz_reset_image_seg(encoder);
+ }
+ lz_dealloc_free_segments(encoder);
+
+ encoder->usr->free(encoder->usr, encoder);
+}
+
+/*******************************************************************
+* encoding and decoding the image
+********************************************************************/
+/*
+ * Give hints to the compiler for branch prediction optimization.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2)
+#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
+#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
+#else
+#define LZ_EXPECT_CONDITIONAL(c) (c)
+#define LZ_UNEXPECT_CONDITIONAL(c) (c)
+#endif
+
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#define ATTR_PACKED
+#pragma pack(push)
+#pragma pack(1)
+#endif
+
+
+/* the palette images will be treated as one byte pixels. Their width should be transformed
+ accordingly.
+*/
+typedef struct ATTR_PACKED one_byte_pixel_t {
+ uint8_t a;
+} one_byte_pixel_t;
+
+typedef struct ATTR_PACKED rgb32_pixel_t {
+ uint8_t b;
+ uint8_t g;
+ uint8_t r;
+ uint8_t pad;
+} rgb32_pixel_t;
+
+typedef struct ATTR_PACKED rgb24_pixel_t {
+ uint8_t b;
+ uint8_t g;
+ uint8_t r;
+} rgb24_pixel_t;
+
+typedef uint16_t rgb16_pixel_t;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+
+#define MAX_COPY 32
+#define MAX_LEN 264 /* 256 + 8 */
+#define BOUND_OFFSET 2
+#define LIMIT_OFFSET 6
+#define MIN_FILE_SIZE 4
+#define COMP_LEVEL_SIZE_LIMIT 65536
+
+// TODO: implemented lz2. should lz1 be an option (no RLE + distance limitation of MAX_DISTANCE)
+// TODO: I think MAX_FARDISTANCE can be changed easily to 2^29
+// (and maybe even more when pixel > byte).
+// i.e. we can support 512M Bytes/Pixels distance instead of only ~68K.
+#define MAX_DISTANCE 8191 // 2^13
+#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1) // ~2^16+2^13
+
+
+#define LZ_PLT
+#include "lz_compress_tmpl.c"
+#define LZ_PLT
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT8
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT4_BE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT4_LE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT1_BE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT1_LE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+
+#define LZ_RGB16
+#include "lz_compress_tmpl.c"
+#define LZ_RGB16
+#include "lz_decompress_tmpl.c"
+#define LZ_RGB16
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_RGB24
+#include "lz_compress_tmpl.c"
+#define LZ_RGB24
+#include "lz_decompress_tmpl.c"
+
+
+#define LZ_RGB32
+#include "lz_compress_tmpl.c"
+#define LZ_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_RGB_ALPHA
+#include "lz_compress_tmpl.c"
+#define LZ_RGB_ALPHA
+#include "lz_decompress_tmpl.c"
+
+#undef LZ_UNEXPECT_CONDITIONAL
+#undef LZ_EXPECT_CONDITIONAL
+
+int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_down,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+ Encoder *encoder = (Encoder *)lz;
+ uint8_t *io_ptr_end = io_ptr + num_io_bytes;
+
+ encoder->type = type;
+ encoder->width = width;
+ encoder->height = height;
+ encoder->stride = stride;
+
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ if (encoder->stride > (width / PLT_PIXELS_PER_BYTE[encoder->type])) {
+ if (((width % PLT_PIXELS_PER_BYTE[encoder->type]) == 0) || (
+ (encoder->stride - (width / PLT_PIXELS_PER_BYTE[encoder->type])) > 1)) {
+ encoder->usr->error(encoder->usr, "sride overflows (plt)\n");
+ }
+ }
+ } else {
+ if (encoder->stride != width * RGB_BYTES_PER_PIXEL[encoder->type]) {
+ encoder->usr->error(encoder->usr, "sride != width*bytes_per_pixel (rgb)\n");
+ }
+ }
+
+ // assign the output buffer
+ if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
+ encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
+ }
+
+ // first read the list of the image segments
+ if (!lz_read_image_segments(encoder, lines, num_lines)) {
+ encoder->usr->error(encoder->usr, "lz encoder reading image segments failed\n");
+ }
+
+ encode_32(encoder, LZ_MAGIC);
+ encode_32(encoder, LZ_VERSION);
+ encode_32(encoder, type);
+ encode_32(encoder, width);
+ encode_32(encoder, height);
+ encode_32(encoder, stride);
+ encode_32(encoder, top_down); // TODO: maybe compress type and top_down to one byte
+
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ case LZ_IMAGE_TYPE_PLT8:
+ lz_plt_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB16:
+ lz_rgb16_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB24:
+ lz_rgb24_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ lz_rgb32_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGBA:
+ lz_rgb32_compress(encoder);
+ lz_rgb_alpha_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+
+ // move all the used segments to the free ones
+ lz_reset_image_seg(encoder);
+
+ encoder->io_bytes_count -= (encoder->io_end - encoder->io_now);
+
+ return encoder->io_bytes_count;
+}
+
+/*
+ initialize and read lz magic
+*/
+void lz_decode_begin(LzContext *lz, uint8_t *io_ptr, unsigned int num_io_bytes,
+ LzImageType *out_type, int *out_width, int *out_height,
+ int *out_n_pixels, int *out_top_down, const Palette *palette)
+{
+ Encoder *encoder = (Encoder *)lz;
+ uint8_t *io_ptr_end = io_ptr + num_io_bytes;
+ uint32_t magic;
+ uint32_t version;
+
+ if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
+ encoder->usr->error(encoder->usr, "io reset failed");
+ }
+
+ magic = decode_32(encoder);
+ if (magic != LZ_MAGIC) {
+ encoder->usr->error(encoder->usr, "bad magic\n");
+ }
+
+ version = decode_32(encoder);
+ if (version != LZ_VERSION) {
+ encoder->usr->error(encoder->usr, "bad version\n");
+ }
+
+ encoder->type = (LzImageType)decode_32(encoder);
+ encoder->width = decode_32(encoder);
+ encoder->height = decode_32(encoder);
+ encoder->stride = decode_32(encoder);
+ *out_top_down = decode_32(encoder);
+
+ *out_width = encoder->width;
+ *out_height = encoder->height;
+// *out_stride = encoder->stride;
+ *out_type = encoder->type;
+
+ // TODO: maybe instead of stride we can encode out_n_pixels
+ // (if stride is not necssary in decoding).
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ encoder->palette = palette;
+ *out_n_pixels = encoder->stride * PLT_PIXELS_PER_BYTE[encoder->type] * encoder->height;
+ } else {
+ *out_n_pixels = encoder->width * encoder->height;
+ }
+}
+
+void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
+{
+ Encoder *encoder = (Encoder *)lz;
+ size_t out_size = 0;
+ size_t alpha_size = 0;
+ int size = 0;
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ if (to_type == encoder->type) {
+ size = encoder->height * encoder->stride;
+ out_size = lz_plt_decompress(encoder, (one_byte_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ size = encoder->height * encoder->stride * PLT_PIXELS_PER_BYTE[encoder->type];
+ if (!encoder->palette) {
+ encoder->usr->error(encoder->usr,
+ "a palette is missing (for bpp to rgb decoding)\n");
+ }
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ out_size = lz_plt1_be_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ out_size = lz_plt1_le_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ out_size = lz_plt4_be_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ out_size = lz_plt4_le_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT8:
+ out_size = lz_plt8_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_RGB16:
+ case LZ_IMAGE_TYPE_RGB24:
+ case LZ_IMAGE_TYPE_RGB32:
+ case LZ_IMAGE_TYPE_RGBA:
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ } else {
+ size = encoder->height * encoder->width;
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_RGB16:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb16_decompress(encoder, (rgb16_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ out_size = lz_rgb16_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGB24:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb24_decompress(encoder, (rgb24_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGBA:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ alpha_size = lz_rgb_alpha_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ ASSERT(encoder->usr, alpha_size == size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ 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:
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ }
+
+ ASSERT(encoder->usr, is_io_to_decode_end(encoder));
+ ASSERT(encoder->usr, out_size == size);
+
+ if (out_size != size) {
+ encoder->usr->error(encoder->usr, "bad decode size\n");
+ }
+}
+
diff --git a/common/lz.h b/common/lz.h
new file mode 100644
index 00000000..074f633b
--- /dev/null
+++ b/common/lz.h
@@ -0,0 +1,75 @@
+/*
+ dictionary compression for images based on fastlz (http://www.fastlz.org/)
+ (Distributed under MIT license).
+*/
+#ifndef __LZ_H
+#define __LZ_H
+
+#include "lz_common.h"
+#include "lz_config.h"
+#include "draw.h"
+
+typedef void *LzContext;
+
+typedef struct LzUsrContext LzUsrContext;
+struct LzUsrContext {
+ void (*error)(LzUsrContext *usr, const char *fmt, ...);
+ void (*warn)(LzUsrContext *usr, const char *fmt, ...);
+ void (*info)(LzUsrContext *usr, const char *fmt, ...);
+ void *(*malloc)(LzUsrContext *usr, int size);
+ void (*free)(LzUsrContext *usr, void *ptr);
+ int (*more_space)(LzUsrContext *usr, uint8_t **io_ptr); // get the next chunk of the
+ // compressed buffer. return
+ // number of bytes in the chunk.
+ int (*more_lines)(LzUsrContext *usr, uint8_t **lines); // get the next chunk of the
+ // original image. If the image
+ // is down to top, return it from
+ // the last line to the first one
+ // (stride should always be
+ // positive)
+};
+
+/*
+ assumes width is in pixels and stride is in bytes
+ return: the number of bytes in the compressed data
+
+ TODO : determine size limit for the first segment and each chunk. check validity
+ of the segment or go to literal copy.
+ TODO : currently support only rgb images in which width*bytes_per_pixel = stride OR
+ paletter images in which stride eqauls the min number of bytes to
+ hold a line. stride is not necessary for now. just for sanity check.
+ stride should be > 0
+*/
+int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_down,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes);
+
+/*
+ prepare encoder and read lz magic.
+ out_n_pixels number of compressed pixels. May differ from Width*height in plt1/4.
+ Use it for allocation the decompressed buffer.
+
+*/
+void lz_decode_begin(LzContext *lz, uint8_t *io_ptr, unsigned int num_io_bytes,
+ LzImageType *out_type, int *out_width, int *out_height,
+ int *out_n_pixels, int *out_top_down, const Palette *palette);
+
+/*
+ to_type = the image output type.
+ We assume the buffer is consecutive. i.e. width = stride
+
+ Improtant: if the image is plt1/4 and to_type is rgb32, the image
+ will decompressed including the last bits in each line. This means buffer should be
+ larger than width*height if neede and you shoud use stride to fix it.
+ Note: If the image is down to top, set the stride in the cairo surface to negative.
+ use cairo_image_surface_create_for_data to create the surface and
+ cairo_surface_set_user_data in order to free the data in the destroy callback.
+*/
+void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf);
+
+LzContext *lz_create(LzUsrContext *usr);
+
+void lz_destroy(LzContext *lz);
+
+
+#endif // __LZ_H
diff --git a/common/lz_common.h b/common/lz_common.h
new file mode 100644
index 00000000..75c32e2e
--- /dev/null
+++ b/common/lz_common.h
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*common header for encoder and decoder*/
+
+#ifndef _LZ_COMMON_H
+#define _LZ_COMMON_H
+
+//#define DEBUG
+
+/* change the max window size will require change in the encoding format*/
+#define LZ_MAX_WINDOW_SIZE (1 << 25)
+#define MAX_COPY 32
+
+typedef enum {
+ LZ_IMAGE_TYPE_INVALID,
+ LZ_IMAGE_TYPE_PLT1_LE,
+ LZ_IMAGE_TYPE_PLT1_BE, // PLT stands for palette
+ LZ_IMAGE_TYPE_PLT4_LE,
+ LZ_IMAGE_TYPE_PLT4_BE,
+ LZ_IMAGE_TYPE_PLT8,
+ LZ_IMAGE_TYPE_RGB16,
+ LZ_IMAGE_TYPE_RGB24,
+ LZ_IMAGE_TYPE_RGB32,
+ LZ_IMAGE_TYPE_RGBA
+} LzImageType;
+
+#define LZ_IMAGE_TYPE_MASK 0x0f
+#define LZ_IMAGE_TYPE_LOG 4 // number of bits required for coding the image type
+
+/* access to the arrays is based on the image types */
+static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0};
+static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
+static const int PLT_PIXELS_PER_BYTE[] = {0, 8, 8, 2, 2, 1};
+static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4};
+
+
+#define LZ_MAGIC (*(uint32_t *)"LZ ")
+#define LZ_VERSION_MAJOR 1U
+#define LZ_VERSION_MINOR 1U
+#define LZ_VERSION ((LZ_VERSION_MAJOR << 16) | (LZ_VERSION_MINOR & 0xffff))
+
+
+#endif // _LZ_COMMON_H
diff --git a/common/lz_compress_tmpl.c b/common/lz_compress_tmpl.c
new file mode 100644
index 00000000..be1b941e
--- /dev/null
+++ b/common/lz_compress_tmpl.c
@@ -0,0 +1,523 @@
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This program is licensed to you under the GNU General Public License,
+ version 2 or (at your option) any later version published by the Free
+ Software Foundation. See the file COPYING for details.
+
+ There is NO WARRANTY for this software, not even the implied
+ warranties of MERCHANTABILITY, NONINFRINGEMENT, or FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+*/
+
+#define DJB2_START 5381;
+#define DJB2_HASH(hash, c) (hash = ((hash << 5) + hash) ^ (c)) //|{hash = ((hash << 5) + hash) + c;}
+
+/*
+ For each pixel type the following macros are defined:
+ PIXEL : input type
+ FNAME(name)
+ ENCODE_PIXEL(encoder, pixel) : writing a pixel to the compressed buffer (byte by byte)
+ SAME_PIXEL(pix1, pix2) : comparing two pixels
+ HASH_FUNC(value, pix_ptr) : hash func of 3 consecutive pixels
+*/
+
+#ifdef LZ_PLT
+#define PIXEL one_byte_pixel_t
+#define FNAME(name) lz_plt_##name
+#define ENCODE_PIXEL(e, pix) encode(e, (pix).a) // gets the pixel and write only the needed bytes
+ // from the pixel
+#define SAME_PIXEL(pix1, pix2) ((pix1).a == (pix2).a)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].a); \
+ DJB2_HASH(v, p[1].a); \
+ DJB2_HASH(v, p[2].a); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+#ifdef LZ_RGB_ALPHA
+//#undef LZ_RGB_ALPHA
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb_alpha_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).pad);}
+#define SAME_PIXEL(pix1, pix2) ((pix1).pad == (pix2).pad)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].pad); \
+ DJB2_HASH(v, p[1].pad); \
+ DJB2_HASH(v, p[2].pad); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+
+#ifdef LZ_RGB16
+#define PIXEL rgb16_pixel_t
+#define FNAME(name) lz_rgb16_##name
+#define GET_r(pix) (((pix) >> 10) & 0x1f)
+#define GET_g(pix) (((pix) >> 5) & 0x1f)
+#define GET_b(pix) ((pix) & 0x1f)
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix) >> 8); encode(e, (pix) & 0xff);}
+
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0] & (0x00ff)); \
+ DJB2_HASH(v, (p[0] >> 8) & (0x007f)); \
+ DJB2_HASH(v, p[1]&(0x00ff)); \
+ DJB2_HASH(v, (p[1] >> 8) & (0x007f)); \
+ DJB2_HASH(v, p[2] & (0x00ff)); \
+ DJB2_HASH(v, (p[2] >> 8) & (0x007f)); \
+ v &= HASH_MASK; \
+}
+#endif
+
+#ifdef LZ_RGB24
+#define PIXEL rgb24_pixel_t
+#define FNAME(name) lz_rgb24_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
+#endif
+
+#ifdef LZ_RGB32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb32_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
+#endif
+
+
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+#define GET_r(pix) ((pix).r)
+#define GET_g(pix) ((pix).g)
+#define GET_b(pix) ((pix).b)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].r); \
+ DJB2_HASH(v, p[0].g); \
+ DJB2_HASH(v, p[0].b); \
+ DJB2_HASH(v, p[1].r); \
+ DJB2_HASH(v, p[1].g); \
+ DJB2_HASH(v, p[1].b); \
+ DJB2_HASH(v, p[2].r); \
+ DJB2_HASH(v, p[2].g); \
+ DJB2_HASH(v, p[2].b); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+#define SAME_PIXEL(p1, p2) (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
+ GET_b(p1) == GET_b(p2))
+
+#endif
+
+#define PIXEL_ID(pix_ptr, seg_ptr) (pix_ptr - ((PIXEL *)seg_ptr->lines) + seg_ptr->size_delta)
+
+// when encoding, the ref can be in previous segment, and we should check that it doesn't
+// exceeds its bounds.
+// TODO: optimization: when only one chunk exists or when the reference is in the same segement,
+// don't make checks if we reach end of segements
+// TODO: optimize to continue match between segments?
+// TODO: check hash function
+// TODO: check times
+
+/* compresses one segment starting from 'from'.*/
+static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *from, int copied)
+{
+ const PIXEL *ip = from;
+ const PIXEL *ip_bound = (PIXEL *)(seg->lines_end) - BOUND_OFFSET;
+ const PIXEL *ip_limit = (PIXEL *)(seg->lines_end) - LIMIT_OFFSET;
+ HashEntry *hslot;
+ int hval;
+ int copy = copied;
+
+ if (copy == 0) {
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+
+
+ while (LZ_EXPECT_CONDITIONAL(ip < ip_limit)) { // TODO: maybe change ip_limit and enabling
+ // moving to the next seg
+ const PIXEL *ref;
+ const PIXEL *ref_limit;
+ size_t distance;
+
+ /* minimum match length */
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ size_t len = 3;
+#elif defined(LZ_RGB16)
+ size_t len = 2;
+#else
+ size_t len = 1;
+#endif
+ /* comparison starting-point */
+ const PIXEL *anchor = ip;
+
+
+
+ // TODO: RLE without checking if not first byte.
+ // TODO: optimize comparisons
+
+ /* check for a run */ // TODO for RGB we can use less pixels
+ if (LZ_EXPECT_CONDITIONAL(ip > (PIXEL *)(seg->lines))) {
+ if (SAME_PIXEL(ip[-1], ip[0]) && SAME_PIXEL(ip[0], ip[1]) && SAME_PIXEL(ip[1], ip[2])) {
+ distance = 1;
+ ip += 3;
+ ref = anchor + 2;
+ ref_limit = (PIXEL *)(seg->lines_end);
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ len = 3;
+#endif
+ goto match;
+ }
+ }
+
+ /* find potential match */
+ HASH_FUNC(hval, ip);
+ hslot = encoder->htab + hval;
+ ref = (PIXEL *)(hslot->ref);
+ ref_limit = (PIXEL *)(hslot->image_seg->lines_end);
+
+ /* calculate distance to the match */
+ distance = PIXEL_ID(anchor, seg) - PIXEL_ID(ref, hslot->image_seg);
+
+ /* update hash table */
+ hslot->image_seg = seg;
+ hslot->ref = (uint8_t *)anchor;
+
+ /* is this a match? check the first 3 pixels */
+ if (distance == 0 || (distance >= MAX_FARDISTANCE)) {
+ goto literal;
+ }
+ /* check if the hval key identical*/
+ // no need to check ref limit here because the word size in the htab is 3 pixels
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+
+ /* minimum match length for rgb16 is 2 and for plt and alpha is 3 */
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_RGB16)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+#endif
+
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+#endif
+ /* far, needs at least 5-byte match */
+ if (distance >= MAX_DISTANCE) {
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (ref >= (ref_limit - 1)) {
+ goto literal;
+ }
+#else
+ if (ref > (ref_limit - 1)) {
+ goto literal;
+ }
+#endif
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+ len++;
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+ len++;
+#endif
+ }
+match: // RLE or dictionary (both are encoded by distance from ref (-1) and length)
+
+ /* distance is biased */
+ distance--;
+
+ // ip is located now at the position of the second mismatch.
+ // later it will be substracted by 3
+
+ if (!distance) {
+ /* zero distance means a run */
+ PIXEL x = *ref;
+ while ((ip < ip_bound) && (ref < ref_limit)) { // TODO: maybe separate a run from
+ // the same seg or from different
+ // ones in order to spare
+ // ref < ref_limit
+ if (!SAME_PIXEL(*ref, x)) {
+ ref++;
+ break;
+ } else {
+ ref++;
+ ip++;
+ }
+ }
+ } else {
+ // TODO: maybe separate a run from the same seg or from different ones in order
+ // to spare ref < ref_limit and that way we can also perform 8 calls of
+ // (ref++ != ip++) outside a loop
+ for (;;) {
+ while ((ip < ip_bound) && (ref < ref_limit)) {
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ break;
+ } else {
+ ref++;
+ ip++;
+ }
+ }
+ break;
+ }
+ }
+
+ /* if we have copied something, adjust the copy count */
+ if (copy) {
+ /* copy is biased, '0' means 1 byte copy */
+ update_copy_count(encoder, copy - 1);
+ } else {
+ /* back, to overwrite the copy count */
+ compress_output_prev(encoder);
+ }
+
+ /* reset literal counter */
+ copy = 0;
+
+ /* length is biased, '1' means a match of 3 pixels for PLT and alpha*/
+ /* for RGB 16 1 means 2 */
+ /* for RGB24/32 1 means 1...*/
+ ip -= 3;
+ len = ip - anchor;
+#if defined(LZ_RGB16)
+ len++;
+#elif defined(LZ_RGB24) || defined(LZ_RGB32)
+ len += 2;
+#endif
+ /* encode the match (like fastlz level 2)*/
+ if (distance < MAX_DISTANCE) { // MAX_DISTANCE is 2^13 - 1
+ // when copy is pefrformed, the byte that holds the copy count is smaller than 32.
+ // When there is a reference, the first byte is always larger then 32
+
+ // 3 bits = length, 5 bits = 5 MSB of distance, 8 bits = 8 LSB of distance
+ if (len < 7) {
+ encode(encoder, (uint8_t)((len << 5) + (distance >> 8)));
+ encode(encoder, (uint8_t)(distance & 255));
+ } else { // more than 3 bits are needed for length
+ // 3 bits 7, 5 bits = 5 MSB of distance, next bytes are 255 till we
+ // recieve a smaller number, last byte = 8 LSB of distance
+ encode(encoder, (uint8_t)((7 << 5) + (distance >> 8)));
+ for (len -= 7; len >= 255; len -= 255) {
+ encode(encoder, 255);
+ }
+ encode(encoder, (uint8_t)len);
+ encode(encoder, (uint8_t)(distance & 255));
+ }
+ } else {
+ /* far away */
+ if (len < 7) { // the max_far_distance is ~2^16+2^13 so two more bytes are needed
+ // 3 bits = length, 5 bits = 5 MSB of MAX_DISTANCE, 8 bits = 8 LSB of MAX_DISTANCE,
+ // 8 bits = 8 MSB distance-MAX_distance (smaller than 2^16),8 bits=8 LSB of
+ // distance-MAX_distance
+ distance -= MAX_DISTANCE;
+ encode(encoder, (uint8_t)((len << 5) + 31));
+ encode(encoder, (uint8_t)255);
+ encode(encoder, (uint8_t)(distance >> 8));
+ encode(encoder, (uint8_t)(distance & 255));
+ } else {
+ // same as before, but the first byte is followed by the left overs of len
+ distance -= MAX_DISTANCE;
+ encode(encoder, (uint8_t)((7 << 5) + 31));
+ for (len -= 7; len >= 255; len -= 255) {
+ encode(encoder, 255);
+ }
+ encode(encoder, (uint8_t)len);
+ encode(encoder, 255);
+ encode(encoder, (uint8_t)(distance >> 8));
+ encode(encoder, (uint8_t)(distance & 255));
+ }
+ }
+
+ /* update the hash at match boundary */
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ if (ip > anchor) {
+#endif
+ HASH_FUNC(hval, ip);
+ encoder->htab[hval].ref = (uint8_t *)ip;
+ ip++;
+ encoder->htab[hval].image_seg = seg;
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ } else {ip++;
+ }
+#endif
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+ if (ip > anchor) {
+#endif
+ HASH_FUNC(hval, ip);
+ encoder->htab[hval].ref = (uint8_t *)ip;
+ ip++;
+ encoder->htab[hval].image_seg = seg;
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+ } else {ip++;
+ }
+#endif
+ /* assuming literal copy */
+ encode_copy_count(encoder, MAX_COPY - 1);
+ continue;
+
+literal:
+ ENCODE_PIXEL(encoder, *anchor);
+ anchor++;
+ ip = anchor;
+ copy++;
+
+ if (LZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
+ copy = 0;
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+ } // END LOOP (ip < ip_limit)
+
+
+ /* left-over as literal copy */
+ ip_bound++;
+ while (ip <= ip_bound) {
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ copy++;
+ if (copy == MAX_COPY) {
+ copy = 0;
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+ }
+
+ /* if we have copied something, adjust the copy length */
+ if (copy) {
+ update_copy_count(encoder, copy - 1);
+ } else {
+ compress_output_prev(encoder); // in case we created a new buffer for copy, check that
+ // red_worker could handle size that do not contain the
+ // ne buffer
+ }
+}
+
+
+/* initializes the hash table. if the file is very small, copies it.
+ copies the first two pixels of the first segment, and sends the segments
+ one by one to compress_seg.
+ the number of bytes compressed are stored inside encoder.
+ */
+static void FNAME(compress)(Encoder *encoder)
+{
+ LzImageSegment *cur_seg = encoder->head_image_segs;
+ HashEntry *hslot;
+ PIXEL *ip;
+
+ // fetch the first image segment that is not too small
+ while (cur_seg && ((((PIXEL *)cur_seg->lines_end) - ((PIXEL *)cur_seg->lines)) < 4)) {
+ // coping the segment
+ if (cur_seg->lines != cur_seg->lines_end) {
+ ip = (PIXEL *)cur_seg->lines;
+ // Note: we assume MAX_COPY > 3
+ encode_copy_count(encoder, (uint8_t)(
+ (((PIXEL *)cur_seg->lines_end) - ((PIXEL *)cur_seg->lines)) - 1));
+ while (ip < (PIXEL *)cur_seg->lines_end) {
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ }
+ }
+ cur_seg = cur_seg->next;
+ }
+
+ if (!cur_seg) {
+ return;
+ }
+
+ ip = (PIXEL *)cur_seg->lines;
+
+ /* initialize hash table */
+ for (hslot = encoder->htab; hslot < encoder->htab + HASH_SIZE; hslot++) {
+ hslot->ref = (uint8_t*)ip;
+ hslot->image_seg = cur_seg;
+ }
+
+ encode_copy_count(encoder, MAX_COPY - 1);
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+
+ // compressing the first segment
+ FNAME(compress_seg)(encoder, cur_seg, ip, 2);
+
+ // compressing the next segments
+ for (cur_seg = cur_seg->next; cur_seg; cur_seg = cur_seg->next) {
+ FNAME(compress_seg)(encoder, cur_seg, (PIXEL *)cur_seg->lines, 0);
+ }
+}
+
+#undef FNAME
+#undef PIXEL_ID
+#undef PIXEL
+#undef ENCODE_PIXEL
+#undef SAME_PIXEL
+#undef LZ_READU16
+#undef HASH_FUNC
+#undef BYTES_TO_16
+#undef HASH_FUNC_16
+#undef GET_r
+#undef GET_g
+#undef GET_b
+#undef GET_CODE
+#undef LZ_PLT
+#undef LZ_RGB_ALPHA
+#undef LZ_RGB16
+#undef LZ_RGB24
+#undef LZ_RGB32
+#undef HASH_FUNC2
diff --git a/common/lz_config.h b/common/lz_config.h
new file mode 100644
index 00000000..1020e7ba
--- /dev/null
+++ b/common/lz_config.h
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __LZ_CONFIG_H
+#define __LZ_CONFIG_H
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+#ifdef __GNUC__
+
+#include <stdint.h>
+#include <string.h>
+
+#define INLINE inline
+
+#else
+
+#ifdef QXLDD
+#include <windef.h>
+#include "os_dep.h"
+#define INLINE _inline
+
+#else
+#include <stddef.h>
+#include <basetsd.h>
+#include <string.h>
+
+#define INLINE inline
+#endif // QXLDD
+
+typedef UINT32 uint32_t;
+typedef UINT16 uint16_t;
+typedef UINT8 uint8_t;
+
+#endif //__GNUC__
+#endif //__LZ_CONFIG_H
diff --git a/common/lz_decompress_tmpl.c b/common/lz_decompress_tmpl.c
new file mode 100644
index 00000000..36502c66
--- /dev/null
+++ b/common/lz_decompress_tmpl.c
@@ -0,0 +1,317 @@
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This program is licensed to you under the GNU General Public License,
+ version 2 or (at your option) any later version published by the Free
+ Software Foundation. See the file COPYING for details.
+
+ There is NO WARRANTY for this software, not even the implied
+ warranties of MERCHANTABILITY, NONINFRINGEMENT, or FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+*/
+
+// External defines: PLT, RGBX/PLTXX/ALPHA, TO_RGB32.
+// If PLT4/1 and TO_RGB32 are defined, we need CAST_PLT_DISTANCE (because then the number of
+// pixels differ from the units used in the compression)
+
+/*
+ For each output pixel type the following macros are defined:
+ OUT_PIXEL - the output pixel type
+ COPY_PIXEL(p, out) - assignes the pixel to the place pointed by out and increases
+ out. Used in RLE. Need special handling because in alpha we
+ copy only the pad byte.
+ COPY_REF_PIXEL(ref, out) - copies the pixel pointed by ref to the pixel pointed by out.
+ Increases ref and out.
+ COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
+ buffer. Increases out.
+*/
+#if !defined(LZ_RGB_ALPHA)
+#define COPY_PIXEL(p, out) (*out++ = p)
+#define COPY_REF_PIXEL(ref, out) (*out++ = *ref++)
+#endif
+
+
+// decompressing plt to plt
+#ifdef LZ_PLT
+#ifndef TO_RGB32
+#define OUT_PIXEL one_byte_pixel_t
+#define FNAME(name) lz_plt_##name
+#define COPY_COMP_PIXEL(encoder, out) {out->a = decode(encoder); out++;}
+#else // TO_RGB32
+#define OUT_PIXEL rgb32_pixel_t
+#define COPY_PLT_ENTRY(ent, out) { \
+ (out)->b = ent; \
+ (out)->g = (ent >> 8); \
+ (out)->r = (ent >> 16); \
+ (out)->pad = 0; \
+}
+#ifdef PLT8
+#define FNAME(name) lz_plt8_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out) { \
+ uint32_t rgb = encoder->palette->ents[decode(encoder)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++;}
+#elif defined(PLT4_BE)
+#define FNAME(name) lz_plt4_be_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ uint32_t rgb = encoder->palette->ents[((byte >> 4) & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+ rgb = encoder->palette->ents[(byte & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*2)
+#elif defined(PLT4_LE)
+#define FNAME(name) lz_plt4_le_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ uint32_t rgb = encoder->palette->ents[(byte & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+ rgb = encoder->palette->ents[((byte >> 4) & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*2)
+#elif defined(PLT1_BE) // TODO store palette entries for direct access
+#define FNAME(name) lz_plt1_be_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ int i; \
+ uint32_t fore = encoder->palette->ents[1]; \
+ uint32_t back = encoder->palette->ents[0]; \
+ for (i = 7; i >= 0; i--) \
+ { \
+ if ((byte >> i) & 1) { \
+ COPY_PLT_ENTRY(fore, out); \
+ } else { \
+ COPY_PLT_ENTRY(back, out); \
+ } \
+ out++; \
+ } \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*8)
+#elif defined(PLT1_LE)
+#define FNAME(name) lz_plt1_le_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ int i; \
+ uint32_t fore = encoder->palette->ents[1]; \
+ uint32_t back = encoder->palette->ents[0]; \
+ for (i = 0; i < 8; i++) \
+ { \
+ if ((byte >> i) & 1) { \
+ COPY_PLT_ENTRY(fore, out); \
+ } else { \
+ COPY_PLT_ENTRY(back, out); \
+ } \
+ out++; \
+ } \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*8)
+#endif // PLT Type
+#endif // TO_RGB32
+#endif
+
+#ifdef LZ_RGB16
+#ifndef TO_RGB32
+#define OUT_PIXEL rgb16_pixel_t
+#define FNAME(name) lz_rgb16_##name
+#define COPY_COMP_PIXEL(e, out) {*out = ((decode(e) << 8) | decode(e)); out++;}
+#else
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb16_to_rgb32_##name
+#define COPY_COMP_PIXEL(e, out) { \
+ out->r = decode(e); \
+ out->b = decode(e); \
+ out->g = (((out->r) << 6) | ((out->b) >> 2)) & ~0x07; \
+ out->g |= (out->g >> 5); \
+ out->r = ((out->r << 1) & ~0x07)| ((out->r >> 4) & 0x07); \
+ out->b = (out->b << 3) | ((out->b >> 2) & 0x07); \
+ out->pad = 0; \
+ out++; \
+}
+#endif
+#endif
+
+#ifdef LZ_RGB24
+#define OUT_PIXEL rgb24_pixel_t
+#define FNAME(name) lz_rgb24_##name
+#define COPY_COMP_PIXEL(e, out) {out->b = decode(e); out->g = decode(e); out->r = decode(e); out++;}
+#endif
+
+#ifdef LZ_RGB32
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb32_##name
+#define COPY_COMP_PIXEL(e, out) { \
+ out->b = decode(e); \
+ out->g = decode(e); \
+ out->r = decode(e); \
+ out->pad = 0; \
+ out++; \
+}
+#endif
+
+#ifdef LZ_RGB_ALPHA
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb_alpha_##name
+#define COPY_PIXEL(p, out) {out->pad = p.pad; out++;}
+#define COPY_REF_PIXEL(ref, out) {out->pad = ref->pad; out++; ref++;}
+#define COPY_COMP_PIXEL(e, out) {out->pad = decode(e); out++;}
+#endif
+
+// return num of bytes in out_buf
+static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
+{
+ OUT_PIXEL *op = out_buf;
+ OUT_PIXEL *op_limit = out_buf + size;
+ uint32_t ctrl = decode(encoder);
+ int loop = TRUE;
+
+ do {
+ const OUT_PIXEL *ref = op;
+ uint32_t len = ctrl >> 5;
+ uint32_t ofs = (ctrl & 31) << 8; // 5 MSb of distance
+
+ if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
+ /* retrieving the reference and the match length */
+
+ uint8_t code;
+ len--;
+ //ref -= ofs;
+ if (len == 7 - 1) { // match length is bigger than 7
+ do {
+ code = decode(encoder);
+ len += code;
+ } while (code == 255); // remaining of len
+ }
+ code = decode(encoder);
+ ofs += code;
+
+ /* match from 16-bit distance */
+ if (LZ_UNEXPECT_CONDITIONAL(code == 255)) {
+ if (LZ_EXPECT_CONDITIONAL((ofs - code) == (31 << 8))) {
+ ofs = decode(encoder) << 8;
+ ofs += decode(encoder);
+ ofs += MAX_DISTANCE;
+ }
+ }
+
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ len += 3; // length is biased by 2 + 1 (fixing bias)
+#elif defined(LZ_RGB16)
+ len += 2; // length is biased by 1 + 1 (fixing bias)
+#else
+ len += 1;
+#endif
+ ofs += 1; // offset is biased by 1 (fixing bias)
+
+#if defined(TO_RGB32)
+#if defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || defined(PLT1_LE)
+ ofs = CAST_PLT_DISTANCE(ofs);
+ len = CAST_PLT_DISTANCE(len);
+#endif
+#endif
+ ref -= ofs;
+
+ ASSERT(encoder->usr, op + len <= op_limit);
+ ASSERT(encoder->usr, ref + len <= op_limit);
+ ASSERT(encoder->usr, ref >= out_buf);
+
+ // TODO: optimize by not calling loop at least 3 times when not PLT_TO_RGB32 (len is
+ // always >=3). in PLT_TO_RGB32 len >= 3*number_of_pixels_per_byte
+
+ /* copying the match*/
+
+ if (ref == (op - 1)) { // run // TODO: this will never be called in PLT4/1_TO_RGB
+ // because the number of pixel copied is larger
+ // then one...
+ /* optimize copy for a run */
+ OUT_PIXEL b = *ref;
+ for (; len; --len) {
+ COPY_PIXEL(b, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ } else {
+ for (; len; --len) {
+ COPY_REF_PIXEL(ref, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ }
+ } else { // copy
+ ctrl++; // copy count is biased by 1
+#if defined(TO_RGB32) && (defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || \
+ defined(PLT1_LE))
+ ASSERT(encoder->usr, op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
+#else
+ ASSERT(encoder->usr, op + ctrl <= op_limit);
+#endif
+ COPY_COMP_PIXEL(encoder, op);
+
+ ASSERT(encoder->usr, op <= op_limit);
+
+ for (--ctrl; ctrl; ctrl--) {
+ COPY_COMP_PIXEL(encoder, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ }
+
+ if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
+ ctrl = decode(encoder);
+ } else {
+ loop = FALSE;
+ }
+ } while (LZ_EXPECT_CONDITIONAL(loop));
+
+ return (op - out_buf);
+}
+
+#undef LZ_PLT
+#undef PLT8
+#undef PLT4_BE
+#undef PLT4_LE
+#undef PLT1_BE
+#undef PLT1_LE
+#undef LZ_RGB16
+#undef LZ_RGB24
+#undef LZ_RGB32
+#undef LZ_RGB_ALPHA
+#undef TO_RGB32
+#undef OUT_PIXEL
+#undef FNAME
+#undef COPY_PIXEL
+#undef COPY_REF_PIXEL
+#undef COPY_COMP_PIXEL
+#undef COPY_PLT_ENTRY
+#undef CAST_PLT_DISTANCE
+
diff --git a/common/mutex.h b/common/mutex.h
new file mode 100644
index 00000000..373d54e0
--- /dev/null
+++ b/common/mutex.h
@@ -0,0 +1,34 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_MUTEX
+#define _H_MUTEX
+#ifdef _WIN32
+#include <windows.h>
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+#else
+#include <pthread.h>
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL);
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
+#endif
+
+#endif // _H_MUTEX
diff --git a/common/ogl_ctx.c b/common/ogl_ctx.c
new file mode 100644
index 00000000..6b175115
--- /dev/null
+++ b/common/ogl_ctx.c
@@ -0,0 +1,253 @@
+/*
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+#include "ogl_ctx.h"
+
+
+#define PANIC(str) { \
+ printf("%s: panic: %s", __FUNCTION__, str); \
+ abort(); \
+}
+
+enum {
+ OGLCTX_TYPE_PBUF,
+ OGLCTX_TYPE_PIXMAP,
+};
+
+struct OGLCtx {
+ int type;
+ Display *x_display;
+ GLXContext glx_context;
+ GLXDrawable drawable;
+};
+
+typedef struct OGLPixmapCtx {
+ OGLCtx base;
+ Pixmap pixmap;
+} OGLPixmapCtx;
+
+
+
+const char *oglctx_type_str(OGLCtx *ctx)
+{
+ static const char *pbuf_str = "pbuf";
+ static const char *pixmap_str = "pixmap";
+ static const char *invalid_str = "invalid";
+
+ switch (ctx->type) {
+ case OGLCTX_TYPE_PBUF:
+ return pbuf_str;
+ case OGLCTX_TYPE_PIXMAP:
+ return pixmap_str;
+ default:
+ return invalid_str;
+ }
+}
+
+void oglctx_make_current(OGLCtx *ctx)
+{
+ if (!glXMakeCurrent(ctx->x_display, ctx->drawable, ctx->glx_context)) {
+ printf("%s: failed\n", __FUNCTION__);
+ }
+}
+
+OGLCtx *pbuf_create(int width, int heigth)
+{
+ OGLCtx *ctx;
+ Display *x_display;
+ int num_configs;
+ GLXFBConfig *fb_config;
+ GLXPbuffer glx_pbuf;
+ GLXContext glx_context;
+
+ const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_STENCIL_SIZE, 4,
+ 0 };
+
+ int pbuf_attrib[] = { GLX_PRESERVED_CONTENTS, True,
+ GLX_PBUFFER_WIDTH, width,
+ GLX_PBUFFER_HEIGHT, heigth,
+ GLX_LARGEST_PBUFFER, False,
+ 0, 0 };
+
+ if (!(ctx = calloc(1, sizeof(*ctx)))) {
+ printf("%s: alloc pbuf failed\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (!(x_display = XOpenDisplay(NULL))) {
+ printf("%s: open display failed\n", __FUNCTION__);
+ goto error_1;
+ }
+
+ if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
+ !num_configs) {
+ printf("%s: choose fb config failed\n", __FUNCTION__);
+ goto error_2;
+ }
+
+ if (!(glx_pbuf = glXCreatePbuffer(x_display, fb_config[0], pbuf_attrib))) {
+ goto error_3;
+ }
+
+ if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
+ printf("%s: create context failed\n", __FUNCTION__);
+ goto error_4;
+ }
+
+ XFree(fb_config);
+
+ ctx->type = OGLCTX_TYPE_PBUF;
+ ctx->drawable = glx_pbuf;
+ ctx->glx_context = glx_context;
+ ctx->x_display = x_display;
+
+ return ctx;
+
+error_4:
+ glXDestroyPbuffer(x_display, glx_pbuf);
+
+error_3:
+ XFree(fb_config);
+
+error_2:
+ XCloseDisplay(x_display);
+
+error_1:
+ free(ctx);
+
+ return NULL;
+}
+
+OGLCtx *pixmap_create(int width, int heigth)
+{
+ Display *x_display;
+ int num_configs;
+ GLXFBConfig *fb_config;
+ GLXPixmap glx_pixmap;
+ GLXContext glx_context;
+ Pixmap pixmap;
+ int screen;
+ Window root_window;
+ OGLPixmapCtx *pix;
+
+ const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_STENCIL_SIZE, 4,
+ 0 };
+
+ if (!(pix = calloc(1, sizeof(*pix)))) {
+ printf("%s: alloc pix failed\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (!(x_display = XOpenDisplay(NULL))) {
+ printf("%s: open display failed\n", __FUNCTION__);
+ goto error_1;
+ }
+
+ screen = DefaultScreen(x_display);
+ root_window = RootWindow(x_display, screen);
+
+ if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
+ !num_configs) {
+ printf("%s: choose fb config failed\n", __FUNCTION__);
+ goto error_2;
+ }
+
+ if (!(pixmap = XCreatePixmap(x_display, root_window, width, heigth, 32 /*use fb config*/))) {
+ printf("%s: create x pixmap failed\n", __FUNCTION__);
+ goto error_3;
+ }
+
+ if (!(glx_pixmap = glXCreatePixmap(x_display, fb_config[0], pixmap, NULL))) {
+ printf("%s: create glx pixmap failed\n", __FUNCTION__);
+ goto error_4;
+ }
+
+
+ if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
+ printf("%s: create context failed\n", __FUNCTION__);
+ goto error_5;
+ }
+
+ XFree(fb_config);
+
+ pix->base.type = OGLCTX_TYPE_PIXMAP;
+ pix->base.x_display = x_display;
+ pix->base.drawable = glx_pixmap;
+ pix->base.glx_context = glx_context;
+ pix->pixmap = pixmap;
+
+ return &pix->base;
+
+error_5:
+ glXDestroyPixmap(x_display, glx_pixmap);
+
+error_4:
+ XFreePixmap(x_display, pixmap);
+
+error_3:
+ XFree(fb_config);
+
+error_2:
+ XCloseDisplay(x_display);
+
+error_1:
+ free(pix);
+
+ return NULL;
+}
+
+void oglctx_destroy(OGLCtx *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+ // test is current ?
+
+ glXDestroyContext(ctx->x_display, ctx->glx_context);
+ switch (ctx->type) {
+ case OGLCTX_TYPE_PBUF:
+ glXDestroyPbuffer(ctx->x_display, ctx->drawable);
+ break;
+ case OGLCTX_TYPE_PIXMAP:
+ glXDestroyPixmap(ctx->x_display, ctx->drawable);
+ XFreePixmap(ctx->x_display, ((OGLPixmapCtx *)ctx)->pixmap);
+ break;
+ default:
+ PANIC("invalid ogl ctx type");
+ }
+
+ XCloseDisplay(ctx->x_display);
+ free(ctx);
+}
+
diff --git a/common/ogl_ctx.h b/common/ogl_ctx.h
new file mode 100644
index 00000000..85a551ab
--- /dev/null
+++ b/common/ogl_ctx.h
@@ -0,0 +1,30 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_GLCTX
+#define _H_GLCTX
+
+typedef struct OGLCtx OGLCtx;
+
+const char *oglctx_type_str(OGLCtx *ctx);
+void oglctx_make_current(OGLCtx *ctx);
+OGLCtx *pbuf_create(int width, int heigth);
+OGLCtx *pixmap_create(int width, int heigth);
+void oglctx_destroy(OGLCtx *ctx);
+
+#endif
+
diff --git a/common/quic.c b/common/quic.c
new file mode 100644
index 00000000..7568f9e1
--- /dev/null
+++ b/common/quic.c
@@ -0,0 +1,1709 @@
+/*
+ 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/>.
+*/
+
+// Red Hat image compression based on SFALIC by Roman Starosolski
+// http://sun.iinf.polsl.gliwice.pl/~rstaros/sfalic/index.html
+
+#include "quic.h"
+
+//#define DEBUG
+
+#define RLE
+#define RLE_STAT
+#define PRED_1
+//#define RLE_PRED_1
+#define RLE_PRED_2
+//#define RLE_PRED_3
+#define QUIC_RGB
+
+#define QUIC_MAGIC (*(uint32_t *)"QUIC")
+#define QUIC_VERSION_MAJOR 0U
+#define QUIC_VERSION_MINOR 1U
+#define QUIC_VERSION ((QUIC_VERSION_MAJOR << 16) | (QUIC_VERSION_MAJOR & 0xffff))
+
+#define ABS(a) ((a) >= 0 ? (a) : -(a))
+
+#ifdef DEBUG
+
+#define ASSERT(usr, x) \
+ if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
+
+#else
+
+#define ASSERT(usr, x)
+
+#endif
+
+#define FALSE 0
+#define TRUE 1
+
+typedef uint8_t BYTE;
+
+/* maximum number of codes in family */
+#define MAXNUMCODES 8
+
+/* model evolution, warning: only 1,3 and 5 allowed */
+#define DEFevol 3
+#define MINevol 0
+#define MAXevol 5
+
+/* starting wait mask index */
+#define DEFwmistart 0
+#define MINwmistart 0
+
+/* codeword length limit */
+#define DEFmaxclen 26
+
+/* target wait mask index */
+#define DEFwmimax 6
+
+/* number of symbols to encode before increasing wait mask index */
+#define DEFwminext 2048
+#define MINwminext 1
+#define MAXwminext 100000000
+
+typedef struct QuicFamily {
+ unsigned int nGRcodewords[MAXNUMCODES]; /* indexed by code number, contains number of
+ unmodofied GR codewords in the code */
+ unsigned int notGRcwlen[MAXNUMCODES]; /* indexed by code number, contains codeword
+ length of the not-GR codeword */
+ unsigned int notGRprefixmask[MAXNUMCODES]; /* indexed by code number, contains mask to
+ determine if the codeword is GR or not-GR */
+ unsigned int notGRsuffixlen[MAXNUMCODES]; /* indexed by code number, contains suffix
+ length of the not-GR codeword */
+
+ /* array for translating distribution U to L for depths up to 8 bpp,
+ initialized by decorelateinit() */
+ BYTE xlatU2L[256];
+
+ /* array for translating distribution L to U for depths up to 8 bpp,
+ initialized by corelateinit() */
+ unsigned int xlatL2U[256];
+} QuicFamily;
+
+static QuicFamily family_8bpc;
+static QuicFamily family_5bpc;
+
+typedef unsigned COUNTER; /* counter in the array of counters in bucket of the data model */
+
+typedef struct s_bucket {
+ COUNTER *pcounters; /* pointer to array of counters */
+ unsigned int bestcode; /* best code so far */
+} s_bucket;
+
+typedef struct Encoder Encoder;
+
+typedef struct CommonState {
+ Encoder *encoder;
+
+ unsigned int waitcnt;
+ unsigned int tabrand_seed;
+ unsigned int wm_trigger;
+ unsigned int wmidx;
+ unsigned int wmileft;
+
+#ifdef RLE_STAT
+ int melcstate; /* index to the state array */
+
+ int melclen; /* contents of the state array location
+ indexed by melcstate: the "expected"
+ run length is 2^melclen, shorter runs are
+ encoded by a 1 followed by the run length
+ in binary representation, wit a fixed length
+ of melclen bits */
+
+ unsigned long melcorder; /* 2^ melclen */
+#endif
+} CommonState;
+
+
+#define MAX_CHANNELS 4
+
+typedef struct FamilyStat {
+ s_bucket **buckets_ptrs;
+ s_bucket *buckets_buf;
+ COUNTER *counters;
+} FamilyStat;
+
+typedef struct Channel {
+ Encoder *encoder;
+
+ int correlate_row_width;
+ BYTE *correlate_row;
+
+ s_bucket **_buckets_ptrs;
+
+ FamilyStat family_stat_8bpc;
+ FamilyStat family_stat_5bpc;
+
+ CommonState state;
+} Channel;
+
+struct Encoder {
+ QuicUsrContext *usr;
+ QuicImageType type;
+ unsigned int width;
+ unsigned int height;
+ unsigned int num_channels;
+
+ unsigned int n_buckets_8bpc;
+ unsigned int n_buckets_5bpc;
+
+ unsigned int io_available_bits;
+ uint32_t io_word;
+ uint32_t io_next_word;
+ uint32_t *io_now;
+ uint32_t *io_end;
+ uint32_t io_words_count;
+
+ int rows_completed;
+
+ Channel channels[MAX_CHANNELS];
+
+ CommonState rgb_state;
+};
+
+/* target wait mask index */
+static int wmimax = DEFwmimax;
+
+/* number of symbols to encode before increasing wait mask index */
+static int wminext = DEFwminext;
+
+/* model evolution mode */
+static int evol = DEFevol;
+
+/* bppmask[i] contains i ones as lsb-s */
+static const unsigned long int bppmask[33] = {
+ 0x00000000, /* [0] */
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
+ 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+ 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+ 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+ 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+ 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+ 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff /* [32] */
+};
+
+static const unsigned int bitat[32] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008,
+ 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000,
+ 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000 /* [31]*/
+};
+
+
+#define TABRAND_TABSIZE 256
+#define TABRAND_SEEDMASK 0x0ff
+
+static const unsigned int tabrand_chaos[TABRAND_TABSIZE] = {
+ 0x02c57542, 0x35427717, 0x2f5a2153, 0x9244f155, 0x7bd26d07, 0x354c6052, 0x57329b28, 0x2993868e,
+ 0x6cd8808c, 0x147b46e0, 0x99db66af, 0xe32b4cac, 0x1b671264, 0x9d433486, 0x62a4c192, 0x06089a4b,
+ 0x9e3dce44, 0xdaabee13, 0x222425ea, 0xa46f331d, 0xcd589250, 0x8bb81d7f, 0xc8b736b9, 0x35948d33,
+ 0xd7ac7fd0, 0x5fbe2803, 0x2cfbc105, 0x013dbc4e, 0x7a37820f, 0x39f88e9e, 0xedd58794, 0xc5076689,
+ 0xfcada5a4, 0x64c2f46d, 0xb3ba3243, 0x8974b4f9, 0x5a05aebd, 0x20afcd00, 0x39e2b008, 0x88a18a45,
+ 0x600bde29, 0xf3971ace, 0xf37b0a6b, 0x7041495b, 0x70b707ab, 0x06beffbb, 0x4206051f, 0xe13c4ee3,
+ 0xc1a78327, 0x91aa067c, 0x8295f72a, 0x732917a6, 0x1d871b4d, 0x4048f136, 0xf1840e7e, 0x6a6048c1,
+ 0x696cb71a, 0x7ff501c3, 0x0fc6310b, 0x57e0f83d, 0x8cc26e74, 0x11a525a2, 0x946934c7, 0x7cd888f0,
+ 0x8f9d8604, 0x4f86e73b, 0x04520316, 0xdeeea20c, 0xf1def496, 0x67687288, 0xf540c5b2, 0x22401484,
+ 0x3478658a, 0xc2385746, 0x01979c2c, 0x5dad73c8, 0x0321f58b, 0xf0fedbee, 0x92826ddf, 0x284bec73,
+ 0x5b1a1975, 0x03df1e11, 0x20963e01, 0xa17cf12b, 0x740d776e, 0xa7a6bf3c, 0x01b5cce4, 0x1118aa76,
+ 0xfc6fac0a, 0xce927e9b, 0x00bf2567, 0x806f216c, 0xbca69056, 0x795bd3e9, 0xc9dc4557, 0x8929b6c2,
+ 0x789d52ec, 0x3f3fbf40, 0xb9197368, 0xa38c15b5, 0xc3b44fa8, 0xca8333b0, 0xb7e8d590, 0xbe807feb,
+ 0xbf5f8360, 0xd99e2f5c, 0x372928e1, 0x7c757c4c, 0x0db5b154, 0xc01ede02, 0x1fc86e78, 0x1f3985be,
+ 0xb4805c77, 0x00c880fa, 0x974c1b12, 0x35ab0214, 0xb2dc840d, 0x5b00ae37, 0xd313b026, 0xb260969d,
+ 0x7f4c8879, 0x1734c4d3, 0x49068631, 0xb9f6a021, 0x6b863e6f, 0xcee5debf, 0x29f8c9fb, 0x53dd6880,
+ 0x72b61223, 0x1f67a9fd, 0x0a0f6993, 0x13e59119, 0x11cca12e, 0xfe6b6766, 0x16b6effc, 0x97918fc4,
+ 0xc2b8a563, 0x94f2f741, 0x0bfa8c9a, 0xd1537ae8, 0xc1da349c, 0x873c60ca, 0x95005b85, 0x9b5c080e,
+ 0xbc8abbd9, 0xe1eab1d2, 0x6dac9070, 0x4ea9ebf1, 0xe0cf30d4, 0x1ef5bd7b, 0xd161043e, 0x5d2fa2e2,
+ 0xff5d3cae, 0x86ed9f87, 0x2aa1daa1, 0xbd731a34, 0x9e8f4b22, 0xb1c2c67a, 0xc21758c9, 0xa182215d,
+ 0xccb01948, 0x8d168df7, 0x04238cfe, 0x368c3dbc, 0x0aeadca5, 0xbad21c24, 0x0a71fee5, 0x9fc5d872,
+ 0x54c152c6, 0xfc329483, 0x6783384a, 0xeddb3e1c, 0x65f90e30, 0x884ad098, 0xce81675a, 0x4b372f7d,
+ 0x68bf9a39, 0x43445f1e, 0x40f8d8cb, 0x90d5acb6, 0x4cd07282, 0x349eeb06, 0x0c9d5332, 0x520b24ef,
+ 0x80020447, 0x67976491, 0x2f931ca3, 0xfe9b0535, 0xfcd30220, 0x61a9e6cc, 0xa487d8d7, 0x3f7c5dd1,
+ 0x7d0127c5, 0x48f51d15, 0x60dea871, 0xc9a91cb7, 0x58b53bb3, 0x9d5e0b2d, 0x624a78b4, 0x30dbee1b,
+ 0x9bdf22e7, 0x1df5c299, 0x2d5643a7, 0xf4dd35ff, 0x03ca8fd6, 0x53b47ed8, 0x6f2c19aa, 0xfeb0c1f4,
+ 0x49e54438, 0x2f2577e6, 0xbf876969, 0x72440ea9, 0xfa0bafb8, 0x74f5b3a0, 0x7dd357cd, 0x89ce1358,
+ 0x6ef2cdda, 0x1e7767f3, 0xa6be9fdb, 0x4f5f88f8, 0xba994a3a, 0x08ca6b65, 0xe0893818, 0x9e00a16a,
+ 0xf42bfc8f, 0x9972eedc, 0x749c8b51, 0x32c05f5e, 0xd706805f, 0x6bfbb7cf, 0xd9210a10, 0x31a1db97,
+ 0x923a9559, 0x37a7a1f6, 0x059f8861, 0xca493e62, 0x65157e81, 0x8f6467dd, 0xab85ff9f, 0x9331aff2,
+ 0x8616b9f5, 0xedbd5695, 0xee7e29b1, 0x313ac44f, 0xb903112f, 0x432ef649, 0xdc0a36c0, 0x61cf2bba,
+ 0x81474925, 0xa8b6c7ad, 0xee5931de, 0xb2f8158d, 0x59fb7409, 0x2e3dfaed, 0x9af25a3f, 0xe1fed4d5,
+};
+
+static unsigned int stabrand()
+{
+ //ASSERT( !(TABRAND_SEEDMASK & TABRAND_TABSIZE));
+ //ASSERT( TABRAND_SEEDMASK + 1 == TABRAND_TABSIZE );
+
+ return TABRAND_SEEDMASK;
+}
+
+static unsigned int tabrand(unsigned int *tabrand_seed)
+{
+ return tabrand_chaos[++*tabrand_seed & TABRAND_SEEDMASK];
+}
+
+static const unsigned short besttrigtab[3][11] = { /* array of wm_trigger for waitmask and evol,
+ used by set_wm_trigger() */
+ /* 1 */ { 550, 900, 800, 700, 500, 350, 300, 200, 180, 180, 160},
+ /* 3 */ { 110, 550, 900, 800, 550, 400, 350, 250, 140, 160, 140},
+ /* 5 */ { 100, 120, 550, 900, 700, 500, 400, 300, 220, 250, 160}
+};
+
+/* set wm_trigger knowing waitmask (param) and evol (glob)*/
+static void set_wm_trigger(CommonState *state)
+{
+ unsigned int wm = state->wmidx;
+ if (wm > 10) {
+ wm = 10;
+ }
+
+ ASSERT(state->encoder->usr, evol < 6);
+
+ state->wm_trigger = besttrigtab[evol / 2][wm];
+
+ ASSERT(state->encoder->usr, state->wm_trigger <= 2000);
+ ASSERT(state->encoder->usr, state->wm_trigger >= 1);
+}
+
+static int ceil_log_2(int val) /* ceil(log_2(val)) */
+{
+ int result;
+
+ //ASSERT(val>0);
+
+ if (val == 1) {
+ return 0;
+ }
+
+ result = 1;
+ val -= 1;
+ while (val >>= 1) {
+ result++;
+ }
+
+ return result;
+}
+
+/* number of leading zeroes in the byte, used by cntlzeroes(uint)*/
+static const BYTE lzeroes[256] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* count leading zeroes */
+static unsigned int cnt_l_zeroes(const unsigned int bits)
+{
+ if (bits & 0xff800000) {
+ return lzeroes[bits >> 24];
+ } else if (bits & 0xffff8000) {
+ return 8 + lzeroes[(bits >> 16) & 0x000000ff];
+ } else if (bits & 0xffffff80) {
+ return 16 + lzeroes[(bits >> 8) & 0x000000ff];
+ } else {
+ return 24 + lzeroes[bits & 0x000000ff];
+ }
+}
+
+#define QUIC_FAMILY_8BPC
+#include "quic_family_tmpl.c"
+
+#ifdef QUIC_RGB
+#define QUIC_FAMILY_5BPC
+#include "quic_family_tmpl.c"
+#endif
+
+static void decorelate_init(QuicFamily *family, int bpc)
+{
+ const unsigned int pixelbitmask = bppmask[bpc];
+ const unsigned int pixelbitmaskshr = pixelbitmask >> 1;
+ unsigned int s;
+
+ //ASSERT(bpc <= 8);
+
+ for (s = 0; s <= pixelbitmask; s++) {
+ if (s <= pixelbitmaskshr) {
+ family->xlatU2L[s] = s << 1;
+ } else {
+ family->xlatU2L[s] = ((pixelbitmask - s) << 1) + 1;
+ }
+ }
+}
+
+static void corelate_init(QuicFamily *family, int bpc)
+{
+ const unsigned long int pixelbitmask = bppmask[bpc];
+ unsigned long int s;
+
+ //ASSERT(bpc <= 8);
+
+ for (s = 0; s <= pixelbitmask; s++) {
+ if (s & 0x01) {
+ family->xlatL2U[s] = pixelbitmask - (s >> 1);
+ } else {
+ family->xlatL2U[s] = (s >> 1);
+ }
+ }
+}
+
+static void family_init(QuicFamily *family, int bpc, int limit)
+{
+ int l;
+
+ for (l = 0; l < bpc; l++) { /* fill arrays indexed by code number */
+ int altprefixlen, altcodewords;
+
+ altprefixlen = limit - bpc;
+ if (altprefixlen > (int)(bppmask[bpc - l])) {
+ altprefixlen = bppmask[bpc - l];
+ }
+
+ altcodewords = bppmask[bpc] + 1 - (altprefixlen << l);
+
+ family->nGRcodewords[l] = (altprefixlen << l);
+ family->notGRcwlen[l] = altprefixlen + ceil_log_2(altcodewords);
+ family->notGRprefixmask[l] = bppmask[32 - altprefixlen]; /* needed for decoding only */
+ family->notGRsuffixlen[l] = ceil_log_2(altcodewords); /* needed for decoding only */
+ }
+
+ decorelate_init(family, bpc);
+ corelate_init(family, bpc);
+}
+
+static void more_io_words(Encoder *encoder)
+{
+ uint32_t *io_ptr;
+ int num_io_words = encoder->usr->more_space(encoder->usr, &io_ptr, encoder->rows_completed);
+ if (num_io_words <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more words\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, io_ptr);
+ encoder->io_words_count += num_io_words;
+ encoder->io_now = io_ptr;
+ encoder->io_end = encoder->io_now + num_io_words;
+}
+
+static void __write_io_word(Encoder *encoder)
+{
+ more_io_words(encoder);
+ *(encoder->io_now++) = encoder->io_word;
+}
+
+static void (*__write_io_word_ptr)(Encoder *encoder) = __write_io_word;
+
+static INLINE void write_io_word(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ __write_io_word_ptr(encoder); //disable inline optimizations
+ return;
+ }
+ *(encoder->io_now++) = encoder->io_word;
+}
+
+static INLINE void encode(Encoder *encoder, unsigned int word, unsigned int len)
+{
+ int delta;
+
+ ASSERT(encoder->usr, len > 0 && len < 32);
+ ASSERT(encoder->usr, !(word & ~bppmask[len]));
+ if ((delta = ((int)encoder->io_available_bits - len)) >= 0) {
+ encoder->io_available_bits = delta;
+ encoder->io_word |= word << encoder->io_available_bits;
+ return;
+ }
+ delta = -delta;
+ encoder->io_word |= word >> delta;
+ write_io_word(encoder);
+ encoder->io_available_bits = 32 - delta;
+ encoder->io_word = word << encoder->io_available_bits;
+
+ ASSERT(encoder->usr, encoder->io_available_bits < 32);
+ ASSERT(encoder->usr, (encoder->io_word & bppmask[encoder->io_available_bits]) == 0);
+}
+
+static INLINE void encode_32(Encoder *encoder, unsigned int word)
+{
+ encode(encoder, word >> 16, 16);
+ encode(encoder, word & 0x0000ffff, 16);
+}
+
+static INLINE void flush(Encoder *encoder)
+{
+ if (encoder->io_available_bits > 0 && encoder->io_available_bits != 32) {
+ encode(encoder, 0, encoder->io_available_bits);
+ }
+ encode_32(encoder, 0);
+ encode(encoder, 0, 1);
+}
+
+static void __read_io_word(Encoder *encoder)
+{
+ more_io_words(encoder);
+ encoder->io_next_word = *(encoder->io_now++);
+}
+
+static void (*__read_io_word_ptr)(Encoder *encoder) = __read_io_word;
+
+
+static INLINE void read_io_word(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ __read_io_word_ptr(encoder); //disable inline optimizations
+ return;
+ }
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ encoder->io_next_word = *(encoder->io_now++);
+}
+
+static INLINE void decode_eatbits(Encoder *encoder, int len)
+{
+ int delta;
+
+ ASSERT(encoder->usr, len > 0 && len < 32);
+ encoder->io_word <<= len;
+
+ if ((delta = ((int)encoder->io_available_bits - len)) >= 0) {
+ encoder->io_available_bits = delta;
+ encoder->io_word |= encoder->io_next_word >> encoder->io_available_bits;
+ return;
+ }
+
+ delta = -delta;
+ encoder->io_word |= encoder->io_next_word << delta;
+ read_io_word(encoder);
+ encoder->io_available_bits = 32 - delta;
+ encoder->io_word |= (encoder->io_next_word >> encoder->io_available_bits);
+}
+
+static INLINE void decode_eat32bits(Encoder *encoder)
+{
+ decode_eatbits(encoder, 16);
+ decode_eatbits(encoder, 16);
+}
+
+#ifdef RLE
+
+#ifdef RLE_STAT
+
+static INLINE void encode_ones(Encoder *encoder, unsigned int n)
+{
+ unsigned int count;
+
+ for (count = n >> 5; count; count--) {
+ encode(encoder, ~0U, 32);
+ }
+
+ if ((n &= 0x1f)) {
+ encode(encoder, (1U << n) - 1, n);
+ }
+}
+
+#define MELCSTATES 32 /* number of melcode states */
+
+static int zeroLUT[256]; /* table to find out number of leading zeros */
+
+static int J[MELCSTATES] = {
+ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+/* creates the bit counting look-up table. */
+static void init_zeroLUT()
+{
+ int i, j, k, l;
+
+ j = k = 1;
+ l = 8;
+ for (i = 0; i < 256; ++i) {
+ zeroLUT[i] = l;
+ --k;
+ if (k == 0) {
+ k = j;
+ --l;
+ j *= 2;
+ }
+ }
+}
+
+static void encoder_init_rle(CommonState *state)
+{
+ state->melcstate = 0;
+ state->melclen = J[0];
+ state->melcorder = 1 << state->melclen;
+}
+
+#ifdef QUIC_RGB
+
+static void encode_run(Encoder *encoder, unsigned int runlen) //todo: try use end of line
+{
+ int hits = 0;
+
+ while (runlen >= encoder->rgb_state.melcorder) {
+ hits++;
+ runlen -= encoder->rgb_state.melcorder;
+ if (encoder->rgb_state.melcstate < MELCSTATES) {
+ encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen);
+ }
+ }
+
+ /* send the required number of "hit" bits (one per occurrence
+ of a run of length melcorder). This number is never too big:
+ after 31 such "hit" bits, each "hit" would represent a run of 32K
+ pixels.
+ */
+ encode_ones(encoder, hits);
+
+ encode(encoder, runlen, encoder->rgb_state.melclen + 1);
+
+ /* adjust melcoder parameters */
+ if (encoder->rgb_state.melcstate) {
+ encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen);
+ }
+}
+
+#endif
+
+static void encode_channel_run(Encoder *encoder, Channel *channel, unsigned int runlen)
+{
+ //todo: try use end of line
+ int hits = 0;
+
+ while (runlen >= channel->state.melcorder) {
+ hits++;
+ runlen -= channel->state.melcorder;
+ if (channel->state.melcstate < MELCSTATES) {
+ channel->state.melclen = J[++channel->state.melcstate];
+ channel->state.melcorder = (1L << channel->state.melclen);
+ }
+ }
+
+ /* send the required number of "hit" bits (one per occurrence
+ of a run of length melcorder). This number is never too big:
+ after 31 such "hit" bits, each "hit" would represent a run of 32K
+ pixels.
+ */
+ encode_ones(encoder, hits);
+
+ encode(encoder, runlen, channel->state.melclen + 1);
+
+ /* adjust melcoder parameters */
+ if (channel->state.melcstate) {
+ channel->state.melclen = J[--channel->state.melcstate];
+ channel->state.melcorder = (1L << channel->state.melclen);
+ }
+}
+
+/* decoding routine: reads bits from the input and returns a run length. */
+/* argument is the number of pixels left to end-of-line (bound on run length) */
+
+#ifdef QUIC_RGB
+static int decode_run(Encoder *encoder)
+{
+ int runlen = 0;
+
+ do {
+ register int temp, hits;
+ temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the
+ input stream, up to 8 */
+ for (hits = 1; hits <= temp; hits++) {
+ runlen += encoder->rgb_state.melcorder;
+
+ if (encoder->rgb_state.melcstate < MELCSTATES) {
+ encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen);
+ }
+ }
+ if (temp != 8) {
+ decode_eatbits(encoder, temp + 1); /* consume the leading
+ 0 of the remainder encoding */
+ break;
+ }
+ decode_eatbits(encoder, 8);
+ } while (1);
+
+ /* read the length of the remainder */
+ if (encoder->rgb_state.melclen) {
+ runlen += encoder->io_word >> (32 - encoder->rgb_state.melclen);
+ decode_eatbits(encoder, encoder->rgb_state.melclen);
+ }
+
+ /* adjust melcoder parameters */
+ if (encoder->rgb_state.melcstate) {
+ encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen);
+ }
+
+ return runlen;
+}
+
+#endif
+
+static int decode_channel_run(Encoder *encoder, Channel *channel)
+{
+ int runlen = 0;
+
+ do {
+ register int temp, hits;
+ temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the
+ input stream, up to 8 */
+ for (hits = 1; hits <= temp; hits++) {
+ runlen += channel->state.melcorder;
+
+ if (channel->state.melcstate < MELCSTATES) {
+ channel->state.melclen = J[++channel->state.melcstate];
+ channel->state.melcorder = (1U << channel->state.melclen);
+ }
+ }
+ if (temp != 8) {
+ decode_eatbits(encoder, temp + 1); /* consume the leading
+ 0 of the remainder encoding */
+ break;
+ }
+ decode_eatbits(encoder, 8);
+ } while (1);
+
+ /* read the length of the remainder */
+ if (channel->state.melclen) {
+ runlen += encoder->io_word >> (32 - channel->state.melclen);
+ decode_eatbits(encoder, channel->state.melclen);
+ }
+
+ /* adjust melcoder parameters */
+ if (channel->state.melcstate) {
+ channel->state.melclen = J[--channel->state.melcstate];
+ channel->state.melcorder = (1U << channel->state.melclen);
+ }
+
+ return runlen;
+}
+
+#else
+
+static INLINE int find_msb(int x)
+{
+ int r;
+
+ __asm__("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:" : "=r" (r) : "rm" (x));
+ return r + 1;
+}
+
+static INLINE void encode_run(Encoder *encoder, unsigned int len)
+{
+ int odd = len & 1U;
+ int msb;
+
+ len &= ~1U;
+
+ while ((msb = find_msb(len))) {
+ len &= ~(1 << (msb - 1));
+ ASSERT(encoder->usr, msb < 32);
+ encode(encoder, (1 << (msb)) - 1, msb);
+ encode(encoder, 0, 1);
+ }
+
+ if (odd) {
+ encode(encoder, 2, 2);
+ } else {
+ encode(encoder, 0, 1);
+ }
+}
+
+static INLINE unsigned int decode_run(Encoder *encoder)
+{
+ unsigned int len = 0;
+ int count;
+
+ do {
+ count = 0;
+ while (encoder->io_word & (1U << 31)) {
+ decode_eatbits(encoder, 1);
+ count++;
+ ASSERT(encoder->usr, count < 32);
+ }
+ decode_eatbits(encoder, 1);
+ len += (1U << count) >> 1;
+ } while (count > 1);
+
+ return len;
+}
+
+#endif
+#endif
+
+static INLINE void init_decode_io(Encoder *encoder)
+{
+ encoder->io_next_word = encoder->io_word = *(encoder->io_now++);
+ encoder->io_available_bits = 0;
+}
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#define ATTR_PACKED
+#pragma pack(push)
+#pragma pack(1)
+#endif
+
+typedef struct ATTR_PACKED one_byte_pixel_t {
+ BYTE a;
+} one_byte_t;
+
+typedef struct ATTR_PACKED three_bytes_pixel_t {
+ BYTE a;
+ BYTE b;
+ BYTE c;
+} three_bytes_t;
+
+typedef struct ATTR_PACKED four_bytes_pixel_t {
+ BYTE a;
+ BYTE b;
+ BYTE c;
+ BYTE d;
+} four_bytes_t;
+
+typedef struct ATTR_PACKED rgb32_pixel_t {
+ BYTE b;
+ BYTE g;
+ BYTE r;
+ BYTE pad;
+} rgb32_pixel_t;
+
+typedef struct ATTR_PACKED rgb24_pixel_t {
+ BYTE b;
+ BYTE g;
+ BYTE r;
+} rgb24_pixel_t;
+
+typedef uint16_t rgb16_pixel_t;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+#define ONE_BYTE
+#include "quic_tmpl.c"
+
+#define FOUR_BYTE
+#include "quic_tmpl.c"
+
+#ifdef QUIC_RGB
+
+#define QUIC_RGB32
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB24
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB16
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB16_TO_32
+#include "quic_rgb_tmpl.c"
+
+#else
+
+#define THREE_BYTE
+#include "quic_tmpl.c"
+
+#endif
+
+static void fill_model_structures(Encoder *encoder, FamilyStat *family_stat,
+ unsigned int rep_first, unsigned int first_size,
+ unsigned int rep_next, unsigned int mul_size,
+ unsigned int levels, unsigned int ncounters,
+ unsigned int nbuckets, unsigned int n_buckets_ptrs)
+{
+ unsigned int
+ bsize,
+ bstart,
+ bend = 0,
+ repcntr,
+ bnumber;
+
+ COUNTER * free_counter = family_stat->counters;/* first free location in the array of
+ counters */
+
+ bnumber = 0;
+
+ repcntr = rep_first + 1; /* first bucket */
+ bsize = first_size;
+
+ do { /* others */
+ if (bnumber) {
+ bstart = bend + 1;
+ } else {
+ bstart = 0;
+ }
+
+ if (!--repcntr) {
+ repcntr = rep_next;
+ bsize *= mul_size;
+ }
+
+ bend = bstart + bsize - 1;
+ if (bend + bsize >= levels) {
+ bend = levels - 1;
+ }
+
+ family_stat->buckets_buf[bnumber].pcounters = free_counter;
+ free_counter += ncounters;
+
+ ASSERT(encoder->usr, bstart < n_buckets_ptrs);
+ {
+ unsigned int i;
+ ASSERT(encoder->usr, bend < n_buckets_ptrs);
+ for (i = bstart; i <= bend; i++) {
+ family_stat->buckets_ptrs[i] = family_stat->buckets_buf + bnumber;
+ }
+ }
+
+ bnumber++;
+ } while (bend < levels - 1);
+
+ ASSERT(encoder->usr, free_counter - family_stat->counters == nbuckets * ncounters);
+}
+
+static void find_model_params(Encoder *encoder,
+ const int bpc,
+ unsigned int *ncounters,
+ unsigned int *levels,
+ unsigned int *n_buckets_ptrs,
+ unsigned int *repfirst,
+ unsigned int *firstsize,
+ unsigned int *repnext,
+ unsigned int *mulsize,
+ unsigned int *nbuckets)
+{
+ unsigned int bsize; /* bucket size */
+ unsigned int bstart, bend = 0; /* bucket start and end, range : 0 to levels-1*/
+ unsigned int repcntr; /* helper */
+
+ ASSERT(encoder->usr, bpc <= 8 && bpc > 0);
+
+
+ *ncounters = 8;
+
+ *levels = 0x1 << bpc;
+
+ *n_buckets_ptrs = 0; /* ==0 means: not set yet */
+
+ switch (evol) { /* set repfirst firstsize repnext mulsize */
+ case 1: /* buckets contain following numbers of contexts: 1 1 1 2 2 4 4 8 8 ... */
+ *repfirst = 3;
+ *firstsize = 1;
+ *repnext = 2;
+ *mulsize = 2;
+ break;
+ case 3: /* 1 2 4 8 16 32 64 ... */
+ *repfirst = 1;
+ *firstsize = 1;
+ *repnext = 1;
+ *mulsize = 2;
+ break;
+ case 5: /* 1 4 16 64 256 1024 4096 16384 65536 */
+ *repfirst = 1;
+ *firstsize = 1;
+ *repnext = 1;
+ *mulsize = 4;
+ break;
+ case 0: /* obsolete */
+ case 2: /* obsolete */
+ case 4: /* obsolete */
+ encoder->usr->error(encoder->usr, "findmodelparams(): evol value obsolete!!!\n");
+ default:
+ encoder->usr->error(encoder->usr, "findmodelparams(): evol out of range!!!\n");
+ }
+
+ *nbuckets = 0;
+ repcntr = *repfirst + 1; /* first bucket */
+ bsize = *firstsize;
+
+ do { /* other buckets */
+ if (nbuckets) { /* bucket start */
+ bstart = bend + 1;
+ } else {
+ bstart = 0;
+ }
+
+ if (!--repcntr) { /* bucket size */
+ repcntr = *repnext;
+ bsize *= *mulsize;
+ }
+
+ bend = bstart + bsize - 1; /* bucket end */
+ if (bend + bsize >= *levels) { /* if following bucked was bigger than current one */
+ bend = *levels - 1; /* concatenate them */
+ }
+
+ if (!*n_buckets_ptrs) { /* array size not set yet? */
+ *n_buckets_ptrs = *levels;
+ #if 0
+ if (bend == *levels - 1) { /* this bucket is last - all in the first array */
+ *n_buckets_ptrs = *levels;
+ } else if (bsize >= 256) { /* this bucket is allowed to reside in the 2nd table */
+ b_lo_ptrs = bstart;
+ assert(bstart); /* previous bucket exists */
+ }
+ #endif
+ }
+
+ (*nbuckets)++;
+ } while (bend < *levels - 1);
+}
+
+static int init_model_structures(Encoder *encoder, FamilyStat *family_stat,
+ unsigned int rep_first, unsigned int first_size,
+ unsigned int rep_next, unsigned int mul_size,
+ unsigned int levels, unsigned int ncounters,
+ unsigned int n_buckets_ptrs, unsigned int n_buckets)
+{
+ family_stat->buckets_ptrs = (s_bucket **)encoder->usr->malloc(encoder->usr,
+ n_buckets_ptrs *
+ sizeof(s_bucket *));
+ if (!family_stat->buckets_ptrs) {
+ return FALSE;
+ }
+
+ family_stat->counters = (COUNTER *)encoder->usr->malloc(encoder->usr,
+ n_buckets * sizeof(COUNTER) *
+ MAXNUMCODES);
+ if (!family_stat->counters) {
+ goto error_1;
+ }
+
+ family_stat->buckets_buf = (s_bucket *)encoder->usr->malloc(encoder->usr,
+ n_buckets * sizeof(s_bucket));
+ if (!family_stat->buckets_buf) {
+ goto error_2;
+ }
+
+ fill_model_structures(encoder, family_stat, rep_first, first_size, rep_next, mul_size, levels,
+ ncounters, n_buckets, n_buckets_ptrs);
+
+ return TRUE;
+
+error_2:
+ encoder->usr->free(encoder->usr, family_stat->counters);
+
+error_1:
+ encoder->usr->free(encoder->usr, family_stat->buckets_ptrs);
+
+ return FALSE;
+}
+
+static void free_family_stat(QuicUsrContext *usr, FamilyStat *family_stat)
+{
+ usr->free(usr, family_stat->buckets_ptrs);
+ usr->free(usr, family_stat->counters);
+ usr->free(usr, family_stat->buckets_buf);
+}
+
+static int init_channel(Encoder *encoder, Channel *channel)
+{
+ unsigned int ncounters;
+ unsigned int levels;
+ unsigned int rep_first;
+ unsigned int first_size;
+ unsigned int rep_next;
+ unsigned int mul_size;
+ unsigned int n_buckets;
+ unsigned int n_buckets_ptrs;
+
+ channel->encoder = encoder;
+ channel->state.encoder = encoder;
+ channel->correlate_row_width = 0;
+ channel->correlate_row = NULL;
+
+ find_model_params(encoder, 8, &ncounters, &levels, &n_buckets_ptrs, &rep_first,
+ &first_size, &rep_next, &mul_size, &n_buckets);
+ encoder->n_buckets_8bpc = n_buckets;
+ if (!init_model_structures(encoder, &channel->family_stat_8bpc, rep_first, first_size,
+ rep_next, mul_size, levels, ncounters, n_buckets_ptrs,
+ n_buckets)) {
+ return FALSE;
+ }
+
+ find_model_params(encoder, 5, &ncounters, &levels, &n_buckets_ptrs, &rep_first,
+ &first_size, &rep_next, &mul_size, &n_buckets);
+ encoder->n_buckets_5bpc = n_buckets;
+ if (!init_model_structures(encoder, &channel->family_stat_5bpc, rep_first, first_size,
+ rep_next, mul_size, levels, ncounters, n_buckets_ptrs,
+ n_buckets)) {
+ free_family_stat(encoder->usr, &channel->family_stat_8bpc);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void destroy_channel(Channel *channel)
+{
+ QuicUsrContext *usr = channel->encoder->usr;
+ if (channel->correlate_row) {
+ usr->free(usr, channel->correlate_row - 1);
+ }
+ free_family_stat(usr, &channel->family_stat_8bpc);
+ free_family_stat(usr, &channel->family_stat_5bpc);
+}
+
+static int init_encoder(Encoder *encoder, QuicUsrContext *usr)
+{
+ int i;
+
+ encoder->usr = usr;
+ encoder->rgb_state.encoder = encoder;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (!init_channel(encoder, &encoder->channels[i])) {
+ for (--i; i >= 0; i--) {
+ destroy_channel(&encoder->channels[i]);
+ }
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int encoder_reste(Encoder *encoder, uint32_t *io_ptr, uint32_t *io_ptr_end)
+{
+ ASSERT(encoder->usr, ((unsigned long)io_ptr % 4) == ((unsigned long)io_ptr_end % 4));
+ ASSERT(encoder->usr, io_ptr <= io_ptr_end);
+
+ encoder->rgb_state.waitcnt = 0;
+ encoder->rgb_state.tabrand_seed = stabrand();
+ encoder->rgb_state.wmidx = DEFwmistart;
+ encoder->rgb_state.wmileft = wminext;
+ set_wm_trigger(&encoder->rgb_state);
+
+#if defined(RLE) && defined(RLE_STAT)
+ encoder_init_rle(&encoder->rgb_state);
+#endif
+
+ encoder->io_words_count = io_ptr_end - io_ptr;
+ encoder->io_now = io_ptr;
+ encoder->io_end = io_ptr_end;
+ encoder->rows_completed = 0;
+
+ return TRUE;
+}
+
+static int encoder_reste_channels(Encoder *encoder, int channels, int width, int bpc)
+{
+ int i;
+
+ encoder->num_channels = channels;
+
+ for (i = 0; i < channels; i++) {
+ s_bucket *bucket;
+ s_bucket *end_bucket;
+
+ if (encoder->channels[i].correlate_row_width < width) {
+ encoder->channels[i].correlate_row_width = 0;
+ if (encoder->channels[i].correlate_row) {
+ encoder->usr->free(encoder->usr, encoder->channels[i].correlate_row - 1);
+ }
+ if (!(encoder->channels[i].correlate_row = (BYTE *)encoder->usr->malloc(encoder->usr,
+ width + 1))) {
+ return FALSE;
+ }
+ encoder->channels[i].correlate_row++;
+ encoder->channels[i].correlate_row_width = width;
+ }
+
+ if (bpc == 8) {
+ MEMCLEAR(encoder->channels[i].family_stat_8bpc.counters,
+ encoder->n_buckets_8bpc * sizeof(COUNTER) * MAXNUMCODES);
+ bucket = encoder->channels[i].family_stat_8bpc.buckets_buf;
+ end_bucket = bucket + encoder->n_buckets_8bpc;
+ for (; bucket < end_bucket; bucket++) {
+ bucket->bestcode = /*BPC*/ 8 - 1;
+ }
+ encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_8bpc.buckets_ptrs;
+ } else if (bpc == 5) {
+ MEMCLEAR(encoder->channels[i].family_stat_5bpc.counters,
+ encoder->n_buckets_5bpc * sizeof(COUNTER) * MAXNUMCODES);
+ bucket = encoder->channels[i].family_stat_5bpc.buckets_buf;
+ end_bucket = bucket + encoder->n_buckets_5bpc;
+ for (; bucket < end_bucket; bucket++) {
+ bucket->bestcode = /*BPC*/ 5 - 1;
+ }
+ encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_5bpc.buckets_ptrs;
+ } else {
+ encoder->usr->warn(encoder->usr, "%s: bad bpc %d\n", __FUNCTION__, bpc);
+ return FALSE;
+ }
+
+ encoder->channels[i].state.waitcnt = 0;
+ encoder->channels[i].state.tabrand_seed = stabrand();
+ encoder->channels[i].state.wmidx = DEFwmistart;
+ encoder->channels[i].state.wmileft = wminext;
+ set_wm_trigger(&encoder->channels[i].state);
+
+#if defined(RLE) && defined(RLE_STAT)
+ encoder_init_rle(&encoder->channels[i].state);
+#endif
+ }
+ return TRUE;
+}
+
+static void quic_image_params(Encoder *encoder, QuicImageType type, int *channels, int *bpc)
+{
+ ASSERT(encoder->usr, channels && bpc);
+ switch (type) {
+ case QUIC_IMAGE_TYPE_GRAY:
+ *channels = 1;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB16:
+ *channels = 3;
+ *bpc = 5;
+#ifndef QUIC_RGB
+ encoder->usr->error(encoder->usr, "not implemented\n");
+#endif
+ break;
+ case QUIC_IMAGE_TYPE_RGB24:
+ *channels = 3;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ *channels = 3;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ *channels = 4;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ *channels = 0;
+ *bpc = 0;
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+}
+
+#define FILL_LINES() { \
+ if (line == lines_end) { \
+ int n = encoder->usr->more_lines(encoder->usr, &line); \
+ if (n <= 0) { \
+ encoder->usr->error(encoder->usr, "more lines failed\n"); \
+ } \
+ lines_end = line + n * stride; \
+ } \
+}
+
+#define NEXT_LINE() { \
+ line += stride; \
+ FILL_LINES(); \
+}
+
+#define QUIC_COMPRESS_RGB(bits) \
+ encoder->channels[0].correlate_row[-1] = 0; \
+ encoder->channels[1].correlate_row[-1] = 0; \
+ encoder->channels[2].correlate_row[-1] = 0; \
+ quic_rgb##bits##_compress_row0(encoder, (rgb##bits##_pixel_t *)(line), width); \
+ encoder->rows_completed++; \
+ for (row = 1; row < height; row++) { \
+ prev = line; \
+ NEXT_LINE(); \
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \
+ quic_rgb##bits##_compress_row(encoder, (rgb##bits##_pixel_t *)prev, \
+ (rgb##bits##_pixel_t *)line, width); \
+ encoder->rows_completed++; \
+ }
+
+int quic_encode(QuicContext *quic, QuicImageType type, int width, int height,
+ uint8_t *line, unsigned int num_lines, int stride,
+ uint32_t *io_ptr, unsigned int num_io_words)
+{
+ Encoder *encoder = (Encoder *)quic;
+ uint32_t *io_ptr_end = io_ptr + num_io_words;
+ uint8_t *lines_end;
+ int row;
+ uint8_t *prev;
+ int channels;
+ int bpc;
+#ifndef QUIC_RGB
+ int i;
+#endif
+
+ ASSERT(encoder->usr, line);
+ lines_end = line + num_lines * stride;
+
+ quic_image_params(encoder, type, &channels, &bpc);
+
+ if (!encoder_reste(encoder, io_ptr, io_ptr_end) ||
+ !encoder_reste_channels(encoder, channels, width, bpc)) {
+ return QUIC_ERROR;
+ }
+
+ encoder->io_word = 0;
+ encoder->io_available_bits = 32;
+
+ encode_32(encoder, QUIC_MAGIC);
+ encode_32(encoder, QUIC_VERSION);
+ encode_32(encoder, type);
+ encode_32(encoder, width);
+ encode_32(encoder, height);
+
+ FILL_LINES();
+
+ switch (type) {
+#ifdef QUIC_RGB
+ case QUIC_IMAGE_TYPE_RGB32:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+ QUIC_COMPRESS_RGB(32);
+ break;
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= width * 3);
+ QUIC_COMPRESS_RGB(24);
+ break;
+ case QUIC_IMAGE_TYPE_RGB16:
+ ASSERT(encoder->usr, ABS(stride) >= width * 2);
+ QUIC_COMPRESS_RGB(16);
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ encoder->channels[1].correlate_row[-1] = 0;
+ encoder->channels[2].correlate_row[-1] = 0;
+ quic_rgb32_compress_row0(encoder, (rgb32_pixel_t *)(line), width);
+
+ encoder->channels[3].correlate_row[-1] = 0;
+ quic_four_compress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(line + 3), width);
+
+ encoder->rows_completed++;
+
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0];
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0];
+ quic_rgb32_compress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)line, width);
+
+ encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0];
+ quic_four_compress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3),
+ (four_bytes_t *)(line + 3), width);
+ encoder->rows_completed++;
+ }
+ break;
+#else
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= width * 3);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_three_compress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(line + i),
+ width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_three_compress_row(encoder, &encoder->channels[i], (three_bytes_t *)(prev + i),
+ (three_bytes_t *)(line + i), width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+ for (i = 0; i < channels; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_compress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(line + i),
+ width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ for (i = 0; i < channels; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_compress_row(encoder, &encoder->channels[i], (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(line + i), width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+#endif
+ case QUIC_IMAGE_TYPE_GRAY:
+ ASSERT(encoder->usr, ABS(stride) >= width);
+ encoder->channels[0].correlate_row[-1] = 0;
+ quic_one_compress_row0(encoder, &encoder->channels[0], (one_byte_t *)line, width);
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ quic_one_compress_row(encoder, &encoder->channels[0], (one_byte_t *)prev,
+ (one_byte_t *)line, width);
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+
+ flush(encoder);
+ encoder->io_words_count -= (encoder->io_end - encoder->io_now);
+
+ return encoder->io_words_count;
+}
+
+int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words,
+ QuicImageType *out_type, int *out_width, int *out_height)
+{
+ Encoder *encoder = (Encoder *)quic;
+ uint32_t *io_ptr_end = io_ptr + num_io_words;
+ QuicImageType type;
+ int width;
+ int height;
+ uint32_t magic;
+ uint32_t version;
+ int channels;
+ int bpc;
+
+ if (!encoder_reste(encoder, io_ptr, io_ptr_end)) {
+ return QUIC_ERROR;
+ }
+
+ init_decode_io(encoder);
+
+ magic = encoder->io_word;
+ decode_eat32bits(encoder);
+ if (magic != QUIC_MAGIC) {
+ encoder->usr->warn(encoder->usr, "bad magic\n");
+ return QUIC_ERROR;
+ }
+
+ version = encoder->io_word;
+ decode_eat32bits(encoder);
+ if (version != QUIC_VERSION) {
+ encoder->usr->warn(encoder->usr, "bad version\n");
+ return QUIC_ERROR;
+ }
+
+ type = (QuicImageType)encoder->io_word;
+ decode_eat32bits(encoder);
+
+ width = encoder->io_word;
+ decode_eat32bits(encoder);
+
+ height = encoder->io_word;
+ decode_eat32bits(encoder);
+
+ quic_image_params(encoder, type, &channels, &bpc);
+
+ if (!encoder_reste_channels(encoder, channels, width, bpc)) {
+ return QUIC_ERROR;
+ }
+
+ *out_width = encoder->width = width;
+ *out_height = encoder->height = height;
+ *out_type = encoder->type = type;
+ return QUIC_OK;
+}
+
+#ifndef QUIC_RGB
+static void clear_row(four_bytes_t *row, int width)
+{
+ four_bytes_t *end;
+ for (end = row + width; row < end; row++) {
+ row->a = 0;
+ }
+}
+
+#endif
+
+#ifdef QUIC_RGB
+
+static void uncompress_rgba(Encoder *encoder, uint8_t *buf, int stride)
+{
+ unsigned int row;
+ uint8_t *prev;
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ encoder->channels[1].correlate_row[-1] = 0;
+ encoder->channels[2].correlate_row[-1] = 0;
+ quic_rgb32_uncompress_row0(encoder, (rgb32_pixel_t *)buf, encoder->width);
+
+ encoder->channels[3].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(buf + 3),
+ encoder->width);
+
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0];
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0];
+ quic_rgb32_uncompress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)buf,
+ encoder->width);
+
+ encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3),
+ (four_bytes_t *)(buf + 3), encoder->width);
+
+ encoder->rows_completed++;
+ }
+}
+
+#endif
+
+static void uncompress_gray(Encoder *encoder, uint8_t *buf, int stride)
+{
+ unsigned int row;
+ uint8_t *prev;
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ quic_one_uncompress_row0(encoder, &encoder->channels[0], (one_byte_t *)buf, encoder->width);
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ quic_one_uncompress_row(encoder, &encoder->channels[0], (one_byte_t *)prev,
+ (one_byte_t *)buf, encoder->width);
+ encoder->rows_completed++;
+ }
+}
+
+#define QUIC_UNCOMPRESS_RGB(prefix, type) \
+ encoder->channels[0].correlate_row[-1] = 0; \
+ encoder->channels[1].correlate_row[-1] = 0; \
+ encoder->channels[2].correlate_row[-1] = 0; \
+ quic_rgb##prefix##_uncompress_row0(encoder, (type *)buf, encoder->width); \
+ encoder->rows_completed++; \
+ for (row = 1; row < encoder->height; row++) { \
+ prev = buf; \
+ buf += stride; \
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \
+ quic_rgb##prefix##_uncompress_row(encoder, (type *)prev, (type *)buf, \
+ encoder->width); \
+ encoder->rows_completed++; \
+ }
+
+int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride)
+{
+ Encoder *encoder = (Encoder *)quic;
+ unsigned int row;
+ uint8_t *prev;
+#ifndef QUIC_RGB
+ int i;
+#endif
+
+ ASSERT(encoder->usr, buf);
+
+ switch (encoder->type) {
+#ifdef QUIC_RGB
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGB24:
+ if (type == QUIC_IMAGE_TYPE_RGB32) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ QUIC_UNCOMPRESS_RGB(32, rgb32_pixel_t);
+ break;
+ } else if (type == QUIC_IMAGE_TYPE_RGB24) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3);
+ QUIC_UNCOMPRESS_RGB(24, rgb24_pixel_t);
+ break;
+ }
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ case QUIC_IMAGE_TYPE_RGB16:
+ if (type == QUIC_IMAGE_TYPE_RGB16) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 2);
+ QUIC_UNCOMPRESS_RGB(16, rgb16_pixel_t);
+ } else if (type == QUIC_IMAGE_TYPE_RGB32) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ QUIC_UNCOMPRESS_RGB(16_to_32, rgb32_pixel_t);
+ } else {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+
+ if (type != QUIC_IMAGE_TYPE_RGBA) {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ uncompress_rgba(encoder, buf, stride);
+ break;
+#else
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_three_uncompress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_three_uncompress_row(encoder, &encoder->channels[i],
+ (three_bytes_t *)(prev + i),
+ (three_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ clear_row((four_bytes_t *)(buf + 3), encoder->width);
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[i],
+ (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ clear_row((four_bytes_t *)(buf + 3), encoder->width);
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4);
+ for (i = 0; i < 4; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 4; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[i],
+ (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+#endif
+ case QUIC_IMAGE_TYPE_GRAY:
+
+ if (type != QUIC_IMAGE_TYPE_GRAY) {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width);
+ uncompress_gray(encoder, buf, stride);
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ return QUIC_OK;
+}
+
+static int need_init = TRUE;
+
+QuicContext *quic_create(QuicUsrContext *usr)
+{
+ Encoder *encoder;
+
+ if (!usr || need_init || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
+ !usr->free || !usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
+ return NULL;
+ }
+
+ if (!init_encoder(encoder, usr)) {
+ usr->free(usr, encoder);
+ return NULL;
+ }
+ return (QuicContext *)encoder;
+}
+
+void quic_destroy(QuicContext *quic)
+{
+ Encoder *encoder = (Encoder *)quic;
+ int i;
+
+ if (!quic) {
+ return;
+ }
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ destroy_channel(&encoder->channels[i]);
+ }
+ encoder->usr->free(encoder->usr, encoder);
+}
+
+void quic_init()
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = FALSE;
+
+ family_init(&family_8bpc, 8, DEFmaxclen);
+ family_init(&family_5bpc, 5, DEFmaxclen);
+#if defined(RLE) && defined(RLE_STAT)
+ init_zeroLUT();
+#endif
+}
+
diff --git a/common/quic.h b/common/quic.h
new file mode 100644
index 00000000..8824db7d
--- /dev/null
+++ b/common/quic.h
@@ -0,0 +1,64 @@
+/*
+ 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/>.
+*/
+
+#ifndef __QUIC_H
+#define __QUIC_H
+
+#include "quic_config.h"
+
+typedef enum {
+ QUIC_IMAGE_TYPE_INVALID,
+ QUIC_IMAGE_TYPE_GRAY,
+ QUIC_IMAGE_TYPE_RGB16,
+ QUIC_IMAGE_TYPE_RGB24,
+ QUIC_IMAGE_TYPE_RGB32,
+ QUIC_IMAGE_TYPE_RGBA
+} QuicImageType;
+
+#define QUIC_ERROR -1
+#define QUIC_OK 0
+
+typedef void *QuicContext;
+
+typedef struct QuicUsrContext QuicUsrContext;
+struct QuicUsrContext {
+ void (*error)(QuicUsrContext *usr, const char *fmt, ...);
+ void (*warn)(QuicUsrContext *usr, const char *fmt, ...);
+ void (*info)(QuicUsrContext *usr, const char *fmt, ...);
+ void *(*malloc)(QuicUsrContext *usr, int size);
+ void (*free)(QuicUsrContext *usr, void *ptr);
+ int (*more_space)(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed);
+ int (*more_lines)(QuicUsrContext *usr, uint8_t **lines); // on return the last line of previous
+ // lines bunch must stil be valid
+};
+
+int quic_encode(QuicContext *quic, QuicImageType type, int width, int height,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint32_t *io_ptr, unsigned int num_io_words);
+
+int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words,
+ QuicImageType *type, int *width, int *height);
+int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride);
+
+
+QuicContext *quic_create(QuicUsrContext *usr);
+void quic_destroy(QuicContext *quic);
+
+void quic_init();
+
+#endif
+
diff --git a/common/quic_config.h b/common/quic_config.h
new file mode 100644
index 00000000..ab418b13
--- /dev/null
+++ b/common/quic_config.h
@@ -0,0 +1,53 @@
+/*
+ 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/>.
+*/
+
+#ifndef __QUIC_CONFIG_H
+#define __QUIC_CONFIG_H
+
+#ifdef __GNUC__
+
+#include <stdint.h>
+#include <string.h>
+
+#define INLINE inline
+
+#define MEMCLEAR(ptr, size) memset(ptr, 0, size)
+
+#else
+
+#ifdef QXLDD
+#include <windef.h>
+#include "os_dep.h"
+#define INLINE _inline
+#define MEMCLEAR(ptr, size) RtlZeroMemory(ptr, size)
+#else
+#include <stddef.h>
+#include <basetsd.h>
+#include <string.h>
+
+#define INLINE inline
+#define MEMCLEAR(ptr, size) memset(ptr, 0, size)
+#endif
+
+typedef UINT32 uint32_t;
+typedef UINT16 uint16_t;
+typedef UINT8 uint8_t;
+
+#endif
+
+#endif
+
diff --git a/common/quic_family_tmpl.c b/common/quic_family_tmpl.c
new file mode 100644
index 00000000..58ae6935
--- /dev/null
+++ b/common/quic_family_tmpl.c
@@ -0,0 +1,114 @@
+/*
+ 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/>.
+*/
+
+#ifdef QUIC_FAMILY_8BPC
+#undef QUIC_FAMILY_8BPC
+#define FNAME(name) name##_8bpc
+#define VNAME(name) name##_8bpc
+#define BPC 8
+#endif
+
+
+#ifdef QUIC_FAMILY_5BPC
+#undef QUIC_FAMILY_5BPC
+#define FNAME(name) name##_5bpc
+#define VNAME(name) name##_5bpc
+#define BPC 5
+#endif
+
+
+static unsigned int FNAME(golomb_code_len)(const BYTE n, const unsigned int l)
+{
+ if (n < VNAME(family).nGRcodewords[l]) {
+ return (n >> l) + 1 + l;
+ } else {
+ return VNAME(family).notGRcwlen[l];
+ }
+}
+
+static void FNAME(golomb_coding)(const BYTE n, const unsigned int l, unsigned int * const codeword,
+ unsigned int * const codewordlen)
+{
+ if (n < VNAME(family).nGRcodewords[l]) {
+ (*codeword) = bitat[l] | (n & bppmask[l]);
+ (*codewordlen) = (n >> l) + l + 1;
+ } else {
+ (*codeword) = n - VNAME(family).nGRcodewords[l];
+ (*codewordlen) = VNAME(family).notGRcwlen[l];
+ }
+}
+
+unsigned int FNAME(golomb_decoding)(const unsigned int l, const unsigned int bits,
+ unsigned int * const codewordlen)
+{
+ if (bits > VNAME(family).notGRprefixmask[l]) { /*GR*/
+ const unsigned int zeroprefix = cnt_l_zeroes(bits); /* leading zeroes in codeword */
+ const unsigned int cwlen = zeroprefix + 1 + l; /* codeword length */
+ (*codewordlen) = cwlen;
+ return (zeroprefix << l) | ((bits >> (32 - cwlen)) & bppmask[l]);
+ } else { /* not-GR */
+ const unsigned int cwlen = VNAME(family).notGRcwlen[l];
+ (*codewordlen) = cwlen;
+ return VNAME(family).nGRcodewords[l] + ((bits) >> (32 - cwlen) &
+ bppmask[VNAME(family).notGRsuffixlen[l]]);
+ }
+}
+
+/* update the bucket using just encoded curval */
+static void FNAME(update_model)(CommonState *state, s_bucket * const bucket,
+ const BYTE curval, unsigned int bpp)
+{
+ COUNTER * const pcounters = bucket->pcounters;
+ unsigned int i;
+ unsigned int bestcode;
+ unsigned int bestcodelen;
+ //unsigned int bpp = encoder->bpp;
+
+ /* update counters, find minimum */
+
+ bestcode = bpp - 1;
+ bestcodelen = (pcounters[bestcode] += FNAME(golomb_code_len)(curval, bestcode));
+
+ for (i = bpp - 2; i < bpp; i--) { /* NOTE: expression i<bpp for signed int i would be: i>=0 */
+ const unsigned int ithcodelen = (pcounters[i] += FNAME(golomb_code_len)(curval, i));
+
+ if (ithcodelen < bestcodelen) {
+ bestcode = i;
+ bestcodelen = ithcodelen;
+ }
+ }
+
+ bucket->bestcode = bestcode; /* store the found minimum */
+
+ if (bestcodelen > state->wm_trigger) { /* halving counters? */
+ for (i = 0; i < bpp; i++) {
+ pcounters[i] >>= 1;
+ }
+ }
+}
+
+static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val)
+{
+ ASSERT(channel->encoder->usr, val < (0x1U << BPC));
+
+ return channel->_buckets_ptrs[val];
+}
+
+#undef FNAME
+#undef VNAME
+#undef BPC
+
diff --git a/common/quic_rgb_tmpl.c b/common/quic_rgb_tmpl.c
new file mode 100644
index 00000000..2007df68
--- /dev/null
+++ b/common/quic_rgb_tmpl.c
@@ -0,0 +1,762 @@
+/*
+ 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/>.
+*/
+
+#ifdef QUIC_RGB32
+#undef QUIC_RGB32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) quic_rgb32_##name
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+#define BPC 8
+#define BPC_MASK 0xffU
+#define COMPRESS_IMP
+#define SET_r(pix, val) ((pix)->r = val)
+#define GET_r(pix) ((pix)->r)
+#define SET_g(pix, val) ((pix)->g = val)
+#define GET_g(pix) ((pix)->g)
+#define SET_b(pix, val) ((pix)->b = val)
+#define GET_b(pix) ((pix)->b)
+#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0)
+#endif
+
+#ifdef QUIC_RGB24
+#undef QUIC_RGB24
+#define PIXEL rgb24_pixel_t
+#define FNAME(name) quic_rgb24_##name
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+#define BPC 8
+#define BPC_MASK 0xffU
+#define COMPRESS_IMP
+#define SET_r(pix, val) ((pix)->r = val)
+#define GET_r(pix) ((pix)->r)
+#define SET_g(pix, val) ((pix)->g = val)
+#define GET_g(pix) ((pix)->g)
+#define SET_b(pix, val) ((pix)->b = val)
+#define GET_b(pix) ((pix)->b)
+#define UNCOMPRESS_PIX_START(pix)
+#endif
+
+#ifdef QUIC_RGB16
+#undef QUIC_RGB16
+#define PIXEL rgb16_pixel_t
+#define FNAME(name) quic_rgb16_##name
+#define golomb_coding golomb_coding_5bpc
+#define golomb_decoding golomb_decoding_5bpc
+#define update_model update_model_5bpc
+#define find_bucket find_bucket_5bpc
+#define family family_5bpc
+#define BPC 5
+#define BPC_MASK 0x1fU
+#define COMPRESS_IMP
+#define SET_r(pix, val) (*(pix) = (*(pix) & ~(0x1f << 10)) | ((val) << 10))
+#define GET_r(pix) ((*(pix) >> 10) & 0x1f)
+#define SET_g(pix, val) (*(pix) = (*(pix) & ~(0x1f << 5)) | ((val) << 5))
+#define GET_g(pix) ((*(pix) >> 5) & 0x1f)
+#define SET_b(pix, val) (*(pix) = (*(pix) & ~0x1f) | (val))
+#define GET_b(pix) (*(pix) & 0x1f)
+#define UNCOMPRESS_PIX_START(pix) (*(pix) = 0)
+#endif
+
+#ifdef QUIC_RGB16_TO_32
+#undef QUIC_RGB16_TO_32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) quic_rgb16_to_32_##name
+#define golomb_coding golomb_coding_5bpc
+#define golomb_decoding golomb_decoding_5bpc
+#define update_model update_model_5bpc
+#define find_bucket find_bucket_5bpc
+#define family family_5bpc
+#define BPC 5
+#define BPC_MASK 0x1fU
+
+#define SET_r(pix, val) ((pix)->r = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_r(pix) ((pix)->r >> 3)
+#define SET_g(pix, val) ((pix)->g = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_g(pix) ((pix)->g >> 3)
+#define SET_b(pix, val) ((pix)->b = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_b(pix) ((pix)->b >> 3)
+#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0)
+#endif
+
+#define SAME_PIXEL(p1, p2) \
+ (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
+ GET_b(p1) == GET_b(p2))
+
+
+#define _PIXEL_A(channel, curr) ((unsigned int)GET_##channel((curr) - 1))
+#define _PIXEL_B(channel, prev) ((unsigned int)GET_##channel(prev))
+#define _PIXEL_C(channel, prev) ((unsigned int)GET_##channel((prev) - 1))
+
+/* a */
+
+#define DECORELATE_0(channel, curr, bpc_mask)\
+ family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)_PIXEL_A(channel, curr)) & bpc_mask]
+
+#define CORELATE_0(channel, curr, correlate, bpc_mask)\
+ ((family.xlatL2U[correlate] + _PIXEL_A(channel, curr)) & bpc_mask)
+
+#ifdef PRED_1
+
+/* (a+b)/2 */
+#define DECORELATE(channel, prev, curr, bpc_mask, r) \
+ r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)((_PIXEL_A(channel, curr) + \
+ _PIXEL_B(channel, prev)) >> 1)) & bpc_mask]
+
+#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) \
+ SET_##channel(r, ((family.xlatL2U[correlate] + \
+ (int)((_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev)) >> 1)) & bpc_mask))
+#endif
+
+#ifdef PRED_2
+
+/* .75a+.75b-.5c */
+#define DECORELATE(channel, prev, curr, bpc_mask, r) { \
+ int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \
+ (int)(_PIXEL_C(channel, prev) << 1)) >> 2; \
+ if (p < 0) { \
+ p = 0; \
+ } else if ((unsigned)p > bpc_mask) { \
+ p = bpc_mask; \
+ } \
+ r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - p) & bpc_mask]; \
+}
+
+#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) { \
+ const int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \
+ (int)(_PIXEL_C(channel, prev) << 1) ) >> 2; \
+ const unsigned int s = family.xlatL2U[correlate]; \
+ if (!(p & ~bpc_mask)) { \
+ SET_##channel(r, (s + (unsigned)p) & bpc_mask); \
+ } else if (p < 0) { \
+ SET_##channel(r, s); \
+ } else { \
+ SET_##channel(r, (s + bpc_mask) & bpc_mask); \
+ } \
+}
+
+#endif
+
+
+#define COMPRESS_ONE_ROW0_0(channel) \
+ correlate_row_##channel[0] = family.xlatU2L[GET_##channel(cur_row)]; \
+ golomb_coding(correlate_row_##channel[0], find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define COMPRESS_ONE_ROW0(channel, index) \
+ correlate_row_##channel[index] = DECORELATE_0(channel, &cur_row[index], bpc_mask); \
+ golomb_coding(correlate_row_##channel[index], find_bucket(channel_##channel, \
+ correlate_row_##channel[index -1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define UPDATE_MODEL(index) \
+ update_model(&encoder->rgb_state, find_bucket(channel_r, correlate_row_r[index - 1]), \
+ correlate_row_r[index], bpc); \
+ update_model(&encoder->rgb_state, find_bucket(channel_g, correlate_row_g[index - 1]), \
+ correlate_row_g[index], bpc); \
+ update_model(&encoder->rgb_state, find_bucket(channel_b, correlate_row_b[index - 1]), \
+ correlate_row_b[index], bpc);
+
+
+#ifdef RLE_PRED_1
+#define RLE_PRED_1_IMP \
+if (SAME_PIXEL(&cur_row[i - 1], &prev_row[i])) { \
+ if (run_index != i && SAME_PIXEL(&prev_row[i - 1], &prev_row[i]) && \
+ i + 1 < end && SAME_PIXEL(&prev_row[i], &prev_row[i + 1])) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_1_IMP
+#endif
+
+#ifdef RLE_PRED_2
+#define RLE_PRED_2_IMP \
+if (SAME_PIXEL(&prev_row[i - 1], &prev_row[i])) { \
+ if (run_index != i && i > 2 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2])) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_2_IMP
+#endif
+
+#ifdef RLE_PRED_3
+#define RLE_PRED_3_IMP \
+if (i > 1 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2]) && i != run_index) { \
+ goto do_run; \
+}
+#else
+#define RLE_PRED_3_IMP
+#endif
+
+#ifdef COMPRESS_IMP
+
+static void FNAME(compress_row0_seg)(Encoder *encoder, int i,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_ROW0_0(r);
+ COMPRESS_ONE_ROW0_0(g);
+ COMPRESS_ONE_ROW0_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ encoder->rgb_state.waitcnt--;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+ COMPRESS_ONE_ROW0(r, i);
+ COMPRESS_ONE_ROW0(g, i);
+ COMPRESS_ONE_ROW0(b, i);
+ }
+
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_ROW0(r, i);
+ COMPRESS_ONE_ROW0(g, i);
+ COMPRESS_ONE_ROW0(b, i);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+}
+
+static void FNAME(compress_row0)(Encoder *encoder, const PIXEL *cur_row,
+ unsigned int width)
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ width -= encoder->rgb_state.wmileft;
+ pos += encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#define COMPRESS_ONE_0(channel) \
+ correlate_row_##channel[0] = family.xlatU2L[(unsigned)((int)GET_##channel(cur_row) - \
+ (int)GET_##channel(prev_row) ) & bpc_mask]; \
+ golomb_coding(correlate_row_##channel[0], \
+ find_bucket(channel_##channel, correlate_row_##channel[-1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define COMPRESS_ONE(channel, index) \
+ DECORELATE(channel, &prev_row[index], &cur_row[index],bpc_mask, \
+ correlate_row_##channel[index]); \
+ golomb_coding(correlate_row_##channel[index], \
+ find_bucket(channel_##channel, correlate_row_##channel[index - 1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+static void FNAME(compress_row_seg)(Encoder *encoder, int i,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_size;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_0(r);
+ COMPRESS_ONE_0(g);
+ COMPRESS_ONE_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ encoder->rgb_state.waitcnt--;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ COMPRESS_ONE(r, i);
+ COMPRESS_ONE(g, i);
+ COMPRESS_ONE(b, i);
+ }
+
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ COMPRESS_ONE(r, i);
+ COMPRESS_ONE(g, i);
+ COMPRESS_ONE(b, i);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ run_index = i;
+ encoder->rgb_state.waitcnt = stopidx - i;
+ run_size = 0;
+
+ while (SAME_PIXEL(&cur_row[i], &cur_row[i - 1])) {
+ run_size++;
+ if (++i == end) {
+ encode_run(encoder, run_size);
+ return;
+ }
+ }
+ encode_run(encoder, run_size);
+ stopidx = i + encoder->rgb_state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(compress_row)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row,
+ pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx],
+ bpc, bpc_mask);
+ width -= encoder->rgb_state.wmileft;
+ pos += encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#endif
+
+#define UNCOMPRESS_ONE_ROW0_0(channel) \
+ correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ encoder->io_word, &codewordlen); \
+ SET_##channel(&cur_row[0], (BYTE)family.xlatL2U[correlate_row_##channel[0]]); \
+ decode_eatbits(encoder, codewordlen);
+
+#define UNCOMPRESS_ONE_ROW0(channel) \
+ correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[i - 1])->bestcode, \
+ encoder->io_word, \
+ &codewordlen); \
+ SET_##channel(&cur_row[i], CORELATE_0(channel, &cur_row[i], correlate_row_##channel[i], \
+ bpc_mask)); \
+ decode_eatbits(encoder, codewordlen);
+
+static void FNAME(uncompress_row0_seg)(Encoder *encoder, int i,
+ PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0_0(r);
+ UNCOMPRESS_ONE_ROW0_0(g);
+ UNCOMPRESS_ONE_ROW0_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ --encoder->rgb_state.waitcnt;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0(r);
+ UNCOMPRESS_ONE_ROW0(g);
+ UNCOMPRESS_ONE_ROW0(b);
+ }
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0(r);
+ UNCOMPRESS_ONE_ROW0(g);
+ UNCOMPRESS_ONE_ROW0(b);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+}
+
+static void FNAME(uncompress_row0)(Encoder *encoder,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(uncompress_row0_seg)(encoder, pos, cur_row,
+ pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx],
+ bpc, bpc_mask);
+ pos += encoder->rgb_state.wmileft;
+ width -= encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row0_seg)(encoder, pos, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#define UNCOMPRESS_ONE_0(channel) \
+ correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ encoder->io_word, &codewordlen); \
+ SET_##channel(&cur_row[0], (family.xlatL2U[correlate_row_##channel[0]] + \
+ GET_##channel(prev_row)) & bpc_mask); \
+ decode_eatbits(encoder, codewordlen);
+
+#define UNCOMPRESS_ONE(channel) \
+ correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[i - 1])->bestcode, \
+ encoder->io_word, \
+ &codewordlen); \
+ CORELATE(channel, &prev_row[i], &cur_row[i], correlate_row_##channel[i], bpc_mask, \
+ &cur_row[i]); \
+ decode_eatbits(encoder, codewordlen);
+
+static void FNAME(uncompress_row_seg)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ int i,
+ const int end,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ const unsigned int waitmask = bppmask[encoder->rgb_state.wmidx];
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_end;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_0(r);
+ UNCOMPRESS_ONE_0(g);
+ UNCOMPRESS_ONE_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ --encoder->rgb_state.waitcnt;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE(r);
+ UNCOMPRESS_ONE(g);
+ UNCOMPRESS_ONE(b);
+ }
+
+ UPDATE_MODEL(stopidx);
+
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE(r);
+ UNCOMPRESS_ONE(g);
+ UNCOMPRESS_ONE(b);
+ }
+
+ encoder->rgb_state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ encoder->rgb_state.waitcnt = stopidx - i;
+ run_index = i;
+ run_end = i + decode_run(encoder);
+
+ for (; i < run_end; i++) {
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ SET_r(&cur_row[i], GET_r(&cur_row[i - 1]));
+ SET_g(&cur_row[i], GET_g(&cur_row[i - 1]));
+ SET_b(&cur_row[i], GET_b(&cur_row[i - 1]));
+ }
+
+ if (i == end) {
+ return;
+ }
+
+ stopidx = i + encoder->rgb_state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(uncompress_row)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos,
+ pos + encoder->rgb_state.wmileft, bpc, bpc_mask);
+ pos += encoder->rgb_state.wmileft;
+ width -= encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos,
+ pos + width, bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#undef PIXEL
+#undef FNAME
+#undef _PIXEL_A
+#undef _PIXEL_B
+#undef _PIXEL_C
+#undef SAME_PIXEL
+#undef RLE_PRED_1_IMP
+#undef RLE_PRED_2_IMP
+#undef RLE_PRED_3_IMP
+#undef UPDATE_MODEL
+#undef DECORELATE_0
+#undef DECORELATE
+#undef COMPRESS_ONE_ROW0_0
+#undef COMPRESS_ONE_ROW0
+#undef COMPRESS_ONE_0
+#undef COMPRESS_ONE
+#undef CORELATE_0
+#undef CORELATE
+#undef UNCOMPRESS_ONE_ROW0_0
+#undef UNCOMPRESS_ONE_ROW0
+#undef UNCOMPRESS_ONE_0
+#undef UNCOMPRESS_ONE
+#undef golomb_coding
+#undef golomb_decoding
+#undef update_model
+#undef find_bucket
+#undef family
+#undef BPC
+#undef BPC_MASK
+#undef COMPRESS_IMP
+#undef SET_r
+#undef GET_r
+#undef SET_g
+#undef GET_g
+#undef SET_b
+#undef GET_b
+#undef UNCOMPRESS_PIX_START
+
diff --git a/common/quic_tmpl.c b/common/quic_tmpl.c
new file mode 100644
index 00000000..5a0a8e55
--- /dev/null
+++ b/common/quic_tmpl.c
@@ -0,0 +1,632 @@
+/*
+ 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/>.
+*/
+
+#ifdef ONE_BYTE
+#undef ONE_BYTE
+#define FNAME(name) quic_one_##name
+#define PIXEL one_byte_t
+#endif
+
+#ifdef THREE_BYTE
+#undef THREE_BYTE
+#define FNAME(name) quic_three_##name
+#define PIXEL three_bytes_t
+#endif
+
+#ifdef FOUR_BYTE
+#undef FOUR_BYTE
+#define FNAME(name) quic_four_##name
+#define PIXEL four_bytes_t
+#endif
+
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+
+#define BPC 8
+#define BPC_MASK 0xffU
+
+#define _PIXEL_A ((unsigned int)curr[-1].a)
+#define _PIXEL_B ((unsigned int)prev[0].a)
+#define _PIXEL_C ((unsigned int)prev[-1].a)
+
+#ifdef RLE_PRED_1
+#define RLE_PRED_1_IMP \
+if (cur_row[i - 1].a == prev_row[i].a) { \
+ if (run_index != i && prev_row[i - 1].a == prev_row[i].a && \
+ i + 1 < end && prev_row[i].a == prev_row[i + 1].a) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_1_IMP
+#endif
+
+#ifdef RLE_PRED_2
+#define RLE_PRED_2_IMP \
+if (prev_row[i - 1].a == prev_row[i].a) { \
+ if (run_index != i && i > 2 && cur_row[i - 1].a == cur_row[i - 2].a) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_2_IMP
+#endif
+
+#ifdef RLE_PRED_3
+#define RLE_PRED_3_IMP \
+if (i > 1 && cur_row[i - 1].a == cur_row[i - 2].a && i != run_index) { \
+ goto do_run; \
+}
+#else
+#define RLE_PRED_3_IMP
+#endif
+
+/* a */
+static INLINE BYTE FNAME(decorelate_0)(const PIXEL * const curr, const unsigned int bpc_mask)
+{
+ return family.xlatU2L[(unsigned)((int)curr[0].a - (int)_PIXEL_A) & bpc_mask];
+}
+
+static INLINE void FNAME(corelate_0)(PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ curr->a = (family.xlatL2U[corelate] + _PIXEL_A) & bpc_mask;
+}
+
+#ifdef PRED_1
+
+/* (a+b)/2 */
+static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr,
+ const unsigned int bpc_mask)
+{
+ return family.xlatU2L[(unsigned)((int)curr->a - (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask];
+}
+
+
+static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ curr->a = (family.xlatL2U[corelate] + (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask;
+}
+
+#endif
+
+#ifdef PRED_2
+
+/* .75a+.75b-.5c */
+static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr,
+ const unsigned int bpc_mask)
+{
+ int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2;
+
+ if (p < 0) {
+ p = 0;
+ } else if ((unsigned)p > bpc_mask) {
+ p = bpc_mask;
+ }
+
+ {
+ return family.xlatU2L[(unsigned)((int)curr->a - p) & bpc_mask];
+ }
+}
+
+static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ const int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2;
+ const unsigned int s = family.xlatL2U[corelate];
+
+ if (!(p & ~bpc_mask)) {
+ curr->a = (s + (unsigned)p) & bpc_mask;
+ } else if (p < 0) {
+ curr->a = s;
+ } else {
+ curr->a = (s + bpc_mask) & bpc_mask;
+ }
+}
+
+#endif
+
+static void FNAME(compress_row0_seg)(Encoder *encoder, Channel *channel, int i,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ BYTE * const decorelate_drow = channel->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codeword, codewordlen;
+
+ decorelate_drow[0] = family.xlatU2L[cur_row->a];
+ golomb_coding(decorelate_drow[0], find_bucket(channel, decorelate_drow[-1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+
+ if (channel->state.waitcnt) {
+ channel->state.waitcnt--;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]),
+ decorelate_drow[i], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+ decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i],
+ find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]),
+ decorelate_drow[stopidx], bpc);
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+ decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i], find_bucket(channel, decorelate_drow[i - 1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+}
+
+static void FNAME(compress_row0)(Encoder *encoder, Channel *channel, const PIXEL *cur_row,
+ unsigned int width)
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + channel->state.wmileft,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ width -= channel->state.wmileft;
+ pos += channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(compress_row_seg)(Encoder *encoder, Channel *channel, int i,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ BYTE * const decorelate_drow = channel->correlate_row;
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_size;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ decorelate_drow[0] = family.xlatU2L[(unsigned)((int)cur_row->a -
+ (int)prev_row->a) & bpc_mask];
+
+ golomb_coding(decorelate_drow[0],
+ find_bucket(channel, decorelate_drow[-1])->bestcode,
+ &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+
+ if (channel->state.waitcnt) {
+ channel->state.waitcnt--;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]),
+ decorelate_drow[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i],
+ find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]),
+ decorelate_drow[stopidx], bpc);
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i], find_bucket(channel,
+ decorelate_drow[i - 1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ run_index = i;
+ channel->state.waitcnt = stopidx - i;
+ run_size = 0;
+
+ while (cur_row[i].a == cur_row[i - 1].a) {
+ run_size++;
+ if (++i == end) {
+#ifdef RLE_STAT
+ encode_channel_run(encoder, channel, run_size);
+#else
+ encode_run(encoder, run_size);
+#endif
+ return;
+ }
+ }
+#ifdef RLE_STAT
+ encode_channel_run(encoder, channel, run_size);
+#else
+ encode_run(encoder, run_size);
+#endif
+ stopidx = i + channel->state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(compress_row)(Encoder *encoder, Channel *channel,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row,
+ pos + channel->state.wmileft, bppmask[channel->state.wmidx],
+ bpc, bpc_mask);
+ width -= channel->state.wmileft;
+ pos += channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(uncompress_row0_seg)(Encoder *encoder, Channel *channel, int i,
+ BYTE * const correlate_row,
+ PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codewordlen;
+
+ correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[-1])->bestcode,
+ encoder->io_word, &codewordlen);
+ cur_row[0].a = (BYTE)family.xlatL2U[correlate_row[0]];
+ decode_eatbits(encoder, codewordlen);
+
+ if (channel->state.waitcnt) {
+ --channel->state.waitcnt;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, correlate_row[-1]),
+ correlate_row[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ struct s_bucket * pbucket = NULL;
+
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+
+ pbucket = find_bucket(channel, correlate_row[i - 1]);
+ correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word,
+ &codewordlen);
+ FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ update_model(&channel->state, pbucket, correlate_row[stopidx], bpc);
+
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+
+ correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[i - 1])->bestcode,
+ encoder->io_word, &codewordlen);
+ FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+}
+
+static void FNAME(uncompress_row0)(Encoder *encoder, Channel *channel,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ BYTE * const correlate_row = channel->correlate_row;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row,
+ pos + channel->state.wmileft, bppmask[channel->state.wmidx],
+ bpc, bpc_mask);
+ pos += channel->state.wmileft;
+ width -= channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(uncompress_row_seg)(Encoder *encoder, Channel *channel,
+ BYTE *correlate_row,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ int i,
+ const int end,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ const unsigned int waitmask = bppmask[channel->state.wmidx];
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_end;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codewordlen;
+
+ correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel, correlate_row[-1])->bestcode,
+ encoder->io_word, &codewordlen);
+ cur_row[0].a = (family.xlatL2U[correlate_row[0]] + prev_row[0].a) & bpc_mask;
+ decode_eatbits(encoder, codewordlen);
+
+ if (channel->state.waitcnt) {
+ --channel->state.waitcnt;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, correlate_row[-1]),
+ correlate_row[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ struct s_bucket * pbucket = NULL;
+
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ pbucket = find_bucket(channel, correlate_row[i - 1]);
+ correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word,
+ &codewordlen);
+ FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ update_model(&channel->state, pbucket, correlate_row[stopidx], bpc);
+
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[i - 1])->bestcode,
+ encoder->io_word, &codewordlen);
+ FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ channel->state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ channel->state.waitcnt = stopidx - i;
+ run_index = i;
+#ifdef RLE_STAT
+ run_end = i + decode_channel_run(encoder, channel);
+#else
+ run_end = i + decode_run(encoder);
+#endif
+
+ for (; i < run_end; i++) {
+ cur_row[i].a = cur_row[i - 1].a;
+ }
+
+ if (i == end) {
+ return;
+ }
+
+ stopidx = i + channel->state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(uncompress_row)(Encoder *encoder, Channel *channel,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ BYTE * const correlate_row = channel->correlate_row;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos,
+ pos + channel->state.wmileft, bpc, bpc_mask);
+ pos += channel->state.wmileft;
+ width -= channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos,
+ pos + width, bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#undef PIXEL
+#undef FNAME
+#undef _PIXEL_A
+#undef _PIXEL_B
+#undef _PIXEL_C
+#undef RLE_PRED_1_IMP
+#undef RLE_PRED_2_IMP
+#undef RLE_PRED_3_IMP
+#undef golomb_coding
+#undef golomb_deoding
+#undef update_model
+#undef find_bucket
+#undef family
+#undef BPC
+#undef BPC_MASK
+
diff --git a/common/qxl_dev.h b/common/qxl_dev.h
new file mode 100644
index 00000000..bb8c7cb0
--- /dev/null
+++ b/common/qxl_dev.h
@@ -0,0 +1,332 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _H_QXL_DEV
+#define _H_QXL_DEV
+
+#include "ipc_ring.h"
+#include "draw.h"
+
+#ifdef __GNUC__
+#ifdef __i386__
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+#else
+//mfence
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%rsp)": : :"memory")
+#endif
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#define mb() __asm {lock add [esp], 0}
+#endif
+
+#define REDHAT_PCI_VENDOR_ID 0x1b36
+#define QXL_DEVICE_ID 0x0100 /* 0x100-0x11f reserved for spice */
+#define QXL_REVISION 0x01
+
+#define QXL_ROM_MAGIC (*(UINT32*)"QXRO")
+#define QXL_RAM_MAGIC (*(UINT32*)"QXRA")
+
+enum {
+ QXL_RAM_RANGE_INDEX,
+ QXL_VRAM_RANGE_INDEX,
+ QXL_ROM_RANGE_INDEX,
+ QXL_IO_RANGE_INDEX,
+
+ QXL_PCI_RANGES
+};
+
+enum {
+ QXL_IO_NOTIFY_CMD,
+ QXL_IO_NOTIFY_CURSOR,
+ QXL_IO_UPDATE_AREA,
+ QXL_IO_UPDATE_IRQ,
+ QXL_IO_NOTIFY_OOM,
+ QXL_IO_RESET,
+ QXL_IO_SET_MODE,
+ QXL_IO_LOG,
+
+ QXL_IO_RANGE_SIZE
+};
+
+typedef struct ATTR_PACKED QXLRom {
+ UINT32 magic;
+ UINT32 id;
+ UINT32 update_id;
+ UINT32 compression_level;
+ UINT32 log_level;
+ UINT32 mode;
+ UINT32 modes_offset;
+ UINT32 num_io_pages;
+ UINT32 pages_offset;
+ UINT32 draw_area_offset;
+ UINT32 draw_area_size;
+ UINT32 ram_header_offset;
+ UINT32 mm_clock;
+} QXLRom;
+
+typedef struct ATTR_PACKED QXLMode {
+ UINT32 id;
+ UINT32 x_res;
+ UINT32 y_res;
+ UINT32 bits;
+ UINT32 stride;
+ UINT32 x_mili;
+ UINT32 y_mili;
+ UINT32 orientation;
+} QXLMode;
+
+typedef struct ATTR_PACKED QXLModes {
+ UINT32 n_modes;
+ QXLMode modes[0];
+} QXLModes;
+
+typedef UINT64 PHYSICAL;
+typedef UINT32 QXLFIXED; //fixed 28.4
+
+enum QXLCmdType {
+ QXL_CMD_NOP,
+ QXL_CMD_DRAW,
+ QXL_CMD_UPDATE,
+ QXL_CMD_CURSOR,
+ QXL_CMD_MESSAGE,
+};
+
+typedef struct ATTR_PACKED QXLCommand {
+ PHYSICAL data;
+ UINT32 type;
+ UINT32 ped;
+} QXLCommand;
+
+
+RING_DECLARE(QXLCommandRing, QXLCommand, 32);
+RING_DECLARE(QXLCursorRing, QXLCommand, 32);
+
+RING_DECLARE(QXLReleaseRing, UINT64, 8);
+
+#define QXL_LOG_BUF_SIZE 4096
+
+#define QXL_INTERRUPT_DISPLAY (1 << 0)
+#define QXL_INTERRUPT_CURSOR (1 << 1)
+
+typedef struct ATTR_PACKED QXLRam {
+ UINT32 magic;
+ UINT32 int_pending;
+ UINT32 int_mask;
+ UINT8 log_buf[QXL_LOG_BUF_SIZE];
+ QXLCommandRing cmd_ring;
+ QXLCursorRing cursor_ring;
+ QXLReleaseRing release_ring;
+ Rect update_area;
+} QXLRam;
+
+typedef union QXLReleaseInfo {
+ UINT64 id; // in
+ UINT64 next; // out
+} QXLReleaseInfo;
+
+typedef struct ATTR_PACKED QXLDataChunk {
+ UINT32 data_size;
+ PHYSICAL prev_chunk;
+ PHYSICAL next_chunk;
+ UINT8 data[0];
+} QXLDataChunk;
+
+typedef struct ATTR_PACKED QXLMessage {
+ QXLReleaseInfo release_info;
+ UINT8 data[0];
+} QXLMessage;
+
+typedef struct ATTR_PACKED QXLUpdateCmd {
+ QXLReleaseInfo release_info;
+ Rect area;
+ UINT32 update_id;
+} QXLUpdateCmd;
+
+typedef struct ATTR_PACKED QXLCursor {
+ CursorHeader header;
+ UINT32 data_size;
+ QXLDataChunk chunk;
+} QXLCursor;
+
+enum {
+ QXL_CURSOR_SET,
+ QXL_CURSOR_MOVE,
+ QXL_CURSOR_HIDE,
+ QXL_CURSOR_TRAIL,
+};
+
+#define QXL_CURSUR_DEVICE_DATA_SIZE 128
+
+typedef struct ATTR_PACKED QXLCursorCmd {
+ QXLReleaseInfo release_info;
+ UINT8 type;
+ union {
+ struct ATTR_PACKED {
+ Point16 position;
+ UINT8 visible;
+ PHYSICAL shape;
+ } set;
+ struct ATTR_PACKED {
+ UINT16 length;
+ UINT16 frequency;
+ } trail;
+ Point16 position;
+ } u;
+ UINT8 device_data[QXL_CURSUR_DEVICE_DATA_SIZE]; //todo: dynamic size from rom
+} QXLCursorCmd;
+
+enum {
+ QXL_DRAW_NOP,
+ QXL_DRAW_FILL,
+ QXL_DRAW_OPAQUE,
+ QXL_DRAW_COPY,
+ QXL_COPY_BITS,
+ QXL_DRAW_BLEND,
+ QXL_DRAW_BLACKNESS,
+ QXL_DRAW_WHITENESS,
+ QXL_DRAW_INVERS,
+ QXL_DRAW_ROP3,
+ QXL_DRAW_STROKE,
+ QXL_DRAW_TEXT,
+ QXL_DRAW_TRANSPARENT,
+ QXL_DRAW_ALPHA_BLEND,
+};
+
+typedef struct ATTR_PACKED QXLString {
+ UINT32 data_size;
+ UINT16 length;
+ UINT16 flags;
+ QXLDataChunk chunk;
+} QXLString;
+
+typedef struct ATTR_PACKED QXLCopyBits {
+ Point src_pos;
+} QXLCopyBits;
+
+#define QXL_EFFECT_BLEND 0
+#define QXL_EFFECT_OPAQUE 1
+#define QXL_EFFECT_REVERT_ON_DUP 2
+#define QXL_EFFECT_BLACKNESS_ON_DUP 3
+#define QXL_EFFECT_WHITENESS_ON_DUP 4
+#define QXL_EFFECT_NOP_ON_DUP 5
+#define QXL_EFFECT_NOP 6
+#define QXL_EFFECT_OPAQUE_BRUSH 7
+
+typedef struct ATTR_PACKED QXLDrawable {
+ QXLReleaseInfo release_info;
+ UINT8 effect;
+ UINT8 type;
+ UINT16 bitmap_offset;
+ Rect bitmap_area;
+ Rect bbox;
+ Clip clip;
+ UINT32 mm_time;
+ union {
+ Fill fill;
+ Opaque opaque;
+ Copy copy;
+ Transparent transparent;
+ AlphaBlnd alpha_blend;
+ QXLCopyBits copy_bits;
+ Blend blend;
+ Rop3 rop3;
+ Stroke stroke;
+ Text text;
+ Blackness blackness;
+ Invers invers;
+ Whiteness whiteness;
+ } u;
+} QXLDrawable;
+
+typedef struct ATTR_PACKED QXLClipRects {
+ UINT32 num_rects;
+ QXLDataChunk chunk;
+} QXLClipRects;
+
+enum {
+ QXL_PATH_BEGIN = (1 << 0),
+ QXL_PATH_END = (1 << 1),
+ QXL_PATH_CLOSE = (1 << 3),
+ QXL_PATH_BEZIER = (1 << 4),
+};
+
+typedef struct ATTR_PACKED QXLPath {
+ UINT32 data_size;
+ QXLDataChunk chunk;
+} QXLPath;
+
+enum {
+ QXL_IMAGE_GROUP_DRIVER,
+ QXL_IMAGE_GROUP_DEVICE,
+ QXL_IMAGE_GROUP_RED,
+ QXL_IMAGE_GROUP_DRIVER_DONT_CACHE,
+};
+
+typedef struct ATTR_PACKED QXLImageID {
+ UINT32 group;
+ UINT32 unique;
+} QXLImageID;
+
+enum {
+ QXL_IMAGE_CACHE = (1 << 0),
+};
+
+enum {
+ QXL_BITMAP_DIRECT = (1 << 0),
+ QXL_BITMAP_UNSTABLE = (1 << 1),
+ QXL_BITMAP_TOP_DOWN = (1 << 2), // == BITMAP_TOP_DOWN
+};
+
+#define QXL_SET_IMAGE_ID(image, _group, _unique) { \
+ UINT64* id_ptr = &(image)->descriptor.id; \
+ QXLImageID *image_id = (QXLImageID *)id_ptr; \
+ image_id->group = _group; \
+ image_id->unique = _unique; \
+}
+
+typedef struct ATTR_PACKED QXLImage {
+ ImageDescriptor descriptor;
+ union { // variable length
+ Bitmap bitmap;
+ QUICData quic;
+ };
+} QXLImage;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+#endif
diff --git a/common/rect.h b/common/rect.h
new file mode 100644
index 00000000..569b530a
--- /dev/null
+++ b/common/rect.h
@@ -0,0 +1,116 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_RECT
+#define _H_RECT
+
+#include "draw.h"
+
+#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
+#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
+
+static inline void rect_sect(Rect* r, const Rect* bounds)
+{
+ r->left = MAX(r->left, bounds->left);
+ r->right = MIN(r->right, bounds->right);
+ r->right = MAX(r->left, r->right);
+
+ r->top = MAX(r->top, bounds->top);
+ r->bottom = MIN(r->bottom, bounds->bottom);
+ r->bottom = MAX(r->top, r->bottom);
+}
+
+static inline void rect_offset(Rect* r, int dx, int dy)
+{
+ r->left += dx;
+ r->right += dx;
+ r->top += dy;
+ r->bottom += dy;
+}
+
+static inline int rect_is_empty(const Rect* r)
+{
+ return r->top == r->bottom || r->left == r->right;
+}
+
+static inline int rect_intersects(const Rect* r1, const Rect* r2)
+{
+ return r1->left < r2->right && r1->right > r2->left &&
+ r1->top < r2->bottom && r1->bottom > r2->top;
+}
+
+static inline int rect_is_equal(const Rect *r1, const Rect *r2)
+{
+ return r1->top == r2->top && r1->left == r2->left &&
+ r1->bottom == r2->bottom && r1->right == r2->right;
+}
+
+static inline void rect_union(Rect *dest, const Rect *r)
+{
+ dest->top = MIN(dest->top, r->top);
+ dest->left = MIN(dest->left, r->left);
+ dest->bottom = MAX(dest->bottom, r->bottom);
+ dest->right = MAX(dest->right, r->right);
+}
+
+static inline int rect_is_same_size(const Rect *r1, const Rect *r2)
+{
+ return r1->right - r1->left == r2->right - r2->left &&
+ r1->bottom - r1->top == r2->bottom - r2->top;
+}
+
+#ifdef __cplusplus
+
+static inline void rect_sect(Rect& r, const Rect& bounds)
+{
+ rect_sect(&r, &bounds);
+}
+
+static inline void rect_offset(Rect& r, int dx, int dy)
+{
+ rect_offset(&r, dx, dy);
+}
+
+static inline int rect_is_empty(const Rect& r)
+{
+ return rect_is_empty(&r);
+}
+
+static inline int rect_intersects(const Rect& r1, const Rect& r2)
+{
+ return rect_intersects(&r1, &r2);
+}
+
+static inline int rect_is_equal(const Rect& r1, const Rect& r2)
+{
+ return rect_is_equal(&r1, &r2);
+}
+
+static inline void rect_union(Rect& dest, const Rect& r)
+{
+ rect_union(&dest, &r);
+}
+
+static inline int rect_is_same_size(const Rect& r1, const Rect& r2)
+{
+ return rect_is_same_size(&r1, &r2);
+}
+
+#endif
+
+#endif
+
diff --git a/common/red.h b/common/red.h
new file mode 100644
index 00000000..4b0a3642
--- /dev/null
+++ b/common/red.h
@@ -0,0 +1,685 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _H_RED
+#define _H_RED
+
+#include <stdint.h>
+#ifdef _WIN32
+#include <basetsd.h>
+#endif
+#include "draw.h"
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#endif
+
+#define RED_MAGIC (*(uint32_t*)"REDQ")
+#define RED_VERSION_MAJOR 1
+#define RED_VERSION_MINOR 0
+
+// Encryption & Ticketing Parameters
+#define RED_MAX_PASSWORD_LENGTH 60
+#define RED_TICKET_KEY_PAIR_LENGTH 1024
+#define RED_TICKET_PUBKEY_BYTES (RED_TICKET_KEY_PAIR_LENGTH / 8 + 34)
+
+enum {
+ RED_CHANNEL_MAIN = 1,
+ RED_CHANNEL_DISPLAY,
+ RED_CHANNEL_INPUTS,
+ RED_CHANNEL_CURSOR,
+ RED_CHANNEL_PLAYBACK,
+ RED_CHANNEL_RECORD,
+
+ RED_CHANNEL_END
+};
+
+enum {
+ RED_ERR_OK,
+ RED_ERR_ERROR,
+ RED_ERR_INVALID_MAGIC,
+ RED_ERR_INVALID_DATA,
+ RED_ERR_VERSION_MISMATCH,
+ RED_ERR_NEED_SECURED,
+ RED_ERR_NEED_UNSECURED,
+ RED_ERR_PERMISSION_DENIED,
+ RED_ERR_BAD_CONNECTION_ID,
+ RED_ERR_CHANNEL_NOT_AVAILABLE,
+};
+
+enum {
+ RED_WARN_GENERAL,
+};
+
+enum {
+ RED_INFO_GENERAL,
+};
+
+typedef struct ATTR_PACKED RedLinkHeader {
+ uint32_t magic;
+ uint32_t major_version;
+ uint32_t minor_version;
+ uint32_t size;
+} RedLinkHeader;
+
+typedef struct ATTR_PACKED RedLinkMess {
+ uint32_t connection_id;
+ uint8_t channel_type;
+ uint8_t channel_id;
+ uint32_t num_common_caps;
+ uint32_t num_channel_caps;
+ uint32_t caps_offset;
+} RedLinkMess;
+
+typedef struct ATTR_PACKED RedLinkReply {
+ uint32_t error;
+ uint8_t pub_key[RED_TICKET_PUBKEY_BYTES];
+ uint32_t num_common_caps;
+ uint32_t num_channel_caps;
+ uint32_t caps_offset;
+} RedLinkReply;
+
+typedef struct ATTR_PACKED RedLinkEncryptedTicket {
+ uint8_t encrypted_data[RED_TICKET_KEY_PAIR_LENGTH / 8];
+} RedLinkEncryptedTicket;
+
+typedef struct ATTR_PACKED RedDataHeader {
+ uint64_t serial;
+ uint16_t type;
+ uint32_t size;
+ uint32_t sub_list; //offset to RedSubMessageList[]
+} RedDataHeader;
+
+typedef struct ATTR_PACKED RedSubMessage {
+ uint16_t type;
+ uint32_t size;
+} RedSubMessage;
+
+typedef struct ATTR_PACKED RedSubMessageList {
+ uint16_t size;
+ uint32_t sub_messages[0]; //offsets to RedSubMessage
+} RedSubMessageList;
+
+enum {
+ RED_MIGRATE = 1,
+ RED_MIGRATE_DATA,
+ RED_SET_ACK,
+ RED_PING,
+ RED_WAIT_FOR_CHANNELS,
+ RED_DISCONNECTING,
+ RED_NOTIFY,
+
+ RED_FIRST_AVAIL_MESSAGE = 101
+};
+
+enum {
+ REDC_ACK_SYNC = 1,
+ REDC_ACK,
+ REDC_PONG,
+ REDC_MIGRATE_FLUSH_MARK,
+ REDC_MIGRATE_DATA,
+ REDC_DISCONNECTING,
+
+ REDC_FIRST_AVAIL_MESSAGE = 101,
+};
+
+enum {
+ RED_MIGRATE_BEGIN = RED_FIRST_AVAIL_MESSAGE,
+ RED_MIGRATE_CANCEL,
+ RED_INIT,
+ RED_CHANNELS_LIST,
+ RED_MOUSE_MODE,
+ RED_MULTI_MEDIA_TIME,
+
+ RED_AGENT_CONNECTED,
+ RED_AGENT_DISCONNECTED,
+ RED_AGENT_DATA,
+ RED_AGENT_TOKEN,
+
+ RED_MESSAGES_END,
+};
+
+enum {
+ REDC_CLIENT_INFO = REDC_FIRST_AVAIL_MESSAGE,
+ REDC_MIGRATE_CONNECTED,
+ REDC_MIGRATE_CONNECT_ERROR,
+ REDC_ATTACH_CHANNELS,
+ REDC_MOUSE_MODE_REQUEST,
+
+ REDC_AGENT_START,
+ REDC_AGENT_DATA,
+ REDC_AGENT_TOKEN,
+};
+
+#define RED_MOTION_ACK_BUNCH 4
+
+enum {
+ RED_INPUTS_INIT = RED_FIRST_AVAIL_MESSAGE,
+ RED_INPUTS_KEY_MODIFAIERS,
+
+ RED_INPUTS_MOUSE_MOTION_ACK = RED_FIRST_AVAIL_MESSAGE + 10,
+
+ RED_INPUTS_MESSAGES_END,
+};
+
+#define RED_SCROLL_LOCK_MODIFIER (1 << 0)
+#define RED_NUM_LOCK_MODIFIER (1 << 1)
+#define RED_CAPS_LOCK_MODIFIER (1 << 2)
+
+typedef struct ATTR_PACKED RedInputsInit {
+ uint32_t keyboard_modifiers;
+} RedInputsInit;
+
+typedef struct ATTR_PACKED RedKeyModifiers {
+ uint32_t modifiers;
+} RedKeyModifiers;
+
+typedef struct ATTR_PACKED RedMultiMediaTime {
+ uint32_t time;
+} RedMultiMediaTime;
+
+typedef struct ATTR_PACKED RedMigrationBegin {
+ uint16_t port;
+ uint16_t sport;
+ char host[0];
+} RedMigrationBegin;
+
+enum {
+ RED_MIGRATE_NEED_FLUSH = (1 << 0),
+ RED_MIGRATE_NEED_DATA_TRANSFER = (1 << 1),
+};
+
+typedef struct ATTR_PACKED RedMigrate {
+ uint32_t flags;
+} RedMigrate;
+
+enum {
+ RED_RES_TYPE_INVALID,
+ RED_RES_TYPE_PIXMAP,
+};
+
+typedef struct ATTR_PACKED RedResorceID {
+ uint8_t type;
+ uint64_t id;
+} RedResorceID;
+
+typedef struct ATTR_PACKED RedResorceList {
+ uint16_t count;
+ RedResorceID resorces[0];
+} RedResorceList;
+
+typedef struct ATTR_PACKED RedSetAck {
+ uint32_t generation;
+ uint32_t window;
+} RedSetAck;
+
+typedef struct ATTR_PACKED RedWaitForChannel {
+ uint8_t channel_type;
+ uint8_t channel_id;
+ uint64_t message_serial;
+} RedWaitForChannel;
+
+typedef struct ATTR_PACKED RedWaitForChannels {
+ uint8_t wait_count;
+ RedWaitForChannel wait_list[0];
+} RedWaitForChannels;
+
+typedef struct ATTR_PACKED RedChannelInit {
+ uint8_t type;
+ uint8_t id;
+} RedChannelInit;
+
+typedef struct ATTR_PACKED RedInit {
+ uint32_t session_id;
+ uint32_t display_channels_hint;
+ uint32_t supported_mouse_modes;
+ uint32_t current_mouse_mode;
+ uint32_t agent_connected;
+ uint32_t agent_tokens;
+ uint32_t multi_media_time;
+ uint32_t ram_hint;
+} RedInit;
+
+typedef struct ATTR_PACKED RedDisconnect {
+ uint64_t time_stamp;
+ uint32_t reason; // RED_ERR_?
+} RedDisconnect;
+
+enum {
+ RED_NOTIFY_SEVERITY_INFO,
+ RED_NOTIFY_SEVERITY_WARN,
+ RED_NOTIFY_SEVERITY_ERROR,
+};
+
+enum {
+ RED_NOTIFY_VISIBILITY_LOW,
+ RED_NOTIFY_VISIBILITY_MEDIUM,
+ RED_NOTIFY_VISIBILITY_HIGH,
+};
+
+typedef struct ATTR_PACKED RedNotify {
+ uint64_t time_stamp;
+ uint32_t severty;
+ uint32_t visibilty;
+ uint32_t what;
+ uint32_t message_len;
+ uint8_t message[0];
+} RedNotify;
+
+typedef struct ATTR_PACKED RedChannels {
+ uint32_t num_of_channels;
+ RedChannelInit channels[0];
+} RedChannels;
+
+typedef struct ATTR_PACKED RedMouseMode {
+ uint32_t supported_modes;
+ uint32_t current_mode;
+} RedMouseMode;
+
+typedef struct ATTR_PACKED RedPing {
+ uint32_t id;
+ uint64_t timestamp;
+} RedPing;
+
+typedef struct ATTR_PACKED RedAgentDisconnect {
+ uint32_t error_code; // RED_ERR_?
+} RedAgentDisconnect;
+
+#define RED_AGENT_MAX_DATA_SIZE 2048
+
+typedef struct ATTR_PACKED RedAgentTokens {
+ uint32_t num_tokens;
+} RedAgentTokens, RedcAgentTokens, RedcAgentStart;
+
+typedef struct ATTR_PACKED RedcClientInfo {
+ uint64_t cache_size;
+} RedcClientInfo;
+
+typedef struct ATTR_PACKED RedcMouseModeRequest {
+ uint32_t mode;
+} RedcMouseModeRequest;
+
+enum {
+ RED_DISPLAY_MODE = RED_FIRST_AVAIL_MESSAGE,
+ RED_DISPLAY_MARK,
+ RED_DISPLAY_RESET,
+ RED_DISPLAY_COPY_BITS,
+
+ RED_DISPLAY_INVAL_LIST,
+ RED_DISPLAY_INVAL_ALL_PIXMAPS,
+ RED_DISPLAY_INVAL_PALETTE,
+ RED_DISPLAY_INVAL_ALL_PALETTES,
+
+ RED_DISPLAY_STREAM_CREATE = RED_FIRST_AVAIL_MESSAGE + 21,
+ RED_DISPLAY_STREAM_DATA,
+ RED_DISPLAY_STREAM_CLIP,
+ RED_DISPLAY_STREAM_DESTROY,
+ RED_DISPLAY_STREAM_DESTROY_ALL,
+
+ RED_DISPLAY_DRAW_FILL = RED_FIRST_AVAIL_MESSAGE + 201,
+ RED_DISPLAY_DRAW_OPAQUE,
+ RED_DISPLAY_DRAW_COPY,
+ RED_DISPLAY_DRAW_BLEND,
+ RED_DISPLAY_DRAW_BLACKNESS,
+ RED_DISPLAY_DRAW_WHITENESS,
+ RED_DISPLAY_DRAW_INVERS,
+ RED_DISPLAY_DRAW_ROP3,
+ RED_DISPLAY_DRAW_STROKE,
+ RED_DISPLAY_DRAW_TEXT,
+ RED_DISPLAY_DRAW_TRANSPARENT,
+ RED_DISPLAY_DRAW_ALPHA_BLEND,
+
+ RED_DISPLAY_MESSAGES_END,
+};
+
+enum {
+ RED_CURSOR_NONE = (1 << 0),
+ RED_CURSOR_CACHE_ME = (1 << 1),
+ RED_CURSOR_FROM_CACHE = (1 << 2),
+};
+
+typedef struct ATTR_PACKED RedCursor {
+ uint32_t flags;
+ CursorHeader header;
+ uint8_t data[0];
+} RedCursor;
+
+typedef struct ATTR_PACKED RedMode {
+ uint32_t x_res;
+ uint32_t y_res;
+ uint32_t bits;
+} RedMode;
+
+typedef struct ATTR_PACKED RedDrawBase {
+ Rect box;
+ Clip clip;
+} RedDrawBase;
+
+typedef struct ATTR_PACKED RedFill {
+ RedDrawBase base;
+ Fill data;
+} RedFill;
+
+typedef struct ATTR_PACKED RedOpaque {
+ RedDrawBase base;
+ Opaque data;
+} RedOpaque;
+
+typedef struct ATTR_PACKED RedCopy {
+ RedDrawBase base;
+ Copy data;
+} RedCopy;
+
+typedef struct ATTR_PACKED RedTransparent {
+ RedDrawBase base;
+ Transparent data;
+} RedTransparent;
+
+typedef struct ATTR_PACKED RedAlphaBlend {
+ RedDrawBase base;
+ AlphaBlnd data;
+} RedAlphaBlend;
+
+typedef struct ATTR_PACKED RedCopyBits {
+ RedDrawBase base;
+ Point src_pos;
+} RedCopyBits;
+
+typedef RedCopy RedBlend;
+
+typedef struct ATTR_PACKED RedRop3 {
+ RedDrawBase base;
+ Rop3 data;
+} RedRop3;
+
+typedef struct ATTR_PACKED RedBlackness {
+ RedDrawBase base;
+ Blackness data;
+} RedBlackness;
+
+typedef struct ATTR_PACKED RedWhiteness {
+ RedDrawBase base;
+ Whiteness data;
+} RedWhiteness;
+
+typedef struct ATTR_PACKED RedInvers {
+ RedDrawBase base;
+ Invers data;
+} RedInvers;
+
+typedef struct ATTR_PACKED RedStroke {
+ RedDrawBase base;
+ Stroke data;
+} RedStroke;
+
+typedef struct ATTR_PACKED RedText {
+ RedDrawBase base;
+ Text data;
+} RedText;
+
+typedef struct ATTR_PACKED RedInvalOne {
+ uint64_t id;
+} RedInvalOne;
+
+enum {
+ RED_VIDEO_CODEC_TYPE_MJPEG = 1,
+};
+
+enum {
+ STREAM_TOP_DOWN = (1 << 0),
+};
+
+typedef struct ATTR_PACKED RedStreamCreate {
+ uint32_t id;
+ uint32_t flags;
+ uint32_t codec_type;
+ uint64_t stamp;
+ uint32_t stream_width;
+ uint32_t stream_height;
+ uint32_t src_width;
+ uint32_t src_height;
+ Rect dest;
+ Clip clip;
+} RedStreamCreate;
+
+typedef struct ATTR_PACKED RedStreamData {
+ uint32_t id;
+ uint32_t multi_media_time;
+ uint32_t data_size;
+ uint32_t ped_size;
+ uint8_t data[0];
+} RedStreamData;
+
+typedef struct ATTR_PACKED RedStreamClip {
+ uint32_t id;
+ Clip clip;
+} RedStreamClip;
+
+typedef struct ATTR_PACKED RedStreamDestroy {
+ uint32_t id;
+} RedStreamDestroy;
+
+enum {
+ RED_CURSOR_INIT = RED_FIRST_AVAIL_MESSAGE,
+ RED_CURSOR_RESET,
+ RED_CURSOR_SET,
+ RED_CURSOR_MOVE,
+ RED_CURSOR_HIDE,
+ RED_CURSOR_TRAIL,
+ RED_CURSOR_INVAL_ONE,
+ RED_CURSOR_INVAL_ALL,
+
+ RED_CURSOR_MESSAGES_END,
+};
+
+typedef struct ATTR_PACKED RedCursorInit {
+ Point16 position;
+ uint16_t trail_length;
+ uint16_t trail_frequency;
+ uint8_t visible;
+ RedCursor cursor;
+} RedCursorInit;
+
+typedef struct ATTR_PACKED RedCursorSet {
+ Point16 postition;
+ uint8_t visible;
+ RedCursor cursor;
+} RedCursorSet;
+
+typedef struct ATTR_PACKED RedCursorMove {
+ Point16 postition;
+} RedCursorMove;
+
+typedef struct ATTR_PACKED RedCursorTrail {
+ uint16_t length;
+ uint16_t frequency;
+} RedCursorTrail;
+
+enum {
+ REDC_DISPLAY_INIT = REDC_FIRST_AVAIL_MESSAGE,
+
+ REDC_DISPLAY_MESSGES_END,
+};
+
+typedef struct ATTR_PACKED RedcDisplayInit {
+ uint8_t pixmap_cache_id;
+ int64_t pixmap_cache_size; //in pixels
+ uint8_t glz_dictionary_id;
+ int glz_dictionary_window_size; // in pixels
+} RedcDisplayInit;
+
+enum {
+ REDC_INPUTS_KEY_DOWN = REDC_FIRST_AVAIL_MESSAGE,
+ REDC_INPUTS_KEY_UP,
+ REDC_INPUTS_KEY_MODIFAIERS,
+
+ REDC_INPUTS_MOUSE_MOTION = REDC_FIRST_AVAIL_MESSAGE + 10,
+ REDC_INPUTS_MOUSE_POSITION,
+ REDC_INPUTS_MOUSE_PRESS,
+ REDC_INPUTS_MOUSE_RELEASE,
+
+ REDC_INPUTS_MESSGES_END,
+};
+
+typedef struct ATTR_PACKED RedcKeyDown {
+ uint32_t code;
+} RedcKeyDown;
+
+typedef struct ATTR_PACKED RedcKeyUp {
+ uint32_t code;
+} RedcKeyUp;
+
+enum {
+ RED_MOUSE_MODE_SERVER = (1 << 0),
+ RED_MOUSE_MODE_CLIENT = (1 << 1),
+};
+
+typedef struct ATTR_PACKED RedcKeyModifiers {
+ uint32_t modifiers;
+} RedcKeyModifiers;
+
+enum RedButton {
+ REDC_MOUSE_INVALID_BUTTON,
+ REDC_MOUSE_LBUTTON,
+ REDC_MOUSE_MBUTTON,
+ REDC_MOUSE_RBUTTON,
+ REDC_MOUSE_UBUTTON,
+ REDC_MOUSE_DBUTTON,
+};
+
+#define REDC_LBUTTON_MASK (1 << (REDC_MOUSE_LBUTTON - 1))
+#define REDC_MBUTTON_MASK (1 << (REDC_MOUSE_MBUTTON - 1))
+#define REDC_RBUTTON_MASK (1 << (REDC_MOUSE_RBUTTON - 1))
+
+typedef struct ATTR_PACKED RedcMouseMotion {
+ int32_t dx;
+ int32_t dy;
+ uint32_t buttons_state;
+} RedcMouseMotion;
+
+typedef struct ATTR_PACKED RedcMousePosition {
+ uint32_t x;
+ uint32_t y;
+ uint32_t buttons_state;
+ uint8_t display_id;
+} RedcMousePosition;
+
+typedef struct ATTR_PACKED RedcMousePress {
+ int32_t button;
+ int32_t buttons_state;
+} RedcMousePress;
+
+typedef struct ATTR_PACKED RedcMouseRelease {
+ int32_t button;
+ int32_t buttons_state;
+} RedcMouseRelease;
+
+enum {
+ RED_AUDIO_FMT_INVALD,
+ RED_AUDIO_FMT_S16,
+};
+
+enum {
+ RED_AUDIO_DATA_MODE_INVALD,
+ RED_AUDIO_DATA_MODE_RAW,
+ RED_AUDIO_DATA_MODE_CELT_0_5_1,
+};
+
+enum {
+ RED_PLAYBACK_DATA = RED_FIRST_AVAIL_MESSAGE,
+ RED_PLAYBACK_MODE,
+ RED_PLAYBACK_START,
+ RED_PLAYBACK_STOP,
+
+ RED_PLAYBACK_MESSAGES_END,
+};
+
+enum {
+ RED_PLAYBACK_CAP_CELT_0_5_1,
+};
+
+enum {
+ RED_RECORD_START = RED_FIRST_AVAIL_MESSAGE,
+ RED_RECORD_STOP,
+
+ RED_RECORD_MESSAGES_END,
+};
+
+enum {
+ REDC_RECORD_DATA = RED_FIRST_AVAIL_MESSAGE,
+ REDC_RECORD_MODE,
+ REDC_RECORD_START_MARK,
+
+ REDC_RECORD_MESSAGES_END,
+};
+
+enum {
+ RED_RECORD_CAP_CELT_0_5_1,
+};
+
+typedef struct ATTR_PACKED RedPlaybackMode {
+ uint32_t time;
+ uint32_t mode; //RED_AUDIO_DATA_MODE_?
+ uint8_t data[0];
+} RedPlaybackMode, RedcRecordMode;
+
+typedef struct ATTR_PACKED RedPlaybackStart {
+ uint32_t channels;
+ uint32_t format; //RED_AUDIO_FMT_?
+ uint32_t frequency;
+ uint32_t time;
+} RedPlaybackStart;
+
+typedef struct ATTR_PACKED RedPlaybackPacket {
+ uint32_t time;
+ uint8_t data[0];
+} RedPlaybackPacket, RedcRecordPacket;
+
+typedef struct ATTR_PACKED RedRecordStart {
+ uint32_t channels;
+ uint32_t format; //RED_AUDIO_FMT_?
+ uint32_t frequency;
+} RedRecordStart;
+
+typedef struct ATTR_PACKED RedcRecordStartMark {
+ uint32_t time;
+} RedcRecordStartMark;
+
+#undef ATTR_PACKED
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#endif
+
diff --git a/common/red_error_codes.h b/common/red_error_codes.h
new file mode 100644
index 00000000..c08fd510
--- /dev/null
+++ b/common/red_error_codes.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef RED_ERROR_CODES_H
+#define RED_ERROR_CODES_H
+
+#define SPICEC_ERROR_CODE_SUCCESS (0)
+#define SPICEC_ERROR_CODE_ERROR (-1)
+#define SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED (-2)
+#define SPICEC_ERROR_CODE_CONNECT_FAILED (-3)
+#define SPICEC_ERROR_CODE_SOCKET_FAILED (-4)
+#define SPICEC_ERROR_CODE_SEND_FAILED (-5)
+#define SPICEC_ERROR_CODE_RECV_FAILED (-6)
+#define SPICEC_ERROR_CODE_SSL_ERROR (-7)
+#define SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY (-8)
+#define SPICEC_ERROR_CODE_AGENT_TIMEOUT (-9)
+#define SPICEC_ERROR_CODE_AGENT_ERROR (-10)
+#define SPICEC_ERROR_CODE_VERSION_MISMATCH (-11)
+#define SPICEC_ERROR_CODE_PERMISSION_DENIED (-12)
+#define SPICEC_ERROR_CODE_INVALID_ARG (-13)
+
+#endif
+
diff --git a/common/reds_stat.h b/common/reds_stat.h
new file mode 100644
index 00000000..0baa13f9
--- /dev/null
+++ b/common/reds_stat.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _H_REDS_STAT
+#define _H_REDS_STAT
+
+#include <stdint.h>
+
+#define REDS_STAT_SHM_NAME "spice.%u"
+#define REDS_STAT_NODE_NAME_MAX_LENGTH 20
+#define REDS_STAT_MAGIC (*(uint32_t*)"STAT")
+#define REDS_STAT_VERSION 1
+
+enum {
+ STAT_NODE_FLAG_ENABLED = (1 << 0),
+ STAT_NODE_FLAG_VISIBLE = (1 << 1),
+ STAT_NODE_FLAG_VALUE = (1 << 2),
+};
+
+#define STAT_NODE_MASK_SHOW (STAT_NODE_FLAG_ENABLED | STAT_NODE_FLAG_VISIBLE)
+#define STAT_NODE_MASK_SHOW_VALUE (STAT_NODE_MASK_SHOW | STAT_NODE_FLAG_VALUE)
+
+typedef struct StatNode {
+ uint64_t value;
+ uint32_t flags;
+ uint32_t next_sibling_index;
+ uint32_t first_child_index;
+ char name[REDS_STAT_NODE_NAME_MAX_LENGTH];
+} StatNode;
+
+typedef struct RedsStat {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t generation;
+ uint32_t num_of_nodes;
+ uint32_t root_index;
+ StatNode nodes[];
+} RedsStat;
+
+#endif
+
diff --git a/common/region.c b/common/region.c
new file mode 100644
index 00000000..d1d7f4a0
--- /dev/null
+++ b/common/region.c
@@ -0,0 +1,1230 @@
+/*
+ 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "region.h"
+#include "rect.h"
+
+//#define ALLOC_ON_STEAL
+//#define REGION_DEBUG
+
+#define FALSE 0
+#define TRUE 1
+
+#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
+#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
+
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+
+#ifdef REGION_DEBUG
+#define REGION_IS_VALID(region) region_is_valid(region)
+#else
+#define REGION_IS_VALID(region) TRUE
+#endif
+
+static int rect_is_valid(const Rect *r)
+{
+ if (r->top > r->bottom || r->left > r->right) {
+ printf("%s: invalid rect\n", __FUNCTION__);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef REGION_TEST
+static void rect_set(Rect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
+{
+ r->top = top;
+ r->left = left;
+ r->bottom = bottom;
+ r->right = right;
+ ASSERT(rect_is_valid(r));
+}
+
+#endif
+
+static inline void __region_init(QRegion *rgn)
+{
+ rgn->num_rects = 0;
+ rgn->rects = rgn->buf;
+ rgn->rects_size = RECTS_BUF_SIZE;
+}
+
+void region_init(QRegion *rgn)
+{
+ __region_init(rgn);
+ ASSERT(REGION_IS_VALID(rgn));
+}
+
+void region_clear(QRegion *rgn)
+{
+ rgn->num_rects = 0;
+}
+
+void region_destroy(QRegion *rgn)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ if (rgn->rects != rgn->buf) {
+ free(rgn->rects);
+ }
+}
+
+void region_clone(QRegion *dest, const QRegion *src)
+{
+ ASSERT(REGION_IS_VALID(src));
+ dest->bbox = src->bbox;
+ if ((dest->num_rects = src->num_rects) <= RECTS_BUF_SIZE) {
+ dest->rects = dest->buf;
+ dest->rects_size = RECTS_BUF_SIZE;
+ } else {
+ dest->rects = (Rect *)malloc(sizeof(Rect) * dest->num_rects);
+ dest->rects_size = dest->num_rects;
+ }
+ memcpy(dest->rects, src->rects, dest->num_rects * sizeof(Rect));
+ ASSERT(REGION_IS_VALID(src));
+ ASSERT(REGION_IS_VALID(dest));
+}
+
+int region_is_valid(const QRegion *rgn)
+{
+ if (rgn->num_rects) {
+ uint32_t i;
+ Rect bbox;
+
+ if (!rect_is_valid(&rgn->bbox)) {
+ return FALSE;
+ }
+ bbox = rgn->rects[0];
+ if (!rect_is_valid(&bbox) || rect_is_empty(&bbox)) {
+ return FALSE;
+ }
+ for (i = 1; i < rgn->num_rects; i++) {
+ Rect *r;
+
+ r = &rgn->rects[i];
+ if (!rect_is_valid(r) || rect_is_empty(r)) {
+ return FALSE;
+ }
+
+ Rect *priv = r - 1;
+ if (r->top < priv->top) {
+ return FALSE;
+ } else if (r->top == priv->top) {
+ if (r->bottom != priv->bottom) {
+ return FALSE;
+ }
+ if (r->left < priv->right) {
+ return FALSE;
+ }
+ } else if (priv->bottom > r->top) {
+ return FALSE;
+ }
+ bbox.top = MIN(bbox.top, r->top);
+ bbox.left = MIN(bbox.left, r->left);
+ bbox.bottom = MAX(bbox.bottom, r->bottom);
+ bbox.right = MAX(bbox.right, r->right);
+ }
+ return rect_is_equal(&bbox, &rgn->bbox);
+ }
+ return TRUE;
+}
+
+void region_dump(const QRegion *rgn, const char *prefix)
+{
+ char *indent;
+ int len;
+ uint32_t i;
+
+ len = strlen(prefix);
+ if (!(indent = (char *)malloc(len + 1))) {
+ printf("%s: malloc failed\n", __FUNCTION__);
+ return;
+ }
+ memset(indent, ' ', len);
+ indent[len] = 0;
+
+
+ printf("%sREGION: %p, size %u storage is %s, ",
+ prefix,
+ rgn,
+ rgn->rects_size,
+ (rgn->rects == rgn->buf) ? "BUF" : "MALLOC");
+
+ if (rgn->num_rects == 0) {
+ printf("EMPTY\n");
+ return;
+ }
+
+ printf("num %u bounds (%d, %d, %d, %d)\n",
+ rgn->num_rects,
+ rgn->bbox.top,
+ rgn->bbox.left,
+ rgn->bbox.bottom,
+ rgn->bbox.right);
+
+ for (i = 0; i < rgn->num_rects; i++) {
+ printf("%s %12d %12d %12d %12d\n",
+ indent,
+ rgn->rects[i].top,
+ rgn->rects[i].left,
+ rgn->rects[i].bottom,
+ rgn->rects[i].right);
+ }
+ free(indent);
+ ASSERT(region_is_valid(rgn));
+}
+
+int region_is_empty(const QRegion *rgn)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ return !rgn->num_rects;
+}
+
+#ifdef REGION_USE_IMPROVED
+
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
+{
+ int test_res;
+
+ ASSERT(REGION_IS_VALID(rgn1));
+ ASSERT(REGION_IS_VALID(rgn2));
+
+ if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
+ return rgn1->num_rects == rgn2->num_rects;
+ }
+
+ if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
+ return FALSE;
+ }
+
+ test_res = region_test(rgn1, rgn2, REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE);
+ return !test_res;
+}
+
+#else
+
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
+{
+ QRegion tmp_rgn;
+ int ret;
+
+ ASSERT(REGION_IS_VALID(rgn1));
+ ASSERT(REGION_IS_VALID(rgn2));
+
+ if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
+ return rgn1->num_rects == rgn2->num_rects;
+ }
+
+ if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
+ return FALSE;
+ }
+
+ region_clone(&tmp_rgn, rgn1);
+ region_xor(&tmp_rgn, rgn2);
+ ret = region_is_empty(&tmp_rgn);
+ region_destroy(&tmp_rgn);
+ return ret;
+}
+
+#endif
+
+typedef struct RgnOpCtx {
+ Rect *now;
+ Rect *end;
+ Rect *scan_line;
+ Rect r;
+ Rect split;
+#ifdef REGION_USE_IMPROVED
+ int abort;
+#endif
+} RgnOpCtx;
+
+static inline int op_ctx_is_valid(RgnOpCtx *ctx)
+{
+ return ctx->now != ctx->end;
+}
+
+static void op_context_next(RgnOpCtx *ctx)
+{
+ Rect *now;
+ Rect *next;
+
+ ASSERT(op_ctx_is_valid(ctx));
+ now = ctx->now;
+ next = now + 1;
+
+ if (next == ctx->end || now->top != next->top) {
+ if (now->bottom != ctx->r.bottom) { //h_split
+ ctx->r.top = ctx->r.bottom;
+ ctx->r.bottom = now->bottom;
+ next = ctx->scan_line;
+ } else {
+ if (next == ctx->end) {
+#ifdef REGION_USE_IMPROVED
+ ctx->scan_line = ++ctx->now;
+#else
+ ++ctx->now;
+#endif
+ ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
+ return;
+ }
+ ctx->scan_line = next;
+ ctx->r.top = next->top;
+ ctx->r.bottom = next->bottom;
+ }
+ }
+ ctx->r.left = next->left;
+ ctx->r.right = next->right;
+ ctx->now = next;
+}
+
+static void op_context_init(RgnOpCtx *ctx, uint32_t num_rects, Rect *rects)
+{
+ ctx->scan_line = ctx->now = rects;
+ ctx->end = ctx->now + num_rects;
+#ifdef REGION_USE_IMPROVED
+ ctx->abort = FALSE;
+#endif
+ if (!op_ctx_is_valid(ctx)) {
+ ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
+ } else {
+ ctx->r = *ctx->now;
+ }
+}
+
+static inline void op_ctx_h_split(RgnOpCtx *ctx, int32_t h_line)
+{
+ ctx->r.bottom = h_line;
+ ctx->split = ctx->r;
+ op_context_next(ctx);
+}
+
+static inline void op_ctx_v_split(RgnOpCtx *ctx, int32_t v_line)
+{
+ ctx->split = ctx->r;
+ ctx->r.left = ctx->split.right = v_line;
+ if (rect_is_empty(&ctx->r)) {
+ op_context_next(ctx);
+ }
+}
+
+static inline void op_ctx_split(RgnOpCtx *ctx, int32_t h_line)
+{
+ ASSERT(ctx->now == ctx->scan_line);
+ ctx->r.bottom = h_line;
+}
+
+static void region_steal_rects(QRegion *rgn, uint32_t *num_rects, Rect **rects)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ if ((*num_rects = rgn->num_rects)) {
+ if (rgn->rects == rgn->buf) {
+ *rects = (Rect *)malloc(sizeof(Rect) * rgn->num_rects);
+ memcpy(*rects, rgn->rects, sizeof(Rect) * rgn->num_rects);
+ } else {
+ *rects = rgn->rects;
+#ifdef ALLOC_ON_STEAL
+ rgn->rects = (Rect *)malloc(sizeof(Rect) * rgn->num_rects);
+ rgn->rects_size = rgn->num_rects;
+ rgn->num_rects = 0;
+ return;
+#endif
+ }
+ } else {
+ *rects = NULL;
+ }
+ __region_init(rgn);
+ ASSERT(REGION_IS_VALID(rgn));
+}
+
+typedef struct JoinContext {
+ QRegion *rgn;
+ Rect *line0;
+ Rect *line1;
+ Rect *end;
+} JoinContext;
+
+static inline Rect *__get_line(QRegion *rgn, Rect *pos)
+{
+ Rect *end = rgn->rects + rgn->num_rects;
+
+ if (pos < end) {
+ int32_t line_top = pos->top;
+ while (++pos < end && pos->top == line_top) {
+ ASSERT((pos - 1)->right < pos->left); //join in region_push_rect
+ }
+ }
+ return pos;
+}
+
+static inline int region_join_init(QRegion *rgn, JoinContext *context)
+{
+ context->rgn = rgn;
+ context->end = __get_line(rgn, (context->line1 = rgn->rects));
+ return context->end != context->line1;
+}
+
+static inline int region_join_next(JoinContext *context)
+{
+ context->line0 = context->line1;
+ context->line1 = context->end;
+ context->end = __get_line(context->rgn, context->line1);
+ return context->end != context->line1;
+}
+
+static inline void region_join_join(JoinContext *context)
+{
+ Rect *pos_0 = context->line0;
+ Rect *pos_1 = context->line1;
+ int32_t bottom;
+ QRegion *rgn;
+
+ if (pos_0->bottom != pos_1->top) {
+ return;
+ }
+
+ if (pos_1 - pos_0 != context->end - pos_1) {
+ return;
+ }
+
+ for (; pos_1 < context->end; pos_0++, pos_1++) {
+ if (pos_0->left != pos_1->left || pos_0->right != pos_1->right) {
+ return;
+ }
+ }
+ bottom = context->line1->bottom;
+ pos_0 = context->line0;
+ for (; pos_0 < context->line1; pos_0++) {
+ pos_0->bottom = bottom;
+ }
+ rgn = context->rgn;
+ memmove(context->line1, context->end,
+ (unsigned long)(rgn->rects + rgn->num_rects) - (unsigned long)context->end);
+ rgn->num_rects -= (context->line1 - context->line0);
+ context->end = context->line1;
+ context->line1 = context->line0;
+}
+
+static inline void region_join(QRegion *rgn)
+{
+ JoinContext context;
+
+ ASSERT(REGION_IS_VALID(rgn));
+
+ if (!region_join_init(rgn, &context)) {
+ return;
+ }
+ while (region_join_next(&context)) {
+ region_join_join(&context);
+ }
+
+ ASSERT(REGION_IS_VALID(rgn));
+}
+
+static void region_push_rect(QRegion *rgn, Rect *r)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(rect_is_valid(r));
+ if (rgn->num_rects == 0) {
+ rgn->num_rects++;
+ rgn->rects[0] = rgn->bbox = *r;
+ return;
+ } else {
+ Rect *priv = &rgn->rects[rgn->num_rects - 1];
+
+ if (priv->top == r->top && priv->right == r->left) {
+ ASSERT(priv->bottom == r->bottom);
+ priv->right = r->right;
+ rgn->bbox.right = MAX(rgn->bbox.right, priv->right);
+ return;
+ }
+ if (rgn->rects_size == rgn->num_rects) {
+ Rect *old = rgn->rects;
+ rgn->rects_size = rgn->rects_size * 2;
+ rgn->rects = (Rect *)malloc(sizeof(Rect) * rgn->rects_size);
+ memcpy(rgn->rects, old, sizeof(Rect) * rgn->num_rects);
+ if (old != rgn->buf) {
+ free(old);
+ }
+ }
+ rgn->rects[rgn->num_rects++] = *r;
+ rect_union(&rgn->bbox, r);
+ }
+}
+
+#ifdef REGION_USE_IMPROVED
+
+static Rect *op_context_find_area_below(RgnOpCtx *ctx, int32_t val)
+{
+ Rect *start = ctx->now;
+ Rect *end = ctx->end;
+
+ while (start != end) {
+ int pos = (end - start) / 2;
+ if (start[pos].bottom <= val) {
+ start = &start[pos + 1];
+ } else {
+ end = &start[pos];
+ }
+ }
+ return start;
+}
+
+static int op_context_skip_v(RgnOpCtx *ctx, int32_t top)
+{
+ Rect *end = op_context_find_area_below(ctx, top);
+ if (end != ctx->now) {
+ ctx->now = ctx->scan_line = end;
+ if (ctx->now == ctx->end) {
+ ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
+ } else {
+ ctx->r = *ctx->now;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+typedef void (*op_callback_t)(RgnOpCtx *context, Rect *, Rect *);
+
+static void op_context_skip(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
+ op_callback_t on_other)
+{
+ Rect *save1 = self->now;
+ Rect *save2 = other->now;
+ int more;
+ do {
+ op_context_skip_v(self, other->r.top);
+ if (save1 != self->now) {
+ if (on_self) {
+ on_self(self, save1, self->now);
+ }
+ save1 = self->now;
+ }
+ more = op_context_skip_v(other, self->r.top);
+ if (save2 != other->now) {
+ if (on_other) {
+ on_other(self, save2, other->now);
+ }
+ save2 = other->now;
+ }
+ } while (more && !self->abort);
+}
+
+static inline int op_context_more_overlap(RgnOpCtx *ctx, int32_t *bottom)
+{
+ if (!op_ctx_is_valid(ctx)) {
+ return FALSE;
+ }
+
+ if (ctx->scan_line->bottom > *bottom && ctx->scan_line->top < *bottom) {
+ *bottom = ctx->scan_line->bottom;
+ }
+ return ctx->scan_line->top < *bottom;
+}
+
+static inline void op_context_overlap(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
+ op_callback_t on_other, op_callback_t on_both)
+{
+ int32_t bottom = MAX(self->scan_line->bottom, other->scan_line->bottom);
+
+ do {
+ if (self->r.top < other->r.top) {
+ op_ctx_h_split(self, MIN(other->r.top, self->r.bottom));
+ if (on_self) {
+ on_self(self, &self->split, &self->split + 1);
+ }
+ } else if (self->r.top > other->r.top) {
+ op_ctx_h_split(other, MIN(self->r.top, other->r.bottom));
+ if (on_other) {
+ on_other(self, &other->split, &other->split + 1);
+ }
+ } else {
+ if (self->r.bottom > other->r.bottom) {
+ op_ctx_split(self, other->r.bottom);
+ } else if (other->r.bottom > self->r.bottom) {
+ op_ctx_split(other, self->r.bottom);
+ }
+ if (self->r.left < other->r.left) {
+ op_ctx_v_split(self, MIN(other->r.left, self->r.right));
+ if (on_self) {
+ on_self(self, &self->split, &self->split + 1);
+ }
+ } else if (self->r.left > other->r.left) {
+ op_ctx_v_split(other, MIN(self->r.left, other->r.right));
+ if (on_other) {
+ on_other(self, &other->split, &other->split + 1);
+ }
+ } else {
+ int32_t right = MIN(self->r.right, other->r.right);
+ op_ctx_v_split(self, right);
+ op_ctx_v_split(other, right);
+ if (on_both) {
+ on_both(self, &self->split, &self->split + 1);
+ }
+ }
+ }
+ } while (!self->abort && (op_context_more_overlap(self, &bottom) ||
+ op_context_more_overlap(other, &bottom)));
+}
+
+static inline void op_context_op(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
+ op_callback_t on_other, op_callback_t on_both)
+{
+ for (;;) {
+ op_context_skip(self, other, on_self, on_other);
+ if (self->abort || !op_ctx_is_valid(self)) {
+ ASSERT(self->abort || !op_ctx_is_valid(other));
+ return;
+ }
+ op_context_overlap(self, other, on_self, on_other, on_both);
+ }
+}
+
+typedef struct SelfOpCtx {
+ RgnOpCtx ctx;
+ QRegion *rgn;
+} SelfOpCtx;
+
+static void add_rects(RgnOpCtx *ctx, Rect *now, Rect *end)
+{
+ SelfOpCtx *self_ctx = (SelfOpCtx *)ctx;
+ for (; now < end; now++) {
+ region_push_rect(self_ctx->rgn, now);
+ }
+}
+
+static void region_op(QRegion *rgn, const QRegion *other_rgn, op_callback_t on_self,
+ op_callback_t on_other, op_callback_t on_both)
+{
+ SelfOpCtx self;
+ RgnOpCtx other;
+ uint32_t num_rects;
+ Rect *rects;
+
+ region_steal_rects(rgn, &num_rects, &rects);
+ op_context_init(&self.ctx, num_rects, rects);
+ self.rgn = rgn;
+ op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
+ op_context_op(&self.ctx, &other, on_self, on_other, on_both);
+ free(rects);
+ region_join(rgn);
+}
+
+void region_or(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, add_rects, add_rects, add_rects);
+}
+
+void region_and(QRegion *rgn, const QRegion *other_rgn)
+{
+ if (!region_bounds_intersects(rgn, other_rgn)) {
+ region_clear(rgn);
+ return;
+ }
+ region_op(rgn, other_rgn, NULL, NULL, add_rects);
+}
+
+void region_xor(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, add_rects, add_rects, NULL);
+}
+
+void region_exclude(QRegion *rgn, const QRegion *other_rgn)
+{
+ if (!region_bounds_intersects(rgn, other_rgn)) {
+ return;
+ }
+ region_op(rgn, other_rgn, add_rects, NULL, NULL);
+}
+
+typedef struct TestOpCtx {
+ RgnOpCtx ctx;
+ int result;
+ int abort_on;
+} TestOpCtx;
+
+
+static void region_test_on_self(RgnOpCtx *ctx, Rect *now, Rect *end)
+{
+ TestOpCtx *test_ctx = (TestOpCtx *)ctx;
+ test_ctx->result |= REGION_TEST_LEFT_EXCLUSIVE;
+ test_ctx->result &= test_ctx->abort_on;
+ if (test_ctx->result == test_ctx->abort_on) {
+ test_ctx->ctx.abort = TRUE;
+ }
+}
+
+static void region_test_on_other(RgnOpCtx *ctx, Rect *now, Rect *end)
+{
+ TestOpCtx *test_ctx = (TestOpCtx *)ctx;
+ test_ctx->result |= REGION_TEST_RIGHT_EXCLUSIVE;
+ test_ctx->result &= test_ctx->abort_on;
+ if (test_ctx->result == test_ctx->abort_on) {
+ test_ctx->ctx.abort = TRUE;
+ }
+}
+
+static void region_test_on_both(RgnOpCtx *ctx, Rect *now, Rect *end)
+{
+ TestOpCtx *test_ctx = (TestOpCtx *)ctx;
+ test_ctx->result |= REGION_TEST_SHARED;
+ test_ctx->result &= test_ctx->abort_on;
+ if (test_ctx->result == test_ctx->abort_on) {
+ test_ctx->ctx.abort = TRUE;
+ }
+}
+
+int region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
+{
+ TestOpCtx self;
+ RgnOpCtx other;
+
+ op_context_init(&self.ctx, rgn->num_rects, rgn->rects);
+ self.result = 0;
+ self.abort_on = (query) ? query & REGION_TEST_ALL : REGION_TEST_ALL;
+ op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
+ op_context_op(&self.ctx, &other, region_test_on_self, region_test_on_other,
+ region_test_on_both);
+ return self.result;
+}
+
+#else
+
+#define RIGION_OP_ADD_SELF (1 << 0)
+#define RIGION_OP_ADD_OTHER (1 << 1)
+#define RIGION_OP_ADD_COMMON (1 << 2)
+
+static inline void region_on_self(QRegion *rgn, Rect *r, uint32_t op)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ if (op & RIGION_OP_ADD_SELF) {
+ region_push_rect(rgn, r);
+ }
+}
+
+static inline void region_on_other(QRegion *rgn, Rect *r, uint32_t op)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ if (op & RIGION_OP_ADD_OTHER) {
+ region_push_rect(rgn, r);
+ }
+}
+
+static inline void region_on_both(QRegion *rgn, Rect *r, uint32_t op)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ if (op & RIGION_OP_ADD_COMMON) {
+ region_push_rect(rgn, r);
+ }
+}
+
+static void region_op(QRegion *rgn, const QRegion *other_rgn, uint32_t op)
+{
+ RgnOpCtx self;
+ RgnOpCtx other;
+ uint32_t num_rects;
+ Rect *rects;
+
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(REGION_IS_VALID(other_rgn));
+ region_steal_rects(rgn, &num_rects, &rects);
+
+ op_context_init(&self, num_rects, rects);
+ op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
+
+ while (op_ctx_is_valid(&self) || op_ctx_is_valid(&other)) {
+ if (self.r.top < other.r.top) {
+ op_ctx_h_split(&self, MIN(other.r.top, self.r.bottom));
+ region_on_self(rgn, &self.split, op);
+ } else if (self.r.top > other.r.top) {
+ op_ctx_h_split(&other, MIN(self.r.top, other.r.bottom));
+ region_on_other(rgn, &other.split, op);
+ } else {
+ if (self.r.bottom > other.r.bottom) {
+ op_ctx_split(&self, other.r.bottom);
+ } else if (other.r.bottom > self.r.bottom) {
+ op_ctx_split(&other, self.r.bottom);
+ }
+ if (self.r.left < other.r.left) {
+ op_ctx_v_split(&self, MIN(other.r.left, self.r.right));
+ region_on_self(rgn, &self.split, op);
+ } else if (self.r.left > other.r.left) {
+ op_ctx_v_split(&other, MIN(self.r.left, other.r.right));
+ region_on_other(rgn, &other.split, op);
+ } else {
+ int32_t right = MIN(self.r.right, other.r.right);
+ op_ctx_v_split(&self, right);
+ op_ctx_v_split(&other, right);
+ region_on_both(rgn, &self.split, op);
+ }
+ }
+ }
+ free(rects);
+ region_join(rgn);
+}
+
+void region_or(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER | RIGION_OP_ADD_COMMON);
+}
+
+void region_and(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, RIGION_OP_ADD_COMMON);
+}
+
+void region_xor(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER);
+}
+
+void region_exclude(QRegion *rgn, const QRegion *other_rgn)
+{
+ region_op(rgn, other_rgn, RIGION_OP_ADD_SELF);
+}
+
+#endif
+
+
+void region_offset(QRegion *rgn, int32_t dx, int32_t dy)
+{
+ Rect *now;
+ Rect *end;
+ ASSERT(REGION_IS_VALID(rgn));
+ if (region_is_empty(rgn)) {
+ return;
+ }
+ rect_offset(&rgn->bbox, dx, dy);
+ now = rgn->rects;
+ end = now + rgn->num_rects;
+ for (; now < end; now++) {
+ rect_offset(now, dx, dy);
+ }
+}
+
+void region_add(QRegion *rgn, const Rect *r)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(rect_is_valid(r));
+
+ if (!rgn->num_rects) {
+ if (rect_is_empty(r)) {
+ return;
+ }
+ rgn->num_rects++;
+ rgn->rects[0] = rgn->bbox = *r;
+ } else {
+ QRegion rect_rgn;
+ region_init(&rect_rgn);
+ region_add(&rect_rgn, r);
+ region_or(rgn, &rect_rgn);
+ }
+}
+
+void region_remove(QRegion *rgn, const Rect *r)
+{
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(rect_is_valid(r));
+ if (rgn->num_rects) {
+ QRegion rect_rgn;
+
+ region_init(&rect_rgn);
+ region_add(&rect_rgn, r);
+ region_exclude(rgn, &rect_rgn);
+ }
+}
+
+#ifdef REGION_USE_IMPROVED
+
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
+{
+ int test_res;
+
+ ASSERT(REGION_IS_VALID(rgn1));
+ ASSERT(REGION_IS_VALID(rgn2));
+
+ if (!region_bounds_intersects(rgn1, rgn2)) {
+ return FALSE;
+ }
+
+ test_res = region_test(rgn1, rgn2, REGION_TEST_SHARED);
+ return !!test_res;
+}
+
+#else
+
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
+{
+ QRegion tmp;
+ int ret;
+
+ ASSERT(REGION_IS_VALID(rgn1));
+ ASSERT(REGION_IS_VALID(rgn2));
+
+ region_clone(&tmp, rgn1);
+ region_and(&tmp, rgn2);
+ ret = !region_is_empty(&tmp);
+ region_destroy(&tmp);
+ return ret;
+}
+
+#endif
+
+int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
+{
+ return !region_is_empty(rgn1) && !region_is_empty(rgn2) &&
+ rect_intersects(&rgn1->bbox, &rgn2->bbox);
+}
+
+#ifdef REGION_USE_IMPROVED
+
+int region_contains(const QRegion *rgn, const QRegion *other)
+{
+ int test_res;
+
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(REGION_IS_VALID(other));
+
+ test_res = region_test(rgn, other, REGION_TEST_RIGHT_EXCLUSIVE);
+ return !test_res;
+}
+
+#else
+
+int region_contains(const QRegion *rgn, const QRegion *other)
+{
+ QRegion tmp;
+ int ret;
+
+ ASSERT(REGION_IS_VALID(rgn));
+ ASSERT(REGION_IS_VALID(other));
+
+ region_clone(&tmp, rgn);
+ region_and(&tmp, other);
+ ret = region_is_equal(&tmp, other);
+ region_destroy(&tmp);
+ return ret;
+}
+
+#endif
+
+int region_contains_point(const QRegion *rgn, int32_t x, int32_t y)
+{
+ if (region_is_empty(rgn)) {
+ return FALSE;
+ }
+ Rect point;
+ point.left = x;
+ point.right = point.left + 1;
+ point.top = y;
+ point.bottom = point.top + 1;
+
+ if (!rect_intersects(&rgn->bbox, &point)) {
+ return FALSE;
+ }
+
+ Rect* now = rgn->rects;
+ Rect* end = now + rgn->num_rects;
+
+ for (; now < end; now++) {
+ if (rect_intersects(now, &point)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#ifdef REGION_TEST
+
+static void test(const QRegion *r1, const QRegion *r2, int *expected)
+{
+ printf("r1 is_empty %s [%s]\n",
+ region_is_empty(r1) ? "TRUE" : "FALSE",
+ (region_is_empty(r1) == *(expected++)) ? "OK" : "ERR");
+ printf("r2 is_empty %s [%s]\n",
+ region_is_empty(r2) ? "TRUE" : "FALSE",
+ (region_is_empty(r2) == *(expected++)) ? "OK" : "ERR");
+ printf("is_equal %s [%s]\n",
+ region_is_equal(r1, r2) ? "TRUE" : "FALSE",
+ (region_is_equal(r1, r2) == *(expected++)) ? "OK" : "ERR");
+ printf("intersects %s [%s]\n",
+ region_intersects(r1, r2) ? "TRUE" : "FALSE",
+ (region_intersects(r1, r2) == *(expected++)) ? "OK" : "ERR");
+ printf("contains %s [%s]\n",
+ region_contains(r1, r2) ? "TRUE" : "FALSE",
+ (region_contains(r1, r2) == *(expected++)) ? "OK" : "ERR");
+}
+
+enum {
+ EXPECT_R1_EMPTY,
+ EXPECT_R2_EMPTY,
+ EXPECT_EQUAL,
+ EXPECT_SECT,
+ EXPECT_CONT,
+};
+
+int main(void)
+{
+ QRegion _r1, _r2, _r3;
+ QRegion *r1 = &_r1;
+ QRegion *r2 = &_r2;
+ QRegion *r3 = &_r3;
+ Rect _r;
+ Rect *r = &_r;
+ int expected[5];
+
+ region_init(r1);
+ region_init(r2);
+
+ printf("dump r1 empty rgn [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clone(r3, r1);
+ printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
+ region_dump(r3, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r3, expected);
+ region_destroy(r3);
+ printf("\n");
+
+ rect_set(r, 0, 0, 100, 100);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r1);
+ rect_set(r, 0, 0, 0, 0);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, -100, -100, 0, 0);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r1);
+ rect_set(r, -100, -100, 100, 100);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+
+ region_clear(r1);
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, 300, 300, 400, 400);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, 500, 500, 600, 600);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r2, r);
+ rect_set(r, 300, 300, 400, 400);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, -2000, -2000, -1000, -1000);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, -2000, -2000, 1000, 1000);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 150, 150, 175, 175);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 150, 150, 350, 350);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_and(r2, r1);
+ printf("dump r2 and r1 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r2, r1, expected);
+ printf("\n");
+
+
+ region_clone(r3, r1);
+ printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
+ region_dump(r3, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r3, expected);
+ printf("\n");
+
+
+ region_destroy(r3);
+ region_destroy(r1);
+ region_destroy(r2);
+
+ return 0;
+}
+
+#endif
+
diff --git a/common/region.h b/common/region.h
new file mode 100644
index 00000000..29a58223
--- /dev/null
+++ b/common/region.h
@@ -0,0 +1,75 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_REGION
+#define _H_REGION
+
+#include <stdint.h>
+#include "draw.h"
+
+#define REGION_USE_IMPROVED
+
+#define RECTS_BUF_SIZE 4
+
+typedef struct QRegion {
+ uint32_t num_rects;
+ Rect bbox;
+ Rect *rects;
+ uint32_t rects_size;
+ Rect buf[RECTS_BUF_SIZE];
+} QRegion;
+
+#ifdef REGION_USE_IMPROVED
+
+#define REGION_TEST_LEFT_EXCLUSIVE (1 << 0)
+#define REGION_TEST_RIGHT_EXCLUSIVE (1 << 1)
+#define REGION_TEST_SHARED (1 << 2)
+#define REGION_TEST_ALL \
+ (REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED)
+
+#endif
+
+void region_init(QRegion *rgn);
+void region_clear(QRegion *rgn);
+void region_destroy(QRegion *rgn);
+void region_clone(QRegion *dest, const QRegion *src);
+
+#ifdef REGION_USE_IMPROVED
+int region_test(const QRegion *rgn, const QRegion *other_rgn, int query);
+#endif
+int region_is_valid(const QRegion *rgn);
+int region_is_empty(const QRegion *rgn);
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2);
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2);
+int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2);
+int region_contains(const QRegion *rgn, const QRegion *other);
+int region_contains_point(const QRegion *rgn, int32_t x, int32_t y);
+
+void region_or(QRegion *rgn, const QRegion *other_rgn);
+void region_and(QRegion *rgn, const QRegion *other_rgn);
+void region_xor(QRegion *rgn, const QRegion *other_rgn);
+void region_exclude(QRegion *rgn, const QRegion *other_rgn);
+
+void region_add(QRegion *rgn, const Rect *r);
+void region_remove(QRegion *rgn, const Rect *r);
+
+void region_offset(QRegion *rgn, int32_t dx, int32_t dy);
+
+void region_dump(const QRegion *rgn, const char *prefix);
+
+#endif
+
diff --git a/common/ring.h b/common/ring.h
new file mode 100644
index 00000000..4c177132
--- /dev/null
+++ b/common/ring.h
@@ -0,0 +1,132 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_RING2
+#define _H_RING2
+
+typedef struct Ring RingItem;
+typedef struct Ring {
+ RingItem *prev;
+ RingItem *next;
+} Ring;
+
+static inline void ring_init(Ring *ring)
+{
+ ring->next = ring->prev = ring;
+}
+
+static inline void ring_item_init(RingItem *item)
+{
+ item->next = item->prev = NULL;
+}
+
+static inline int ring_item_is_linked(RingItem *item)
+{
+ return !!item->next;
+}
+
+static inline int ring_is_empty(Ring *ring)
+{
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ return ring == ring->next;
+}
+
+static inline void ring_add(Ring *ring, RingItem *item)
+{
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(item->next == NULL && item->prev == NULL);
+
+ item->next = ring->next;
+ item->prev = ring;
+ ring->next = item->next->prev = item;
+}
+
+static inline void ring_add_after(RingItem *item, RingItem *pos)
+{
+ ring_add(pos, item);
+}
+
+static inline void ring_add_before(RingItem *item, RingItem *pos)
+{
+ ring_add(pos->prev, item);
+}
+
+static inline void __ring_remove(RingItem *item)
+{
+ item->next->prev = item->prev;
+ item->prev->next = item->next;
+ item->prev = item->next = 0;
+}
+
+static inline void ring_remove(RingItem *item)
+{
+ ASSERT(item->next != NULL && item->prev != NULL);
+ ASSERT(item->next != item);
+
+ __ring_remove(item);
+}
+
+static inline RingItem *ring_get_head(Ring *ring)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+
+ if (ring_is_empty(ring)) {
+ return NULL;
+ }
+ ret = ring->next;
+ return ret;
+}
+
+static inline RingItem *ring_get_tail(Ring *ring)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+
+ if (ring_is_empty(ring)) {
+ return NULL;
+ }
+ ret = ring->prev;
+ return ret;
+}
+
+static inline RingItem *ring_next(Ring *ring, RingItem *pos)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(pos);
+ ASSERT(pos->next != NULL && pos->prev != NULL);
+ ret = pos->next;
+ return (ret == ring) ? NULL : ret;
+}
+
+static inline RingItem *ring_prev(Ring *ring, RingItem *pos)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(pos);
+ ASSERT(pos->next != NULL && pos->prev != NULL);
+ ret = pos->prev;
+ return (ret == ring) ? NULL : ret;
+}
+
+#endif
+
diff --git a/common/rop3.c b/common/rop3.c
new file mode 100644
index 00000000..08f9cad3
--- /dev/null
+++ b/common/rop3.c
@@ -0,0 +1,613 @@
+/*
+ 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 <stdio.h>
+
+#include "rop3.h"
+
+#ifndef WARN
+#define WARN(x) printf("warning: %s\n", x)
+#endif
+
+typedef void (*rop3_with_pattern_handler_t)(cairo_surface_t *d, cairo_surface_t *s,
+ Point *src_pos, cairo_surface_t *p,
+ Point *pat_pos);
+
+typedef void (*rop3_with_color_handler_t)(cairo_surface_t *d, cairo_surface_t *s,
+ Point *src_pos, uint32_t rgb);
+
+typedef void (*rop3_test_handler_t)();
+
+#define ROP3_NUM_OPS 256
+
+static rop3_with_pattern_handler_t rop3_with_pattern_handlers[ROP3_NUM_OPS];
+static rop3_with_color_handler_t rop3_with_color_handlers[ROP3_NUM_OPS];
+static rop3_test_handler_t rop3_test_handlers[ROP3_NUM_OPS];
+
+
+static void default_rop3_with_pattern_handler(cairo_surface_t *d, cairo_surface_t *s,
+ Point *src_pos, cairo_surface_t *p,
+ Point *pat_pos)
+{
+ WARN("not implemented 0x%x");
+}
+
+static void default_rop3_withe_color_handler(cairo_surface_t *d, cairo_surface_t *s, Point *src_pos,
+ uint32_t rgb)
+{
+ WARN("not implemented 0x%x");
+}
+
+static void default_rop3_test_handler()
+{
+}
+
+#define ROP3_HANDLERS(name, formula, index) \
+static void rop3_handle_p_##name(cairo_surface_t *d, cairo_surface_t *s, Point *src_pos, \
+ cairo_surface_t *p, Point *pat_pos) \
+{ \
+ int width = cairo_image_surface_get_width(d); \
+ int height = cairo_image_surface_get_height(d); \
+ uint8_t *dest_line = cairo_image_surface_get_data(d); \
+ int dest_stride = cairo_image_surface_get_stride(d); \
+ uint8_t *end_line = dest_line + height * dest_stride; \
+ \
+ int pat_width = cairo_image_surface_get_width(p); \
+ int pat_height = cairo_image_surface_get_height(p); \
+ uint8_t *pat_base = cairo_image_surface_get_data(p); \
+ int pat_stride = cairo_image_surface_get_stride(p); \
+ int pat_v_offset = pat_pos->y; \
+ \
+ int src_stride = cairo_image_surface_get_stride(s); \
+ uint8_t *src_line; \
+ src_line = cairo_image_surface_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2); \
+ \
+ for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) { \
+ uint32_t *dest = (uint32_t *)dest_line; \
+ uint32_t *end = dest + width; \
+ uint32_t *src = (uint32_t *)src_line; \
+ \
+ int pat_h_offset = pat_pos->x; \
+ \
+ for (; dest < end; dest++, src++) { \
+ uint32_t *pat; \
+ pat = (uint32_t *)(pat_base + pat_v_offset * pat_stride + (pat_h_offset << 2)); \
+ *dest = formula; \
+ pat_h_offset = (pat_h_offset + 1) % pat_width; \
+ } \
+ \
+ pat_v_offset = (pat_v_offset + 1) % pat_height; \
+ } \
+} \
+ \
+static void rop3_handle_c_##name(cairo_surface_t *d, cairo_surface_t *s, Point *src_pos, \
+ uint32_t rgb) \
+{ \
+ int width = cairo_image_surface_get_width(d); \
+ int height = cairo_image_surface_get_height(d); \
+ uint8_t *dest_line = cairo_image_surface_get_data(d); \
+ int dest_stride = cairo_image_surface_get_stride(d); \
+ uint8_t *end_line = dest_line + height * dest_stride; \
+ uint32_t *pat = &rgb; \
+ \
+ int src_stride = cairo_image_surface_get_stride(s); \
+ uint8_t *src_line; \
+ src_line = cairo_image_surface_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2); \
+ \
+ for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) { \
+ uint32_t *dest = (uint32_t *)dest_line; \
+ uint32_t *end = dest + width; \
+ uint32_t *src = (uint32_t *)src_line; \
+ for (; dest < end; dest++, src++) { \
+ *dest = formula; \
+ } \
+ } \
+} \
+ \
+static void rop3_test_##name() \
+{ \
+ uint8_t d = 0xaa; \
+ uint8_t s = 0xcc; \
+ uint8_t p = 0xf0; \
+ uint8_t *pat = &p; \
+ uint8_t *src = &s; \
+ uint8_t *dest = &d; \
+ \
+ d = formula; \
+ if (d != index) { \
+ printf("%s: failed, result is 0x%x expect 0x%x\n", __FUNCTION__, d, index); \
+ } \
+}
+
+ROP3_HANDLERS(DPSoon, ~(*pat | *src | *dest), 0x01);
+ROP3_HANDLERS(DPSona, ~(*pat | *src) & *dest, 0x02);
+ROP3_HANDLERS(SDPona, ~(*pat | *dest) & *src, 0x04);
+ROP3_HANDLERS(PDSxnon, ~(~(*src ^ *dest) | *pat), 0x06);
+ROP3_HANDLERS(PDSaon, ~((*src & *dest) | *pat), 0x07);
+ROP3_HANDLERS(SDPnaa, ~*pat & *dest & *src, 0x08);
+ROP3_HANDLERS(PDSxon, ~((*src ^ *dest) | *pat), 0x09);
+ROP3_HANDLERS(PSDnaon, ~((~*dest & *src) | *pat), 0x0b);
+ROP3_HANDLERS(PDSnaon, ~((~*src & *dest) | *pat), 0x0d);
+ROP3_HANDLERS(PDSonon, ~(~(*src | *dest) | *pat), 0x0e);
+ROP3_HANDLERS(PDSona, ~(*src | *dest) & *pat, 0x10);
+ROP3_HANDLERS(SDPxnon, ~(~(*pat ^ *dest) | *src), 0x12);
+ROP3_HANDLERS(SDPaon, ~((*pat & *dest) | *src), 0x13);
+ROP3_HANDLERS(DPSxnon, ~(~(*pat ^ *src) | *dest), 0x14);
+ROP3_HANDLERS(DPSaon, ~((*pat & *src) | *dest), 0x15);
+ROP3_HANDLERS(PSDPSanaxx, (~(*pat & *src) & *dest) ^ *src ^ *pat, 0x16);
+ROP3_HANDLERS(SSPxDSxaxn, ~(((*src ^ *dest) & (*src ^ *pat)) ^ *src), 0x17);
+ROP3_HANDLERS(SPxPDxa, (*src ^ *pat) & (*pat ^ *dest), 0x18);
+ROP3_HANDLERS(SDPSanaxn, ~((~(*pat & *src) & *dest) ^ *src), 0x19);
+ROP3_HANDLERS(PDSPaox, ((*pat & *src) | *dest) ^ *pat, 0x1a);
+ROP3_HANDLERS(SDPSxaxn, ~(((*pat ^ *src) & *dest) ^ *src), 0x1b);
+ROP3_HANDLERS(PSDPaox, ((*pat & *dest) | *src) ^ *pat, 0x1c);
+ROP3_HANDLERS(DSPDxaxn, ~(((*pat ^ *dest) & *src) ^ *dest), 0x1d);
+ROP3_HANDLERS(PDSox, (*dest | *src) ^ *pat, 0x1e);
+ROP3_HANDLERS(PDSoan, ~((*src | *dest) & *pat), 0x1f);
+ROP3_HANDLERS(DPSnaa, ~*src & *pat & *dest, 0x20);
+ROP3_HANDLERS(SDPxon, ~((*pat ^ *dest) | *src), 0x21);
+ROP3_HANDLERS(SPDnaon, ~((~*dest & *pat) | *src), 0x23);
+ROP3_HANDLERS(SPxDSxa, (*src ^ *pat) & (*dest ^ *src), 0x24);
+ROP3_HANDLERS(PDSPanaxn, ~((~(*src & *pat) & *dest) ^ *pat), 0x25);
+ROP3_HANDLERS(SDPSaox, ((*src & *pat) | *dest) ^ *src, 0x26);
+ROP3_HANDLERS(SDPSxnox, (~(*src ^ *pat) | *dest) ^ *src, 0x27);
+ROP3_HANDLERS(DPSxa, (*pat ^ *src) & *dest, 0x28);
+ROP3_HANDLERS(PSDPSaoxxn, ~(((*src & *pat) | *dest) ^ *src ^ *pat), 0x29);
+ROP3_HANDLERS(DPSana, ~(*src & *pat) & *dest, 0x2a);
+ROP3_HANDLERS(SSPxPDxaxn, ~(((*pat ^ *dest) & (*src ^ *pat)) ^ *src), 0x2b);
+ROP3_HANDLERS(SPDSoax, ((*src | *dest) & *pat) ^ *src, 0x2c);
+ROP3_HANDLERS(PSDnox, (~*dest | *src) ^ *pat, 0x2d);
+ROP3_HANDLERS(PSDPxox, ((*pat ^ *dest) | *src) ^ *pat, 0x2e);
+ROP3_HANDLERS(PSDnoan, ~((~*dest | *src) & *pat), 0x2f);
+ROP3_HANDLERS(SDPnaon, ~((~*pat & *dest) | *src), 0x31);
+ROP3_HANDLERS(SDPSoox, (*src | *pat | *dest) ^ *src, 0x32);
+ROP3_HANDLERS(SPDSaox, ((*src & *dest) | *pat) ^ *src, 0x34);
+ROP3_HANDLERS(SPDSxnox, (~(*src ^ *dest) | *pat) ^ *src, 0x35);
+ROP3_HANDLERS(SDPox, (*pat | *dest) ^ *src, 0x36);
+ROP3_HANDLERS(SDPoan, ~((*pat | *dest) & *src), 0x37);
+ROP3_HANDLERS(PSDPoax, ((*pat | *dest) & *src) ^ *pat, 0x38);
+ROP3_HANDLERS(SPDnox, (~*dest | *pat) ^ *src, 0x39);
+ROP3_HANDLERS(SPDSxox, ((*src ^ *dest) | *pat) ^ *src, 0x3a);
+ROP3_HANDLERS(SPDnoan, ~((~*dest | *pat) & *src), 0x3b);
+ROP3_HANDLERS(SPDSonox, (~(*src | *dest) | *pat) ^ *src, 0x3d);
+ROP3_HANDLERS(SPDSnaox, ((~*src & *dest) | *pat) ^ *src, 0x3e);
+ROP3_HANDLERS(PSDnaa, ~*dest & *src & *pat, 0x40);
+ROP3_HANDLERS(DPSxon, ~((*src ^ *pat) | *dest), 0x41);
+ROP3_HANDLERS(SDxPDxa, (*src ^ *dest) & (*pat ^ *dest), 0x42);
+ROP3_HANDLERS(SPDSanaxn, ~((~(*src & *dest) & *pat) ^ *src), 0x43);
+ROP3_HANDLERS(DPSnaon, ~((~*src & *pat) | *dest), 0x45);
+ROP3_HANDLERS(DSPDaox, ((*dest & *pat) | *src) ^ *dest, 0x46);
+ROP3_HANDLERS(PSDPxaxn, ~(((*pat ^ *dest) & *src) ^ *pat), 0x47);
+ROP3_HANDLERS(SDPxa, (*pat ^ *dest) & *src, 0x48);
+ROP3_HANDLERS(PDSPDaoxxn, ~(((*dest & *pat) | *src) ^ *dest ^ *pat), 0x49);
+ROP3_HANDLERS(DPSDoax, ((*dest | *src) & *pat) ^ *dest, 0x4a);
+ROP3_HANDLERS(PDSnox, (~*src | *dest) ^ *pat, 0x4b);
+ROP3_HANDLERS(SDPana, ~(*pat & *dest) & *src, 0x4c);
+ROP3_HANDLERS(SSPxDSxoxn, ~(((*src ^ *dest) | (*src ^ *pat)) ^ *src), 0x4d);
+ROP3_HANDLERS(PDSPxox, ((*pat ^ *src) | *dest) ^ *pat, 0x4e);
+ROP3_HANDLERS(PDSnoan, ~((~*src | *dest) & *pat), 0x4f);
+ROP3_HANDLERS(DSPnaon, ~((~*pat & *src) | *dest), 0x51);
+ROP3_HANDLERS(DPSDaox, ((*dest & *src) | *pat) ^ *dest, 0x52);
+ROP3_HANDLERS(SPDSxaxn, ~(((*src ^ *dest) & *pat) ^ *src), 0x53);
+ROP3_HANDLERS(DPSonon, ~(~(*src | *pat) | *dest), 0x54);
+ROP3_HANDLERS(DPSox, (*src | *pat) ^ *dest, 0x56);
+ROP3_HANDLERS(DPSoan, ~((*src | *pat) & *dest), 0x57);
+ROP3_HANDLERS(PDSPoax, ((*pat | *src) & *dest) ^ *pat, 0x58);
+ROP3_HANDLERS(DPSnox, (~*src | *pat) ^ *dest, 0x59);
+ROP3_HANDLERS(DPSDonox, (~(*dest | *src) | *pat) ^ *dest, 0x5b);
+ROP3_HANDLERS(DPSDxox, ((*dest ^ *src) | *pat) ^ *dest, 0x5c);
+ROP3_HANDLERS(DPSnoan, ~((~*src | *pat) & *dest), 0x5d);
+ROP3_HANDLERS(DPSDnaox, ((~*dest & *src) | *pat) ^ *dest, 0x5e);
+ROP3_HANDLERS(PDSxa, (*src ^ *dest) & *pat, 0x60);
+ROP3_HANDLERS(DSPDSaoxxn, ~(((*src & *dest) | *pat) ^ *src ^ *dest), 0x61);
+ROP3_HANDLERS(DSPDoax, ((*dest | *pat) & *src) ^ *dest, 0x62);
+ROP3_HANDLERS(SDPnox, (~*pat | *dest) ^ *src, 0x63);
+ROP3_HANDLERS(SDPSoax, ((*src | *pat) & *dest) ^ *src, 0x64);
+ROP3_HANDLERS(DSPnox, (~*pat | *src) ^ *dest, 0x65);
+ROP3_HANDLERS(SDPSonox, (~(*src | *pat) | *dest) ^ *src, 0x67);
+ROP3_HANDLERS(DSPDSonoxxn, ~((~(*src | *dest) | *pat) ^ *src ^ *dest), 0x68);
+ROP3_HANDLERS(PDSxxn, ~(*src ^ *dest ^ *pat), 0x69);
+ROP3_HANDLERS(DPSax, (*src & *pat) ^ *dest, 0x6a);
+ROP3_HANDLERS(PSDPSoaxxn, ~(((*src | *pat) & *dest) ^ *src ^ *pat), 0x6b);
+ROP3_HANDLERS(SDPax, (*pat & *dest) ^ *src, 0x6c);
+ROP3_HANDLERS(PDSPDoaxxn, ~(((*dest | *pat) & *src) ^ *dest ^ *pat), 0x6d);
+ROP3_HANDLERS(SDPSnoax, ((~*src | *pat) & *dest) ^ *src, 0x6e);
+ROP3_HANDLERS(PDSxnan, ~(~(*src ^ *dest) & *pat), 0x6f);
+ROP3_HANDLERS(PDSana, ~(*src & *dest) & *pat, 0x70);
+ROP3_HANDLERS(SSDxPDxaxn, ~(((*dest ^ *pat) & (*src ^ *dest)) ^ *src), 0x71);
+ROP3_HANDLERS(SDPSxox, ((*src ^ *pat) | *dest) ^ *src, 0x72);
+ROP3_HANDLERS(SDPnoan, ~((~*pat | *dest) & *src), 0x73);
+ROP3_HANDLERS(DSPDxox, ((*dest ^ *pat) | *src) ^ *dest, 0x74);
+ROP3_HANDLERS(DSPnoan, ~((~*pat | *src) & *dest), 0x75);
+ROP3_HANDLERS(SDPSnaox, ((~*src & *pat) | *dest) ^ *src, 0x76);
+ROP3_HANDLERS(PDSax, (*src & *dest) ^ *pat, 0x78);
+ROP3_HANDLERS(DSPDSoaxxn, ~(((*src | *dest) & *pat) ^ *src ^ *dest), 0x79);
+ROP3_HANDLERS(DPSDnoax, ((~*dest | *src) & *pat) ^ *dest, 0x7a);
+ROP3_HANDLERS(SDPxnan, ~(~(*pat ^ *dest) & *src), 0x7b);
+ROP3_HANDLERS(SPDSnoax, ((~*src | *dest) & *pat) ^ *src, 0x7c);
+ROP3_HANDLERS(DPSxnan, ~(~(*src ^ *pat) & *dest), 0x7d);
+ROP3_HANDLERS(SPxDSxo, (*src ^ *dest) | (*pat ^ *src), 0x7e);
+ROP3_HANDLERS(DPSaan, ~(*src & *pat & *dest), 0x7f);
+ROP3_HANDLERS(DPSaa, *src & *pat & *dest, 0x80);
+ROP3_HANDLERS(SPxDSxon, ~((*src ^ *dest) | (*pat ^ *src)), 0x81);
+ROP3_HANDLERS(DPSxna, ~(*src ^ *pat) & *dest, 0x82);
+ROP3_HANDLERS(SPDSnoaxn, ~(((~*src | *dest) & *pat) ^ *src), 0x83);
+ROP3_HANDLERS(SDPxna, ~(*pat ^ *dest) & *src, 0x84);
+ROP3_HANDLERS(PDSPnoaxn, ~(((~*pat | *src) & *dest) ^ *pat), 0x85);
+ROP3_HANDLERS(DSPDSoaxx, ((*src | *dest) & *pat) ^ *src ^ *dest, 0x86);
+ROP3_HANDLERS(PDSaxn, ~((*src & *dest) ^ *pat), 0x87);
+ROP3_HANDLERS(SDPSnaoxn, ~(((~*src & *pat) | *dest) ^ *src), 0x89);
+ROP3_HANDLERS(DSPnoa, (~*pat | *src) & *dest, 0x8a);
+ROP3_HANDLERS(DSPDxoxn, ~(((*dest ^ *pat) | *src) ^ *dest), 0x8b);
+ROP3_HANDLERS(SDPnoa, (~*pat | *dest) & *src, 0x8c);
+ROP3_HANDLERS(SDPSxoxn, ~(((*src ^ *pat) | *dest) ^ *src), 0x8d);
+ROP3_HANDLERS(SSDxPDxax, ((*dest ^ *pat) & (*dest ^ *src)) ^ *src, 0x8e);
+ROP3_HANDLERS(PDSanan, ~(~(*src & *dest) & *pat), 0x8f);
+ROP3_HANDLERS(PDSxna, ~(*src ^ *dest) & *pat, 0x90);
+ROP3_HANDLERS(SDPSnoaxn, ~(((~*src | *pat) & *dest) ^ *src), 0x91);
+ROP3_HANDLERS(DPSDPoaxx, ((*pat | *dest) & *src) ^ *pat ^ *dest, 0x92);
+ROP3_HANDLERS(SPDaxn, ~((*dest & *pat) ^ *src), 0x93);
+ROP3_HANDLERS(PSDPSoaxx, ((*src | *pat) & *dest) ^ *src ^ *pat, 0x94);
+ROP3_HANDLERS(DPSaxn, ~((*src & *pat) ^ *dest), 0x95);
+ROP3_HANDLERS(DPSxx, *src ^ *pat ^ *dest, 0x96);
+ROP3_HANDLERS(PSDPSonoxx, (~(*src | *pat) | *dest) ^ *src ^ *pat, 0x97);
+ROP3_HANDLERS(SDPSonoxn, ~((~(*src | *pat) | *dest) ^ *src), 0x98);
+ROP3_HANDLERS(DPSnax, (~*src & *pat) ^ *dest, 0x9a);
+ROP3_HANDLERS(SDPSoaxn, ~(((*src | *pat) & *dest) ^ *src), 0x9b);
+ROP3_HANDLERS(SPDnax, (~*dest & *pat) ^ *src, 0x9c);
+ROP3_HANDLERS(DSPDoaxn, ~(((*dest | *pat) & *src) ^ *dest), 0x9d);
+ROP3_HANDLERS(DSPDSaoxx, ((*src & *dest) | *pat) ^ *src ^ *dest, 0x9e);
+ROP3_HANDLERS(PDSxan, ~((*src ^ *dest) & *pat), 0x9f);
+ROP3_HANDLERS(PDSPnaoxn, ~(((~*pat & *src) | *dest) ^ *pat), 0xa1);
+ROP3_HANDLERS(DPSnoa, (~*src | *pat) & *dest, 0xa2);
+ROP3_HANDLERS(DPSDxoxn, ~(((*dest ^ *src) | *pat) ^ *dest), 0xa3);
+ROP3_HANDLERS(PDSPonoxn, ~((~(*pat | *src) | *dest) ^ *pat), 0xa4);
+ROP3_HANDLERS(DSPnax, (~*pat & *src) ^ *dest, 0xa6);
+ROP3_HANDLERS(PDSPoaxn, ~(((*pat | *src) & *dest) ^ *pat), 0xa7);
+ROP3_HANDLERS(DPSoa, (*src | *pat) & *dest, 0xa8);
+ROP3_HANDLERS(DPSoxn, ~((*src | *pat) ^ *dest), 0xa9);
+ROP3_HANDLERS(DPSono, ~(*src | *pat) | *dest, 0xab);
+ROP3_HANDLERS(SPDSxax, ((*src ^ *dest) & *pat) ^ *src, 0xac);
+ROP3_HANDLERS(DPSDaoxn, ~(((*dest & *src) | *pat) ^ *dest), 0xad);
+ROP3_HANDLERS(DSPnao, (~*pat & *src) | *dest, 0xae);
+ROP3_HANDLERS(PDSnoa, (~*src | *dest) & *pat, 0xb0);
+ROP3_HANDLERS(PDSPxoxn, ~(((*pat ^ *src) | *dest) ^ *pat), 0xb1);
+ROP3_HANDLERS(SSPxDSxox, ((*src ^ *dest) | (*pat ^ *src)) ^ *src, 0xb2);
+ROP3_HANDLERS(SDPanan, ~(~(*pat & *dest) & *src), 0xb3);
+ROP3_HANDLERS(PSDnax, (~*dest & *src) ^ *pat, 0xb4);
+ROP3_HANDLERS(DPSDoaxn, ~(((*dest | *src) & *pat) ^ *dest), 0xb5);
+ROP3_HANDLERS(DPSDPaoxx, ((*pat & *dest) | *src) ^ *pat ^ *dest, 0xb6);
+ROP3_HANDLERS(SDPxan, ~((*pat ^ *dest) & *src), 0xb7);
+ROP3_HANDLERS(PSDPxax, ((*dest ^ *pat) & *src) ^ *pat, 0xb8);
+ROP3_HANDLERS(DSPDaoxn, ~(((*dest & *pat) | *src) ^ *dest), 0xb9);
+ROP3_HANDLERS(DPSnao, (~*src & *pat) | *dest, 0xba);
+ROP3_HANDLERS(SPDSanax, (~(*src & *dest) & *pat) ^ *src, 0xbc);
+ROP3_HANDLERS(SDxPDxan, ~((*dest ^ *pat) & (*dest ^ *src)), 0xbd);
+ROP3_HANDLERS(DPSxo, (*src ^ *pat) | *dest, 0xbe);
+ROP3_HANDLERS(DPSano, ~(*src & *pat) | *dest, 0xbf);
+ROP3_HANDLERS(SPDSnaoxn, ~(((~*src & *dest) | *pat) ^ *src), 0xc1);
+ROP3_HANDLERS(SPDSonoxn, ~((~(*src | *dest) | *pat) ^ *src), 0xc2);
+ROP3_HANDLERS(SPDnoa, (~*dest | *pat) & *src, 0xc4);
+ROP3_HANDLERS(SPDSxoxn, ~(((*src ^ *dest) | *pat) ^ *src), 0xc5);
+ROP3_HANDLERS(SDPnax, (~*pat & *dest) ^ *src, 0xc6);
+ROP3_HANDLERS(PSDPoaxn, ~(((*pat | *dest) & *src) ^ *pat), 0xc7);
+ROP3_HANDLERS(SDPoa, (*pat | *dest) & *src, 0xc8);
+ROP3_HANDLERS(SPDoxn, ~((*dest | *pat) ^ *src), 0xc9);
+ROP3_HANDLERS(DPSDxax, ((*dest ^ *src) & *pat) ^ *dest, 0xca);
+ROP3_HANDLERS(SPDSaoxn, ~(((*src & *dest) | *pat) ^ *src), 0xcb);
+ROP3_HANDLERS(SDPono, ~(*pat | *dest) | *src, 0xcd);
+ROP3_HANDLERS(SDPnao, (~*pat & *dest) | *src, 0xce);
+ROP3_HANDLERS(PSDnoa, (~*dest | *src) & *pat, 0xd0);
+ROP3_HANDLERS(PSDPxoxn, ~(((*pat ^ *dest) | *src) ^ *pat), 0xd1);
+ROP3_HANDLERS(PDSnax, (~*src & *dest) ^ *pat, 0xd2);
+ROP3_HANDLERS(SPDSoaxn, ~(((*src | *dest) & *pat) ^ *src), 0xd3);
+ROP3_HANDLERS(SSPxPDxax, ((*dest ^ *pat) & (*pat ^ *src)) ^ *src, 0xd4);
+ROP3_HANDLERS(DPSanan, ~(~(*src & *pat) & *dest), 0xd5);
+ROP3_HANDLERS(PSDPSaoxx, ((*src & *pat) | *dest) ^ *src ^ *pat, 0xd6);
+ROP3_HANDLERS(DPSxan, ~((*src ^ *pat) & *dest), 0xd7);
+ROP3_HANDLERS(PDSPxax, ((*pat ^ *src) & *dest) ^ *pat, 0xd8);
+ROP3_HANDLERS(SDPSaoxn, ~(((*src & *pat) | *dest) ^ *src), 0xd9);
+ROP3_HANDLERS(DPSDanax, (~(*dest & *src) & *pat) ^ *dest, 0xda);
+ROP3_HANDLERS(SPxDSxan, ~((*src ^ *dest) & (*pat ^ *src)), 0xdb);
+ROP3_HANDLERS(SPDnao, (~*dest & *pat) | *src, 0xdc);
+ROP3_HANDLERS(SDPxo, (*pat ^ *dest) | *src, 0xde);
+ROP3_HANDLERS(SDPano, ~(*pat & *dest) | *src, 0xdf);
+ROP3_HANDLERS(PDSoa, (*src | *dest) & *pat, 0xe0);
+ROP3_HANDLERS(PDSoxn, ~((*src | *dest) ^ *pat), 0xe1);
+ROP3_HANDLERS(DSPDxax, ((*dest ^ *pat) & *src) ^ *dest, 0xe2);
+ROP3_HANDLERS(PSDPaoxn, ~(((*pat & *dest) | *src) ^ *pat), 0xe3);
+ROP3_HANDLERS(SDPSxax, ((*src ^ *pat) & *dest) ^ *src, 0xe4);
+ROP3_HANDLERS(PDSPaoxn, ~(((*pat & *src) | *dest) ^ *pat), 0xe5);
+ROP3_HANDLERS(SDPSanax, (~(*src & *pat) & *dest) ^ *src, 0xe6);
+ROP3_HANDLERS(SPxPDxan, ~((*dest ^ *pat) & (*pat ^ *src)), 0xe7);
+ROP3_HANDLERS(SSPxDSxax, ((*src ^ *dest) & (*pat ^ *src)) ^ *src, 0xe8);
+ROP3_HANDLERS(DSPDSanaxxn, ~((~(*src & *dest) & *pat) ^ *src ^ *dest), 0xe9);
+ROP3_HANDLERS(DPSao, (*src & *pat) | *dest, 0xea);
+ROP3_HANDLERS(DPSxno, ~(*src ^ *pat) | *dest, 0xeb);
+ROP3_HANDLERS(SDPao, (*pat & *dest) | *src, 0xec);
+ROP3_HANDLERS(SDPxno, ~(*pat ^ *dest) | *src, 0xed);
+ROP3_HANDLERS(SDPnoo, ~*pat | *dest | *src, 0xef);
+ROP3_HANDLERS(PDSono, ~(*src | *dest) | *pat, 0xf1);
+ROP3_HANDLERS(PDSnao, (~*src & *dest) | *pat, 0xf2);
+ROP3_HANDLERS(PSDnao, (~*dest & *src) | *pat, 0xf4);
+ROP3_HANDLERS(PDSxo, (*src ^ *dest) | *pat, 0xf6);
+ROP3_HANDLERS(PDSano, ~(*src & *dest) | *pat, 0xf7);
+ROP3_HANDLERS(PDSao, (*src & *dest) | *pat, 0xf8);
+ROP3_HANDLERS(PDSxno, ~(*src ^ *dest) | *pat, 0xf9);
+ROP3_HANDLERS(DPSnoo, ~*src | *pat | *dest, 0xfb);
+ROP3_HANDLERS(PSDnoo, ~*dest | *src | *pat, 0xfd);
+ROP3_HANDLERS(DPSoo, *src | *pat | *dest, 0xfe);
+
+
+
+#define ROP3_FILL_HANDLERS(op, index) \
+ rop3_with_pattern_handlers[index] = rop3_handle_p_##op; \
+ rop3_with_color_handlers[index] = rop3_handle_c_##op; \
+ rop3_test_handlers[index] = rop3_test_##op;
+
+void rop3_init()
+{
+ static int need_init = 1;
+ int i;
+
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+
+ for (i = 0; i < ROP3_NUM_OPS; i++) {
+ rop3_with_pattern_handlers[i] = default_rop3_with_pattern_handler;
+ rop3_with_color_handlers[i] = default_rop3_withe_color_handler;
+ rop3_test_handlers[i] = default_rop3_test_handler;
+ }
+
+ ROP3_FILL_HANDLERS(DPSoon, 0x01);
+ ROP3_FILL_HANDLERS(DPSona, 0x02);
+ ROP3_FILL_HANDLERS(SDPona, 0x04);
+ ROP3_FILL_HANDLERS(PDSxnon, 0x06);
+ ROP3_FILL_HANDLERS(PDSaon, 0x07);
+ ROP3_FILL_HANDLERS(SDPnaa, 0x08);
+ ROP3_FILL_HANDLERS(PDSxon, 0x09);
+ ROP3_FILL_HANDLERS(PSDnaon, 0x0b);
+ ROP3_FILL_HANDLERS(PDSnaon, 0x0d);
+ ROP3_FILL_HANDLERS(PDSonon, 0x0e);
+ ROP3_FILL_HANDLERS(PDSona, 0x10);
+ ROP3_FILL_HANDLERS(SDPxnon, 0x12);
+ ROP3_FILL_HANDLERS(SDPaon, 0x13);
+ ROP3_FILL_HANDLERS(DPSxnon, 0x14);
+ ROP3_FILL_HANDLERS(DPSaon, 0x15);
+ ROP3_FILL_HANDLERS(PSDPSanaxx, 0x16);
+ ROP3_FILL_HANDLERS(SSPxDSxaxn, 0x17);
+ ROP3_FILL_HANDLERS(SPxPDxa, 0x18);
+ ROP3_FILL_HANDLERS(SDPSanaxn, 0x19);
+ ROP3_FILL_HANDLERS(PDSPaox, 0x1a);
+ ROP3_FILL_HANDLERS(SDPSxaxn, 0x1b);
+ ROP3_FILL_HANDLERS(PSDPaox, 0x1c);
+ ROP3_FILL_HANDLERS(DSPDxaxn, 0x1d);
+ ROP3_FILL_HANDLERS(PDSox, 0x1e);
+ ROP3_FILL_HANDLERS(PDSoan, 0x1f);
+ ROP3_FILL_HANDLERS(DPSnaa, 0x20);
+ ROP3_FILL_HANDLERS(SDPxon, 0x21);
+ ROP3_FILL_HANDLERS(SPDnaon, 0x23);
+ ROP3_FILL_HANDLERS(SPxDSxa, 0x24);
+ ROP3_FILL_HANDLERS(PDSPanaxn, 0x25);
+ ROP3_FILL_HANDLERS(SDPSaox, 0x26);
+ ROP3_FILL_HANDLERS(SDPSxnox, 0x27);
+ ROP3_FILL_HANDLERS(DPSxa, 0x28);
+ ROP3_FILL_HANDLERS(PSDPSaoxxn, 0x29);
+ ROP3_FILL_HANDLERS(DPSana, 0x2a);
+ ROP3_FILL_HANDLERS(SSPxPDxaxn, 0x2b);
+ ROP3_FILL_HANDLERS(SPDSoax, 0x2c);
+ ROP3_FILL_HANDLERS(PSDnox, 0x2d);
+ ROP3_FILL_HANDLERS(PSDPxox, 0x2e);
+ ROP3_FILL_HANDLERS(PSDnoan, 0x2f);
+ ROP3_FILL_HANDLERS(SDPnaon, 0x31);
+ ROP3_FILL_HANDLERS(SDPSoox, 0x32);
+ ROP3_FILL_HANDLERS(SPDSaox, 0x34);
+ ROP3_FILL_HANDLERS(SPDSxnox, 0x35);
+ ROP3_FILL_HANDLERS(SDPox, 0x36);
+ ROP3_FILL_HANDLERS(SDPoan, 0x37);
+ ROP3_FILL_HANDLERS(PSDPoax, 0x38);
+ ROP3_FILL_HANDLERS(SPDnox, 0x39);
+ ROP3_FILL_HANDLERS(SPDSxox, 0x3a);
+ ROP3_FILL_HANDLERS(SPDnoan, 0x3b);
+ ROP3_FILL_HANDLERS(SPDSonox, 0x3d);
+ ROP3_FILL_HANDLERS(SPDSnaox, 0x3e);
+ ROP3_FILL_HANDLERS(PSDnaa, 0x40);
+ ROP3_FILL_HANDLERS(DPSxon, 0x41);
+ ROP3_FILL_HANDLERS(SDxPDxa, 0x42);
+ ROP3_FILL_HANDLERS(SPDSanaxn, 0x43);
+ ROP3_FILL_HANDLERS(DPSnaon, 0x45);
+ ROP3_FILL_HANDLERS(DSPDaox, 0x46);
+ ROP3_FILL_HANDLERS(PSDPxaxn, 0x47);
+ ROP3_FILL_HANDLERS(SDPxa, 0x48);
+ ROP3_FILL_HANDLERS(PDSPDaoxxn, 0x49);
+ ROP3_FILL_HANDLERS(DPSDoax, 0x4a);
+ ROP3_FILL_HANDLERS(PDSnox, 0x4b);
+ ROP3_FILL_HANDLERS(SDPana, 0x4c);
+ ROP3_FILL_HANDLERS(SSPxDSxoxn, 0x4d);
+ ROP3_FILL_HANDLERS(PDSPxox, 0x4e);
+ ROP3_FILL_HANDLERS(PDSnoan, 0x4f);
+ ROP3_FILL_HANDLERS(DSPnaon, 0x51);
+ ROP3_FILL_HANDLERS(DPSDaox, 0x52);
+ ROP3_FILL_HANDLERS(SPDSxaxn, 0x53);
+ ROP3_FILL_HANDLERS(DPSonon, 0x54);
+ ROP3_FILL_HANDLERS(DPSox, 0x56);
+ ROP3_FILL_HANDLERS(DPSoan, 0x57);
+ ROP3_FILL_HANDLERS(PDSPoax, 0x58);
+ ROP3_FILL_HANDLERS(DPSnox, 0x59);
+ ROP3_FILL_HANDLERS(DPSDonox, 0x5b);
+ ROP3_FILL_HANDLERS(DPSDxox, 0x5c);
+ ROP3_FILL_HANDLERS(DPSnoan, 0x5d);
+ ROP3_FILL_HANDLERS(DPSDnaox, 0x5e);
+ ROP3_FILL_HANDLERS(PDSxa, 0x60);
+ ROP3_FILL_HANDLERS(DSPDSaoxxn, 0x61);
+ ROP3_FILL_HANDLERS(DSPDoax, 0x62);
+ ROP3_FILL_HANDLERS(SDPnox, 0x63);
+ ROP3_FILL_HANDLERS(SDPSoax, 0x64);
+ ROP3_FILL_HANDLERS(DSPnox, 0x65);
+ ROP3_FILL_HANDLERS(SDPSonox, 0x67);
+ ROP3_FILL_HANDLERS(DSPDSonoxxn, 0x68);
+ ROP3_FILL_HANDLERS(PDSxxn, 0x69);
+ ROP3_FILL_HANDLERS(DPSax, 0x6a);
+ ROP3_FILL_HANDLERS(PSDPSoaxxn, 0x6b);
+ ROP3_FILL_HANDLERS(SDPax, 0x6c);
+ ROP3_FILL_HANDLERS(PDSPDoaxxn, 0x6d);
+ ROP3_FILL_HANDLERS(SDPSnoax, 0x6e);
+ ROP3_FILL_HANDLERS(PDSxnan, 0x6f);
+ ROP3_FILL_HANDLERS(PDSana, 0x70);
+ ROP3_FILL_HANDLERS(SSDxPDxaxn, 0x71);
+ ROP3_FILL_HANDLERS(SDPSxox, 0x72);
+ ROP3_FILL_HANDLERS(SDPnoan, 0x73);
+ ROP3_FILL_HANDLERS(DSPDxox, 0x74);
+ ROP3_FILL_HANDLERS(DSPnoan, 0x75);
+ ROP3_FILL_HANDLERS(SDPSnaox, 0x76);
+ ROP3_FILL_HANDLERS(PDSax, 0x78);
+ ROP3_FILL_HANDLERS(DSPDSoaxxn, 0x79);
+ ROP3_FILL_HANDLERS(DPSDnoax, 0x7a);
+ ROP3_FILL_HANDLERS(SDPxnan, 0x7b);
+ ROP3_FILL_HANDLERS(SPDSnoax, 0x7c);
+ ROP3_FILL_HANDLERS(DPSxnan, 0x7d);
+ ROP3_FILL_HANDLERS(SPxDSxo, 0x7e);
+ ROP3_FILL_HANDLERS(DPSaan, 0x7f);
+ ROP3_FILL_HANDLERS(DPSaa, 0x80);
+ ROP3_FILL_HANDLERS(SPxDSxon, 0x81);
+ ROP3_FILL_HANDLERS(DPSxna, 0x82);
+ ROP3_FILL_HANDLERS(SPDSnoaxn, 0x83);
+ ROP3_FILL_HANDLERS(SDPxna, 0x84);
+ ROP3_FILL_HANDLERS(PDSPnoaxn, 0x85);
+ ROP3_FILL_HANDLERS(DSPDSoaxx, 0x86);
+ ROP3_FILL_HANDLERS(PDSaxn, 0x87);
+ ROP3_FILL_HANDLERS(SDPSnaoxn, 0x89);
+ ROP3_FILL_HANDLERS(DSPnoa, 0x8a);
+ ROP3_FILL_HANDLERS(DSPDxoxn, 0x8b);
+ ROP3_FILL_HANDLERS(SDPnoa, 0x8c);
+ ROP3_FILL_HANDLERS(SDPSxoxn, 0x8d);
+ ROP3_FILL_HANDLERS(SSDxPDxax, 0x8e);
+ ROP3_FILL_HANDLERS(PDSanan, 0x8f);
+ ROP3_FILL_HANDLERS(PDSxna, 0x90);
+ ROP3_FILL_HANDLERS(SDPSnoaxn, 0x91);
+ ROP3_FILL_HANDLERS(DPSDPoaxx, 0x92);
+ ROP3_FILL_HANDLERS(SPDaxn, 0x93);
+ ROP3_FILL_HANDLERS(PSDPSoaxx, 0x94);
+ ROP3_FILL_HANDLERS(DPSaxn, 0x95);
+ ROP3_FILL_HANDLERS(DPSxx, 0x96);
+ ROP3_FILL_HANDLERS(PSDPSonoxx, 0x97);
+ ROP3_FILL_HANDLERS(SDPSonoxn, 0x98);
+ ROP3_FILL_HANDLERS(DPSnax, 0x9a);
+ ROP3_FILL_HANDLERS(SDPSoaxn, 0x9b);
+ ROP3_FILL_HANDLERS(SPDnax, 0x9c);
+ ROP3_FILL_HANDLERS(DSPDoaxn, 0x9d);
+ ROP3_FILL_HANDLERS(DSPDSaoxx, 0x9e);
+ ROP3_FILL_HANDLERS(PDSxan, 0x9f);
+ ROP3_FILL_HANDLERS(PDSPnaoxn, 0xa1);
+ ROP3_FILL_HANDLERS(DPSnoa, 0xa2);
+ ROP3_FILL_HANDLERS(DPSDxoxn, 0xa3);
+ ROP3_FILL_HANDLERS(PDSPonoxn, 0xa4);
+ ROP3_FILL_HANDLERS(DSPnax, 0xa6);
+ ROP3_FILL_HANDLERS(PDSPoaxn, 0xa7);
+ ROP3_FILL_HANDLERS(DPSoa, 0xa8);
+ ROP3_FILL_HANDLERS(DPSoxn, 0xa9);
+ ROP3_FILL_HANDLERS(DPSono, 0xab);
+ ROP3_FILL_HANDLERS(SPDSxax, 0xac);
+ ROP3_FILL_HANDLERS(DPSDaoxn, 0xad);
+ ROP3_FILL_HANDLERS(DSPnao, 0xae);
+ ROP3_FILL_HANDLERS(PDSnoa, 0xb0);
+ ROP3_FILL_HANDLERS(PDSPxoxn, 0xb1);
+ ROP3_FILL_HANDLERS(SSPxDSxox, 0xb2);
+ ROP3_FILL_HANDLERS(SDPanan, 0xb3);
+ ROP3_FILL_HANDLERS(PSDnax, 0xb4);
+ ROP3_FILL_HANDLERS(DPSDoaxn, 0xb5);
+ ROP3_FILL_HANDLERS(DPSDPaoxx, 0xb6);
+ ROP3_FILL_HANDLERS(SDPxan, 0xb7);
+ ROP3_FILL_HANDLERS(PSDPxax, 0xb8);
+ ROP3_FILL_HANDLERS(DSPDaoxn, 0xb9);
+ ROP3_FILL_HANDLERS(DPSnao, 0xba);
+ ROP3_FILL_HANDLERS(SPDSanax, 0xbc);
+ ROP3_FILL_HANDLERS(SDxPDxan, 0xbd);
+ ROP3_FILL_HANDLERS(DPSxo, 0xbe);
+ ROP3_FILL_HANDLERS(DPSano, 0xbf);
+ ROP3_FILL_HANDLERS(SPDSnaoxn, 0xc1);
+ ROP3_FILL_HANDLERS(SPDSonoxn, 0xc2);
+ ROP3_FILL_HANDLERS(SPDnoa, 0xc4);
+ ROP3_FILL_HANDLERS(SPDSxoxn, 0xc5);
+ ROP3_FILL_HANDLERS(SDPnax, 0xc6);
+ ROP3_FILL_HANDLERS(PSDPoaxn, 0xc7);
+ ROP3_FILL_HANDLERS(SDPoa, 0xc8);
+ ROP3_FILL_HANDLERS(SPDoxn, 0xc9);
+ ROP3_FILL_HANDLERS(DPSDxax, 0xca);
+ ROP3_FILL_HANDLERS(SPDSaoxn, 0xcb);
+ ROP3_FILL_HANDLERS(SDPono, 0xcd);
+ ROP3_FILL_HANDLERS(SDPnao, 0xce);
+ ROP3_FILL_HANDLERS(PSDnoa, 0xd0);
+ ROP3_FILL_HANDLERS(PSDPxoxn, 0xd1);
+ ROP3_FILL_HANDLERS(PDSnax, 0xd2);
+ ROP3_FILL_HANDLERS(SPDSoaxn, 0xd3);
+ ROP3_FILL_HANDLERS(SSPxPDxax, 0xd4);
+ ROP3_FILL_HANDLERS(DPSanan, 0xd5);
+ ROP3_FILL_HANDLERS(PSDPSaoxx, 0xd6);
+ ROP3_FILL_HANDLERS(DPSxan, 0xd7);
+ ROP3_FILL_HANDLERS(PDSPxax, 0xd8);
+ ROP3_FILL_HANDLERS(SDPSaoxn, 0xd9);
+ ROP3_FILL_HANDLERS(DPSDanax, 0xda);
+ ROP3_FILL_HANDLERS(SPxDSxan, 0xdb);
+ ROP3_FILL_HANDLERS(SPDnao, 0xdc);
+ ROP3_FILL_HANDLERS(SDPxo, 0xde);
+ ROP3_FILL_HANDLERS(SDPano, 0xdf);
+ ROP3_FILL_HANDLERS(PDSoa, 0xe0);
+ ROP3_FILL_HANDLERS(PDSoxn, 0xe1);
+ ROP3_FILL_HANDLERS(DSPDxax, 0xe2);
+ ROP3_FILL_HANDLERS(PSDPaoxn, 0xe3);
+ ROP3_FILL_HANDLERS(SDPSxax, 0xe4);
+ ROP3_FILL_HANDLERS(PDSPaoxn, 0xe5);
+ ROP3_FILL_HANDLERS(SDPSanax, 0xe6);
+ ROP3_FILL_HANDLERS(SPxPDxan, 0xe7);
+ ROP3_FILL_HANDLERS(SSPxDSxax, 0xe8);
+ ROP3_FILL_HANDLERS(DSPDSanaxxn, 0xe9);
+ ROP3_FILL_HANDLERS(DPSao, 0xea);
+ ROP3_FILL_HANDLERS(DPSxno, 0xeb);
+ ROP3_FILL_HANDLERS(SDPao, 0xec);
+ ROP3_FILL_HANDLERS(SDPxno, 0xed);
+ ROP3_FILL_HANDLERS(SDPnoo, 0xef);
+ ROP3_FILL_HANDLERS(PDSono, 0xf1);
+ ROP3_FILL_HANDLERS(PDSnao, 0xf2);
+ ROP3_FILL_HANDLERS(PSDnao, 0xf4);
+ ROP3_FILL_HANDLERS(PDSxo, 0xf6);
+ ROP3_FILL_HANDLERS(PDSano, 0xf7);
+ ROP3_FILL_HANDLERS(PDSao, 0xf8);
+ ROP3_FILL_HANDLERS(PDSxno, 0xf9);
+ ROP3_FILL_HANDLERS(DPSnoo, 0xfb);
+ ROP3_FILL_HANDLERS(PSDnoo, 0xfd);
+ ROP3_FILL_HANDLERS(DPSoo, 0xfe);
+
+ for (i = 0; i < ROP3_NUM_OPS; i++) {
+ rop3_test_handlers[i]();
+ }
+}
+
+void do_rop3_with_pattern(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, Point *src_pos,
+ cairo_surface_t *p, Point *pat_pos)
+{
+ rop3_with_pattern_handlers[rop3](d, s, src_pos, p, pat_pos);
+}
+
+void do_rop3_with_color(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, Point *src_pos,
+ uint32_t rgb)
+{
+ rop3_with_color_handlers[rop3](d, s, src_pos, rgb);
+}
+
diff --git a/common/rop3.h b/common/rop3.h
new file mode 100644
index 00000000..fcf86423
--- /dev/null
+++ b/common/rop3.h
@@ -0,0 +1,33 @@
+/*
+ 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/>.
+*/
+
+#ifndef _H_ROP3
+#define _H_ROP3
+
+#include <stdint.h>
+
+#include "draw.h"
+#include "cairo.h"
+
+void do_rop3_with_pattern(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, Point *src_pos,
+ cairo_surface_t *p, Point *pat_pos);
+void do_rop3_with_color(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, Point *src_pos,
+ uint32_t rgb);
+
+void rop3_init();
+#endif
+
diff --git a/common/vd_agent.h b/common/vd_agent.h
new file mode 100644
index 00000000..74512109
--- /dev/null
+++ b/common/vd_agent.h
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _H_VD_AGENT
+#define _H_VD_AGENT
+
+#include <stdint.h>
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#endif
+
+
+typedef struct ATTR_PACKED VDAgentMessage {
+ uint32_t protocol;
+ uint32_t type;
+ uint64_t opaque;
+ uint32_t size;
+ uint8_t data[0];
+} VDAgentMessage;
+
+#define VD_AGENT_PROTOCOL 1
+
+enum {
+ VD_AGENT_MOUSE_STATE = 1,
+ VD_AGENT_MONITORS_CONFIG,
+ VD_AGENT_REPLY,
+};
+
+typedef struct ATTR_PACKED VDAgentMonConfig {
+ uint32_t height;
+ uint32_t width;
+ uint32_t depth;
+ int32_t x;
+ int32_t y;
+} VDAgentMonConfig;
+
+enum {
+ VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS = (1 << 0),
+};
+
+typedef struct ATTR_PACKED VDAgentMonitorsConfig {
+ uint32_t num_of_monitors;
+ uint32_t flags;
+ VDAgentMonConfig monitors[0];
+} VDAgentMonitorsConfig;
+
+#define VD_AGENT_LBUTTON_MASK (1 << 1)
+#define VD_AGENT_MBUTTON_MASK (1 << 2)
+#define VD_AGENT_RBUTTON_MASK (1 << 3)
+#define VD_AGENT_UBUTTON_MASK (1 << 4)
+#define VD_AGENT_DBUTTON_MASK (1 << 5)
+
+typedef struct ATTR_PACKED VDAgentMouseState {
+ uint32_t x;
+ uint32_t y;
+ uint32_t buttons;
+ uint8_t display_id;
+} VDAgentMouseState;
+
+typedef struct ATTR_PACKED VDAgentReply {
+ uint32_t type;
+ uint32_t error;
+} VDAgentReply;
+
+enum {
+ VD_AGENT_SUCCESS = 1,
+ VD_AGENT_ERROR,
+};
+
+#undef ATTR_PACKED
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#endif
+
+
diff --git a/common/vdi_dev.h b/common/vdi_dev.h
new file mode 100644
index 00000000..d8b5add3
--- /dev/null
+++ b/common/vdi_dev.h
@@ -0,0 +1,97 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _H_VDI_DEV
+#define _H_VDI_DEV
+
+#include "ipc_ring.h"
+
+#ifdef __GNUC__
+#ifdef __i386__
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+#else
+//mfence
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%rsp)": : :"memory")
+#endif
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#define mb() __asm {lock add [esp], 0}
+#endif
+
+#define REDHAT_PCI_VENDOR_ID 0x1b36
+
+#define VDI_PORT_DEVICE_ID 0x0105
+#define VDI_PORT_REVISION 0x01
+
+#define VDI_PORT_INTERRUPT (1 << 0)
+
+#define VDI_PORT_MAGIC (*(UINT32*)"VDIP")
+
+typedef struct ATTR_PACKED VDIPortPacket {
+ UINT32 gen;
+ UINT32 size;
+ UINT8 data[512 - 2 * sizeof(UINT32)];
+} VDIPortPacket;
+
+RING_DECLARE(VDIPortRing, VDIPortPacket, 32);
+
+enum {
+ VDI_PORT_IO_RANGE_INDEX,
+ VDI_PORT_RAM_RANGE_INDEX,
+};
+
+enum {
+ VDI_PORT_IO_CONNECTION,
+ VDI_PORT_IO_NOTIFY = 4,
+ VDI_PORT_IO_UPDATE_IRQ = 8,
+
+ VDI_PORT_IO_RANGE_SIZE = 12
+};
+
+typedef struct ATTR_PACKED VDIPortRam {
+ UINT32 magic;
+ UINT32 generation;
+ UINT32 int_pending;
+ UINT32 int_mask;
+ VDIPortRing input;
+ VDIPortRing output;
+ UINT32 reserv[32];
+} VDIPortRam;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+#endif
diff --git a/common/win/my_getopt-1.5/ChangeLog b/common/win/my_getopt-1.5/ChangeLog
new file mode 100644
index 00000000..a671fdb9
--- /dev/null
+++ b/common/win/my_getopt-1.5/ChangeLog
@@ -0,0 +1,22 @@
+2006-12-09 Benjamin C. W. Sittler <bsittler@>
+
+ * my_getopt.c: add my_getopt_reset to reset the argument parser
+
+ * README: updated email address, updated for version 1.5
+
+2002-07-26 Benjamin C. W. Sittler <bsittler@knownow.com>
+
+ * README: updated for version 1.4
+
+ * my_getopt.c: now we include <sys/types.h> explicitly for those
+ systems that narrowly (mis-)interpret ANSI C and POSIX
+ (_my_getopt_internal): added an explicit cast to size_t to make
+ g++ happy
+
+ * getopt.h, my_getopt.h: added extern "C" { ... } for C++
+ compilation (thanks to Jeff Lawson <bovine@ud.com> and others)
+
+2001-08-20 Benjamin C. W. Sittler <bsittler@knownow.com>
+
+ * getopt.h (getopt_long_only): fixed typo (thanks to Justin Lee
+ <justin_lee@ud.com>)
diff --git a/common/win/my_getopt-1.5/LICENSE b/common/win/my_getopt-1.5/LICENSE
new file mode 100644
index 00000000..2224aba0
--- /dev/null
+++ b/common/win/my_getopt-1.5/LICENSE
@@ -0,0 +1,22 @@
+my_getopt - a command-line argument parser
+Copyright 1997-2001, Benjamin Sittler
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/common/win/my_getopt-1.5/Makefile b/common/win/my_getopt-1.5/Makefile
new file mode 100644
index 00000000..083cc4aa
--- /dev/null
+++ b/common/win/my_getopt-1.5/Makefile
@@ -0,0 +1,26 @@
+all: copy
+
+# Compiler options
+#CCOPTS = -g -O3 -Wall -Werror
+CCOPTS =
+
+# Compiler
+CC = gcc -Wall -Werror
+#CC = cc
+
+# Linker
+LD = $(CC)
+
+# Utility to remove a file
+RM = rm
+
+OBJS = main.o my_getopt.o
+
+copy: $(OBJS)
+ $(LD) -o $@ $(OBJS)
+
+clean:
+ $(RM) -f copy $(OBJS) *~
+
+%.o: %.c getopt.h my_getopt.h
+ $(CC) $(CCOPTS) -o $@ -c $<
diff --git a/common/win/my_getopt-1.5/README b/common/win/my_getopt-1.5/README
new file mode 100644
index 00000000..3a9afad7
--- /dev/null
+++ b/common/win/my_getopt-1.5/README
@@ -0,0 +1,140 @@
+my_getopt - a command-line argument parser
+Copyright 1997-2006, Benjamin Sittler
+
+The author can be reached by sending email to <bsittler@gmail.com>.
+
+The version of my_getopt in this package (1.5) has a BSD-like license;
+see the file LICENSE for details. Version 1.0 of my_getopt was similar
+to the GPL'ed version of my_getopt included with SMOKE-16 Version 1,
+Release 19990717. SMOKE-16 packages are available from:
+
+ http://geocities.com/bsittler/#smoke16
+
+OVERVIEW OF THE ARGUMENT PARSER
+===============================
+
+The getopt(), getopt_long() and getopt_long_only() functions parse
+command line arguments. The argc and argv parameters passed to these
+functions correspond to the argument count and argument list passed to
+your program's main() function at program start-up. Element 0 of the
+argument list conventionally contains the name of your program. Any
+remaining arguments starting with "-" (except for "-" or "--" by
+themselves) are option arguments, some of include option values. This
+family of getopt() functions allows intermixed option and non-option
+arguments anywhere in the argument list, except that "--" by itself
+causes the remaining elements of the argument list to be treated as
+non-option arguments.
+
+[ See the parts of this document labeled "DOCUMENTATION" and
+ "WHY RE-INVENT THE WHEEL?" for a more information. ]
+
+FILES
+=====
+
+The following four files constitute the my_getopt package:
+
+ LICENSE - license and warranty information for my_getopt
+ my_getopt.c - implementation of my getopt replacement
+ my_getopt.h - interface for my getopt replacement
+ getopt.h - a header file to make my getopt look like GNU getopt
+
+USAGE
+=====
+
+To use my_getopt in your application, include the following line to
+your main program source:
+
+ #include "getopt.h"
+
+This line should appear after your standard system header files to
+avoid conflicting with your system's built-in getopt.
+
+Then compile my_getopt.c into my_getopt.o, and link my_getopt.o into
+your application:
+
+ $ cc -c my_getopt.c
+ $ ld -o app app.o ... my_getopt.o
+
+To avoid conflicting with standard library functions, the function
+names and global variables used by my_getopt all begin with `my_'. To
+ensure compatibility with existing C programs, the `getopt.h' header
+file uses the C preprocessor to redefine names like getopt, optarg,
+optind, and so forth to my_getopt, my_optarg, my_optind, etc.
+
+SAMPLE PROGRAM
+==============
+
+There is also a public-domain sample program:
+
+ main.c - main() for a sample program using my_getopt
+ Makefile - build script for the sample program (called `copy')
+
+To build and test the sample program:
+
+ $ make
+ $ ./copy -help
+ $ ./copy -version
+
+The sample program bears a slight resemblance to the UNIX `cat'
+utility, but can be used rot13-encode streams, and can redirect output
+to a file.
+
+DOCUMENTATION
+=============
+
+There is not yet any real documentation for my_getopt. For the moment,
+use the Linux manual page for getopt. It has its own copyright and
+license; view the file `getopt.3' in a text editor for more details.
+
+ getopt.3 - the manual page for GNU getopt
+ getopt.txt - preformatted copy of the manual page for GNU getopt,
+ for your convenience
+
+WHY RE-INVENT THE WHEEL?
+========================
+
+I re-implemented getopt, getopt_long, and getopt_long_only because
+there were noticable bugs in several versions of the GNU
+implementations, and because the GNU versions aren't always available
+on some systems (*BSD, for example.) Other systems don't include any
+sort of standard argument parser (Win32 with Microsoft tools, for
+example, has no getopt.)
+
+These should do all the expected Unix- and GNU-style argument
+parsing, including permution, bunching, long options with single or
+double dashes (double dashes are required if you use
+my_getopt_long,) and optional arguments for both long and short
+options. A word with double dashes all by themselves halts argument
+parsing. A required long option argument can be in the same word as
+the option name, separated by '=', or in the next word. An optional
+long option argument must be in the same word as the option name,
+separated by '='.
+
+As with the GNU versions, a '+' prefix to the short option
+specification (or the POSIXLY_CORRECT environment variable) disables
+permution, a '-' prefix to the short option specification returns 1
+for non-options, ':' after a short option indicates a required
+argument, and '::' after a short option specification indicates an
+optional argument (which must appear in the same word.) If you'd like
+to recieve ':' instead of '?' for missing option arguments, prefix the
+short option specification with ':'.
+
+The original intent was to re-implement the documented behavior of
+the GNU versions, but I have found it necessary to emulate some of
+the undocumented behavior as well. Some programs depend on it.
+
+KNOWN BUGS
+==========
+
+The GNU versions support POSIX-style -W "name=value" long
+options. Currently, my_getopt does not support these, because I
+don't have any documentation on them (other than the fact that they
+are enabled by "W;" in the short option specification.) As a
+temporary workaround, my_getopt treats "W;" in the short option
+string identically to "W:".
+
+The GNU versions support internationalized/localized
+messages. Currently, my_getopt does not.
+
+There should be re-entrant versions of all these functions so that
+multiple threads can parse arguments simultaneously.
diff --git a/common/win/my_getopt-1.5/getopt.3 b/common/win/my_getopt-1.5/getopt.3
new file mode 100644
index 00000000..a63fcd82
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.3
@@ -0,0 +1,288 @@
+.\" (c) 1993 by Thomas Koenig (ig25@rz.uni-karlsruhe.de)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" License.
+.\" Modified Sat Jul 24 19:27:50 1993 by Rik Faith (faith@cs.unc.edu)
+.\" Modified Mon Aug 30 22:02:34 1995 by Jim Van Zandt <jrv@vanzandt.mv.com>
+.\" longindex is a pointer, has_arg can take 3 values, using consistent
+.\" names for optstring and longindex, "\n" in formats fixed. Documenting
+.\" opterr and getopt_long_only. Clarified explanations (borrowing heavily
+.\" from the source code).
+.TH GETOPT 3 "Aug 30, 1995" "GNU" "Linux Programmer's Manual"
+.SH NAME
+getopt \- Parse command line options
+.SH SYNOPSIS
+.nf
+.B #include <unistd.h>
+.sp
+.BI "int getopt(int " argc ", char * const " argv[] ","
+.BI " const char *" optstring ");"
+.sp
+.BI "extern char *" optarg ;
+.BI "extern int " optind ", " opterr ", " optopt ;
+.sp
+.B #include <getopt.h>
+.sp
+.BI "int getopt_long(int " argc ", char * const " argv[] ",
+.BI " const char *" optstring ,
+.BI " const struct option *" longopts ", int *" longindex ");"
+.sp
+.BI "int getopt_long_only(int " argc ", char * const " argv[] ",
+.BI " const char *" optstring ,
+.BI " const struct option *" longopts ", int *" longindex ");"
+.fi
+.SH DESCRIPTION
+The
+.B getopt()
+function parses the command line arguments. Its arguments
+.I argc
+and
+.I argv
+are the argument count and array as passed to the
+.B main()
+function on program invocation.
+An element of \fIargv\fP that starts with `-' (and is not exactly "-" or "--")
+is an option element. The characters of this element
+(aside from the initial `-') are option characters. If \fBgetopt()\fP
+is called repeatedly, it returns successively each of the option characters
+from each of the option elements.
+.PP
+If \fBgetopt()\fP finds another option character, it returns that
+character, updating the external variable \fIoptind\fP and a static
+variable \fInextchar\fP so that the next call to \fBgetopt()\fP can
+resume the scan with the following option character or
+\fIargv\fP-element.
+.PP
+If there are no more option characters, \fBgetopt()\fP returns
+\fBEOF\fP. Then \fIoptind\fP is the index in \fIargv\fP of the first
+\fIargv\fP-element that is not an option.
+.PP
+.I optstring
+is a string containing the legitimate option characters. If such a
+character is followed by a colon, the option requires an argument, so
+\fBgetopt\fP places a pointer to the following text in the same
+\fIargv\fP-element, or the text of the following \fIargv\fP-element, in
+.IR optarg .
+Two colons mean an option takes
+an optional arg; if there is text in the current \fIargv\fP-element,
+it is returned in \fIoptarg\fP, otherwise \fIoptarg\fP is set to zero.
+.PP
+By default, \fBgetargs()\fP permutes the contents of \fIargv\fP as it
+scans, so that eventually all the non-options are at the end. Two
+other modes are also implemented. If the first character of
+\fIoptstring\fP is `+' or the environment variable POSIXLY_CORRECT is
+set, then option processing stops as soon as a non-option argument is
+encountered. If the first character of \fIoptstring\fP is `-', then
+each non-option \fIargv\fP-element is handled as if it were the argument of
+an option with character code 1. (This is used by programs that were
+written to expect options and other \fIargv\fP-elements in any order
+and that care about the ordering of the two.)
+The special argument `--' forces an end of option-scanning regardless
+of the scanning mode.
+.PP
+If \fBgetopt()\fP does not recognize an option character, it prints an
+error message to stderr, stores the character in \fIoptopt\fP, and
+returns `?'. The calling program may prevent the error message by
+setting \fIopterr\fP to 0.
+.PP
+The
+.B getopt_long()
+function works like
+.B getopt()
+except that it also accepts long options, started out by two dashes.
+Long option names may be abbreviated if the abbreviation is
+unique or is an exact match for some defined option. A long option
+may take a parameter, of the form
+.B --arg=param
+or
+.BR "--arg param" .
+.PP
+.I longopts
+is a pointer to the first element of an array of
+.B struct option
+declared in
+.B <getopt.h>
+as
+.nf
+.sp
+.in 10
+struct option {
+.in 14
+const char *name;
+int has_arg;
+int *flag;
+int val;
+.in 10
+};
+.fi
+.PP
+The meanings of the different fields are:
+.TP
+.I name
+is the name of the long option.
+.TP
+.I has_arg
+is:
+\fBno_argument\fP (or 0) if the option does not take an argument,
+\fBrequired_argument\fP (or 1) if the option requires an argument, or
+\fBoptional_argument\fP (or 2) if the option takes an optional argument.
+.TP
+.I flag
+specifies how results are returned for a long option. If \fIflag\fP
+is \fBNULL\fP, then \fBgetopt_long()\fP returns \fIval\fP. (For
+example, the calling program may set \fIval\fP to the equivalent short
+option character.) Otherwise, \fBgetopt_long()\fP returns 0, and
+\fIflag\fP points to a variable which is set to \fIval\fP if the
+option is found, but left unchanged if the option is not found.
+.TP
+\fIval\fP
+is the value to return, or to load into the variable pointed
+to by \fIflag\fP.
+.PP
+The last element of the array has to be filled with zeroes.
+.PP
+If \fIlongindex\fP is not \fBNULL\fP, it
+points to a variable which is set to the index of the long option relative to
+.IR longopts .
+.PP
+\fBgetopt_long_only()\fP is like \fBgetopt_long()\fP, but `-' as well
+as `--' can indicate a long option. If an option that starts with `-'
+(not `--') doesn't match a long option, but does match a short option,
+it is parsed as a short option instead.
+.SH "RETURN VALUE"
+The
+.B getopt()
+function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the
+options, `?' for an unknown option character, or \fBEOF\fP
+for the end of the option list.
+.PP
+\fBgetopt_long()\fP and \fBgetopt_long_only()\fP also return the option
+character when a short option is recognized. For a long option, they
+return \fIval\fP if \fIflag\fP is \fBNULL\fP, and 0 otherwise. Error
+and EOF returns are the same as for \fBgetopt()\fP, plus `?' for an
+ambiguous match or an extraneous parameter.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+.SM
+.B POSIXLY_CORRECT
+If this is set, then option processing stops as soon as a non-option
+argument is encountered.
+.SH "EXAMPLE"
+The following example program, from the source code, illustrates the
+use of
+.BR getopt_long()
+with most of its features.
+.nf
+.sp
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 1, 0, 'c'},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:012",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\\n");
+ break;
+
+ case 'b':
+ printf ("option b\\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\\n");
+ }
+
+ exit (0);
+}
+.fi
+.SH "BUGS"
+This manpage is confusing.
+.SH "CONFORMING TO"
+.TP
+\fBgetopt()\fP:
+POSIX.1, provided the environment variable POSIXLY_CORRECT is set.
+Otherwise, the elements of \fIargv\fP aren't really const, because we
+permute them. We pretend they're const in the prototype to be
+compatible with other systems.
+
diff --git a/common/win/my_getopt-1.5/getopt.h b/common/win/my_getopt-1.5/getopt.h
new file mode 100644
index 00000000..5f08ccb4
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.h
@@ -0,0 +1,56 @@
+/*
+ * getopt.h - cpp wrapper for my_getopt to make it look like getopt.
+ * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MY_WRAPPER_GETOPT_H_INCLUDED
+#define MY_WRAPPER_GETOPT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "my_getopt.h"
+
+#undef getopt
+#define getopt my_getopt
+#undef getopt_long
+#define getopt_long my_getopt_long
+#undef getopt_long_only
+#define getopt_long_only my_getopt_long_only
+#undef _getopt_internal
+#define _getopt_internal _my_getopt_internal
+#undef opterr
+#define opterr my_opterr
+#undef optind
+#define optind my_optind
+#undef optopt
+#define optopt my_optopt
+#undef optarg
+#define optarg my_optarg
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MY_WRAPPER_GETOPT_H_INCLUDED */
diff --git a/common/win/my_getopt-1.5/getopt.txt b/common/win/my_getopt-1.5/getopt.txt
new file mode 100644
index 00000000..ae08824e
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.txt
@@ -0,0 +1,330 @@
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+NAME
+ getopt - Parse command line options
+
+SYNOPSIS
+ #include <unistd.h>
+
+ int getopt(int argc, char * const argv[],
+ const char *optstring);
+
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+
+ #include <getopt.h>
+
+ int getopt_long(int argc, char * const argv[],
+ const char *optstring,
+ const struct option *longopts, int *longindex);
+
+ int getopt_long_only(int argc, char * const argv[],
+ const char *optstring,
+ const struct option *longopts, int *longindex);
+
+DESCRIPTION
+ The getopt() function parses the command line arguments.
+ Its arguments argc and argv are the argument count and
+ array as passed to the main() function on program invoca-
+ tion. An element of argv that starts with `-' (and is not
+ exactly "-" or "--") is an option element. The characters
+ of this element (aside from the initial `-') are option
+ characters. If getopt() is called repeatedly, it returns
+ successively each of the option characters from each of
+ the option elements.
+
+ If getopt() finds another option character, it returns
+ that character, updating the external variable optind and
+ a static variable nextchar so that the next call to
+ getopt() can resume the scan with the following option
+ character or argv-element.
+
+ If there are no more option characters, getopt() returns
+ EOF. Then optind is the index in argv of the first argv-
+ element that is not an option.
+
+ optstring is a string containing the legitimate option
+ characters. If such a character is followed by a colon,
+ the option requires an argument, so getopt places a
+ pointer to the following text in the same argv-element, or
+ the text of the following argv-element, in optarg. Two
+ colons mean an option takes an optional arg; if there is
+ text in the current argv-element, it is returned in
+ optarg, otherwise optarg is set to zero.
+
+ By default, getargs() permutes the contents of argv as it
+ scans, so that eventually all the non-options are at the
+
+
+
+GNU Aug 30, 1995 1
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ end. Two other modes are also implemented. If the first
+ character of optstring is `+' or the environment variable
+ POSIXLY_CORRECT is set, then option processing stops as
+ soon as a non-option argument is encountered. If the
+ first character of optstring is `-', then each non-option
+ argv-element is handled as if it were the argument of an
+ option with character code 1. (This is used by programs
+ that were written to expect options and other argv-ele-
+ ments in any order and that care about the ordering of the
+ two.) The special argument `--' forces an end of option-
+ scanning regardless of the scanning mode.
+
+ If getopt() does not recognize an option character, it
+ prints an error message to stderr, stores the character in
+ optopt, and returns `?'. The calling program may prevent
+ the error message by setting opterr to 0.
+
+ The getopt_long() function works like getopt() except that
+ it also accepts long options, started out by two dashes.
+ Long option names may be abbreviated if the abbreviation
+ is unique or is an exact match for some defined option. A
+ long option may take a parameter, of the form --arg=param
+ or --arg param.
+
+ longopts is a pointer to the first element of an array of
+ struct option declared in <getopt.h> as
+
+ struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+
+ The meanings of the different fields are:
+
+ name is the name of the long option.
+
+ has_arg
+ is: no_argument (or 0) if the option does not take
+ an argument, required_argument (or 1) if the option
+ requires an argument, or optional_argument (or 2)
+ if the option takes an optional argument.
+
+ flag specifies how results are returned for a long
+ option. If flag is NULL, then getopt_long()
+ returns val. (For example, the calling program may
+ set val to the equivalent short option character.)
+ Otherwise, getopt_long() returns 0, and flag points
+ to a variable which is set to val if the option is
+ found, but left unchanged if the option is not
+ found.
+
+ val is the value to return, or to load into the
+
+
+
+GNU Aug 30, 1995 2
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ variable pointed to by flag.
+
+ The last element of the array has to be filled with
+ zeroes.
+
+ If longindex is not NULL, it points to a variable which is
+ set to the index of the long option relative to longopts.
+
+ getopt_long_only() is like getopt_long(), but `-' as well
+ as `--' can indicate a long option. If an option that
+ starts with `-' (not `--') doesn't match a long option,
+ but does match a short option, it is parsed as a short
+ option instead.
+
+RETURN VALUE
+ The getopt() function returns the option character if the
+ option was found successfully, `:' if there was a missing
+ parameter for one of the options, `?' for an unknown
+ option character, or EOF for the end of the option list.
+
+ getopt_long() and getopt_long_only() also return the
+ option character when a short option is recognized. For a
+ long option, they return val if flag is NULL, and 0 other-
+ wise. Error and EOF returns are the same as for getopt(),
+ plus `?' for an ambiguous match or an extraneous parame-
+ ter.
+
+ENVIRONMENT VARIABLES
+ POSIXLY_CORRECT
+ If this is set, then option processing stops as
+ soon as a non-option argument is encountered.
+
+EXAMPLE
+ The following example program, from the source code,
+ illustrates the use of getopt_long() with most of its fea-
+ tures.
+
+ #include <stdio.h>
+
+ int
+ main (argc, argv)
+ int argc;
+ char **argv;
+ {
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+
+
+
+GNU Aug 30, 1995 3
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 1, 0, 'c'},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:012",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+
+
+GNU Aug 30, 1995 4
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+ }
+
+BUGS
+ This manpage is confusing.
+
+CONFORMING TO
+ getopt():
+ POSIX.1, provided the environment variable
+ POSIXLY_CORRECT is set. Otherwise, the elements of
+ argv aren't really const, because we permute them.
+ We pretend they're const in the prototype to be
+ compatible with other systems.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+GNU Aug 30, 1995 5
+
+
diff --git a/common/win/my_getopt-1.5/main.c b/common/win/my_getopt-1.5/main.c
new file mode 100644
index 00000000..25674e1c
--- /dev/null
+++ b/common/win/my_getopt-1.5/main.c
@@ -0,0 +1,387 @@
+/*
+ * copy - test program for my getopt() re-implementation
+ *
+ * This program is in the public domain.
+ */
+
+#define VERSION \
+"0.3"
+
+#define COPYRIGHT \
+"This program is in the public domain."
+
+/* for isprint(), printf(), fopen(), perror(), getenv(), strcmp(), etc. */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for my getopt() re-implementation */
+#include "getopt.h"
+
+/* the default verbosity level is 0 (no verbose reporting) */
+static unsigned verbose = 0;
+
+/* print version and copyright information */
+static void
+version(char *progname)
+{
+ printf("%s version %s\n"
+ "%s\n",
+ progname,
+ VERSION,
+ COPYRIGHT);
+}
+
+/* print a help summary */
+static void
+help(char *progname)
+{
+ printf("Usage: %s [options] [FILE]...\n"
+ "Options:\n"
+ "-h or -help show this message and exit\n"
+ "-append append to the output file\n"
+ "-o FILE or\n"
+ "-output FILE send output to FILE (default is stdout)\n"
+ "-r or --rotate rotate letters 13 positions (rot13)\n"
+ "-rNUM or\n"
+ "--rotate=NUM rotate letters NUM positions\n"
+ "-truncate truncate the output file "
+ "(this is the default)\n"
+ "-v or -verbose increase the level of verbosity by 1"
+ "(the default is 0)\n"
+ "-vNUM or\n"
+ "-verbose=NUM set the level of verbosity to NUM\n"
+ "-V or -version print program version and exit\n"
+ "\n"
+ "This program reads the specified FILEs "
+ "(or stdin if none are given)\n"
+ "and writes their bytes to the specified output FILE "
+ "(or stdout if none is\n"
+ "given.) It can optionally rotate letters.\n",
+ progname);
+}
+
+/* print usage information to stderr */
+static void
+usage(char *progname)
+{
+ fprintf(stderr,
+ "Summary: %s [-help] [-version] [options] [FILE]...\n",
+ progname);
+}
+
+/* input file handler -- returns nonzero or exit()s on failure */
+static int
+handle(char *progname,
+ FILE *infile, char *infilename,
+ FILE *outfile, char *outfilename,
+ int rotate)
+{
+ int c;
+ unsigned long bytes_copied = 0;
+
+ if (verbose > 2)
+ {
+ fprintf(stderr,
+ "%s: copying from `%s' to `%s'\n",
+ progname,
+ infilename,
+ outfilename);
+ }
+ while ((c = getc(infile)) != EOF)
+ {
+ if (rotate && isalpha(c))
+ {
+ const char *letters = "abcdefghijklmnopqrstuvwxyz";
+ char *match;
+ if ((match = strchr(letters, tolower(c))))
+ {
+ char rc = letters[(match - letters + rotate) % 26];
+ if (isupper(c))
+ rc = toupper(rc);
+ c = rc;
+ }
+ }
+ if (putc(c, outfile) == EOF)
+ {
+ perror(outfilename);
+ exit(1);
+ }
+ bytes_copied ++;
+ }
+ if (! feof(infile))
+ {
+ perror(infilename);
+ return 1;
+ }
+ if (verbose > 2)
+ {
+ fprintf(stderr,
+ "%s: %lu bytes copied from `%s' to `%s'\n",
+ progname,
+ bytes_copied,
+ infilename,
+ outfilename);
+ }
+ return 0;
+}
+
+/* argument parser and dispatcher */
+int
+main(int argc, char * argv[])
+{
+ /* the program name */
+ char *progname = argv[0];
+ /* during argument parsing, opt contains the return value from getopt() */
+ int opt;
+ /* the output filename is initially 0 (a.k.a. stdout) */
+ char *outfilename = 0;
+ /* the default return value is initially 0 (success) */
+ int retval = 0;
+ /* initially we truncate */
+ int append = 0;
+ /* initially we don't rotate letters */
+ int rotate = 0;
+
+ /* short options string */
+ char *shortopts = "Vho:r::v::";
+ /* long options list */
+ struct option longopts[] =
+ {
+ /* name, has_arg, flag, val */ /* longind */
+ { "append", no_argument, 0, 0 }, /* 0 */
+ { "truncate", no_argument, 0, 0 }, /* 1 */
+ { "version", no_argument, 0, 'V' }, /* 3 */
+ { "help", no_argument, 0, 'h' }, /* 4 */
+ { "output", required_argument, 0, 'o' }, /* 5 */
+ { "rotate", optional_argument, 0, 'r' }, /* 6 */
+ { "verbose", optional_argument, 0, 'v' }, /* 7 */
+ /* end-of-list marker */
+ { 0, 0, 0, 0 }
+ };
+ /* long option list index */
+ int longind = 0;
+
+ /*
+ * print a warning when the POSIXLY_CORRECT environment variable will
+ * interfere with argument placement
+ */
+ if (getenv("POSIXLY_CORRECT"))
+ {
+ fprintf(stderr,
+ "%s: "
+ "Warning: implicit argument reordering disallowed by "
+ "POSIXLY_CORRECT\n",
+ progname);
+ }
+
+ /* parse all options from the command line */
+ while ((opt =
+ getopt_long_only(argc, argv, shortopts, longopts, &longind)) != -1)
+ switch (opt)
+ {
+ case 0: /* a long option without an equivalent short option */
+ switch (longind)
+ {
+ case 0: /* -append */
+ append = 1;
+ break;
+ case 1: /* -truncate */
+ append = 0;
+ break;
+ default: /* something unexpected has happened */
+ fprintf(stderr,
+ "%s: "
+ "getopt_long_only unexpectedly returned %d for `--%s'\n",
+ progname,
+ opt,
+ longopts[longind].name);
+ return 1;
+ }
+ break;
+ case 'V': /* -version */
+ version(progname);
+ return 0;
+ case 'h': /* -help */
+ help(progname);
+ return 0;
+ case 'r': /* -rotate[=NUM] */
+ if (optarg)
+ {
+ /* we use this while trying to parse a numeric argument */
+ char ignored;
+ if (sscanf(optarg,
+ "%d%c",
+ &rotate,
+ &ignored) != 1)
+ {
+ fprintf(stderr,
+ "%s: "
+ "rotation `%s' is not a number\n",
+ progname,
+ optarg);
+ usage(progname);
+ return 2;
+ }
+ /* normalize rotation */
+ while (rotate < 0)
+ {
+ rotate += 26;
+ }
+ rotate %= 26;
+ }
+ else
+ rotate = 13;
+ break;
+ case 'o': /* -output=FILE */
+ outfilename = optarg;
+ /* we allow "-" as a synonym for stdout here */
+ if (! strcmp(optarg, "-"))
+ {
+ outfilename = 0;
+ }
+ break;
+ case 'v': /* -verbose[=NUM] */
+ if (optarg)
+ {
+ /* we use this while trying to parse a numeric argument */
+ char ignored;
+ if (sscanf(optarg,
+ "%u%c",
+ &verbose,
+ &ignored) != 1)
+ {
+ fprintf(stderr,
+ "%s: "
+ "verbosity level `%s' is not a number\n",
+ progname,
+ optarg);
+ usage(progname);
+ return 2;
+ }
+ }
+ else
+ verbose ++;
+ break;
+ case '?': /* getopt_long_only noticed an error */
+ usage(progname);
+ return 2;
+ default: /* something unexpected has happened */
+ fprintf(stderr,
+ "%s: "
+ "getopt_long_only returned an unexpected value (%d)\n",
+ progname,
+ opt);
+ return 1;
+ }
+
+ /* re-open stdout to outfilename, if requested */
+ if (outfilename)
+ {
+ if (! freopen(outfilename, (append ? "a" : "w"), stdout))
+ {
+ perror(outfilename);
+ return 1;
+ }
+ }
+ else
+ {
+ /* make a human-readable version of the output filename "-" */
+ outfilename = "stdout";
+ /* you can't truncate stdout */
+ append = 1;
+ }
+
+ if (verbose)
+ {
+ fprintf(stderr,
+ "%s: verbosity level is %u; %s `%s'; rotation %d\n",
+ progname,
+ verbose,
+ (append ? "appending to" : "truncating"),
+ outfilename,
+ rotate);
+ }
+
+ if (verbose > 1)
+ {
+ fprintf(stderr,
+ "%s: %d input file(s) were given\n",
+ progname,
+ ((argc > optind) ? (argc - optind) : 0));
+ }
+
+ if (verbose > 3)
+ {
+ fprintf(stderr,
+ "\topterr: %d\n\toptind: %d\n\toptopt: %d (%c)\n\toptarg: %s\n",
+ opterr,
+ optind,
+ optopt, optopt,
+ optarg ? optarg : "(null)");
+ }
+
+ /* handle each of the input files (or stdin, if no files were given) */
+ if (optind < argc)
+ {
+ int argindex;
+
+ for (argindex = optind; argindex < argc; argindex ++)
+ {
+ char *infilename = argv[argindex];
+ FILE *infile;
+
+ /* we allow "-" as a synonym for stdin here */
+ if (! strcmp(infilename, "-"))
+ {
+ infile = stdin;
+ infilename = "stdin";
+ }
+ else if (! (infile = fopen(infilename, "r")))
+ {
+ perror(infilename);
+ retval = 1;
+ continue;
+ }
+ if (handle(progname,
+ infile, argv[optind],
+ stdout, outfilename,
+ rotate))
+ {
+ retval = 1;
+ fclose(infile);
+ continue;
+ }
+ if ((infile != stdin) && fclose(infile))
+ {
+ perror(infilename);
+ retval = 1;
+ }
+ }
+ }
+ else
+ {
+ retval =
+ handle(progname,
+ stdin, "stdin",
+ stdout, outfilename,
+ rotate);
+ }
+
+ /* close stdout */
+ if (fclose(stdout))
+ {
+ perror(outfilename);
+ return 1;
+ }
+
+ if (verbose > 3)
+ {
+ fprintf(stderr,
+ "%s: normal return, exit code is %d\n",
+ progname,
+ retval);
+ }
+ return retval;
+}
diff --git a/common/win/my_getopt-1.5/my_getopt.c b/common/win/my_getopt-1.5/my_getopt.c
new file mode 100644
index 00000000..e3737df8
--- /dev/null
+++ b/common/win/my_getopt-1.5/my_getopt.c
@@ -0,0 +1,281 @@
+/*
+ * my_getopt.c - my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "my_getopt.h"
+
+int my_optind=1, my_opterr=1, my_optopt=0;
+char *my_optarg=0;
+
+/* reset argument parser to start-up values */
+int my_getopt_reset(void)
+{
+ my_optind = 1;
+ my_opterr = 1;
+ my_optopt = 0;
+ my_optarg = 0;
+ return 0;
+}
+
+/* this is the plain old UNIX getopt, with GNU-style extensions. */
+/* if you're porting some piece of UNIX software, this is all you need. */
+/* this supports GNU-style permution and optional arguments */
+
+int my_getopt(int argc, char * argv[], const char *opts)
+{
+ static int charind=0;
+ char mode, colon_mode;
+ int off = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *opts) == ':') off ++;
+ if(((mode = opts[off]) == '+') || (mode == '-')) {
+ off++;
+ if((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
+ off ++;
+ }
+ }
+ my_optarg = 0;
+ if(charind) {
+ const char *s;
+ my_optopt = argv[my_optind][charind];
+ for(s=opts+off; *s; s++) if(my_optopt == *s) {
+ charind++;
+ if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) {
+ if(argv[my_optind][charind]) {
+ my_optarg = &(argv[my_optind++][charind]);
+ charind = 0;
+ } else if(*(++s) != ':') {
+ charind = 0;
+ if(++my_optind >= argc) {
+ if(my_opterr) fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], my_optopt);
+ opt = (colon_mode == ':') ? ':' : '?';
+ goto my_getopt_ok;
+ }
+ my_optarg = argv[my_optind++];
+ }
+ }
+ opt = my_optopt;
+ goto my_getopt_ok;
+ }
+ if(my_opterr) fprintf(stderr,
+ "%s: illegal option -- %c\n",
+ argv[0], my_optopt);
+ opt = '?';
+ if(argv[my_optind][++charind] == '\0') {
+ my_optind++;
+ charind = 0;
+ }
+ my_getopt_ok:
+ if(charind && ! argv[my_optind][charind]) {
+ my_optind++;
+ charind = 0;
+ }
+ } else if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ if(mode == '+') opt = -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ charind = 0;
+ opt = 1;
+ } else {
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=my_getopt(argc, argv, opts);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++) argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ if(i == argc) opt = -1;
+ }
+ } else {
+ charind++;
+ opt = my_getopt(argc, argv, opts);
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+/* this is the extended getopt_long{,_only}, with some GNU-like
+ * extensions. Implements _getopt_internal in case any programs
+ * expecting GNU libc getopt call it.
+ */
+
+int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only)
+{
+ char mode, colon_mode = *shortopts;
+ int shortoff = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *shortopts) == ':') shortoff ++;
+ if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
+ shortoff++;
+ if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
+ shortoff ++;
+ }
+ }
+ my_optarg = 0;
+ if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ opt = -1;
+ if(mode == '+') return -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ return 1;
+ }
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=_my_getopt_internal(argc, argv, shortopts,
+ longopts, longind,
+ long_only);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++)
+ argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ } else if((!long_only) && (argv[my_optind][1] != '-'))
+ opt = my_getopt(argc, argv, shortopts);
+ else {
+ int charind, offset;
+ int found = 0, ind, hits = 0;
+
+ if(((my_optopt = argv[my_optind][1]) != '-') && ! argv[my_optind][2]) {
+ int c;
+
+ ind = shortoff;
+ while((c = shortopts[ind++])) {
+ if(((shortopts[ind] == ':') ||
+ ((c == 'W') && (shortopts[ind] == ';'))) &&
+ (shortopts[++ind] == ':'))
+ ind ++;
+ if(my_optopt == c) return my_getopt(argc, argv, shortopts);
+ }
+ }
+ offset = 2 - (argv[my_optind][1] != '-');
+ for(charind = offset;
+ (argv[my_optind][charind] != '\0') &&
+ (argv[my_optind][charind] != '=');
+ charind++);
+ for(ind = 0; longopts[ind].name && !hits; ind++)
+ if((strlen(longopts[ind].name) == (size_t) (charind - offset)) &&
+ (strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0))
+ found = ind, hits++;
+ if(!hits) for(ind = 0; longopts[ind].name; ind++)
+ if(strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0)
+ found = ind, hits++;
+ if(hits == 1) {
+ opt = 0;
+
+ if(argv[my_optind][charind] == '=') {
+ if(longopts[found].has_arg == 0) {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], longopts[found].name);
+ } else {
+ my_optarg = argv[my_optind] + ++charind;
+ charind = 0;
+ }
+ } else if(longopts[found].has_arg == 1) {
+ if(++my_optind >= argc) {
+ opt = (colon_mode == ':') ? ':' : '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' requires an argument\n",
+ argv[0], longopts[found].name);
+ } else my_optarg = argv[my_optind];
+ }
+ if(!opt) {
+ if (longind) *longind = found;
+ if(!longopts[found].flag) opt = longopts[found].val;
+ else *(longopts[found].flag) = longopts[found].val;
+ }
+ my_optind++;
+ } else if(!hits) {
+ if(offset == 1) opt = my_getopt(argc, argv, shortopts);
+ else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: unrecognized option `%s'\n",
+ argv[0], argv[my_optind++]);
+ }
+ } else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `%s' is ambiguous\n",
+ argv[0], argv[my_optind++]);
+ }
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0);
+}
+
+int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1);
+}
diff --git a/common/win/my_getopt-1.5/my_getopt.h b/common/win/my_getopt-1.5/my_getopt.h
new file mode 100644
index 00000000..2c1dd66f
--- /dev/null
+++ b/common/win/my_getopt-1.5/my_getopt.h
@@ -0,0 +1,72 @@
+/*
+ * my_getopt.h - interface to my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MY_GETOPT_H_INCLUDED
+#define MY_GETOPT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* reset argument parser to start-up values */
+extern int my_getopt_reset(void);
+
+/* UNIX-style short-argument parser */
+extern int my_getopt(int argc, char * argv[], const char *opts);
+
+extern int my_optind, my_opterr, my_optopt;
+extern char *my_optarg;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* human-readable values for has_arg */
+#undef no_argument
+#define no_argument 0
+#undef required_argument
+#define required_argument 1
+#undef optional_argument
+#define optional_argument 2
+
+/* GNU-style long-argument parsers */
+extern int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MY_GETOPT_H_INCLUDED */