First version
[3rdparty/ote_partner/tlk.git] / lib / gfx / gfx.c
1 /*
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 <assert.h>
39 #include <arch/ops.h>
40 #include <sys/types.h>
41 #include <lib/gfx.h>
42 #include <dev/display.h>
43
44 #define LOCAL_TRACE 0
45
46 static uint16_t ARGB8888_to_RGB565(uint32_t in)
47 {
48         uint16_t out;
49
50         out = (in >> 3) & 0x1f;    // b
51         out |= ((in >> 10) & 0x3f) << 5;  // g
52         out |= ((in >> 19) & 0x1f) << 11;  // r
53
54         return out;
55 }
56
57 /**
58  * @brief  Copy a rectangle of pixels from one part of the display to another.
59  */
60 void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
61 {
62         // trim
63         if (x >= surface->width)
64                 return;
65         if (x2 >= surface->width)
66                 return;
67         if (y >= surface->height)
68                 return;
69         if (y2 >= surface->height)
70                 return;
71         if (width == 0 || height == 0)
72                 return;
73
74         // clip the width to src or dest
75         if (x + width > surface->width)
76                 width = surface->width - x;
77         if (x2 + width > surface->width)
78                 width = surface->width - x2;
79
80         // clip the height to src or dest
81         if (y + height > surface->height)
82                 height = surface->height - y;
83         if (y2 + height > surface->height)
84                 height = surface->height - y2;
85
86         surface->copyrect(surface, x, y, width, height, x2, y2);
87 }
88
89 /**
90  * @brief  Fill a rectangle on the screen with a constant color.
91  */
92 void gfx_fillrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
93 {
94         LTRACEF("surface %p, x %u y %u w %u h %u c %u\n", surface, x, y, width, height, color);
95         // trim
96         if (unlikely(x >= surface->width))
97                 return;
98         if (y >= surface->height)
99                 return;
100         if (width == 0 || height == 0)
101                 return;
102
103         // clip the width
104         if (x + width > surface->width)
105                 width = surface->width - x;
106
107         // clip the height
108         if (y + height > surface->height)
109                 height = surface->height - y;
110
111         surface->fillrect(surface, x, y, width, height, color);
112 }
113
114 /**
115  * @brief  Write a single pixel to the screen.
116  */
117 void gfx_putpixel(gfx_surface *surface, uint x, uint y, uint color)
118 {
119         if (unlikely(x >= surface->width))
120                 return;
121         if (y >= surface->height)
122                 return;
123
124         surface->putpixel(surface, x, y, color);
125 }
126
127 static void putpixel16(gfx_surface *surface, uint x, uint y, uint color)
128 {
129         uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
130
131         // colors come in in ARGB 8888 form, flatten them
132         *dest = ARGB8888_to_RGB565(color);
133 }
134
135 static void putpixel32(gfx_surface *surface, uint x, uint y, uint color)
136 {
137         uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
138
139         *dest = color;
140 }
141
142 static void copyrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
143 {
144         // copy
145         const uint16_t *src = &((const uint16_t *)surface->ptr)[x + y * surface->stride];
146         uint16_t *dest = &((uint16_t *)surface->ptr)[x2 + y2 * surface->stride];
147         uint stride_diff = surface->stride - width;
148
149         if (dest < src) {
150                 uint i, j;
151                 for (i=0; i < height; i++) {
152                         for (j=0; j < width; j++) {
153                                 *dest = *src;
154                                 dest++;
155                                 src++;
156                         }
157                         dest += stride_diff;
158                         src += stride_diff;
159                 }
160         } else {
161                 // copy backwards
162                 src += height * surface->stride + width;
163                 dest += height * surface->stride + width;
164
165                 uint i, j;
166                 for (i=0; i < height; i++) {
167                         for (j=0; j < width; j++) {
168                                 *dest = *src;
169                                 dest--;
170                                 src--;
171                         }
172                         dest -= stride_diff;
173                         src -= stride_diff;
174                 }
175         }
176 }
177
178 static void fillrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
179 {
180         uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride];
181         uint stride_diff = surface->stride - width;
182
183         uint16_t color16 = ARGB8888_to_RGB565(color);
184         
185         uint i, j;
186         for (i=0; i < height; i++) {
187                 for (j=0; j < width; j++) {
188                         *dest = color16;
189                         dest++;
190                 }
191                 dest += stride_diff;
192         }
193 }
194
195 static void copyrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2)
196 {
197         // copy
198         const uint32_t *src = &((const uint32_t *)surface->ptr)[x + y * surface->stride];
199         uint32_t *dest = &((uint32_t *)surface->ptr)[x2 + y2 * surface->stride];
200         uint stride_diff = surface->stride - width;
201
202         if (dest < src) {
203                 uint i, j;
204                 for (i=0; i < height; i++) {
205                         for (j=0; j < width; j++) {
206                                 *dest = *src;
207                                 dest++;
208                                 src++;
209                         }
210                         dest += stride_diff;
211                         src += stride_diff;
212                 }
213         } else {
214                 // copy backwards
215                 src += height * surface->stride + width;
216                 dest += height * surface->stride + width;
217
218                 uint i, j;
219                 for (i=0; i < height; i++) {
220                         for (j=0; j < width; j++) {
221                                 *dest = *src;
222                                 dest--;
223                                 src--;
224                         }
225                         dest -= stride_diff;
226                         src -= stride_diff;
227                 }
228         }
229 }
230
231 static void fillrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color)
232 {
233         uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride];
234         uint stride_diff = surface->stride - width;
235         
236         uint i, j;
237         for (i=0; i < height; i++) {
238                 for (j=0; j < width; j++) {
239                         *dest = color;
240                         dest++;
241                 }
242                 dest += stride_diff;
243         }
244 }
245
246 uint32_t alpha32_add_ignore_destalpha(uint32_t dest, uint32_t src)
247 {
248         uint32_t cdest[3];
249         uint32_t csrc[3];
250
251         uint32_t srca;
252         uint32_t srcainv;
253
254         srca = (src >> 24) & 0xff;
255         if (srca == 0) {
256                 return dest;
257         } else if (srca == 255) {
258                 return src;
259         }
260         srca++;
261         srcainv = (255 - srca);
262
263         cdest[0] = (dest >> 16) & 0xff;
264         cdest[1] = (dest >> 8) & 0xff;
265         cdest[2] = (dest >> 0) & 0xff;
266
267         csrc[0] = (src >> 16) & 0xff;
268         csrc[1] = (src >> 8) & 0xff;
269         csrc[2] = (src >> 0) & 0xff;
270
271 //      if (srca > 0)
272 //              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);
273
274         uint32_t cres[3];
275
276         cres[0] = ((csrc[0] * srca) / 256) + ((cdest[0] * srcainv) / 256);
277         cres[1] = ((csrc[1] * srca) / 256) + ((cdest[1] * srcainv) / 256);
278         cres[2] = ((csrc[2] * srca) / 256) + ((cdest[2] * srcainv) / 256);
279
280         return (srca << 24) | (cres[0] << 16) | (cres[1] << 8) | (cres[2]);
281 }
282
283 /**
284  * @brief  Copy pixels from source to dest.
285  *
286  * Currently does not support alpha channel.
287  */
288 void gfx_surface_blend(struct gfx_surface *target, struct gfx_surface *source, uint destx, uint desty)
289 {
290         DEBUG_ASSERT(target->format == source->format);
291
292         LTRACEF("target %p, source %p, destx %u, desty %u\n", target, source, destx, desty);
293
294         if (destx >= target->width)
295                 return;
296         if (desty >= target->height)
297                 return;
298
299         uint width = source->width;
300         if (destx + width > target->width)
301                 width = target->width - destx;
302
303         uint height = source->height;
304         if (desty + height > target->height)
305                 height = target->height - desty;
306
307         // XXX total hack to deal with various blends
308         if (source->format == GFX_FORMAT_RGB_565 && target->format == GFX_FORMAT_RGB_565) {
309                 // 16 bit to 16 bit
310                 const uint16_t *src = (const uint16_t *)source->ptr;
311                 uint16_t *dest = &((uint16_t *)target->ptr)[destx + desty * target->stride];
312                 uint dest_stride_diff = target->stride - width;
313                 uint source_stride_diff = source->stride - width;
314
315                 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
316
317                 uint i, j;
318                 for (i=0; i < height; i++) {
319                         for (j=0; j < width; j++) {
320                                 *dest = *src;
321                                 dest++;
322                                 src++;
323                         }
324                         dest += dest_stride_diff;
325                         src += source_stride_diff;
326                 }
327         } else if (source->format == GFX_FORMAT_ARGB_8888 && target->format == GFX_FORMAT_ARGB_8888) {
328                 // both are 32 bit modes, both alpha
329                 const uint32_t *src = (const uint32_t *)source->ptr;
330                 uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
331                 uint dest_stride_diff = target->stride - width;
332                 uint source_stride_diff = source->stride - width;
333
334                 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
335
336                 uint i, j;
337                 for (i=0; i < height; i++) {
338                         for (j=0; j < width; j++) {
339                                 // XXX ignores destination alpha
340                                 *dest = alpha32_add_ignore_destalpha(*dest, *src);
341                                 dest++;
342                                 src++;
343                         }
344                         dest += dest_stride_diff;
345                         src += source_stride_diff;
346                 }
347         } else if (source->format == GFX_FORMAT_RGB_x888 && target->format == GFX_FORMAT_RGB_x888) {
348                 // both are 32 bit modes, no alpha
349                 const uint32_t *src = (const uint32_t *)source->ptr;
350                 uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride];
351                 uint dest_stride_diff = target->stride - width;
352                 uint source_stride_diff = source->stride - width;
353
354                 LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
355
356                 uint i, j;
357                 for (i=0; i < height; i++) {
358                         for (j=0; j < width; j++) {
359                                 *dest = *src;
360                                 dest++;
361                                 src++;
362                         }
363                         dest += dest_stride_diff;
364                         src += source_stride_diff;
365                 }
366         } else {
367                 panic("gfx_surface_blend: unimplemented colorspace combination (source %d target %d)\n", source->format, target->format);
368         }
369 }
370
371 /**
372  * @brief  Ensure all graphics rendering is sent to display
373  */
374 void gfx_flush(gfx_surface *surface)
375 {
376         arch_clean_cache_range((addr_t)surface->ptr, surface->len);
377
378         if (surface->flush)
379                 surface->flush(0, surface->height-1);
380 }
381
382 /**
383  * @brief  Ensure that a sub-region of the display is up to date.
384  */
385 void gfx_flush_rows(struct gfx_surface *surface, uint start, uint end)
386 {
387         if (start > end) {
388                 uint temp = start;
389                 start = end;
390                 end = temp;
391         }
392
393         if (start >= surface->height)
394                 return;
395         if (end >= surface->height)
396                 end = surface->height - 1;
397
398         arch_clean_cache_range((addr_t)surface->ptr + start * surface->stride * surface->pixelsize, (end - start + 1) * surface->stride * surface->pixelsize);
399
400         if (surface->flush)
401                 surface->flush(start, end);
402
403 }
404
405
406 /**
407  * @brief  Create a new graphics surface object
408  */
409 gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format)
410 {
411         DEBUG_ASSERT(width > 0);
412         DEBUG_ASSERT(height > 0);
413         DEBUG_ASSERT(stride >= width);
414         DEBUG_ASSERT(format < GFX_FORMAT_MAX);
415
416         gfx_surface *surface = malloc(sizeof(gfx_surface));
417
418         surface->free_on_destroy = false;
419         surface->format = format;
420         surface->width = width;
421         surface->height = height;
422         surface->stride = stride;
423         surface->alpha = MAX_ALPHA;
424
425         // set up some function pointers
426         switch (format) {
427                 case GFX_FORMAT_RGB_565:
428                         surface->copyrect = &copyrect16;
429                         surface->fillrect = &fillrect16;
430                         surface->putpixel = &putpixel16;
431                         surface->pixelsize = 2;
432                         surface->len = surface->height * surface->stride * surface->pixelsize;
433                         break;
434                 case GFX_FORMAT_RGB_x888:
435                 case GFX_FORMAT_ARGB_8888:
436                         surface->copyrect = &copyrect32;
437                         surface->fillrect = &fillrect32;
438                         surface->putpixel = &putpixel32;
439                         surface->pixelsize = 4;
440                         surface->len = surface->height * surface->stride * surface->pixelsize;
441                         break;
442                 default:
443                         dprintf(INFO, "invalid graphics format\n");
444                         DEBUG_ASSERT(0);
445                         free(surface);
446                         return NULL;
447         }
448
449         if (ptr == NULL) {
450                 // allocate a buffer
451                 ptr = malloc(surface->len);
452                 DEBUG_ASSERT(ptr);
453                 surface->free_on_destroy = true;
454         }
455         surface->ptr = ptr;
456
457         return surface;
458 }
459
460 /**
461  * @brief  Create a new graphics surface object from a display
462  */
463 gfx_surface *gfx_create_surface_from_display(struct display_info *info)
464 {
465         gfx_surface* surface;
466         surface = gfx_create_surface(info->framebuffer, info->width, info->height, info->stride, info->format);
467
468         surface->flush = info->flush;
469
470         return surface;
471 }
472
473 /**
474  * @brief  Destroy a graphics surface and free all resources allocated to it.
475  *
476  * @param  surface  Surface to destroy.  This pointer is no longer valid after
477  *              this call.
478  */
479 void gfx_surface_destroy(struct gfx_surface *surface)
480 {
481         if (surface->free_on_destroy)
482                 free(surface->ptr);
483         free(surface);
484 }
485
486 /**
487  * @brief  Write a test pattern to the default display.
488  */
489 void gfx_draw_pattern(void)
490 {
491         struct display_info info;
492         display_get_info(&info);
493
494         gfx_surface *surface = gfx_create_surface_from_display(&info);
495
496         uint x, y;
497         for (y = 0; y < surface->height; y++) {
498                 for (x = 0; x < surface->width; x++) {
499                         uint scaledx;
500                         uint scaledy;
501
502                         scaledx = x * 256 / surface->width;
503                         scaledy = y * 256 / surface->height;
504
505                         gfx_putpixel(surface, x, y, (scaledx * scaledy) << 16 | (scaledx >> 1) << 8 | scaledy >> 1);
506                 }
507         }
508
509         if (surface->flush)
510                 surface->flush(0, surface->height-1);
511
512         gfx_surface_destroy(surface);
513 }
514
515 /**
516  * @brief  Fill default display with white
517  */
518 void gfx_draw_pattern_white(void)
519 {
520         struct display_info info;
521         display_get_info(&info);
522
523         gfx_surface *surface = gfx_create_surface_from_display(&info);
524
525         uint x, y;
526         for (y = 0; y < surface->height; y++) {
527                 for (x = 0; x < surface->width; x++) {
528                         gfx_putpixel(surface, x, y, 0xFFFFFF);
529                 }
530         }
531
532         if (surface->flush)
533                 surface->flush(0, surface->height-1);
534
535         gfx_surface_destroy(surface);
536 }
537
538 #if defined(WITH_LIB_CONSOLE)
539
540 #if DEBUGLEVEL > 1
541 #include <lib/console.h>
542
543 static int cmd_gfx(int argc, const cmd_args *argv);
544
545 STATIC_COMMAND_START
546 STATIC_COMMAND("gfx", "gfx commands", &cmd_gfx)
547 STATIC_COMMAND_END(gfx);
548
549 static int gfx_draw_rgb_bars(gfx_surface *surface)
550 {
551         uint x, y;
552
553         int step32 = surface->height*100 / 32;
554         int step64 = surface->height*100 / 64;
555         int color;
556
557         for (y = 0; y < surface->height; y++) {
558                 //R
559                 for (x = 0; x < surface->width/3; x++) {
560                         color = y*100 / step32;
561                         gfx_putpixel(surface, x, y, color << 16);
562                 }
563                 //G
564                 for (;x < 2*(surface->width/3); x++) {
565                         color = y*100 / step64;
566                         gfx_putpixel(surface, x, y, color << 8);
567                 }
568                 //B
569                 for (;x < surface->width; x++) {
570                         color = y*100 / step32;
571                         gfx_putpixel(surface, x, y, color);
572                 }
573         }
574
575         return 0;
576 }
577
578
579 static int cmd_gfx(int argc, const cmd_args *argv)
580 {
581         if (argc < 2) {
582                 printf("not enough arguments:\n");
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