main
pixman.c
1/* wld: pixman.c
2 *
3 * Copyright (c) 2013, 2014 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "pixman.h"
25#include "wld-private.h"
26
27#include <string.h>
28
29#define PIXMAN_COLOR(c) \
30 { \
31 .alpha = ((c >> 24) & 0xff) * 0x101, \
32 .red = ((c >> 16) & 0xff) * 0x101, \
33 .green = ((c >> 8) & 0xff) * 0x101, \
34 .blue = ((c >> 0) & 0xff) * 0x101, \
35 }
36
37struct pixman_renderer {
38 struct wld_renderer base;
39 pixman_image_t *target;
40 pixman_glyph_cache_t *glyph_cache;
41};
42
43struct pixman_buffer {
44 struct buffer base;
45 pixman_image_t *image;
46};
47
48struct pixman_map {
49 struct wld_exporter exporter;
50 struct wld_destructor destructor;
51 pixman_image_t *image;
52};
53
54#include "interface/context.h"
55#define RENDERER_IMPLEMENTS_REGION
56#include "interface/buffer.h"
57#include "interface/renderer.h"
58IMPL(pixman_renderer, wld_renderer)
59IMPL(pixman_buffer, wld_buffer)
60
61static struct wld_context context = { .impl = &wld_context_impl };
62
63EXPORT
64struct wld_context *wld_pixman_context = &context;
65
66struct wld_renderer *
67context_create_renderer(struct wld_context *context)
68{
69 struct pixman_renderer *renderer;
70
71 if (!(renderer = malloc(sizeof *renderer)))
72 goto error0;
73
74 if (!(renderer->glyph_cache = pixman_glyph_cache_create()))
75 goto error1;
76
77 renderer_initialize(&renderer->base, &wld_renderer_impl);
78 renderer->target = NULL;
79
80 return &renderer->base;
81
82error1:
83 free(renderer);
84error0:
85 return NULL;
86}
87
88static struct buffer *
89new_buffer(pixman_image_t *image)
90{
91 struct pixman_buffer *buffer;
92
93 if (!(buffer = malloc(sizeof *buffer)))
94 return NULL;
95
96 buffer_initialize(&buffer->base, &wld_buffer_impl,
97 pixman_image_get_width(image),
98 pixman_image_get_height(image),
99 format_pixman_to_wld(pixman_image_get_format(image)),
100 pixman_image_get_stride(image));
101 buffer->base.base.map = pixman_image_get_data(image);
102 buffer->image = image;
103
104 return &buffer->base;
105}
106
107struct buffer *
108context_create_buffer(struct wld_context *context,
109 uint32_t width, uint32_t height,
110 uint32_t format, uint32_t flags)
111{
112 struct buffer *buffer;
113 pixman_image_t *image;
114
115 image = pixman_image_create_bits(format_wld_to_pixman(format),
116 width, height, NULL, 0);
117
118 if (!image)
119 goto error0;
120
121 if (!(buffer = new_buffer(image)))
122 goto error1;
123
124 return buffer;
125
126error1:
127 pixman_image_unref(image);
128error0:
129 return NULL;
130}
131
132struct buffer *
133context_import_buffer(struct wld_context *context,
134 uint32_t type, union wld_object object,
135 uint32_t width, uint32_t height,
136 uint32_t format, uint32_t pitch)
137{
138 struct buffer *buffer;
139 pixman_image_t *image;
140
141 switch (type) {
142 case WLD_OBJECT_DATA:
143 image = pixman_image_create_bits(format_wld_to_pixman(format),
144 width, height, object.ptr, pitch);
145 break;
146 default:
147 image = NULL;
148 }
149
150 if (!image)
151 goto error0;
152
153 if (!(buffer = new_buffer(image)))
154 goto error1;
155
156 return buffer;
157
158error1:
159 pixman_image_unref(image);
160error0:
161 return NULL;
162}
163
164void
165context_destroy(struct wld_context *context)
166{
167}
168
169uint32_t
170renderer_capabilities(struct wld_renderer *renderer, struct buffer *buffer)
171{
172 /* The pixman renderer can read and write to any buffer using it's map
173 * implementation. */
174 return WLD_CAPABILITY_READ | WLD_CAPABILITY_WRITE;
175}
176
177static void
178destroy_image(pixman_image_t *image, void *data)
179{
180 struct buffer *buffer = data;
181
182 wld_unmap(&buffer->base);
183}
184
185bool
186map_export(struct wld_exporter *exporter, struct wld_buffer *buffer,
187 uint32_t type, union wld_object *object)
188{
189 struct pixman_map *map = CONTAINER_OF(exporter, struct pixman_map, exporter);
190
191 switch (type) {
192 case WLD_PIXMAN_OBJECT_IMAGE:
193 object->ptr = pixman_image_ref(map->image);
194 return true;
195 default:
196 return false;
197 }
198}
199
200void
201map_destroy(struct wld_destructor *destructor)
202{
203 struct pixman_map *map = CONTAINER_OF(destructor, struct pixman_map, destructor);
204
205 pixman_image_unref(map->image);
206 free(map);
207}
208
209static pixman_image_t *
210pixman_image(struct buffer *buffer)
211{
212 if (buffer->base.impl == &wld_buffer_impl)
213 return pixman_image_ref(pixman_buffer(&buffer->base)->image);
214
215 union wld_object object;
216
217 if (wld_export(&buffer->base, WLD_PIXMAN_OBJECT_IMAGE, &object))
218 return object.ptr;
219
220 struct pixman_map *map;
221 pixman_image_t *image;
222
223 if (!wld_map(&buffer->base))
224 goto error0;
225
226 image = pixman_image_create_bits(format_wld_to_pixman(buffer->base.format),
227 buffer->base.width, buffer->base.height,
228 buffer->base.map, buffer->base.pitch);
229
230 if (!image)
231 goto error1;
232
233 if (!(map = malloc(sizeof *map)))
234 goto error2;
235
236 map->image = image;
237 map->exporter.export = &map_export;
238 wld_buffer_add_exporter(&buffer->base, &map->exporter);
239 map->destructor.destroy = &map_destroy;
240 wld_buffer_add_destructor(&buffer->base, &map->destructor);
241 pixman_image_set_destroy_function(image, &destroy_image, buffer);
242
243 return pixman_image_ref(image);
244
245error2:
246 pixman_image_unref(image);
247error1:
248 wld_unmap(&buffer->base);
249error0:
250 return NULL;
251}
252
253bool
254renderer_set_target(struct wld_renderer *base, struct buffer *buffer)
255{
256 struct pixman_renderer *renderer = pixman_renderer(base);
257
258 if (renderer->target)
259 pixman_image_unref(renderer->target);
260
261 if (buffer)
262 return (renderer->target = pixman_image(buffer));
263
264 renderer->target = NULL;
265 return true;
266}
267
268void
269renderer_fill_rectangle(struct wld_renderer *base, uint32_t color,
270 int32_t x, int32_t y,
271 uint32_t width, uint32_t height)
272{
273 struct pixman_renderer *renderer = pixman_renderer(base);
274 pixman_color_t pixman_color = PIXMAN_COLOR(color);
275 pixman_box32_t box = { x, y, x + width, y + height };
276
277 pixman_image_fill_boxes(PIXMAN_OP_SRC, renderer->target,
278 &pixman_color, 1, &box);
279}
280
281void
282renderer_fill_region(struct wld_renderer *base, uint32_t color,
283 pixman_region32_t *region)
284{
285 struct pixman_renderer *renderer = pixman_renderer(base);
286 pixman_color_t pixman_color = PIXMAN_COLOR(color);
287 pixman_box32_t *boxes;
288 int num_boxes;
289
290 boxes = pixman_region32_rectangles(region, &num_boxes);
291 pixman_image_fill_boxes(PIXMAN_OP_SRC, renderer->target,
292 &pixman_color, num_boxes, boxes);
293}
294
295void
296renderer_copy_rectangle(struct wld_renderer *base, struct buffer *buffer,
297 int32_t dst_x, int32_t dst_y,
298 int32_t src_x, int32_t src_y,
299 uint32_t width, uint32_t height)
300{
301 struct pixman_renderer *renderer = pixman_renderer(base);
302 pixman_image_t *src = pixman_image(buffer), *dst = renderer->target;
303
304 if (!src)
305 return;
306
307 pixman_image_composite32(PIXMAN_OP_SRC, src, NULL, dst,
308 src_x, src_y, 0, 0, dst_x, dst_y, width, height);
309 pixman_image_unref(src);
310}
311
312void
313renderer_copy_region(struct wld_renderer *base, struct buffer *buffer,
314 int32_t dst_x, int32_t dst_y,
315 pixman_region32_t *region)
316{
317 struct pixman_renderer *renderer = pixman_renderer(base);
318 pixman_image_t *src = pixman_image(buffer), *dst = renderer->target;
319 pixman_region32_t clip;
320
321 if (!src)
322 return;
323
324 pixman_region32_init(&clip);
325 pixman_region32_copy(&clip, region);
326 pixman_region32_translate(&clip, dst_x, dst_y);
327
328 pixman_image_set_clip_region32(dst, &clip);
329 pixman_image_composite32(PIXMAN_OP_SRC, src, NULL, dst,
330 region->extents.x1, region->extents.y1, 0, 0,
331 region->extents.x1 + dst_x,
332 region->extents.y1 + dst_y,
333 region->extents.x2 - region->extents.x1,
334 region->extents.y2 - region->extents.y1);
335 pixman_image_unref(src);
336 pixman_image_set_clip_region32(dst, NULL);
337
338 pixman_region32_fini(&clip);
339}
340
341static inline uint8_t
342reverse(uint8_t byte)
343{
344 byte = ((byte << 1) & 0xaa) | ((byte >> 1) & 0x55);
345 byte = ((byte << 2) & 0xcc) | ((byte >> 2) & 0x33);
346 byte = ((byte << 4) & 0xf0) | ((byte >> 4) & 0x0f);
347
348 return byte;
349}
350
351static pixman_image_t *
352glyph_bitmap_to_pixman_image(struct glyph *glyph)
353{
354 FT_Bitmap *bitmap = &glyph->bitmap;
355 pixman_image_t *image;
356 uint8_t *src, *dst;
357 uint32_t row, byte_index, bytes_per_row, pitch;
358
359 switch (bitmap->pixel_mode) {
360 case FT_PIXEL_MODE_MONO:
361 image = pixman_image_create_bits(PIXMAN_a1, bitmap->width,
362 bitmap->rows, NULL, bitmap->pitch);
363 if (!image)
364 return NULL;
365
366 pitch = pixman_image_get_stride(image);
367 bytes_per_row = (bitmap->width + 7) / 8;
368 src = bitmap->buffer;
369 dst = (uint8_t *)pixman_image_get_data(image);
370
371 for (row = 0; row < bitmap->rows; ++row) {
372 /* Pixman's A1 format expects the bits in the opposite order
373 * that Freetype gives us. */
374 for (byte_index = 0; byte_index < bytes_per_row; ++byte_index)
375 dst[byte_index] = reverse(src[byte_index]);
376
377 dst += pitch;
378 src += bitmap->pitch;
379 }
380 return image;
381 case FT_PIXEL_MODE_GRAY:
382 image = pixman_image_create_bits(PIXMAN_a8, bitmap->width,
383 bitmap->rows, NULL, 0);
384 if (!image)
385 return NULL;
386
387 pitch = pixman_image_get_stride(image);
388 src = bitmap->buffer;
389 dst = (uint8_t *)pixman_image_get_data(image);
390
391 for (row = 0; row < bitmap->rows; ++row) {
392 memset(dst, 0, pitch);
393 memcpy(dst, src, bitmap->width);
394 dst += pitch;
395 src += bitmap->pitch;
396 }
397 return image;
398 default:
399 return NULL;
400 }
401}
402
403void
404renderer_draw_text(struct wld_renderer *base,
405 struct font *font, uint32_t color,
406 int32_t x, int32_t y, const char *text,
407 uint32_t length, struct wld_extents *extents)
408{
409 struct pixman_renderer *renderer = pixman_renderer(base);
410 int ret;
411 uint32_t c;
412 struct glyph *glyph;
413 FT_UInt glyph_index;
414 pixman_glyph_t *glyphs;
415 uint32_t index = 0, origin_x = 0;
416 pixman_color_t pixman_color = PIXMAN_COLOR(color);
417 pixman_image_t *solid;
418
419 if (length == -1)
420 length = strlen(text);
421 glyphs = malloc(length * sizeof(glyphs[0]));
422 if (!glyphs)
423 return;
424 solid = pixman_image_create_solid_fill(&pixman_color);
425
426 while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length)) > 0 && c != '\0') {
427 text += ret;
428 length -= ret;
429 glyph_index = FT_Get_Char_Index(font->face, c);
430
431 if (!font_ensure_glyph(font, glyph_index))
432 continue;
433
434 glyph = font->glyphs[glyph_index];
435
436 glyphs[index].x = origin_x;
437 glyphs[index].y = 0;
438 glyphs[index].glyph = pixman_glyph_cache_lookup(renderer->glyph_cache,
439 font, glyph);
440
441 /* If we don't have the glyph in our cache, do some conversions to make
442 * pixman happy, and then insert it. */
443 if (!glyphs[index].glyph) {
444 pixman_image_t *image;
445 image = glyph_bitmap_to_pixman_image(glyph);
446
447 if (!image)
448 goto advance;
449
450 /* Insert the glyph into the cache. */
451 pixman_glyph_cache_freeze(renderer->glyph_cache);
452 glyphs[index].glyph = pixman_glyph_cache_insert(renderer->glyph_cache, font, glyph,
453 -glyph->x, -glyph->y, image);
454 pixman_glyph_cache_thaw(renderer->glyph_cache);
455
456 /* The glyph cache copies the contents of the glyph bitmap. */
457 pixman_image_unref(image);
458 }
459
460 ++index;
461
462 advance:
463 origin_x += glyph->advance;
464 }
465
466 pixman_composite_glyphs_no_mask(PIXMAN_OP_OVER, solid, renderer->target,
467 0, 0, x, y, renderer->glyph_cache,
468 index, glyphs);
469
470 free(glyphs);
471 pixman_image_unref(solid);
472
473 if (extents)
474 extents->advance = origin_x;
475}
476
477void
478renderer_flush(struct wld_renderer *renderer)
479{
480}
481
482void
483renderer_destroy(struct wld_renderer *base)
484{
485 struct pixman_renderer *renderer = pixman_renderer(base);
486
487 pixman_glyph_cache_destroy(renderer->glyph_cache);
488 free(renderer);
489}
490
491bool
492buffer_map(struct buffer *buffer)
493{
494 return true;
495}
496
497bool
498buffer_unmap(struct buffer *buffer)
499{
500 return true;
501}
502
503void
504buffer_destroy(struct buffer *base)
505{
506 struct pixman_buffer *buffer = pixman_buffer(&base->base);
507
508 pixman_image_unref(buffer->image);
509 free(buffer);
510}