[lib] generic graphics routines
diff --git a/include/dev/display.h b/include/dev/display.h
new file mode 100644
index 0000000..aa0bff6
--- /dev/null
+++ b/include/dev/display.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+#ifndef __DEV_DISPLAY_H
+#define __DEV_DISPLAY_H
+
+#include <sys/types.h>
+#include <lib/gfx.h>
+
+int display_init(void *framebuffer);
+int display_enable(bool enable);
+void display_pre_freq_change(void);
+void display_post_freq_change(void);
+
+struct display_info {
+ void *framebuffer;
+ gfx_format format;
+ uint width;
+ uint height;
+ uint stride;
+
+ // Update function
+ void (*flush)(uint starty, uint endy);
+};
+
+void display_get_info(struct display_info *info);
+
+#endif
+
diff --git a/include/lib/gfx.h b/include/lib/gfx.h
new file mode 100644
index 0000000..2f251e4
--- /dev/null
+++ b/include/lib/gfx.h
@@ -0,0 +1,86 @@
+#ifndef __LIB_GFX_H
+#define __LIB_GFX_H
+
+#include <sys/types.h>
+
+// gfx library
+
+// different graphics formats
+typedef enum {
+ GFX_FORMAT_RGB_565,
+ GFX_FORMAT_ARGB_8888,
+ GFX_FORMAT_RGB_x888,
+
+ GFX_FORMAT_MAX
+} gfx_format;
+
+#define MAX_ALPHA 255
+
+/**
+ * @brief Describe a graphics drawing surface
+ *
+ * The gfx_surface object represents a framebuffer that can be rendered
+ * to. Elements include a pointer to the actual pixel memory, its size, its
+ * layout, and pointers to basic drawing functions.
+ *
+ * @ingroup graphics
+ */
+typedef struct gfx_surface {
+ void *ptr;
+ bool free_on_destroy;
+ gfx_format format;
+ uint width;
+ uint height;
+ uint stride;
+ uint pixelsize;
+ size_t len;
+ uint alpha;
+
+ // function pointers
+ void (*copyrect)(struct gfx_surface *, uint x, uint y, uint width, uint height, uint x2, uint y2);
+ void (*fillrect)(struct gfx_surface *, uint x, uint y, uint width, uint height, uint color);
+ void (*putpixel)(struct gfx_surface *, uint x, uint y, uint color);
+ void (*flush)(uint starty, uint endy);
+} gfx_surface;
+
+// copy a rect from x,y with width x height to x2, y2
+void gfx_copyrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint x2, uint y2);
+
+// fill a rect within the surface with a color
+void gfx_fillrect(gfx_surface *surface, uint x, uint y, uint width, uint height, uint color);
+
+// draw a pixel at x, y in the surface
+void gfx_putpixel(gfx_surface *surface, uint x, uint y, uint color);
+
+// clear the entire surface with a color
+static inline void gfx_clear(gfx_surface *surface, uint color)
+{
+ surface->fillrect(surface, 0, 0, surface->width, surface->height, color);
+
+ if (surface->flush)
+ surface->flush(0, surface->height-1);
+}
+
+// blend between two surfaces
+void gfx_surface_blend(struct gfx_surface *target, struct gfx_surface *source, uint destx, uint desty);
+
+void gfx_flush(struct gfx_surface *surface);
+
+void gfx_flush_rows(struct gfx_surface *surface, uint start, uint end);
+
+// surface setup
+gfx_surface *gfx_create_surface(void *ptr, uint width, uint height, uint stride, gfx_format format);
+
+// utility routine to make a surface out of a display info
+struct display_info;
+gfx_surface *gfx_create_surface_from_display(struct display_info *);
+
+// free the surface
+// optionally frees the buffer if the free bit is set
+void gfx_surface_destroy(struct gfx_surface *surface);
+
+// utility routine to fill the display with a little moire pattern
+void gfx_draw_pattern(void);
+
+#endif
+
diff --git a/lib/gfx/gfx.c b/lib/gfx/gfx.c
new file mode 100644
index 0000000..9296be4
--- /dev/null
+++ b/lib/gfx/gfx.c
@@ -0,0 +1,621 @@
+/*
+ * 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 <debug.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arch/ops.h>
+#include <sys/types.h>
+#include <lib/gfx.h>
+#include <dev/display.h>
+
+#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 <lib/console.h>
+
+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
diff --git a/lib/gfx/rules.mk b/lib/gfx/rules.mk
new file mode 100644
index 0000000..9527360
--- /dev/null
+++ b/lib/gfx/rules.mk
@@ -0,0 +1,4 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+OBJS += \
+ $(LOCAL_DIR)/gfx.o