aboutsummaryrefslogtreecommitdiff
path: root/src/wld/pixman.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wld/pixman.c')
-rw-r--r--src/wld/pixman.c450
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);
+}
+