/* * Copyright (c) 2008-2010 Travis Geiselbrecht * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @defgroup graphics Graphics * * @{ */ /** * @file * @brief Graphics drawing library */ #include #include #include #include #include #include #include #define LOCAL_TRACE 0 static uint16_t ARGB8888_to_RGB565(uint32_t in) { uint16_t out; out = (in >> 3) & 0x1f; // b out |= ((in >> 10) & 0x3f) << 5; // g out |= ((in >> 19) & 0x1f) << 11; // r return out; } /** * @brief Copy a rectangle of pixels from one part of the display to another. */ void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint 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, uint x, uint y, uint width, uint height, uint color) { LTRACEF("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, uint x, uint y, uint color) { if (unlikely(x >= surface->width)) return; if (y >= surface->height) return; surface->putpixel(surface, x, y, color); } static void putpixel16(gfx_surface *surface, uint x, uint y, uint color) { uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride]; // colors come in in ARGB 8888 form, flatten them *dest = ARGB8888_to_RGB565(color); } static void putpixel32(gfx_surface *surface, uint x, uint y, uint color) { uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride]; *dest = color; } static void copyrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2) { // copy const uint16_t *src = &((const uint16_t *)surface->ptr)[x + y * surface->stride]; uint16_t *dest = &((uint16_t *)surface->ptr)[x2 + y2 * surface->stride]; uint stride_diff = surface->stride - width; if (dest < src) { uint 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 * surface->stride + width; dest += height * surface->stride + width; uint i, j; for (i=0; i < height; i++) { for (j=0; j < width; j++) { *dest = *src; dest--; src--; } dest -= stride_diff; src -= stride_diff; } } } static void fillrect16(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color) { uint16_t *dest = &((uint16_t *)surface->ptr)[x + y * surface->stride]; uint stride_diff = surface->stride - width; uint16_t color16 = ARGB8888_to_RGB565(color); uint i, j; for (i=0; i < height; i++) { for (j=0; j < width; j++) { *dest = color16; dest++; } dest += stride_diff; } } static void copyrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2) { // copy const uint32_t *src = &((const uint32_t *)surface->ptr)[x + y * surface->stride]; uint32_t *dest = &((uint32_t *)surface->ptr)[x2 + y2 * surface->stride]; uint stride_diff = surface->stride - width; if (dest < src) { uint 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 * surface->stride + width; dest += height * surface->stride + width; uint i, j; for (i=0; i < height; i++) { for (j=0; j < width; j++) { *dest = *src; dest--; src--; } dest -= stride_diff; src -= stride_diff; } } } static void fillrect32(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color) { uint32_t *dest = &((uint32_t *)surface->ptr)[x + y * surface->stride]; uint stride_diff = surface->stride - width; uint i, j; for (i=0; i < height; i++) { for (j=0; j < width; j++) { *dest = color; dest++; } dest += stride_diff; } } 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, uint destx, uint desty) { DEBUG_ASSERT(target->format == source->format); LTRACEF("target %p, source %p, destx %u, desty %u\n", target, source, destx, desty); if (destx >= target->width) return; if (desty >= target->height) return; uint width = source->width; if (destx + width > target->width) width = target->width - destx; uint height = source->height; if (desty + height > target->height) height = target->height - desty; // XXX total hack to deal with various blends if (source->format == GFX_FORMAT_RGB_565 && target->format == GFX_FORMAT_RGB_565) { // 16 bit to 16 bit const uint16_t *src = (const uint16_t *)source->ptr; uint16_t *dest = &((uint16_t *)target->ptr)[destx + desty * target->stride]; uint dest_stride_diff = target->stride - width; uint source_stride_diff = source->stride - width; LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff); uint 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 == GFX_FORMAT_ARGB_8888 && target->format == GFX_FORMAT_ARGB_8888) { // both are 32 bit modes, both alpha const uint32_t *src = (const uint32_t *)source->ptr; uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride]; uint dest_stride_diff = target->stride - width; uint source_stride_diff = source->stride - width; LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff); uint i, j; for (i=0; i < height; i++) { for (j=0; j < width; j++) { // XXX ignores destination alpha *dest = alpha32_add_ignore_destalpha(*dest, *src); dest++; src++; } dest += dest_stride_diff; src += source_stride_diff; } } else if (source->format == GFX_FORMAT_RGB_x888 && target->format == GFX_FORMAT_RGB_x888) { // both are 32 bit modes, no alpha const uint32_t *src = (const uint32_t *)source->ptr; uint32_t *dest = &((uint32_t *)target->ptr)[destx + desty * target->stride]; uint dest_stride_diff = target->stride - width; uint source_stride_diff = source->stride - width; LTRACEF("w %u h %u dstride %u sstride %u\n", width, height, dest_stride_diff, source_stride_diff); uint 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 { panic("gfx_surface_blend: unimplemented colorspace combination (source %d target %d)\n", source->format, target->format); } } /** * @brief Ensure all graphics rendering is sent to display */ void gfx_flush(gfx_surface *surface) { arch_clean_cache_range((addr_t)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, uint start, uint end) { if (start > end) { uint temp = start; start = end; end = temp; } if (start >= surface->height) return; if (end >= surface->height) end = surface->height - 1; arch_clean_cache_range((addr_t)surface->ptr + start * surface->stride * surface->pixelsize, (end - start + 1) * surface->stride * surface->pixelsize); if (surface->flush) surface->flush(start, end); } /** * @brief Create a new graphics surface object */ gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format) { DEBUG_ASSERT(width > 0); DEBUG_ASSERT(height > 0); DEBUG_ASSERT(stride >= width); DEBUG_ASSERT(format < GFX_FORMAT_MAX); gfx_surface *surface = malloc(sizeof(gfx_surface)); surface->free_on_destroy = false; surface->format = format; surface->width = width; surface->height = height; surface->stride = stride; surface->alpha = MAX_ALPHA; // set up some function pointers switch (format) { case GFX_FORMAT_RGB_565: surface->copyrect = ©rect16; surface->fillrect = &fillrect16; surface->putpixel = &putpixel16; surface->pixelsize = 2; surface->len = surface->height * surface->stride * surface->pixelsize; break; case GFX_FORMAT_RGB_x888: case GFX_FORMAT_ARGB_8888: surface->copyrect = ©rect32; surface->fillrect = &fillrect32; surface->putpixel = &putpixel32; surface->pixelsize = 4; surface->len = surface->height * surface->stride * surface->pixelsize; break; default: dprintf(INFO, "invalid graphics format\n"); DEBUG_ASSERT(0); free(surface); return NULL; } if (ptr == NULL) { // allocate a buffer ptr = malloc(surface->len); DEBUG_ASSERT(ptr); surface->free_on_destroy = true; } surface->ptr = ptr; return surface; } /** * @brief Create a new graphics surface object from a display */ gfx_surface *gfx_create_surface_from_display(struct display_info *info) { gfx_surface* surface; surface = gfx_create_surface(info->framebuffer, info->width, info->height, info->stride, info->format); surface->flush = info->flush; return surface; } /** * @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->free_on_destroy) free(surface->ptr); free(surface); } /** * @brief Write a test pattern to the default display. */ void gfx_draw_pattern(void) { struct display_info info; display_get_info(&info); gfx_surface *surface = gfx_create_surface_from_display(&info); uint x, y; for (y = 0; y < surface->height; y++) { for (x = 0; x < surface->width; x++) { uint scaledx; uint scaledy; scaledx = x * 256 / surface->width; scaledy = y * 256 / surface->height; gfx_putpixel(surface, x, y, (scaledx * scaledy) << 16 | (scaledx >> 1) << 8 | scaledy >> 1); } } if (surface->flush) surface->flush(0, surface->height-1); gfx_surface_destroy(surface); } /** * @brief Fill default display with white */ void gfx_draw_pattern_white(void) { struct display_info info; display_get_info(&info); gfx_surface *surface = gfx_create_surface_from_display(&info); uint x, y; for (y = 0; y < surface->height; y++) { for (x = 0; x < surface->width; x++) { gfx_putpixel(surface, x, y, 0xFFFFFF); } } if (surface->flush) surface->flush(0, surface->height-1); gfx_surface_destroy(surface); } #if defined(WITH_LIB_CONSOLE) #if DEBUGLEVEL > 1 #include static int cmd_gfx(int argc, const cmd_args *argv); STATIC_COMMAND_START STATIC_COMMAND("gfx", "gfx commands", &cmd_gfx) STATIC_COMMAND_END(gfx); static int gfx_draw_rgb_bars(gfx_surface *surface) { uint x, y; int step32 = surface->height*100 / 32; int step64 = surface->height*100 / 64; int color; for (y = 0; y < surface->height; y++) { //R for (x = 0; x < surface->width/3; x++) { color = y*100 / step32; gfx_putpixel(surface, x, y, color << 16); } //G for (;x < 2*(surface->width/3); x++) { color = y*100 / step64; gfx_putpixel(surface, x, y, color << 8); } //B for (;x < surface->width; x++) { color = y*100 / step32; gfx_putpixel(surface, x, y, color); } } return 0; } static int cmd_gfx(int argc, const cmd_args *argv) { if (argc < 2) { printf("not enough arguments:\n"); usage: printf("%s rgb_bars : Fill frame buffer with rgb bars\n", argv[0].str); printf("%s fill r g b : Fill frame buffer with RGB565 value and force update\n", argv[0].str); return -1; } struct display_info info; display_get_info(&info); gfx_surface *surface = gfx_create_surface_from_display(&info); if (!strcmp(argv[1].str, "rgb_bars")) { gfx_draw_rgb_bars(surface); } else if (!strcmp(argv[1].str, "fill")) { uint x, y; for (y = 0; y < surface->height; y++) { for (x = 0; x < surface->width; x++) { /* write pixel to frame buffer */ gfx_putpixel(surface, x, y, (argv[2].i << 16) | (argv[3].i << 8) | argv[4].i); } } } if (surface->flush) surface->flush(0, surface->height-1); gfx_surface_destroy(surface); return 0; } #endif #endif