blob: 9296be4879bb0e9c964e77a3e267e680e2a5af6d [file] [log] [blame]
Travis Geiselbrechte06c67c2010-05-21 22:30:10 -07001/*
2 * Copyright (c) 2008-2010 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24/**
25 * @defgroup graphics Graphics
26 *
27 * @{
28 */
29
30/**
31 * @file
32 * @brief Graphics drawing library
33 */
34
35#include <debug.h>
36#include <string.h>
37#include <stdlib.h>
38#include <arch/ops.h>
39#include <sys/types.h>
40#include <lib/gfx.h>
41#include <dev/display.h>
42
43#define LOCAL_TRACE 0
44
45static uint16_t ARGB8888_to_RGB565(uint32_t in)
46{
47 uint16_t out;
48
49 out = (in >> 3) & 0x1f; // b
50 out |= ((in >> 10) & 0x3f) << 5; // g
51 out |= ((in >> 19) & 0x1f) << 11; // r
52
53 return out;
54}
55
56/**
57 * @brief Copy a rectangle of pixels from one part of the display to another.
58 */
59void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
60{
61 // trim
62 if (x >= surface->width)
63 return;
64 if (x2 >= surface->width)
65 return;
66 if (y >= surface->height)
67 return;
68 if (y2 >= surface->height)
69 return;
70 if (width == 0 || height == 0)
71 return;
72
73 // clip the width to src or dest
74 if (x + width > surface->width)
75 width = surface->width - x;
76 if (x2 + width > surface->width)
77 width = surface->width - x2;
78
79 // clip the height to src or dest
80 if (y + height > surface->height)
81 height = surface->height - y;
82 if (y2 + height > surface->height)
83 height = surface->height - y2;
84
85 surface->copyrect(surface, x, y, width, height, x2, y2);
86}
87
88/**
89 * @brief Fill a rectangle on the screen with a constant color.
90 */
91void gfx_fillrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
92{
93 LTRACEF("surface %p, x %u y %u w %u h %u c %u\n", surface, x, y, width, height, color);
94 // trim
95 if (unlikely(x >= surface->width))
96 return;
97 if (y >= surface->height)
98 return;
99 if (width == 0 || height == 0)
100 return;
101
102 // clip the width
103 if (x + width > surface->width)
104 width = surface->width - x;
105
106 // clip the height
107 if (y + height > surface->height)
108 height = surface->height - y;
109
110 surface->fillrect(surface, x, y, width, height, color);
111}
112
113/**
114 * @brief Write a single pixel to the screen.
115 */
116void gfx_putpixel(gfx_surface *surface, uint x, uint y, uint color)
117{
118 if (unlikely(x >= surface->width))
119 return;
120 if (y >= surface->height)
121 return;
122
123 surface->putpixel(surface, x, y, color);
124}
125
126static void putpixel16(gfx_surface *surface, uint x, uint y, uint color)
127{
128 uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
129
130 // colors come in in ARGB 8888 form, flatten them
131 *dest = ARGB8888_to_RGB565(color);
132}
133
134static void putpixel32(gfx_surface *surface, uint x, uint y, uint color)
135{
136 uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
137
138 *dest = color;
139}
140
141static void copyrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
142{
143 // copy
144 const uint16_t *src = &((const uint16_t *)surface->ptr)[x + y * surface->stride];
145 uint16_t *dest = &((uint16_t *)surface->ptr)[x2 + y2 * surface->stride];
146 uint stride_diff = surface->stride - width;
147
148 if (dest < src) {
149 uint i, j;
150 for (i=0; i < height; i++) {
151 for (j=0; j < width; j++) {
152 *dest = *src;
153 dest++;
154 src++;
155 }
156 dest += stride_diff;
157 src += stride_diff;
158 }
159 } else {
160 // copy backwards
161 src += height * surface->stride + width;
162 dest += height * surface->stride + width;
163
164 uint i, j;
165 for (i=0; i < height; i++) {
166 for (j=0; j < width; j++) {
167 *dest = *src;
168 dest--;
169 src--;
170 }
171 dest -= stride_diff;
172 src -= stride_diff;
173 }
174 }
175}
176
177static void fillrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
178{
179 uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
180 uint stride_diff = surface->stride - width;
181
182 uint16_t color16 = ARGB8888_to_RGB565(color);
183
184 uint i, j;
185 for (i=0; i < height; i++) {
186 for (j=0; j < width; j++) {
187 *dest = color16;
188 dest++;
189 }
190 dest += stride_diff;
191 }
192}
193
194static void copyrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
195{
196 // copy
197 const uint32_t *src = &((const uint32_t *)surface->ptr)[x + y * surface->stride];
198 uint32_t *dest = &((uint32_t *)surface->ptr)[x2 + y2 * surface->stride];
199 uint stride_diff = surface->stride - width;
200
201 if (dest < src) {
202 uint i, j;
203 for (i=0; i < height; i++) {
204 for (j=0; j < width; j++) {
205 *dest = *src;
206 dest++;
207 src++;
208 }
209 dest += stride_diff;
210 src += stride_diff;
211 }
212 } else {
213 // copy backwards
214 src += height * surface->stride + width;
215 dest += height * surface->stride + width;
216
217 uint i, j;
218 for (i=0; i < height; i++) {
219 for (j=0; j < width; j++) {
220 *dest = *src;
221 dest--;
222 src--;
223 }
224 dest -= stride_diff;
225 src -= stride_diff;
226 }
227 }
228}
229
230static void fillrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
231{
232 uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
233 uint stride_diff = surface->stride - width;
234
235 uint i, j;
236 for (i=0; i < height; i++) {
237 for (j=0; j < width; j++) {
238 *dest = color;
239 dest++;
240 }
241 dest += stride_diff;
242 }
243}
244
245uint32_t alpha32_add_ignore_destalpha(uint32_t dest, uint32_t src)
246{
247 uint32_t cdest[3];
248 uint32_t csrc[3];
249
250 uint32_t srca;
251 uint32_t srcainv;
252
253 srca = (src >> 24) & 0xff;
254 if (srca == 0) {
255 return dest;
256 } else if (srca == 255) {
257 return src;
258 }
259 srca++;
260 srcainv = (255 - srca);
261
262 cdest[0] = (dest >> 16) & 0xff;
263 cdest[1] = (dest >> 8) & 0xff;
264 cdest[2] = (dest >> 0) & 0xff;
265
266 csrc[0] = (src >> 16) & 0xff;
267 csrc[1] = (src >> 8) & 0xff;
268 csrc[2] = (src >> 0) & 0xff;
269
270// if (srca > 0)
271// printf("s %d %d %d d %d %d %d a %d ai %d\n", csrc[0], csrc[1], csrc[2], cdest[0], cdest[1], cdest[2], srca, srcainv);
272
273 uint32_t cres[3];
274
275 cres[0] = ((csrc[0] * srca) / 256) + ((cdest[0] * srcainv) / 256);
276 cres[1] = ((csrc[1] * srca) / 256) + ((cdest[1] * srcainv) / 256);
277 cres[2] = ((csrc[2] * srca) / 256) + ((cdest[2] * srcainv) / 256);
278
279 return (srca << 24) | (cres[0] << 16) | (cres[1] << 8) | (cres[2]);
280}
281
282/**
283 * @brief Copy pixels from source to dest.
284 *
285 * Currently does not support alpha channel.
286 */
287void gfx_surface_blend(struct gfx_surface *target, struct gfx_surface *source, uint destx, uint desty)
288{
289 DEBUG_ASSERT(target->format == source->format);
290
291 LTRACEF("target %p, source %p, destx %u, desty %u\n", target, source, destx, desty);
292
293 if (destx >= target->width)
294 return;
295 if (desty >= target->height)
296 return;
297
298 uint width = source->width;
299 if (destx + width > target->width)
300 width = target->width - destx;
301
302 uint height = source->height;
303 if (desty + height > target->height)
304 height = target->height - desty;
305
306 // XXX total hack to deal with various blends
307 if (source->format == GFX_FORMAT_RGB_565 && target->format == GFX_FORMAT_RGB_565) {
308 // 16 bit to 16 bit
309 const uint16_t *src = (const uint16_t *)source->ptr;
310 uint16_t *dest = &((uint16_t *)target->ptr)[destx + desty * target->stride];
311 uint dest_stride_diff = target->stride - width;
312 uint source_stride_diff = source->stride - width;
313
314 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
315
316 uint i, j;
317 for (i=0; i < height; i++) {
318 for (j=0; j < width; j++) {
319 *dest = *src;
320 dest++;
321 src++;
322 }
323 dest += dest_stride_diff;
324 src += source_stride_diff;
325 }
326 } else if (source->format == GFX_FORMAT_ARGB_8888 && target->format == GFX_FORMAT_ARGB_8888) {
327 // both are 32 bit modes, both alpha
328 const uint32_t *src = (const uint32_t *)source->ptr;
329 uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
330 uint dest_stride_diff = target->stride - width;
331 uint source_stride_diff = source->stride - width;
332
333 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
334
335 uint i, j;
336 for (i=0; i < height; i++) {
337 for (j=0; j < width; j++) {
338 // XXX ignores destination alpha
339 *dest = alpha32_add_ignore_destalpha(*dest, *src);
340 dest++;
341 src++;
342 }
343 dest += dest_stride_diff;
344 src += source_stride_diff;
345 }
346 } else if (source->format == GFX_FORMAT_RGB_x888 && target->format == GFX_FORMAT_RGB_x888) {
347 // both are 32 bit modes, no alpha
348 const uint32_t *src = (const uint32_t *)source->ptr;
349 uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
350 uint dest_stride_diff = target->stride - width;
351 uint source_stride_diff = source->stride - width;
352
353 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
354
355 uint i, j;
356 for (i=0; i < height; i++) {
357 for (j=0; j < width; j++) {
358 *dest = *src;
359 dest++;
360 src++;
361 }
362 dest += dest_stride_diff;
363 src += source_stride_diff;
364 }
365 } else {
366 panic("gfx_surface_blend: unimplemented colorspace combination (source %d target %d)\n", source->format, target->format);
367 }
368}
369
370/**
371 * @brief Ensure all graphics rendering is sent to display
372 */
373void gfx_flush(gfx_surface *surface)
374{
375 arch_clean_cache_range((addr_t)surface->ptr, surface->len);
376
377 if (surface->flush)
378 surface->flush(0, surface->height-1);
379}
380
381/**
382 * @brief Ensure that a sub-region of the display is up to date.
383 */
384void gfx_flush_rows(struct gfx_surface *surface, uint start, uint end)
385{
386 if (start > end) {
387 uint temp = start;
388 start = end;
389 end = temp;
390 }
391
392 if (start >= surface->height)
393 return;
394 if (end >= surface->height)
395 end = surface->height - 1;
396
397 arch_clean_cache_range((addr_t)surface->ptr + start * surface->stride * surface->pixelsize, (end - start + 1) * surface->stride * surface->pixelsize);
398
399 if (surface->flush)
400 surface->flush(start, end);
401
402}
403
404
405/**
406 * @brief Create a new graphics surface object
407 */
408gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format)
409{
410 DEBUG_ASSERT(width > 0);
411 DEBUG_ASSERT(height > 0);
412 DEBUG_ASSERT(stride >= width);
413 DEBUG_ASSERT(format < GFX_FORMAT_MAX);
414
415 gfx_surface *surface = malloc(sizeof(gfx_surface));
416
417 surface->free_on_destroy = false;
418 surface->format = format;
419 surface->width = width;
420 surface->height = height;
421 surface->stride = stride;
422 surface->alpha = MAX_ALPHA;
423
424 // set up some function pointers
425 switch (format) {
426 case GFX_FORMAT_RGB_565:
427 surface->copyrect = &copyrect16;
428 surface->fillrect = &fillrect16;
429 surface->putpixel = &putpixel16;
430 surface->pixelsize = 2;
431 surface->len = surface->height * surface->stride * surface->pixelsize;
432 break;
433 case GFX_FORMAT_RGB_x888:
434 case GFX_FORMAT_ARGB_8888:
435 surface->copyrect = &copyrect32;
436 surface->fillrect = &fillrect32;
437 surface->putpixel = &putpixel32;
438 surface->pixelsize = 4;
439 surface->len = surface->height * surface->stride * surface->pixelsize;
440 break;
441 default:
442 dprintf(INFO, "invalid graphics format\n");
443 DEBUG_ASSERT(0);
444 free(surface);
445 return NULL;
446 }
447
448 if (ptr == NULL) {
449 // allocate a buffer
450 ptr = malloc(surface->len);
451 DEBUG_ASSERT(ptr);
452 surface->free_on_destroy = true;
453 }
454 surface->ptr = ptr;
455
456 return surface;
457}
458
459/**
460 * @brief Create a new graphics surface object from a display
461 */
462gfx_surface *gfx_create_surface_from_display(struct display_info *info)
463{
464 gfx_surface* surface;
465 surface = gfx_create_surface(info->framebuffer, info->width, info->height, info->stride, info->format);
466
467 surface->flush = info->flush;
468
469 return surface;
470}
471
472/**
473 * @brief Destroy a graphics surface and free all resources allocated to it.
474 *
475 * @param surface Surface to destroy. This pointer is no longer valid after
476 * this call.
477 */
478void gfx_surface_destroy(struct gfx_surface *surface)
479{
480 if (surface->free_on_destroy)
481 free(surface->ptr);
482 free(surface);
483}
484
485/**
486 * @brief Write a test pattern to the default display.
487 */
488void gfx_draw_pattern(void)
489{
490 struct display_info info;
491 display_get_info(&info);
492
493 gfx_surface *surface = gfx_create_surface_from_display(&info);
494
495 uint x, y;
496 for (y = 0; y < surface->height; y++) {
497 for (x = 0; x < surface->width; x++) {
498 uint scaledx;
499 uint scaledy;
500
501 scaledx = x * 256 / surface->width;
502 scaledy = y * 256 / surface->height;
503
504 gfx_putpixel(surface, x, y, (scaledx * scaledy) << 16 | (scaledx >> 1) << 8 | scaledy >> 1);
505 }
506 }
507
508 if (surface->flush)
509 surface->flush(0, surface->height-1);
510
511 gfx_surface_destroy(surface);
512}
513
514/**
515 * @brief Fill default display with white
516 */
517void gfx_draw_pattern_white(void)
518{
519 struct display_info info;
520 display_get_info(&info);
521
522 gfx_surface *surface = gfx_create_surface_from_display(&info);
523
524 uint x, y;
525 for (y = 0; y < surface->height; y++) {
526 for (x = 0; x < surface->width; x++) {
527 gfx_putpixel(surface, x, y, 0xFFFFFF);
528 }
529 }
530
531 if (surface->flush)
532 surface->flush(0, surface->height-1);
533
534 gfx_surface_destroy(surface);
535}
536
537#if defined(WITH_LIB_CONSOLE)
538
539#if DEBUGLEVEL > 1
540#include <lib/console.h>
541
542static int cmd_gfx(int argc, const cmd_args *argv);
543
544STATIC_COMMAND_START
545STATIC_COMMAND("gfx", "gfx commands", &cmd_gfx)
546STATIC_COMMAND_END(gfx);
547
548static int gfx_draw_rgb_bars(gfx_surface *surface)
549{
550 uint x, y;
551
552 int step32 = surface->height*100 / 32;
553 int step64 = surface->height*100 / 64;
554 int color;
555
556 for (y = 0; y < surface->height; y++) {
557 //R
558 for (x = 0; x < surface->width/3; x++) {
559 color = y*100 / step32;
560 gfx_putpixel(surface, x, y, color << 16);
561 }
562 //G
563 for (;x < 2*(surface->width/3); x++) {
564 color = y*100 / step64;
565 gfx_putpixel(surface, x, y, color << 8);
566 }
567 //B
568 for (;x < surface->width; x++) {
569 color = y*100 / step32;
570 gfx_putpixel(surface, x, y, color);
571 }
572 }
573
574 return 0;
575}
576
577
578static int cmd_gfx(int argc, const cmd_args *argv)
579{
580 if (argc < 2) {
581 printf("not enough arguments:\n");
582usage:
583 printf("%s rgb_bars : Fill frame buffer with rgb bars\n", argv[0].str);
584 printf("%s fill r g b : Fill frame buffer with RGB565 value and force update\n", argv[0].str);
585
586 return -1;
587 }
588
589 struct display_info info;
590 display_get_info(&info);
591
592 gfx_surface *surface = gfx_create_surface_from_display(&info);
593
594 if (!strcmp(argv[1].str, "rgb_bars"))
595 {
596 gfx_draw_rgb_bars(surface);
597 }
598 else if (!strcmp(argv[1].str, "fill"))
599 {
600 uint x, y;
601
602 for (y = 0; y < surface->height; y++)
603 {
604 for (x = 0; x < surface->width; x++)
605 {
606 /* write pixel to frame buffer */
607 gfx_putpixel(surface, x, y, (argv[2].i << 16) | (argv[3].i << 8) | argv[4].i);
608 }
609 }
610 }
611
612 if (surface->flush)
613 surface->flush(0, surface->height-1);
614
615 gfx_surface_destroy(surface);
616
617 return 0;
618}
619
620#endif
621#endif