/* wld: pixman.c * * Copyright (c) 2013, 2014 Michael Forney * * 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 "pixman.h" #include "wld-private.h" #define PIXMAN_COLOR(c) { \ .alpha = ((c >> 24) & 0xff) * 0x101, \ .red = ((c >> 16) & 0xff) * 0x101, \ .green = ((c >> 8) & 0xff) * 0x101, \ .blue = ((c >> 0) & 0xff) * 0x101, \ } struct pixman_renderer { struct wld_renderer base; pixman_image_t * target; pixman_glyph_cache_t * glyph_cache; }; struct pixman_buffer { struct buffer base; pixman_image_t * image; }; struct pixman_map { struct wld_exporter exporter; struct wld_destructor destructor; pixman_image_t * image; }; #include "interface/context.h" #define RENDERER_IMPLEMENTS_REGION #include "interface/renderer.h" #include "interface/buffer.h" IMPL(pixman_renderer, wld_renderer) IMPL(pixman_buffer, wld_buffer) static struct wld_context context = { .impl = &wld_context_impl }; EXPORT struct wld_context * wld_pixman_context = &context; struct wld_renderer * context_create_renderer(struct wld_context * context) { struct pixman_renderer * renderer; if (!(renderer = malloc(sizeof *renderer))) goto error0; if (!(renderer->glyph_cache = pixman_glyph_cache_create())) goto error1; renderer_initialize(&renderer->base, &wld_renderer_impl); renderer->target = NULL; return &renderer->base; error1: free(renderer); error0: return NULL; } static struct buffer * new_buffer(pixman_image_t * image) { struct pixman_buffer * buffer; if (!(buffer = malloc(sizeof *buffer))) return NULL; buffer_initialize(&buffer->base, &wld_buffer_impl, pixman_image_get_width(image), pixman_image_get_height(image), format_pixman_to_wld(pixman_image_get_format(image)), pixman_image_get_stride(image)); buffer->base.base.map = pixman_image_get_data(image); buffer->image = image; return &buffer->base; } struct buffer * context_create_buffer(struct wld_context * context, uint32_t width, uint32_t height, uint32_t format, uint32_t flags) { struct buffer * buffer; pixman_image_t * image; image = pixman_image_create_bits(format_wld_to_pixman(format), width, height, NULL, 0); if (!image) goto error0; if (!(buffer = new_buffer(image))) goto error1; return buffer; error1: pixman_image_unref(image); error0: return NULL; } struct buffer * context_import_buffer(struct wld_context * context, uint32_t type, union wld_object object, uint32_t width, uint32_t height, uint32_t format, uint32_t pitch) { struct buffer * buffer; pixman_image_t * image; switch (type) { case WLD_OBJECT_DATA: image = pixman_image_create_bits(format_wld_to_pixman(format), width, height, object.ptr, pitch); break; default: image = NULL; } if (!image) goto error0; if (!(buffer = new_buffer(image))) goto error1; return buffer; error1: pixman_image_unref(image); error0: return NULL; } void context_destroy(struct wld_context * context) { } uint32_t renderer_capabilities(struct wld_renderer * renderer, struct buffer * buffer) { /* The pixman renderer can read and write to any buffer using it's map * implementation. */ return WLD_CAPABILITY_READ | WLD_CAPABILITY_WRITE; } static void destroy_image(pixman_image_t * image, void * data) { struct buffer * buffer = data; wld_unmap(&buffer->base); } bool map_export(struct wld_exporter * exporter, struct wld_buffer * buffer, uint32_t type, union wld_object * object) { struct pixman_map * map = CONTAINER_OF(exporter, struct pixman_map, exporter); switch (type) { case WLD_PIXMAN_OBJECT_IMAGE: object->ptr = pixman_image_ref(map->image); return true; default: return false; } } void map_destroy(struct wld_destructor * destructor) { struct pixman_map * map = CONTAINER_OF(destructor, struct pixman_map, destructor); pixman_image_unref(map->image); free(map); } static pixman_image_t * pixman_image(struct buffer * buffer) { if (buffer->base.impl == &wld_buffer_impl) return pixman_image_ref(pixman_buffer(&buffer->base)->image); union wld_object object; if (wld_export(&buffer->base, WLD_PIXMAN_OBJECT_IMAGE, &object)) return object.ptr; struct pixman_map * map; pixman_image_t * image; if (!wld_map(&buffer->base)) goto error0; image = pixman_image_create_bits(format_wld_to_pixman(buffer->base.format), buffer->base.width, buffer->base.height, buffer->base.map, buffer->base.pitch); if (!image) goto error1; if (!(map = malloc(sizeof *map))) goto error2; map->image = image; map->exporter.export = &map_export; wld_buffer_add_exporter(&buffer->base, &map->exporter); map->destructor.destroy = &map_destroy; wld_buffer_add_destructor(&buffer->base, &map->destructor); pixman_image_set_destroy_function(image, &destroy_image, buffer); return pixman_image_ref(image); error2: pixman_image_unref(image); error1: wld_unmap(&buffer->base); error0: return NULL; } bool renderer_set_target(struct wld_renderer * base, struct buffer * buffer) { struct pixman_renderer * renderer = pixman_renderer(base); if (renderer->target) pixman_image_unref(renderer->target); if (buffer) return (renderer->target = pixman_image(buffer)); renderer->target = NULL; return true; } void renderer_fill_rectangle(struct wld_renderer * base, uint32_t color, int32_t x, int32_t y, uint32_t width, uint32_t height) { struct pixman_renderer * renderer = pixman_renderer(base); pixman_color_t pixman_color = PIXMAN_COLOR(color); pixman_box32_t box = { x, y, x + width, y + height }; pixman_image_fill_boxes(PIXMAN_OP_SRC, renderer->target, &pixman_color, 1, &box); } void renderer_fill_region(struct wld_renderer * base, uint32_t color, pixman_region32_t * region) { struct pixman_renderer * renderer = pixman_renderer(base); pixman_color_t pixman_color = PIXMAN_COLOR(color); pixman_box32_t * boxes; int num_boxes; boxes = pixman_region32_rectangles(region, &num_boxes); pixman_image_fill_boxes(PIXMAN_OP_SRC, renderer->target, &pixman_color, num_boxes, boxes); } void renderer_copy_rectangle(struct wld_renderer * base, struct buffer * buffer, int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height) { struct pixman_renderer * renderer = pixman_renderer(base); pixman_image_t * src = pixman_image(buffer), * dst = renderer->target; if (!src) return; pixman_image_composite32(PIXMAN_OP_SRC, src, NULL, dst, src_x, src_y, 0, 0, dst_x, dst_y, width, height); } void renderer_copy_region(struct wld_renderer * base, struct buffer * buffer, int32_t dst_x, int32_t dst_y, pixman_region32_t * region) { struct pixman_renderer * renderer = pixman_renderer(base); pixman_image_t * src = pixman_image(buffer), * dst = renderer->target; if (!src) return; pixman_image_set_clip_region32(src, region); pixman_image_composite32(PIXMAN_OP_SRC, src, NULL, dst, region->extents.x1, region->extents.y1, 0, 0, region->extents.x1 + dst_x, region->extents.y1 + dst_y, region->extents.x2 - region->extents.x1, region->extents.y2 - region->extents.y1); pixman_image_set_clip_region32(src, NULL); } static inline uint8_t reverse(uint8_t byte) { byte = ((byte << 1) & 0xaa) | ((byte >> 1) & 0x55); byte = ((byte << 2) & 0xcc) | ((byte >> 2) & 0x33); byte = ((byte << 4) & 0xf0) | ((byte >> 4) & 0x0f); return byte; } void renderer_draw_text(struct wld_renderer * base, struct font * font, uint32_t color, int32_t x, int32_t y, const char * text, uint32_t length, struct wld_extents * extents) { struct pixman_renderer * renderer = pixman_renderer(base); int ret; uint32_t c; struct glyph * glyph; FT_UInt glyph_index; pixman_glyph_t glyphs[length == -1 ? (length = strlen(text)) : length]; uint32_t index = 0, origin_x = 0; pixman_color_t pixman_color = PIXMAN_COLOR(color); pixman_image_t * solid; solid = pixman_image_create_solid_fill(&pixman_color); while ((ret = FcUtf8ToUcs4((FcChar8 *) text, &c, length)) > 0 && c != '\0') { text += ret; length -= ret; glyph_index = FT_Get_Char_Index(font->face, c); if (!font_ensure_glyph(font, glyph_index)) continue; glyph = font->glyphs[glyph_index]; glyphs[index].x = origin_x; glyphs[index].y = 0; glyphs[index].glyph = pixman_glyph_cache_lookup(renderer->glyph_cache, font, glyph); /* If we don't have the glyph in our cache, do some conversions to make * pixman happy, and then insert it. */ if (!glyphs[index].glyph) { uint8_t * src, * dst; uint32_t row, byte_index, bytes_per_row, pitch; pixman_image_t * image; FT_Bitmap * bitmap; bitmap = &glyph->bitmap; image = pixman_image_create_bits (PIXMAN_a1, bitmap->width, bitmap->rows, NULL, bitmap->pitch); if (!image) goto advance; pitch = pixman_image_get_stride(image); bytes_per_row = (bitmap->width + 7) / 8; src = bitmap->buffer; dst = (uint8_t *) pixman_image_get_data(image); for (row = 0; row < bitmap->rows; ++row) { /* Pixman's A1 format expects the bits in the opposite order * that Freetype gives us. Sigh... */ for (byte_index = 0; byte_index < bytes_per_row; ++byte_index) dst[byte_index] = reverse(src[byte_index]); dst += pitch; src += bitmap->pitch; } /* Insert the glyph into the cache. */ pixman_glyph_cache_freeze(renderer->glyph_cache); glyphs[index].glyph = pixman_glyph_cache_insert (renderer->glyph_cache, font, glyph, -glyph->x, -glyph->y, image); pixman_glyph_cache_thaw(renderer->glyph_cache); /* The glyph cache copies the contents of the glyph bitmap. */ pixman_image_unref(image); } ++index; advance: origin_x += glyph->advance; } pixman_composite_glyphs_no_mask(PIXMAN_OP_OVER, solid, renderer->target, 0, 0, x, y, renderer->glyph_cache, index, glyphs); pixman_image_unref(solid); if (extents) extents->advance = origin_x; } void renderer_flush(struct wld_renderer * renderer) { } void renderer_destroy(struct wld_renderer * base) { struct pixman_renderer * renderer = pixman_renderer(base); pixman_glyph_cache_destroy(renderer->glyph_cache); free(renderer); } bool buffer_map(struct buffer * buffer) { return true; } bool buffer_unmap(struct buffer * buffer) { return true; } void buffer_destroy(struct buffer * base) { struct pixman_buffer * buffer = pixman_buffer(&base->base); pixman_image_unref(buffer->image); free(buffer); }