1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2008-2010, 2015 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8/**
9 * @defgroup graphics Graphics
10 *
11 * @{
12 */
13
14/**
15 * @file
16 * @brief  Graphics drawing library
17 */
18#include <lib/gfx.h>
19
20#include <arch/ops.h>
21#include <assert.h>
22#include <debug.h>
23#include <dev/display.h>
24#include <err.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <trace.h>
29
30#include <zircon/font/font-18x32.h>
31#include <zircon/font/font-9x16.h>
32
33const struct gfx_font font_9x16 = {
34    .data = FONT9X16,
35    .width = FONT9X16_WIDTH,
36    .height = FONT9X16_HEIGHT,
37};
38
39const struct gfx_font font_18x32 = {
40    .data = FONT18X32,
41    .width = FONT18X32_WIDTH,
42    .height = FONT18X32_HEIGHT,
43};
44
45#define LOCAL_TRACE 0
46
47// Convert a 32bit ARGB image to its respective gamma corrected grayscale value.
48static uint32_t ARGB8888_to_Luma(uint32_t in) {
49    uint8_t out;
50
51    uint32_t blue = (in & 0xFF) * 74;
52    uint32_t green = ((in >> 8) & 0xFF) * 732;
53    uint32_t red = ((in >> 16) & 0xFF) * 218;
54
55    uint32_t intensity = red + blue + green;
56
57    out = (intensity >> 10) & 0xFF;
58
59    return out;
60}
61
62static uint32_t ARGB8888_to_RGB565(uint32_t in) {
63    uint32_t out;
64
65    out = (in >> 3) & 0x1f;           // b
66    out |= ((in >> 10) & 0x3f) << 5;  // g
67    out |= ((in >> 19) & 0x1f) << 11; // r
68
69    return out;
70}
71
72static uint32_t ARGB8888_to_RGB332(uint32_t in) {
73    uint32_t out = 0;
74
75    out = (in >> 6) & 0x3;          // b
76    out |= ((in >> 13) & 0x7) << 2; // g
77    out |= ((in >> 21) & 0x7) << 5; // r
78
79    return out;
80}
81
82static uint32_t ARGB8888_to_RGB2220(uint32_t in) {
83    uint32_t out = 0;
84
85    out = ((in >> 6) & 0x3) << 2;
86    out |= ((in >> 14) & 0x3) << 4;
87    out |= ((in >> 22) & 0x3) << 6;
88
89    return out;
90}
91
92/**
93 * @brief  Copy a rectangle of pixels from one part of the display to another.
94 */
95void gfx_copyrect(gfx_surface* surface, uint x, uint y, uint width, uint height, uint x2, uint y2) {
96    // trim
97    if (x >= surface->width)
98        return;
99    if (x2 >= surface->width)
100        return;
101    if (y >= surface->height)
102        return;
103    if (y2 >= surface->height)
104        return;
105    if (width == 0 || height == 0)
106        return;
107
108    // clip the width to src or dest
109    if (x + width > surface->width)
110        width = surface->width - x;
111    if (x2 + width > surface->width)
112        width = surface->width - x2;
113
114    // clip the height to src or dest
115    if (y + height > surface->height)
116        height = surface->height - y;
117    if (y2 + height > surface->height)
118        height = surface->height - y2;
119
120    surface->copyrect(surface, x, y, width, height, x2, y2);
121}
122
123/**
124 * @brief  Fill a rectangle on the screen with a constant color.
125 */
126void gfx_fillrect(gfx_surface* surface, uint x, uint y, uint width, uint height, uint color) {
127    LTRACEF("surface %p, x %u y %u w %u h %u c %u\n", surface, x, y, width, height, color);
128    // trim
129    if (unlikely(x >= surface->width))
130        return;
131    if (y >= surface->height)
132        return;
133    if (width == 0 || height == 0)
134        return;
135
136    // clip the width
137    if (x + width > surface->width)
138        width = surface->width - x;
139
140    // clip the height
141    if (y + height > surface->height)
142        height = surface->height - y;
143
144    surface->fillrect(surface, x, y, width, height, color);
145}
146
147/**
148 * @brief  Write a single pixel to the screen.
149 */
150void gfx_putpixel(gfx_surface* surface, uint x, uint y, uint color) {
151    if (unlikely(x >= surface->width))
152        return;
153    if (y >= surface->height)
154        return;
155
156    surface->putpixel(surface, x, y, color);
157}
158
159template <typename T>
160static void putpixel(gfx_surface* surface, uint x, uint y, uint color) {
161    T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride);
162
163    if (sizeof(T) == sizeof(uint32_t)) {
164        *dest = static_cast<T>(color);
165    } else {
166        // colors come in in ARGB 8888 form, flatten them
167        *dest = static_cast<T>(surface->translate_color(color));
168    }
169}
170
171template <typename T>
172static void copyrect(gfx_surface* surface, uint x, uint y, uint width, uint height, uint x2, uint y2) {
173    // copy
174    const T* src = static_cast<const T*>(surface->ptr) + (x + y * surface->stride);
175    T* dest = static_cast<T*>(surface->ptr) + (x2 + y2 * surface->stride);
176    uint stride_diff = surface->stride - width;
177
178    if (dest < src) {
179        uint i, j;
180        for (i = 0; i < height; i++) {
181            for (j = 0; j < width; j++) {
182                *dest = *src;
183                dest++;
184                src++;
185            }
186            dest += stride_diff;
187            src += stride_diff;
188        }
189    } else {
190        // copy backwards
191        src += height * surface->stride + width;
192        dest += height * surface->stride + width;
193
194        uint i, j;
195        for (i = 0; i < height; i++) {
196            for (j = 0; j < width; j++) {
197                *dest = *src;
198                dest--;
199                src--;
200            }
201            dest -= stride_diff;
202            src -= stride_diff;
203        }
204    }
205}
206
207template <typename T>
208static void fillrect(gfx_surface* surface, uint x, uint y, uint width, uint height, uint _color) {
209    T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride);
210    uint stride_diff = surface->stride - width;
211
212    T color;
213    if (sizeof(_color) == sizeof(color)) {
214        color = static_cast<T>(_color);
215    } else {
216        color = static_cast<T>(surface->translate_color(_color));
217    }
218
219    uint i, j;
220    for (i = 0; i < height; i++) {
221        for (j = 0; j < width; j++) {
222            *dest = color;
223            dest++;
224        }
225        dest += stride_diff;
226    }
227}
228
229void gfx_line(gfx_surface* surface, uint x1, uint y1, uint x2, uint y2, uint color) {
230    if (unlikely(x1 >= surface->width))
231        return;
232    if (unlikely(x2 >= surface->width))
233        return;
234
235    if (y1 >= surface->height)
236        return;
237    if (y2 >= surface->height)
238        return;
239
240    int dx = x2 - x1;
241    int dy = y2 - y1;
242
243    int sdx = (0 < dx) - (dx < 0);
244    int sdy = (0 < dy) - (dy < 0);
245
246    uint dxabs = (dx > 0) ? dx : -dx;
247    uint dyabs = (dy > 0) ? dy : -dy;
248
249    uint x = dyabs >> 1;
250    uint y = dxabs >> 1;
251
252    uint px = x1;
253    uint py = y1;
254
255    if (dxabs >= dyabs) {
256        // mostly horizontal line.
257        for (uint i = 0; i < dxabs; i++) {
258            y += dyabs;
259            if (y >= dxabs) {
260                y -= dxabs;
261                py += sdy;
262            }
263            px += sdx;
264            surface->putpixel(surface, px, py, color);
265        }
266    } else {
267        // mostly vertical line.
268        for (uint i = 0; i < dyabs; i++) {
269            x += dxabs;
270            if (x >= dyabs) {
271                x -= dyabs;
272                px += sdx;
273            }
274            py += sdy;
275            surface->putpixel(surface, px, py, color);
276        }
277    }
278}
279
280static uint32_t alpha32_add_ignore_destalpha(uint32_t dest, uint32_t src) {
281    uint32_t cdest[3];
282    uint32_t csrc[3];
283
284    uint32_t srca;
285    uint32_t srcainv;
286
287    srca = (src >> 24) & 0xff;
288    if (srca == 0) {
289        return dest;
290    } else if (srca == 255) {
291        return src;
292    }
293    srca++;
294    srcainv = (255 - srca);
295
296    cdest[0] = (dest >> 16) & 0xff;
297    cdest[1] = (dest >> 8) & 0xff;
298    cdest[2] = (dest >> 0) & 0xff;
299
300    csrc[0] = (src >> 16) & 0xff;
301    csrc[1] = (src >> 8) & 0xff;
302    csrc[2] = (src >> 0) & 0xff;
303
304    //    if (srca > 0)
305    //        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);
306
307    uint32_t cres[3];
308
309    cres[0] = ((csrc[0] * srca) / 256) + ((cdest[0] * srcainv) / 256);
310    cres[1] = ((csrc[1] * srca) / 256) + ((cdest[1] * srcainv) / 256);
311    cres[2] = ((csrc[2] * srca) / 256) + ((cdest[2] * srcainv) / 256);
312
313    return (srca << 24) | (cres[0] << 16) | (cres[1] << 8) | (cres[2]);
314}
315
316/**
317 * @brief  Copy pixels from source to dest.
318 *
319 * Currently does not support alpha channel.
320 */
321void gfx_surface_blend(struct gfx_surface* target, struct gfx_surface* source, uint destx, uint desty) {
322    DEBUG_ASSERT(target->format == source->format);
323
324    LTRACEF("target %p, source %p, destx %u, desty %u\n", target, source, destx, desty);
325
326    if (destx >= target->width)
327        return;
328    if (desty >= target->height)
329        return;
330
331    uint width = source->width;
332    if (destx + width > target->width)
333        width = target->width - destx;
334
335    uint height = source->height;
336    if (desty + height > target->height)
337        height = target->height - desty;
338
339    // XXX total hack to deal with various blends
340    if (source->format == ZX_PIXEL_FORMAT_RGB_565 && target->format == ZX_PIXEL_FORMAT_RGB_565) {
341        // 16 bit to 16 bit
342        const uint16_t* src = static_cast<const uint16_t*>(source->ptr);
343        uint16_t* dest = static_cast<uint16_t*>(target->ptr) + (destx + desty * target->stride);
344        uint dest_stride_diff = target->stride - width;
345        uint source_stride_diff = source->stride - width;
346
347        LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
348
349        uint i, j;
350        for (i = 0; i < height; i++) {
351            for (j = 0; j < width; j++) {
352                *dest = *src;
353                dest++;
354                src++;
355            }
356            dest += dest_stride_diff;
357            src += source_stride_diff;
358        }
359    } else if (source->format == ZX_PIXEL_FORMAT_ARGB_8888 && target->format == ZX_PIXEL_FORMAT_ARGB_8888) {
360        // both are 32 bit modes, both alpha
361        const uint32_t* src = static_cast<const uint32_t*>(source->ptr);
362        uint32_t* dest = static_cast<uint32_t*>(target->ptr) + (destx + desty * target->stride);
363        uint dest_stride_diff = target->stride - width;
364        uint source_stride_diff = source->stride - width;
365
366        LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
367
368        uint i, j;
369        for (i = 0; i < height; i++) {
370            for (j = 0; j < width; j++) {
371                // XXX ignores destination alpha
372                *dest = alpha32_add_ignore_destalpha(*dest, *src);
373                dest++;
374                src++;
375            }
376            dest += dest_stride_diff;
377            src += source_stride_diff;
378        }
379    } else if (source->format == ZX_PIXEL_FORMAT_RGB_x888 && target->format == ZX_PIXEL_FORMAT_RGB_x888) {
380        // both are 32 bit modes, no alpha
381        const uint32_t* src = static_cast<const uint32_t*>(source->ptr);
382        uint32_t* dest = static_cast<uint32_t*>(target->ptr) + (destx + desty * target->stride);
383        uint dest_stride_diff = target->stride - width;
384        uint source_stride_diff = source->stride - width;
385
386        LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
387
388        uint i, j;
389        for (i = 0; i < height; i++) {
390            for (j = 0; j < width; j++) {
391                *dest = *src;
392                dest++;
393                src++;
394            }
395            dest += dest_stride_diff;
396            src += source_stride_diff;
397        }
398    } else if (source->format == ZX_PIXEL_FORMAT_MONO_8 && target->format == ZX_PIXEL_FORMAT_MONO_8) {
399        // both are 8 bit modes, no alpha
400        const uint8_t* src = static_cast<const uint8_t*>(source->ptr);
401        uint8_t* dest = static_cast<uint8_t*>(target->ptr) + (destx + desty * target->stride);
402        uint dest_stride_diff = target->stride - width;
403        uint source_stride_diff = source->stride - width;
404
405        LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff);
406
407        uint i, j;
408        for (i = 0; i < height; i++) {
409            for (j = 0; j < width; j++) {
410                *dest = *src;
411                dest++;
412                src++;
413            }
414            dest += dest_stride_diff;
415            src += source_stride_diff;
416        }
417    } else {
418        panic("gfx_surface_blend: unimplemented colorspace combination (source %u target %u)\n", source->format, target->format);
419    }
420}
421
422template <typename T>
423static void putchar(gfx_surface* surface, const struct gfx_font* font,
424                    uint ch, uint x, uint y, uint fg, uint bg) {
425    T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride);
426
427    const uint16_t* cdata = font->data + ch * font->height;
428    unsigned fw = font->width;
429    for (unsigned i = font->height; i > 0; i--) {
430        uint16_t xdata = *cdata++;
431        for (unsigned j = fw; j > 0; j--) {
432            *dest++ = static_cast<T>((xdata & 1) ? fg : bg);
433            xdata = static_cast<uint16_t>(xdata >> 1);
434        }
435        dest += (surface->stride - fw);
436    }
437}
438
439void gfx_putchar(gfx_surface* surface, const struct gfx_font* font,
440                 uint ch, uint x, uint y, uint fg, uint bg) {
441    if (unlikely(ch > 127)) {
442        return;
443    }
444    if (unlikely(x > (surface->width - font->width))) {
445        return;
446    }
447    if (unlikely(y > (surface->height - font->height))) {
448        return;
449    }
450    if (surface->translate_color) {
451        fg = surface->translate_color(fg);
452        bg = surface->translate_color(bg);
453    }
454    surface->putchar(surface, font, ch, x, y, fg, bg);
455}
456
457/**
458 * @brief  Ensure all graphics rendering is sent to display
459 */
460void gfx_flush(gfx_surface* surface) {
461    if (surface->flags & GFX_FLAG_FLUSH_CPU_CACHE)
462        arch_clean_cache_range((addr_t)surface->ptr, surface->len);
463
464    if (surface->flush)
465        surface->flush(0, surface->height - 1);
466}
467
468/**
469 * @brief  Ensure that a sub-region of the display is up to date.
470 */
471void gfx_flush_rows(struct gfx_surface* surface, uint start, uint end) {
472    if (start > end) {
473        uint temp = start;
474        start = end;
475        end = temp;
476    }
477
478    if (start >= surface->height)
479        return;
480    if (end >= surface->height)
481        end = surface->height - 1;
482
483    if (surface->flags & GFX_FLAG_FLUSH_CPU_CACHE) {
484        uint32_t runlen = surface->stride * surface->pixelsize;
485        arch_clean_cache_range((addr_t)surface->ptr + start * runlen, (end - start + 1) * runlen);
486    }
487
488    if (surface->flush)
489        surface->flush(start, end);
490}
491
492/**
493 * @brief  Create a new graphics surface object
494 */
495gfx_surface* gfx_create_surface(void* ptr, uint width, uint height, uint stride, gfx_format format, uint32_t flags) {
496    gfx_surface* surface = static_cast<gfx_surface*>(calloc(1, sizeof(*surface)));
497    if (surface == NULL)
498        return NULL;
499    if (gfx_init_surface(surface, ptr, width, height, stride, format, flags)) {
500        free(surface);
501        return NULL;
502    }
503    return surface;
504}
505
506int gfx_init_surface(gfx_surface* surface, void* ptr, uint width, uint height, uint stride, gfx_format format, uint32_t flags) {
507    if ((width == 0) || (height == 0) || (stride < width)) {
508        return ZX_ERR_INVALID_ARGS;
509    }
510
511    surface->flags = flags;
512    surface->format = format;
513    surface->width = width;
514    surface->height = height;
515    surface->stride = stride;
516    surface->alpha = MAX_ALPHA;
517
518    // set up some function pointers
519    switch (format) {
520    case ZX_PIXEL_FORMAT_RGB_565:
521        surface->translate_color = &ARGB8888_to_RGB565;
522        surface->copyrect = &copyrect<uint16_t>;
523        surface->fillrect = &fillrect<uint16_t>;
524        surface->putpixel = &putpixel<uint16_t>;
525        surface->putchar = &putchar<uint16_t>;
526        surface->pixelsize = 2;
527        surface->len = (surface->height * surface->stride * surface->pixelsize);
528        break;
529    case ZX_PIXEL_FORMAT_RGB_x888:
530    case ZX_PIXEL_FORMAT_ARGB_8888:
531        surface->translate_color = NULL;
532        surface->copyrect = &copyrect<uint32_t>;
533        surface->fillrect = &fillrect<uint32_t>;
534        surface->putpixel = &putpixel<uint32_t>;
535        surface->putchar = &putchar<uint32_t>;
536        surface->pixelsize = 4;
537        surface->len = (surface->height * surface->stride * surface->pixelsize);
538        break;
539    case ZX_PIXEL_FORMAT_MONO_8:
540        surface->translate_color = &ARGB8888_to_Luma;
541        surface->copyrect = &copyrect<uint8_t>;
542        surface->fillrect = &fillrect<uint8_t>;
543        surface->putpixel = &putpixel<uint8_t>;
544        surface->putchar = &putchar<uint8_t>;
545        surface->pixelsize = 1;
546        surface->len = (surface->height * surface->stride * surface->pixelsize);
547        break;
548    case ZX_PIXEL_FORMAT_RGB_332:
549        surface->translate_color = &ARGB8888_to_RGB332;
550        surface->copyrect = &copyrect<uint8_t>;
551        surface->fillrect = &fillrect<uint8_t>;
552        surface->putpixel = &putpixel<uint8_t>;
553        surface->putchar = &putchar<uint8_t>;
554        surface->pixelsize = 1;
555        surface->len = (surface->height * surface->stride * surface->pixelsize);
556        break;
557    case ZX_PIXEL_FORMAT_RGB_2220:
558        surface->translate_color = &ARGB8888_to_RGB2220;
559        surface->copyrect = &copyrect<uint8_t>;
560        surface->fillrect = &fillrect<uint8_t>;
561        surface->putpixel = &putpixel<uint8_t>;
562        surface->putchar = &putchar<uint8_t>;
563        surface->pixelsize = 1;
564        surface->len = (surface->height * surface->stride * surface->pixelsize);
565        break;
566    default:
567        dprintf(INFO, "invalid graphics format\n");
568        return ZX_ERR_INVALID_ARGS;
569    }
570
571    if (ptr == NULL) {
572        // allocate a buffer
573        ptr = malloc(surface->len);
574        if (ptr == NULL) {
575            return ZX_ERR_NO_MEMORY;
576        }
577        DEBUG_ASSERT(ptr);
578        surface->flags |= GFX_FLAG_FREE_ON_DESTROY;
579    }
580    surface->ptr = ptr;
581    return 0;
582}
583
584/**
585 * @brief  Create a new graphics surface object from a display
586 */
587gfx_surface* gfx_create_surface_from_display(struct display_info* info) {
588    gfx_surface* surface = static_cast<gfx_surface*>(calloc(1, sizeof(*surface)));
589    if (surface == NULL)
590        return NULL;
591    if (gfx_init_surface_from_display(surface, info)) {
592        free(surface);
593        return NULL;
594    }
595    return surface;
596}
597
598int gfx_init_surface_from_display(gfx_surface* surface, struct display_info* info) {
599    int r;
600    switch (info->format) {
601    case ZX_PIXEL_FORMAT_RGB_565:
602    case ZX_PIXEL_FORMAT_RGB_332:
603    case ZX_PIXEL_FORMAT_RGB_2220:
604    case ZX_PIXEL_FORMAT_ARGB_8888:
605    case ZX_PIXEL_FORMAT_RGB_x888:
606    case ZX_PIXEL_FORMAT_MONO_8:
607        // supported formats
608        break;
609    default:
610        dprintf(CRITICAL, "invalid graphics format %x", info->format);
611        return ZX_ERR_INVALID_ARGS;
612    }
613
614    uint32_t flags = (info->flags & DISPLAY_FLAG_NEEDS_CACHE_FLUSH) ? GFX_FLAG_FLUSH_CPU_CACHE : 0;
615    r = gfx_init_surface(surface, info->framebuffer, info->width, info->height, info->stride, info->format, flags);
616
617    surface->flush = info->flush;
618    return r;
619}
620
621/**
622 * @brief  Destroy a graphics surface and free all resources allocated to it.
623 *
624 * @param  surface  Surface to destroy.  This pointer is no longer valid after
625 *    this call.
626 */
627void gfx_surface_destroy(struct gfx_surface* surface) {
628    if (surface->flags & GFX_FLAG_FREE_ON_DESTROY)
629        free(surface->ptr);
630    free(surface);
631}
632
633/**
634 * @brief  Write a test pattern to the default display.
635 */
636void gfx_draw_pattern(void) {
637    struct display_info info;
638    if (display_get_info(&info) < 0)
639        return;
640
641    gfx_surface* surface = gfx_create_surface_from_display(&info);
642
643    uint x, y;
644    for (y = 0; y < surface->height; y++) {
645        for (x = 0; x < surface->width; x++) {
646            uint scaledx;
647            uint scaledy;
648
649            scaledx = x * 256 / surface->width;
650            scaledy = y * 256 / surface->height;
651
652            gfx_putpixel(surface, x, y, (0xff << 24) | (scaledx * scaledy) << 16 | (scaledx >> 1) << 8 | scaledy >> 1);
653        }
654    }
655
656    gfx_flush(surface);
657
658    gfx_surface_destroy(surface);
659}
660
661/**
662 * @brief  Fill default display with white
663 */
664static void gfx_draw_pattern_white(void) {
665    struct display_info info;
666    if (display_get_info(&info) < 0)
667        return;
668
669    gfx_surface* surface = gfx_create_surface_from_display(&info);
670
671    uint x, y;
672    for (y = 0; y < surface->height; y++) {
673        for (x = 0; x < surface->width; x++) {
674            gfx_putpixel(surface, x, y, 0xFFFFFFFF);
675        }
676    }
677
678    gfx_flush(surface);
679
680    gfx_surface_destroy(surface);
681}
682
683#if LK_DEBUGLEVEL > 1
684#include <lib/console.h>
685
686static int cmd_gfx(int argc, const cmd_args* argv, uint32_t flags);
687
688STATIC_COMMAND_START
689STATIC_COMMAND("gfx", "gfx commands", &cmd_gfx)
690STATIC_COMMAND_END(gfx);
691
692static int gfx_draw_rgb_bars(gfx_surface* surface) {
693    uint x, y;
694
695    uint step = surface->height * 100 / 256;
696    uint color;
697
698    for (y = 0; y < surface->height; y++) {
699        //R
700        for (x = 0; x < surface->width / 3; x++) {
701            color = y * 100 / step;
702            gfx_putpixel(surface, x, y, 0xff << 24 | color << 16);
703        }
704        //G
705        for (; x < 2 * (surface->width / 3); x++) {
706            color = y * 100 / step;
707            gfx_putpixel(surface, x, y, 0xff << 24 | color << 8);
708        }
709        //B
710        for (; x < surface->width; x++) {
711            color = y * 100 / step;
712            gfx_putpixel(surface, x, y, 0xff << 24 | color);
713        }
714    }
715
716    return 0;
717}
718
719static int cmd_gfx(int argc, const cmd_args* argv, uint32_t flags) {
720    if (argc < 2) {
721        printf("not enough arguments:\n");
722        printf("%s display_info : output information bout the current display\n", argv[0].str);
723        printf("%s rgb_bars   : Fill frame buffer with rgb bars\n", argv[0].str);
724        printf("%s test_pattern : Fill frame with test pattern\n", argv[0].str);
725        printf("%s fill r g b   : Fill frame buffer with RGB888 value and force update\n", argv[0].str);
726
727        return -1;
728    }
729
730    struct display_info info;
731    if (display_get_info(&info) < 0) {
732        printf("no display to draw on!\n");
733        return -1;
734    }
735
736    gfx_surface* surface = gfx_create_surface_from_display(&info);
737
738    if (!strcmp(argv[1].str, "display_info")) {
739        printf("display:\n");
740        printf("\tframebuffer %p\n", info.framebuffer);
741        printf("\twidth %u height %u stride %u\n", info.width, info.height, info.stride);
742        printf("\tformat 0x%x\n", info.format);
743        printf("\tflags 0x%x\n", info.flags);
744    } else if (!strcmp(argv[1].str, "rgb_bars")) {
745        gfx_draw_rgb_bars(surface);
746    } else if (!strcmp(argv[1].str, "test_pattern")) {
747        gfx_draw_pattern();
748    } else if (!strcmp(argv[1].str, "fill")) {
749        uint x, y;
750
751        uint fillval = static_cast<uint>(
752            (0xff << 24) |
753            (argv[2].u << 16) |
754            (argv[3].u << 8) |
755            argv[4].u);
756        for (y = 0; y < surface->height; y++) {
757            for (x = 0; x < surface->width; x++) {
758                /* write pixel to frame buffer */
759                gfx_putpixel(surface, x, y, fillval);
760            }
761        }
762    }
763
764    gfx_flush(surface);
765
766    gfx_surface_destroy(surface);
767
768    return 0;
769}
770
771#endif
772