main renderer.c
  1/* wld: renderer.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 "wld-private.h"
 25
 26void
 27default_fill_region(struct wld_renderer *renderer, uint32_t color, pixman_region32_t *region)
 28{
 29	pixman_box32_t *box;
 30	int num_boxes;
 31
 32	box = pixman_region32_rectangles(region, &num_boxes);
 33
 34	while (num_boxes--) {
 35		renderer->impl->fill_rectangle(renderer, color, box->x1, box->y1,
 36		                               box->x2 - box->x1, box->y2 - box->y1);
 37		++box;
 38	}
 39}
 40
 41void
 42default_copy_region(struct wld_renderer *renderer, struct buffer *buffer,
 43                    int32_t dst_x, int32_t dst_y,
 44                    pixman_region32_t *region)
 45{
 46	pixman_box32_t *box;
 47	int num_boxes;
 48
 49	box = pixman_region32_rectangles(region, &num_boxes);
 50
 51	while (num_boxes--) {
 52		renderer->impl->copy_rectangle(renderer, buffer,
 53		                               dst_x + box->x1, dst_y + box->y1,
 54		                               box->x1, box->y1,
 55		                               box->x2 - box->x1, box->y2 - box->y1);
 56		++box;
 57	}
 58}
 59
 60void
 61renderer_initialize(struct wld_renderer *renderer, const struct wld_renderer_impl *impl)
 62{
 63	*((const struct wld_renderer_impl **)&renderer->impl) = impl;
 64	renderer->target = NULL;
 65}
 66
 67EXPORT
 68void
 69wld_destroy_renderer(struct wld_renderer *renderer)
 70{
 71	renderer->impl->destroy(renderer);
 72}
 73
 74EXPORT
 75uint32_t
 76wld_capabilities(struct wld_renderer *renderer, struct wld_buffer *buffer)
 77{
 78	return renderer->impl->capabilities(renderer, (struct buffer *)buffer);
 79}
 80
 81EXPORT
 82bool
 83wld_set_target_buffer(struct wld_renderer *renderer, struct wld_buffer *buffer)
 84{
 85	if (!renderer->impl->set_target(renderer, (struct buffer *)buffer))
 86		return false;
 87
 88	renderer->target = buffer;
 89
 90	return true;
 91}
 92
 93EXPORT
 94bool
 95wld_set_target_surface(struct wld_renderer *renderer, struct wld_surface *surface)
 96{
 97	struct buffer *back_buffer;
 98
 99	if (!(back_buffer = surface->impl->back(surface)))
100		return false;
101
102	if (!renderer->impl->set_target(renderer, back_buffer))
103		return false;
104
105	renderer->target = &back_buffer->base;
106	return true;
107}
108
109EXPORT
110void
111wld_fill_rectangle(struct wld_renderer *renderer, uint32_t color,
112                   int32_t x, int32_t y, uint32_t width, uint32_t height)
113{
114	renderer->impl->fill_rectangle(renderer, color, x, y, width, height);
115}
116
117EXPORT
118void
119wld_fill_region(struct wld_renderer *renderer, uint32_t color, pixman_region32_t *region)
120{
121	renderer->impl->fill_region(renderer, color, region);
122}
123
124EXPORT
125void
126wld_copy_rectangle(struct wld_renderer *renderer,
127                   struct wld_buffer *buffer,
128                   int32_t dst_x, int32_t dst_y,
129                   int32_t src_x, int32_t src_y,
130                   uint32_t width, uint32_t height)
131{
132	renderer->impl->copy_rectangle(renderer, (struct buffer *)buffer,
133	                               dst_x, dst_y, src_x, src_y, width, height);
134}
135
136EXPORT
137void
138wld_copy_region(struct wld_renderer *renderer,
139                struct wld_buffer *buffer,
140                int32_t dst_x, int32_t dst_y, pixman_region32_t *region)
141{
142	renderer->impl->copy_region(renderer, (struct buffer *)buffer,
143	                            dst_x, dst_y, region);
144}
145
146EXPORT
147void
148wld_blend_region(struct wld_renderer *renderer, struct wld_buffer *buffer,
149                 int32_t dst_x, int32_t dst_y, pixman_region32_t *region)
150{
151	pixman_image_t *src = NULL, *dst = NULL;
152	pixman_region32_t clip;
153	pixman_box32_t *extents;
154
155	if (!renderer->target || !pixman_region32_not_empty(region))
156		return;
157
158	/* Complete accelerator writes before accessing the buffers on the CPU. */
159	renderer->impl->flush(renderer);
160	if (!wld_map(buffer))
161		return;
162	if (!wld_map(renderer->target))
163		goto unmap_src;
164
165	src = pixman_image_create_bits(format_wld_to_pixman(buffer->format),
166	                               buffer->width, buffer->height,
167	                               buffer->map, buffer->pitch);
168	dst = pixman_image_create_bits(format_wld_to_pixman(renderer->target->format),
169	                               renderer->target->width,
170	                               renderer->target->height,
171	                               renderer->target->map,
172	                               renderer->target->pitch);
173	if (!src || !dst)
174		goto destroy;
175
176	pixman_region32_init(&clip);
177	pixman_region32_copy(&clip, region);
178	pixman_region32_translate(&clip, dst_x, dst_y);
179	extents = pixman_region32_extents(region);
180	pixman_image_set_clip_region32(dst, &clip);
181	pixman_image_composite32(PIXMAN_OP_OVER, src, NULL, dst,
182	                         extents->x1, extents->y1, 0, 0,
183	                         extents->x1 + dst_x, extents->y1 + dst_y,
184	                         extents->x2 - extents->x1,
185	                         extents->y2 - extents->y1);
186	pixman_region32_fini(&clip);
187
188destroy:
189	if (src)
190		pixman_image_unref(src);
191	if (dst)
192		pixman_image_unref(dst);
193	wld_unmap(renderer->target);
194unmap_src:
195	wld_unmap(buffer);
196}
197
198
199/* https://en.wikipedia.org/wiki/Midpoint_circle_algorithm */
200static void
201circle_points(struct wld_renderer *renderer, uint32_t color,
202			 int32_t x1, int32_t y1, int32_t x2, int32_t y2, bool fill)
203{
204	/* hacky */
205	if (fill) {
206		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1+y2, 2*x2 + 1, 1);
207		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1-y2, 2*x2 + 1, 1);
208		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1+x2, 2*y2 + 1, 1);
209		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1-x2, 2*y2 + 1, 1);
210	}
211	
212	else {
213		renderer->impl->fill_rectangle(renderer, color, x1+x2, y1+y2, 1, 1);
214		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1+y2, 1, 1);
215		renderer->impl->fill_rectangle(renderer, color, x1+x2, y1-y2, 1, 1);
216		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1-y2, 1, 1);
217		renderer->impl->fill_rectangle(renderer, color, x1+y2, y1+x2, 1, 1);
218		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1+x2, 1, 1);
219		renderer->impl->fill_rectangle(renderer, color, x1+y2, y1-x2, 1, 1);
220		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1-x2, 1, 1);
221	}
222}
223
224EXPORT
225void
226wld_draw_circle(struct wld_renderer *renderer, uint32_t color, 
227				int32_t x, int32_t y, uint32_t r, bool fill)
228{
229	int32_t x1 = 0, y1 = r;
230	int32_t d = 3 - 2 * r;
231	circle_points(renderer, color, x, y, x1, y1, fill);
232
233	while (y1 >= x1) {
234		if (d > 0) {
235			y1--;
236			d = d + 4 * (x1 - y1) + 10;
237		}
238		else
239			d = d + 4 * x1 + 6;
240
241		x1++;
242
243		circle_points(renderer, color, x, y, x1, y1, fill);
244	}
245}
246
247EXPORT
248void
249wld_draw_line(struct wld_renderer *renderer, uint32_t color,
250			 int32_t x1, int32_t y1, int32_t x2, int32_t y2)
251{
252	int32_t dx = abs(x2-x1),  sx = x1<x2 ? 1 : -1;
253	int32_t dy = -abs(y2-y1), sy = y1<y2 ? 1 : -1;
254	int32_t err = dx+dy, e2;
255
256	while(true) {
257		renderer->impl->fill_rectangle(renderer, color, x1, y1, 1, 1);
258
259		if (x1==x2 && y1==y2)
260			break;
261		
262		e2 = 2*err;
263		
264		if (e2 >= dy) {
265			err += dy;
266			x1 += sx;
267		}
268
269		if (e2 <= dx) {
270			err += dx;
271			y1 += sy;
272		}
273	}
274}
275
276EXPORT
277void
278wld_draw_text(struct wld_renderer *renderer,
279              struct wld_font *font_base, uint32_t color,
280              int32_t x, int32_t y, const char *text, uint32_t length,
281              struct wld_extents *extents)
282{
283	struct font *font = (void *)font_base;
284
285	renderer->impl->draw_text(renderer, font, color, x, y, text, length,
286	                          extents);
287}
288
289EXPORT
290void
291wld_flush(struct wld_renderer *renderer)
292{
293	renderer->impl->flush(renderer);
294	if (renderer->target && ((struct buffer *)renderer->target)->base.impl->flush)
295		((struct buffer *)renderer->target)->base.impl->flush(
296		    (struct buffer *)renderer->target);
297	renderer->impl->set_target(renderer, NULL);
298	renderer->target = NULL;
299}