diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-09-19 21:25:46 +0300 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@gmail.com> | 2012-03-20 15:25:36 +0100 |
commit | a9ae774c901269c55b7ce8ccf5c26483bedeea58 (patch) | |
tree | a51ff01c4900976a621f535e00cf2ecdbf003566 |
fresh start
57 files changed, 21770 insertions, 0 deletions
diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 0000000..07491dd --- /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 0000000..7fcdfe9 --- /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 0000000..732df91 --- /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, ©->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, ©->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(®ion); + for (; now < end; now++) { + region_add(®ion, now); + } + __canvas_copy_region_bits(data, stride, bbox, src_pos, ®ion); + region_destroy(®ion); + 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 0000000..3d9a930 --- /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 0000000..a5519ff --- /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 0000000..778244f --- /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 0000000..f6470ca --- /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 0000000..8ccc304 --- /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 0000000..e277b2d --- /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 0000000..99079f5 --- /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, ©->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, ©->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, ©->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 0000000..13d7ef3 --- /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 0000000..aac93c9 --- /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, ©->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, ©->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 0000000..b9a4eb8 --- /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 0000000..4126677 --- /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 0000000..5300ebd --- /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 *)¤t_point[2], + (GLdouble *)¤t_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 0000000..1958110 --- /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 *)¤t_point[2], + (GLdouble *)¤t_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 0000000..6213c37 --- /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 0000000..1d9b3e1 --- /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 0000000..f23461a --- /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 0000000..a2fbdea --- /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 0000000..eac44af --- /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 0000000..074f633 --- /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 0000000..75c32e2 --- /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 0000000..be1b941 --- /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 0000000..1020e7b --- /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 0000000..36502c6 --- /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 0000000..373d54e --- /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 0000000..6b17511 --- /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 0000000..85a551a --- /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 0000000..7568f9e --- /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 0000000..8824db7 --- /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 0000000..ab418b1 --- /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 0000000..58ae693 --- /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 0000000..2007df6 --- /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 0000000..5a0a8e5 --- /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 0000000..bb8c7cb --- /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 0000000..569b530 --- /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 0000000..4b0a364 --- /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 0000000..c08fd51 --- /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 0000000..0baa13f --- /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 0000000..d1d7f4a --- /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 0000000..29a5822 --- /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 0000000..4c17713 --- /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 0000000..08f9cad --- /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 0000000..fcf8642 --- /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 0000000..7451210 --- /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 0000000..d8b5add --- /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 0000000..a671fdb --- /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 0000000..2224aba --- /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 0000000..083cc4a --- /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 0000000..3a9afad --- /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 0000000..a63fcd8 --- /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 0000000..5f08ccb --- /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 0000000..ae08824 --- /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 0000000..25674e1 --- /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 0000000..e3737df --- /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 0000000..2c1dd66 --- /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 */ |