| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @defgroup graphics Graphics |
| * |
| * @{ |
| */ |
| |
| /** |
| * @file |
| * @brief Graphics drawing library |
| */ |
| #include <assert.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <zircon/compiler.h> |
| #include <zircon/syscalls.h> |
| |
| #include <gfx-common/gfx-common.h> |
| |
| #define GFX_LOG(ctx, fmt, ...) \ |
| do { \ |
| (ctx)->log("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ |
| } while (0) |
| |
| #define GFX_ASSERT_MSG(ctx, cond, fmt, ...) \ |
| do { \ |
| if (!(cond)) { \ |
| (ctx)->panic("[%s:%d]" fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| #define GFX_ASSERT(ctx, cond) \ |
| do { \ |
| if (!(cond)) { \ |
| (ctx)->panic("[%s:%d] failed assertion: " #cond "\n", __FILE__, __LINE__); \ |
| } \ |
| } while (0) |
| |
| namespace { |
| |
| struct Rgb888 { |
| Rgb888() = default; |
| explicit Rgb888(uint32_t rgba) { |
| b = rgba & 0xff; |
| g = (rgba & 0xff00) >> 8; |
| r = (rgba & 0xff0000) >> 16; |
| } |
| uint32_t ToRgba32() const { return b | (g << 8) | (r << 16) | (0xff << 24); } |
| |
| uint8_t b = 0; |
| uint8_t g = 0; |
| uint8_t r = 0; |
| } __attribute__((packed)); |
| |
| } // namespace |
| |
| // Convert a 32bit ARGB image to its respective gamma corrected grayscale value. |
| static uint32_t ARGB8888_to_Luma(uint32_t in) { |
| uint8_t out; |
| |
| uint32_t blue = (in & 0xFF) * 74; |
| uint32_t green = ((in >> 8) & 0xFF) * 732; |
| uint32_t red = ((in >> 16) & 0xFF) * 218; |
| |
| uint32_t intensity = red + blue + green; |
| |
| out = (intensity >> 10) & 0xFF; |
| |
| return out; |
| } |
| |
| static uint32_t ARGB8888_to_RGB888(uint32_t in) { return in & 0xFFFFFF; } |
| |
| static uint32_t ARGB8888_to_RGB565(uint32_t in) { |
| uint32_t out; |
| |
| out = (in >> 3) & 0x1f; // b |
| out |= ((in >> 10) & 0x3f) << 5; // g |
| out |= ((in >> 19) & 0x1f) << 11; // r |
| |
| return out; |
| } |
| |
| static uint32_t ARGB8888_to_RGB332(uint32_t in) { |
| uint32_t out = 0; |
| |
| out = (in >> 6) & 0x3; // b |
| out |= ((in >> 13) & 0x7) << 2; // g |
| out |= ((in >> 21) & 0x7) << 5; // r |
| |
| return out; |
| } |
| |
| static uint32_t ARGB8888_to_RGB2220(uint32_t in) { |
| uint32_t out = 0; |
| |
| out = ((in >> 6) & 0x3) << 2; |
| out |= ((in >> 14) & 0x3) << 4; |
| out |= ((in >> 22) & 0x3) << 6; |
| |
| return out; |
| } |
| |
| /** |
| * @brief Copy a rectangle of pixels from one part of the display to another. |
| */ |
| void gfx_copyrect(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t width, uint32_t height, |
| uint32_t x2, uint32_t y2) { |
| // trim |
| if (x >= surface->width) |
| return; |
| if (x2 >= surface->width) |
| return; |
| if (y >= surface->height) |
| return; |
| if (y2 >= surface->height) |
| return; |
| if (width == 0 || height == 0) |
| return; |
| |
| // clip the width to src or dest |
| if (x + width > surface->width) |
| width = surface->width - x; |
| if (x2 + width > surface->width) |
| width = surface->width - x2; |
| |
| // clip the height to src or dest |
| if (y + height > surface->height) |
| height = surface->height - y; |
| if (y2 + height > surface->height) |
| height = surface->height - y2; |
| |
| surface->copyrect(surface, x, y, width, height, x2, y2); |
| } |
| |
| /** |
| * @brief Fill a rectangle on the screen with a constant color. |
| */ |
| void gfx_fillrect(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t width, uint32_t height, |
| uint32_t color) { |
| GFX_LOG(surface->ctx, "surface %p, x %u y %u w %u h %u c %u\n", surface, x, y, width, height, |
| color); |
| // trim |
| if (unlikely(x >= surface->width)) |
| return; |
| if (y >= surface->height) |
| return; |
| if (width == 0 || height == 0) |
| return; |
| |
| // clip the width |
| if (x + width > surface->width) |
| width = surface->width - x; |
| |
| // clip the height |
| if (y + height > surface->height) |
| height = surface->height - y; |
| |
| surface->fillrect(surface, x, y, width, height, color); |
| } |
| |
| /** |
| * @brief Write a single pixel to the screen. |
| */ |
| void gfx_putpixel(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t color) { |
| if (unlikely(x >= surface->width)) |
| return; |
| if (y >= surface->height) |
| return; |
| |
| surface->putpixel(surface, x, y, color); |
| } |
| |
| template <typename T> |
| static void putpixel(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t color) { |
| T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride); |
| |
| if (sizeof(T) == sizeof(uint32_t)) { |
| *dest = static_cast<T>(color); |
| } else { |
| // colors come in in ARGB 8888 form, flatten them |
| *dest = static_cast<T>(surface->translate_color(color)); |
| } |
| } |
| |
| template <typename T> |
| static void copyrect(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t width, uint32_t height, |
| uint32_t x2, uint32_t y2) { |
| // copy |
| const T* src = static_cast<const T*>(surface->ptr) + (x + y * surface->stride); |
| T* dest = static_cast<T*>(surface->ptr) + (x2 + y2 * surface->stride); |
| uint32_t stride_diff = surface->stride - width; |
| |
| if (dest < src) { |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = *src; |
| dest++; |
| src++; |
| } |
| dest += stride_diff; |
| src += stride_diff; |
| } |
| } else { |
| // copy backwards |
| src += (height - 1) * surface->stride + (width - 1); |
| dest += (height - 1) * surface->stride + (width - 1); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = *src; |
| dest--; |
| src--; |
| } |
| dest -= stride_diff; |
| src -= stride_diff; |
| } |
| } |
| } |
| |
| template <typename T> |
| static void fillrect(gfx_surface* surface, uint32_t x, uint32_t y, uint32_t width, uint32_t height, |
| uint32_t _color) { |
| T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride); |
| uint32_t stride_diff = surface->stride - width; |
| |
| T color; |
| if (sizeof(_color) == sizeof(color)) { |
| color = static_cast<T>(_color); |
| } else { |
| color = static_cast<T>(surface->translate_color(_color)); |
| } |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = color; |
| dest++; |
| } |
| dest += stride_diff; |
| } |
| } |
| |
| void gfx_line(gfx_surface* surface, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, |
| uint32_t color) { |
| if (unlikely(x1 >= surface->width)) |
| return; |
| if (unlikely(x2 >= surface->width)) |
| return; |
| |
| if (y1 >= surface->height) |
| return; |
| if (y2 >= surface->height) |
| return; |
| |
| int dx = x2 - x1; |
| int dy = y2 - y1; |
| |
| int sdx = (0 < dx) - (dx < 0); |
| int sdy = (0 < dy) - (dy < 0); |
| |
| uint32_t dxabs = (dx > 0) ? dx : -dx; |
| uint32_t dyabs = (dy > 0) ? dy : -dy; |
| |
| uint32_t x = dyabs >> 1; |
| uint32_t y = dxabs >> 1; |
| |
| uint32_t px = x1; |
| uint32_t py = y1; |
| |
| if (dxabs >= dyabs) { |
| // mostly horizontal line. |
| for (uint32_t i = 0; i < dxabs; i++) { |
| y += dyabs; |
| if (y >= dxabs) { |
| y -= dxabs; |
| py += sdy; |
| } |
| px += sdx; |
| surface->putpixel(surface, px, py, color); |
| } |
| } else { |
| // mostly vertical line. |
| for (uint32_t i = 0; i < dyabs; i++) { |
| x += dxabs; |
| if (x >= dyabs) { |
| x -= dyabs; |
| px += sdx; |
| } |
| py += sdy; |
| surface->putpixel(surface, px, py, color); |
| } |
| } |
| } |
| |
| static uint32_t alpha32_add_ignore_destalpha(uint32_t dest, uint32_t src) { |
| uint32_t cdest[3]; |
| uint32_t csrc[3]; |
| |
| uint32_t srca; |
| uint32_t srcainv; |
| |
| srca = (src >> 24) & 0xff; |
| if (srca == 0) { |
| return dest; |
| } else if (srca == 255) { |
| return src; |
| } |
| srca++; |
| srcainv = (255 - srca); |
| |
| cdest[0] = (dest >> 16) & 0xff; |
| cdest[1] = (dest >> 8) & 0xff; |
| cdest[2] = (dest >> 0) & 0xff; |
| |
| csrc[0] = (src >> 16) & 0xff; |
| csrc[1] = (src >> 8) & 0xff; |
| csrc[2] = (src >> 0) & 0xff; |
| |
| // if (srca > 0) |
| // 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); |
| |
| uint32_t cres[3]; |
| |
| cres[0] = ((csrc[0] * srca) / 256) + ((cdest[0] * srcainv) / 256); |
| cres[1] = ((csrc[1] * srca) / 256) + ((cdest[1] * srcainv) / 256); |
| cres[2] = ((csrc[2] * srca) / 256) + ((cdest[2] * srcainv) / 256); |
| |
| return (srca << 24) | (cres[0] << 16) | (cres[1] << 8) | (cres[2]); |
| } |
| |
| /** |
| * @brief Copy pixels from source to dest. |
| * |
| * Currently does not support alpha channel. |
| */ |
| void gfx_surface_blend(struct gfx_surface* target, struct gfx_surface* source, uint32_t destx, |
| uint32_t desty) { |
| gfx_blend(target, source, 0, 0, source->width, source->height, destx, desty); |
| } |
| |
| void gfx_blend(struct gfx_surface* target, struct gfx_surface* source, uint32_t srcx, uint32_t srcy, |
| uint32_t width, uint32_t height, uint32_t destx, uint32_t desty) { |
| const gfx_context* ctx = source->ctx; |
| GFX_ASSERT(ctx, target->format == source->format); |
| GFX_LOG(ctx, "target %p, source %p, destx %u, desty %u\n", target, source, destx, desty); |
| |
| if (destx >= target->width) |
| return; |
| if (desty >= target->height) |
| return; |
| |
| if (srcx >= source->width) |
| return; |
| if (srcy >= source->height) |
| return; |
| |
| if (destx + width > target->width) |
| width = target->width - destx; |
| if (desty + height > target->height) |
| height = target->height - desty; |
| |
| if (srcx + width > source->width) |
| width = source->width - srcx; |
| if (srcy + height > source->height) |
| height = source->height - srcy; |
| |
| // XXX total hack to deal with various blends |
| if (source->format == ZX_PIXEL_FORMAT_RGB_565 && target->format == ZX_PIXEL_FORMAT_RGB_565) { |
| // 16 bit to 16 bit |
| const uint16_t* src = static_cast<uint16_t*>(source->ptr) + (srcx + srcy * source->stride); |
| uint16_t* dest = static_cast<uint16_t*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = *src; |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else if (source->format == ZX_PIXEL_FORMAT_ARGB_8888 && |
| target->format == ZX_PIXEL_FORMAT_ARGB_8888) { |
| // both are 32 bit modes, both alpha |
| const uint32_t* src = static_cast<uint32_t*>(source->ptr) + (srcx + srcy * source->stride); |
| uint32_t* dest = static_cast<uint32_t*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| // TODO(fxbug.dev/84457): Currently it ignores destination alpha. |
| // We should implement alpha blending correctly. |
| *dest = alpha32_add_ignore_destalpha(*dest, *src); |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else if (source->format == ZX_PIXEL_FORMAT_RGB_x888 && |
| target->format == ZX_PIXEL_FORMAT_RGB_x888) { |
| // both are 32 bit modes, no alpha |
| const uint32_t* src = static_cast<uint32_t*>(source->ptr) + (srcx + srcy * source->stride); |
| uint32_t* dest = static_cast<uint32_t*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = *src; |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else if (source->format == ZX_PIXEL_FORMAT_ARGB_8888 && |
| target->format == ZX_PIXEL_FORMAT_RGB_888) { |
| // 32 bit to 24 bit modes, alpha to no-alpha |
| const uint32_t* src = static_cast<uint32_t*>(source->ptr) + (srcx + srcy * source->stride); |
| Rgb888* dest = static_cast<Rgb888*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| // TODO(fxbug.dev/84457): Currently it ignores destination alpha. |
| // We should implement alpha blending correctly. |
| *dest = Rgb888(alpha32_add_ignore_destalpha(dest->ToRgba32(), *src)); |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else if (source->format == ZX_PIXEL_FORMAT_RGB_x888 && |
| target->format == ZX_PIXEL_FORMAT_RGB_888) { |
| // 32 bit to 24 bit modes, no alpha |
| const uint32_t* src = static_cast<uint32_t*>(source->ptr) + (srcx + srcy * source->stride); |
| Rgb888* dest = static_cast<Rgb888*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = Rgb888(*src); |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else if (source->format == ZX_PIXEL_FORMAT_MONO_8 && target->format == ZX_PIXEL_FORMAT_MONO_8) { |
| // both are 8 bit modes, no alpha |
| const uint8_t* src = static_cast<uint8_t*>(source->ptr) + (srcx + srcy * source->stride); |
| uint8_t* dest = static_cast<uint8_t*>(target->ptr) + (destx + desty * target->stride); |
| uint32_t dest_stride_diff = target->stride - width; |
| uint32_t source_stride_diff = source->stride - width; |
| |
| GFX_LOG(ctx, "w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, |
| source_stride_diff); |
| |
| uint32_t i, j; |
| for (i = 0; i < height; i++) { |
| for (j = 0; j < width; j++) { |
| *dest = *src; |
| dest++; |
| src++; |
| } |
| dest += dest_stride_diff; |
| src += source_stride_diff; |
| } |
| } else { |
| GFX_ASSERT_MSG( |
| ctx, false, |
| "gfx_surface_blend: unimplemented colorspace combination (source %u target %u)\n", |
| source->format, target->format); |
| } |
| } |
| |
| template <typename T> |
| static void putchar(gfx_surface* surface, const gfx_font_t* font, uint32_t ch, uint32_t x, |
| uint32_t y, uint32_t fg, uint32_t bg) { |
| T* dest = static_cast<T*>(surface->ptr) + (x + y * surface->stride); |
| |
| const uint16_t* cdata = font->data + ch * font->height; |
| uint32_t fw = font->width; |
| for (uint32_t i = font->height; i > 0; i--) { |
| uint16_t xdata = *cdata++; |
| for (uint32_t j = fw; j > 0; j--) { |
| *dest++ = static_cast<T>((xdata & 1) ? fg : bg); |
| xdata = static_cast<uint16_t>(xdata >> 1); |
| } |
| dest += (surface->stride - fw); |
| } |
| } |
| |
| void gfx_putchar(gfx_surface* surface, const gfx_font_t* font, uint32_t ch, uint32_t x, uint32_t y, |
| uint32_t fg, uint32_t bg) { |
| if (unlikely(ch > 127)) { |
| return; |
| } |
| if (unlikely(x > (surface->width - font->width))) { |
| return; |
| } |
| if (unlikely(y > (surface->height - font->height))) { |
| return; |
| } |
| if (surface->translate_color) { |
| fg = surface->translate_color(fg); |
| bg = surface->translate_color(bg); |
| } |
| surface->putchar(surface, font, ch, x, y, fg, bg); |
| } |
| |
| void gfx_copylines(gfx_surface* dst, gfx_surface* src, uint32_t srcy, uint32_t dsty, |
| uint32_t height) { |
| if ((dst->stride != src->stride) || (dst->format != src->format)) { |
| return; |
| } |
| if ((srcy >= src->height) || ((src->height - srcy) < height)) { |
| return; |
| } |
| if ((dsty >= dst->height) || (dst->height - dsty) < height) { |
| return; |
| } |
| memmove(reinterpret_cast<uint8_t*>(dst->ptr) + dsty * dst->stride * dst->pixelsize, |
| reinterpret_cast<uint8_t*>(src->ptr) + srcy * src->stride * src->pixelsize, |
| height * src->stride * src->pixelsize); |
| } |
| |
| /** |
| * @brief Ensure all graphics rendering is sent to display |
| */ |
| void gfx_flush(gfx_surface* surface) { |
| if (surface->flags & GFX_FLAG_FLUSH_CPU_CACHE) |
| surface->ctx->flush_cache(surface->ptr, surface->len); |
| |
| if (surface->flush) |
| surface->flush(0, surface->height - 1); |
| } |
| |
| /** |
| * @brief Ensure that a sub-region of the display is up to date. |
| */ |
| void gfx_flush_rows(struct gfx_surface* surface, uint32_t start, uint32_t end) { |
| if (start > end) { |
| uint32_t temp = start; |
| start = end; |
| end = temp; |
| } |
| |
| if (start >= surface->height) |
| return; |
| if (end >= surface->height) |
| end = surface->height - 1; |
| |
| if (surface->flags & GFX_FLAG_FLUSH_CPU_CACHE) { |
| uint32_t runlen = surface->stride * surface->pixelsize; |
| surface->ctx->flush_cache(reinterpret_cast<uint8_t*>(surface->ptr) + start * runlen, |
| (end - start + 1) * runlen); |
| } |
| |
| if (surface->flush) |
| surface->flush(start, end); |
| } |
| |
| /** |
| * @brief Create a new graphics surface object |
| */ |
| gfx_surface* gfx_create_surface_with_context(void* ptr, const gfx_context* ctx, uint32_t width, |
| uint32_t height, uint32_t stride, gfx_format format, |
| uint32_t flags) { |
| gfx_surface* surface = static_cast<gfx_surface*>(calloc(1, sizeof(*surface))); |
| if (surface == NULL) |
| return NULL; |
| surface->ctx = ctx; |
| if (gfx_init_surface(surface, ptr, width, height, stride, format, flags)) { |
| free(surface); |
| return NULL; |
| } |
| return surface; |
| } |
| |
| zx_status_t gfx_init_surface(gfx_surface* surface, void* ptr, uint32_t width, uint32_t height, |
| uint32_t stride, gfx_format format, uint32_t flags) { |
| if ((width == 0) || (height == 0) || (stride < width)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| surface->flags = flags; |
| surface->format = format; |
| surface->width = width; |
| surface->height = height; |
| surface->stride = stride; |
| surface->alpha = MAX_ALPHA; |
| |
| // set up some function pointers |
| switch (format) { |
| case ZX_PIXEL_FORMAT_RGB_565: |
| surface->translate_color = &ARGB8888_to_RGB565; |
| surface->copyrect = ©rect<uint16_t>; |
| surface->fillrect = &fillrect<uint16_t>; |
| surface->putpixel = &putpixel<uint16_t>; |
| surface->putchar = &putchar<uint16_t>; |
| surface->pixelsize = 2; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| case ZX_PIXEL_FORMAT_RGB_888: |
| surface->translate_color = &ARGB8888_to_RGB888; |
| surface->copyrect = ©rect<Rgb888>; |
| surface->fillrect = &fillrect<Rgb888>; |
| surface->putpixel = &putpixel<Rgb888>; |
| surface->putchar = &putchar<Rgb888>; |
| surface->pixelsize = 3; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| case ZX_PIXEL_FORMAT_RGB_x888: |
| case ZX_PIXEL_FORMAT_ARGB_8888: |
| surface->translate_color = NULL; |
| surface->copyrect = ©rect<uint32_t>; |
| surface->fillrect = &fillrect<uint32_t>; |
| surface->putpixel = &putpixel<uint32_t>; |
| surface->putchar = &putchar<uint32_t>; |
| surface->pixelsize = 4; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| case ZX_PIXEL_FORMAT_MONO_8: |
| surface->translate_color = &ARGB8888_to_Luma; |
| surface->copyrect = ©rect<uint8_t>; |
| surface->fillrect = &fillrect<uint8_t>; |
| surface->putpixel = &putpixel<uint8_t>; |
| surface->putchar = &putchar<uint8_t>; |
| surface->pixelsize = 1; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| case ZX_PIXEL_FORMAT_RGB_332: |
| surface->translate_color = &ARGB8888_to_RGB332; |
| surface->copyrect = ©rect<uint8_t>; |
| surface->fillrect = &fillrect<uint8_t>; |
| surface->putpixel = &putpixel<uint8_t>; |
| surface->putchar = &putchar<uint8_t>; |
| surface->pixelsize = 1; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| case ZX_PIXEL_FORMAT_RGB_2220: |
| surface->translate_color = &ARGB8888_to_RGB2220; |
| surface->copyrect = ©rect<uint8_t>; |
| surface->fillrect = &fillrect<uint8_t>; |
| surface->putpixel = &putpixel<uint8_t>; |
| surface->putchar = &putchar<uint8_t>; |
| surface->pixelsize = 1; |
| surface->len = (surface->height * surface->stride * surface->pixelsize); |
| break; |
| default: |
| GFX_LOG(surface->ctx, "invalid graphics format\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (ptr == NULL) { |
| // allocate a buffer |
| ptr = malloc(surface->len); |
| if (ptr == NULL) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| GFX_ASSERT(surface->ctx, ptr); |
| surface->flags |= GFX_FLAG_FREE_ON_DESTROY; |
| } |
| surface->ptr = ptr; |
| return ZX_OK; |
| } |
| |
| /** |
| * @brief Destroy a graphics surface and free all resources allocated to it. |
| * |
| * @param surface Surface to destroy. This pointer is no longer valid after |
| * this call. |
| */ |
| void gfx_surface_destroy(struct gfx_surface* surface) { |
| if (surface->flags & GFX_FLAG_FREE_ON_DESTROY) |
| free(surface->ptr); |
| free(surface); |
| } |