diff options
Diffstat (limited to 'src/wld/pixman.c')
-rw-r--r-- | src/wld/pixman.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/wld/pixman.c b/src/wld/pixman.c new file mode 100644 index 0000000..5f14a7b --- /dev/null +++ b/src/wld/pixman.c @@ -0,0 +1,450 @@ +/* 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); +} + |