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