From 1ebab2409066e033539021976bfaa6f27d5dd15d Mon Sep 17 00:00:00 2001 From: Yann MAGNIN Date: Sun, 19 Jun 2022 20:11:52 +0200 Subject: [PATCH] VxKernel 0.6.0-10 : Add image API @add <> include/vhex/display/image && src/modules/display/draw | add image "types" information | add image "object" manipulation API | add image "render" API (first version) @update <> include/vhex/display/shader.h | update surface structure to add some precalculated information <> src/drivers/screen/r61524 | precalculate some surface geometry information | remove useless code <> src/module/display | use the new surface precalculated information @fix <> include/display/draw/circle | fix DCIRCLE_* flags value <> src/modules/display/draw | fix dhline culling check | fix dvline culling check | fix circle position | fix circle render freeze --- include/vhex/display/draw/circle.h | 12 +- include/vhex/display/image.h | 3 + include/vhex/display/image/information.h | 58 +++++ include/vhex/display/image/object.h | 91 ++++++++ include/vhex/display/image/render.h | 42 ++++ include/vhex/display/image/types.h | 82 +++++++ include/vhex/display/shader.h | 6 +- src/drivers/mpu/sh/sh7305/intc/intc.c | 3 +- src/drivers/screen/R61524/r61524.c | 37 ++- src/modules/display/dclear.c | 2 +- src/modules/display/draw/dcircle.c | 12 +- src/modules/display/draw/dline.c | 34 +-- src/modules/display/draw/dpixel.c | 12 +- src/modules/display/dstack.c | 30 +-- src/modules/display/image/information.c | 84 +++++++ src/modules/display/image/object.c | 149 ++++++++++++ src/modules/display/image/render.c | 278 +++++++++++++++++++++++ src/modules/display/text/dfont.c | 10 +- 18 files changed, 847 insertions(+), 98 deletions(-) create mode 100644 include/vhex/display/image.h create mode 100644 include/vhex/display/image/information.h create mode 100644 include/vhex/display/image/object.h create mode 100644 include/vhex/display/image/render.h create mode 100644 include/vhex/display/image/types.h create mode 100644 src/modules/display/image/information.c create mode 100644 src/modules/display/image/object.c create mode 100644 src/modules/display/image/render.c diff --git a/include/vhex/display/draw/circle.h b/include/vhex/display/draw/circle.h index 91378ce..3130f97 100644 --- a/include/vhex/display/draw/circle.h +++ b/include/vhex/display/draw/circle.h @@ -22,13 +22,13 @@ enum { /* Horizontal settings: default in dcircle*() is DCIRCLE_LEFT */ - DCIRCLE_LEFT = 0x00, - DCIRCLE_CENTER = 0x01, - DCIRCLE_RIGHT = 0x02, + DCIRCLE_LEFT = 0x01, + DCIRCLE_CENTER = 0x02, + DCIRCLE_RIGHT = 0x04, /* Vertical settings: default in dcircle() is DCIRCLE_TOP */ - DCIRCLE_TOP = 0x00, - DCIRCLE_MIDDLE = 0x10, - DCIRCLE_BOTTOM = 0x20, + DCIRCLE_TOP = 0x10, + DCIRCLE_MIDDLE = 0x20, + DCIRCLE_BOTTOM = 0x40, }; /* dcircle_filled() : draw a filled circle */ diff --git a/include/vhex/display/image.h b/include/vhex/display/image.h new file mode 100644 index 0000000..dedf9d0 --- /dev/null +++ b/include/vhex/display/image.h @@ -0,0 +1,3 @@ +#include +#include +#include diff --git a/include/vhex/display/image/information.h b/include/vhex/display/image/information.h new file mode 100644 index 0000000..0461a82 --- /dev/null +++ b/include/vhex/display/image/information.h @@ -0,0 +1,58 @@ +#ifndef __VHEX_DISPLAY_DRAW_IMAGE_INFORMATION__ +# define __VHEX_DISPLAY_DRAW_IMAGE_INFORMATION__ + +#include + +//--- +// Basic image access and information +//--- + +/* image_valid(): Check if an image is valid + An image is considered valid if it has a valid profile, a non-NULL data + pointer, and for palette formats a valid palette pointer. */ +extern bool image_valid(image_t const *img); + +/* image_alpha(): Get the alpha value for an image format + + This function returns the alpha value for any specific image format: + * RGB16: 0x0001 + * P8: -128 (0x80) + * P4: 0 + For non-transparent formats, it returns a value that is different from all + valid pixel values of the format, which means it is always safe to compare a + pixel value to the image_alpha() of the format. */ +extern int image_alpha(int format); + +/* image_get_pixel(): Read a pixel from the data array + + This function reads a pixel from the image's data array at position (x,y). + It returns the pixel's value, which is either a full-color value (RGB16) or + a possibly-negative palette index (P8/P4). See the description of the [data] + field of image_t for more details. The value of the pixel can be decoded + into a 16-bit color either manually or by using the image_decode_pixel() + function. + + Note that reading large amounts of image data with this function will be + slow; if you need reasonable performance, consider iterating on the data + array manually. */ +extern int image_get_pixel(image_t const *img, int x, int y); + +/* image_decode_pixel(): Decode a pixel value + + This function decodes a pixel's value obtained from the data array (for + instance with image_get_pixel()). For RGB16 formats this does nothing, but + for palette formats this accesses the palette at a suitable position. + + Note that reading large amounts of data with this function will be slow; if + you need reasonable performance, consider inlining the format-specific + method or iterating on the data array manually. */ +extern int image_decode_pixel(image_t const *img, int pixel); + +/* image_data_size(): Compute the size of the [data] array + This function returns the size of the data array, in bytes. This can be used + to duplicate it. Note that for sub-images this is a subsection of another + image's data array, and might be much larger than the sub-image. */ +extern int image_data_size(image_t const *img); + + +#endif /* __VHEX_DISPLAY_DRAW_IMAGE_INFORMATION__ */ diff --git a/include/vhex/display/image/object.h b/include/vhex/display/image/object.h new file mode 100644 index 0000000..9a88a50 --- /dev/null +++ b/include/vhex/display/image/object.h @@ -0,0 +1,91 @@ +#ifndef __VHEX_DISPLAY_DRAW_IMAGE_OBJECT__ +# define __VHEX_DISPLAY_DRAW_IMAGE_OBJECT__ + +#include + +//--- +// Image creation and destruction +//--- + +/* image_alloc(): Create a new (uninitialized) image + + This function allocates a new image of the specified dimensions and format. + It always allocates a new data array; if you need to reuse a data array, use + the lower-level image_create() or image_create_sub(). + + The first parameters [width] and [height] specify the dimensions of the new + image in pixels. The [format] should be one of the IMAGE_* formats, for + example IMAGE_RGB565A or IMAGE_P4_RGB565. + + This function does not specify or initialize the palette of the new image; + use image_set_palette(), image_alloc_palette() or image_copy_palette() + after calling this function. + + The returned image structure must be freed with image_free() after use. + + @width Width of the new image + @height Height of the new image + @format Pixel format; one of the IMAGE_* formats defined above */ +extern image_t *image_alloc(size_t width, size_t height, int format); + +/* image_set_palette(): Specify an external palette for an image + + This function sets the image's palette to the provided address. The number + of entries allocated must be specified in size. It is also the caller's + responsibility to ensure that the palette covers all the indices used in the + image data. + + The old palette, if owned by the image, is freed. */ +extern void image_set_palette(image_t *img, uint16_t *p, size_t sz, bool owns); + +/* image_alloc_palette(): Allocate a new palette for an image + + This function allocates a new palette for an image. The number of entries is + specified in size; for P8 it can vary between 1 and 256, for P4 it is + ignored (P4 images always have 16 colors). + + The old palette, if owned by the image, is freed. The entries of the new + palette are all initialized to 0. If size is -1, the format's default + palette size is used. Returns true on success. */ +extern bool image_alloc_palette(image_t *img, int size); + +/* image_copy_palette(): Copy another image's palette + + This function allocates a new palette for an image, and initializes it with + a copy of another image's palette. For P8 the palette can be resized by + specifying a value other than -1 as the size; by default, the source image's + palette size is used (within the limits of the new format). Retuns true on + success. */ +extern bool image_copy_palette(image_t const *src, image_t *dst, int size); + +/* image_create(): Create a bare image with no data/palette + + This function allocates a new image structure but without data or palette. + The [data] and [palette] members are NULL, [color_count] and [stride] are 0. + + This function is useful to create images that reuse externally-provided + information. It is intended that the user of this function sets the [data] + and [stride] fields themselves, along with the IMAGE_FLAGS_DATA_ALLOC flag + if the image should own its data. + + The [palette] and [color_count] members can be set with image_set_palette(), + image_alloc_palette(), image_copy_palette(), or manually. + + The returned image structure must be freed with image_free() after use. */ +extern image_t *image_create(size_t width, size_t height, int format); + +/* image_free(): Free and image and the data it owns + + This function frees the provided image structure and the data that it owns. + Images converted by fxconv should not be freed; nonetheless, this functions + distinguishes them and should work. Images are not expected to be created on + the stack. + + If the image has the IMAGE_FLAGS_DATA_ALLOC flag, the data pointer is also + freed. Similarly, the image has the IMAGE_FLAGS_PALETTE_ALLOC flag, the + palette is freed. Make sure to not free images when references to them still + exist, as this could cause the reference's pointers to become dangling. */ +extern void image_free(image_t *img); + + +#endif /* __VHEX_DISPLAY_DRAW_IMAGE_OBJECT__ */ diff --git a/include/vhex/display/image/render.h b/include/vhex/display/image/render.h new file mode 100644 index 0000000..7a6ab48 --- /dev/null +++ b/include/vhex/display/image/render.h @@ -0,0 +1,42 @@ +#ifndef __VHEX_DISPLAY_DRAW_IMAGE_RENDER__ +# define __VHEX_DISPLAY_DRAW_IMAGE_RENDER__ + +#include + +/* Alignment settings for dimage*(). Combining a vertical and a horizontal + alignment option specifies where a given point (x,y) should be relative to + the rendered image. */ +enum { + /* Horizontal settings: default in diage*() is DIMAGE_LEFT */ + DIMAGE_LEFT = 0x00, + DIMAGE_CENTER = 0x01, + DIMAGE_RIGHT = 0x02, + /* Vertical settings: default in dimage*() is DIMAGE_TOP */ + DIMAGE_TOP = 0x10, + DIMAGE_MIDDLE = 0x20, + DIMAGE_BOTTOM = 0x40, +}; + +/* dimage(): Render a full image + This function blits an image on the VRAM using vhex's special format. It is + a special case of dsubimage() where the full image is drawn with clipping. + + @x @y Coordinates of the top-left corner of the image + @image Pointer to image encoded with fxconv for bopti */ +extern void dimage(image_t const *image, int x, int y, int mode); + +/* dsubimage(): Render a section of an image + This function blits a subrectangle [left, top, width, height] of an image. + + @x @y Coordinates on screen of the rendered subrectangle + @image Pointer to image encoded with fxconv for bopti + @left @top Top-left coordinates of the subrectangle within the image + @width @height Subrectangle dimensions + @flags OR-combination of DIMAGE_* flags */ +extern void dsubimage( + int x, int y, + image_t const *image, + int left, int top, int width, int height +); + +#endif /* __VHEX_DISPLAY_DRAW_IMAGE_RENDER__ */ diff --git a/include/vhex/display/image/types.h b/include/vhex/display/image/types.h new file mode 100644 index 0000000..dd90670 --- /dev/null +++ b/include/vhex/display/image/types.h @@ -0,0 +1,82 @@ +#ifndef __VHEX_DISPLAY_DRAW_IMAGE_TYPES__ +# define __VHEX_DISPLAY_DRAW_IMAGE_TYPES__ + +#include +#include + +/* Image formats. Note that transparency really only indicates the default + rendering method, as a transparent background can always be added or removed + by a dynamic effect (shader) on any image. */ +enum { + IMAGE_RGB565 = 0, /* RGB565 without alpha */ + IMAGE_RGB565A = 1, /* RGB565 with one transparent color */ + IMAGE_P8_RGB565 = 4, /* 8-bit palette, all opaque colors */ + IMAGE_P8_RGB565A = 5, /* 8-bit with one transparent color */ + IMAGE_P4_RGB565 = 6, /* 4-bit palette, all opaque colors */ + IMAGE_P4_RGB565A = 3, /* 4-bit with one transparent color */ +}; + +/* Quick macros to compare formats by storage size */ +#define IMAGE_IS_RGB16(format) \ + ((format) == IMAGE_RGB565 || (format) == IMAGE_RGB565A) +#define IMAGE_IS_P8(format) \ + ((format) == IMAGE_P8_RGB565 || (format) == IMAGE_P8_RGB565A) +#define IMAGE_IS_P4(format) \ + ((format) == IMAGE_P4_RGB565 || (format) == IMAGE_P4_RGB565A) +/* Check whether image format has an alpha color */ +#define IMAGE_IS_ALPHA(format) \ + ((format) == IMAGE_RGB565A || \ + (format) == IMAGE_P8_RGB565A || \ + (format) == IMAGE_P4_RGB565A) +/* Check whether image format uses a palette */ +#define IMAGE_IS_INDEXED(format) \ + (IMAGE_IS_P8(format) || IMAGE_IS_P4(format)) + +/* Image flags. These are used for memory management, mostly. */ +enum { + IMAGE_FLAGS_DATA_RO = 0x01, /* Data is read-only */ + IMAGE_FLAGS_PALETTE_RO = 0x02, /* Palette is read-only */ + IMAGE_FLAGS_DATA_ALLOC = 0x04, /* Data is malloc()'d */ + IMAGE_FLAGS_PALETTE_ALLOC = 0x08, /* Palette is malloc()'d */ + IMAGE_FLAGS_RO = 0x10, /* image is const */ + IMAGE_FLAGS_OWN = 0x20, /* image is owned */ +}; + +/* image_t: vhex's native bitmap image format + Images of this format can be created through this header's API but also by + using the `vxSDK conv` command. */ +typedef struct +{ + /* Color format, one of the IMAGE_* values defined above */ + uint8_t format; + /* Additional flags, a combination of IMAGE_FLAGS_* values */ + uint8_t flags; + /* Number of colors in the palette; this includes alpha for transparent + images, as alpha is always the first entry. + RGB16: 0 + P8: Ranges between 1 and 256 + P4: 16 */ + int16_t color_count; + /* Full width and height, in pixels */ + uint16_t width; + uint16_t height; + /* Byte stride between lines */ + int stride; + + /* Pixel data in row-major order, left to right. + - RGB16: 2 bytes per entry, each row padded to 4 bytes for alignment. + Each 2-byte value is an RGB565 color. + - P8: 1 signed byte per entry. Each byte is a palette index shifted by + 128 (to access the color, use palette[+128]). + - P4: 4 bits per entry, each row padded to a full byte. Each entry is a + direct palette index between 0 and 15. */ + void *data; + + /* For P8 and P4, color palette. The number of entries allocated in the + array is equal to the color_count attribute. */ + uint16_t *palette; + +} VPACKED(4) image_t; + + +#endif /* __VHEX_DISPLAY_DRAW_IMAGE_TYPES__ */ diff --git a/include/vhex/display/shader.h b/include/vhex/display/shader.h index 2f36c69..fb7462e 100644 --- a/include/vhex/display/shader.h +++ b/include/vhex/display/shader.h @@ -10,8 +10,10 @@ struct dshader_surface { void *frag; size_t width; size_t height; - int x; - int y; + int x1; + int y1; + int x2; + int y2; }; typedef struct dshader_surface dsurface_t; diff --git a/src/drivers/mpu/sh/sh7305/intc/intc.c b/src/drivers/mpu/sh/sh7305/intc/intc.c index 09c536d..23b1c16 100644 --- a/src/drivers/mpu/sh/sh7305/intc/intc.c +++ b/src/drivers/mpu/sh/sh7305/intc/intc.c @@ -40,8 +40,7 @@ struct intc_ctx { // hardware-level API //--- -/* Interrupt IPR and IMR positions. The order of entries is as in the named - list of interrupt signals in . */ +/* Interrupt IPR and IMR positions. */ /* Friendly names for IPR and IMR register numbers */ enum{ IPRA, IPRB, IPRC, IPRD, IPRE, IPRF, IPRG, IPRH, IPRI, IPRJ, IPRK, IPRL }; diff --git a/src/drivers/screen/R61524/r61524.c b/src/drivers/screen/R61524/r61524.c index 072f822..f7703eb 100644 --- a/src/drivers/screen/R61524/r61524.c +++ b/src/drivers/screen/R61524/r61524.c @@ -10,19 +10,11 @@ /* r61524_clear_surface() - optimal way to clear the draw and render surface */ VINLINE void r61524_clear_surface(struct dshader_surface *surface) { - //TODO: check cache behaviour: - // > ILRAM !! - // > full xram then yram - // > xram and yram - // > yram and xram - // > restrict / non-restrict uint32_t *xram = surface->draw; - int size = (surface->y == 220) ? 792 : 1980; + int size = (surface->y1 == 220) ? 792 : 1980; -// uint32_t * restrict yram = surface->frag; for (int i = 0; i < size; ++i) { xram[i] = 0x00010001; -// yram[i] = 0x00000000; } } @@ -53,8 +45,10 @@ int r61524_frame_start(struct dshader_surface *surface) surface->frag = (void*)0xe5017000; surface->width = 396; surface->height = 10; - surface->x = 0; - surface->y = 0; + surface->x1 = 0; + surface->y1 = 0; + surface->x2 = 395; + surface->y2 = 9; /* reset the two surfaces */ r61524_clear_surface(surface); @@ -63,28 +57,23 @@ int r61524_frame_start(struct dshader_surface *surface) int r61524_frame_frag_next(struct dshader_surface *surface) { - surface->y += 10; - if (surface->y >= 224) { - surface->y -= 10; + surface->y1 += 10; + if (surface->y1 >= 224) { + surface->y1 -= 10; return (-1); } - //TODO: wipe only the YRAM - //TODO: move this in dclear() ? -// r61524_clear_surface(surface); + + if (surface->y1 >= 220) + surface->height = 4; + surface->y2 += surface->height; return (0); } VWEAK int r61524_frame_frag_send(struct dshader_surface *surface) { uint16_t * restrict yram = surface->frag; - int size = (surface->y == 220) ? 1584 : 3960; + int size = (surface->y1 == 220) ? 1584 : 3960; - //TODO: assembly - //TODO: check cache behaviour: - // > full xram then yram - // > xram and yram - // > yram and xram - // > restrict / non-restrict for (int i = 0; i < size; ++i) { r61524_write(yram[i]); } diff --git a/src/modules/display/dclear.c b/src/modules/display/dclear.c index 3ebb8af..9fe1441 100644 --- a/src/modules/display/dclear.c +++ b/src/modules/display/dclear.c @@ -10,7 +10,7 @@ void dclear_render(struct dshader_surface *surface, uint32_t color) { uint32_t *vram; - int size = (surface->y == 220) ? 792 : 1980; + int size = (surface->y1 == 220) ? 792 : 1980; vram = surface->frag; for (int i = 0; i < size; ++i) diff --git a/src/modules/display/draw/dcircle.c b/src/modules/display/draw/dcircle.c index 2909bd3..4bd7e7d 100644 --- a/src/modules/display/draw/dcircle.c +++ b/src/modules/display/draw/dcircle.c @@ -21,7 +21,7 @@ void dcircle_filled_render( py = radius; d = 1 - radius; - dhline(y, x - py, x + py, color); + dhline_render(surface, y, x - py, x + py, color); while(py > px) { if(d < 0) { @@ -65,10 +65,12 @@ void dcircle_filled(int x, int y, size_t radius, int mode, int color) { if (color == C_NONE) return; - if (mode & DCIRCLE_CENTER) x -= radius; - if (mode & DCIRCLE_RIGHT) x -= radius << 1; - if (mode & DCIRCLE_MIDDLE) y -= radius; - if (mode & DCIRCLE_BOTTOM) y -= radius << 1; + int offset = radius & 1; + + if (mode & DCIRCLE_LEFT) x += (radius >> 1) + offset; + if (mode & DCIRCLE_RIGHT) x -= (radius >> 1) + offset; + if (mode & DCIRCLE_TOP) y += (radius >> 1) + offset; + if (mode & DCIRCLE_BOTTOM) y -= (radius >> 1) + offset; dstack_add_action( &DSTACK_CALL(&dcircle_filled_dstack, x, y, radius, color), diff --git a/src/modules/display/draw/dline.c b/src/modules/display/draw/dline.c index 1fb4fe1..836c6b2 100644 --- a/src/modules/display/draw/dline.c +++ b/src/modules/display/draw/dline.c @@ -63,14 +63,14 @@ void dline_render(dsurface_t *s, int x1, int y1, int x2, int y2, int color) void dhline_render(dsurface_t *surface, int y, int x1, int x2, int color) { /* Order and bounds */ - y -= surface->y; - if(y < 0 || y >= (int)surface->height) return; + if(y < surface->y1 || y > surface->y2) return; + if(x1 > surface->x2 || x2 < surface->x1) return; if(x1 > x2) swap(x1, x2); - x1 -= surface->x; - x2 -= surface->x; - if(x1 >= (int)surface->width || x2 < 0) return; - if(x2 >= (int)surface->width) x2 = surface->width; - if(x1 < 0) x1 = surface->x; + if(x1 < surface->x1) x1 = surface->x1; + if(x2 > surface->x2) x2 = surface->x2; + y -= surface->y1; + x1 -= surface->x1; + x2 -= surface->x1; int offset = surface->width * y; @@ -108,26 +108,26 @@ void dhline_render(dsurface_t *surface, int y, int x1, int x2, int color) void dvline_render(dsurface_t *surface, int x, int y1, int y2, int color) { /* Order and bounds */ - x -= surface->x; - if(x < 0 || x >= (int)surface->width) return; + if(x < surface->x1 || x > surface->x2) return; + if(y1 > surface->y2 || y2 < surface->y1) return; if(y1 > y2) swap(y1, y2); - y1 -= surface->y; - y2 -= surface->y; - if (y1 >= (int)surface->height || y2 < 0) return; - if(y1 < 0) y1 = 0; - if(y2 >= (int)surface->height) y2 = surface->height; + if(y1 < surface->y1) y1 = surface->y1; + if(y2 > surface->y2) y2 = surface->y2; + x -= surface->x1; + y1 -= surface->y1; + y2 -= surface->y1; uint16_t *vram = surface->frag; uint16_t *v = &vram[(surface->width * y1) + x]; - int height = y2 - y1 + 1; + int height = y2 - y1; if (color != C_INVERT) { - while(height-- > 0) { + while(height-- >= 0) { *v = color; v += surface->width; } } else { - while(height-- > 0) { + while(height-- >= 0) { *v ^= 0xffff; v += surface->width; } diff --git a/src/modules/display/draw/dpixel.c b/src/modules/display/draw/dpixel.c index 3a78f23..1a1bbe9 100644 --- a/src/modules/display/draw/dpixel.c +++ b/src/modules/display/draw/dpixel.c @@ -13,16 +13,16 @@ void dpixel_render(dsurface_t *surface, int x, int y, int color) return; /* check point culling */ - if (y < surface->y - || x < surface->x - || y >= (int)(surface->y + surface->height) - || x >= (int)(surface->x + surface->width)) { + if (y < surface->y1 + || x < surface->x1 + || y > surface->y2 + || x > surface->x2) { return; } /* calculate the draw index */ - int real_y = (y - surface->y) * surface->width; - int real_x = (x - surface->x); + int real_y = (y - surface->y1) * surface->width; + int real_x = (x - surface->x1); int draw_idx = real_y + real_x; /* handle special color */ diff --git a/src/modules/display/dstack.c b/src/modules/display/dstack.c index 85fb1f4..699b22d 100644 --- a/src/modules/display/dstack.c +++ b/src/modules/display/dstack.c @@ -28,12 +28,11 @@ static struct { } }; -// +//--- // Kernel API -// +//--- /* dstack_init() : Initialise the draw stack (should not be involved) */ -//TODO: safe int dstack_init(struct dstack_config *config) { dstack_info.pool.action = calloc( @@ -133,7 +132,7 @@ did_t dstack_add_action( dshader_call_t *shader, void (*quit)(uint32_t *arg) ) { - return dstack_action_alloc(call, shader, quit); + return (dstack_action_alloc(call, shader, quit)); } /* dstack_render(): render a frame */ @@ -186,26 +185,3 @@ size_t dstack_display_height(void) { return dstack_info.driver.display_height; } - - -#if 0 -void dstack_test(void) -{ - extern void dclear_render(struct dshader_surface *surface, uint32_t color); - - struct dshader_surface surface; - uint32_t buff[] = { - 'a', 1, 1, 0x0000, 0xffff - }; - - dstack_info.driver.frame_start(&surface); - do { - dclear_render(&surface, 0xffffffff); - dascii_dstack_render(&surface, buff); - //TODO: char draw - - dstack_info.driver.frame_frag_send(&surface); - } while (dstack_info.driver.frame_frag_next(&surface) == 0); - dstack_info.driver.frame_end(&surface); -} -#endif diff --git a/src/modules/display/image/information.c b/src/modules/display/image/information.c new file mode 100644 index 0000000..fbcc701 --- /dev/null +++ b/src/modules/display/image/information.c @@ -0,0 +1,84 @@ +#include +#include + +/* image_valid(): Check if an image is valid */ +bool image_valid(image_t const *img) +{ + if(img == NULL) + return (false); + + if(IMAGE_IS_RGB16(img->format)) { + return (img->data != NULL); + } + if(IMAGE_IS_P8(img->format) || IMAGE_IS_P4(img->format)) { + return ( + (img->data != NULL) + && (img->palette != NULL) + && (img->color_count != 0) + ); + } + + return (false); +} + +/* image_alpha(): Get the alpha value for an image format */ +int image_alpha(int format) +{ + switch(format) + { + case IMAGE_RGB565A: + return (0x0001); + case IMAGE_P8_RGB565A: + return (0); + case IMAGE_P4_RGB565A: + return (0); + default: + /* A value that cannot be found in any pixel of any format */ + return (0x10000); + } +} + +/* image_get_pixel(): Read a pixel from the data array */ +int image_get_pixel(image_t const *img, int x, int y) +{ + if((unsigned)x >= img->width || (unsigned)y >= img->height) + return (0); + + void *data = img->data + (y * img->stride); + uint8_t *data_u8 = data; + uint16_t *data_u16 = data; + + if(IMAGE_IS_RGB16(img->format)) { + return (data_u16[x]); + } + else if(IMAGE_IS_P8(img->format)) { + return ((int8_t)data_u8[x]); + } + else if(IMAGE_IS_P4(img->format)) { + if(x & 1) { + return (data_u8[x >> 1] & 0x0f); + } else { + return (data_u8[x >> 1] >> 4); + } + } + + return (0); +} + +/* image_decode_pixel(): Decode a pixel value */ +int image_decode_pixel(image_t const *img, int pixel) +{ + if(IMAGE_IS_RGB16(img->format)) + return (pixel); + else if(IMAGE_IS_P8(img->format)) + return (img->palette[pixel]); + else if(IMAGE_IS_P4(img->format)) + return (img->palette[pixel]); + return (-1); +} + +/* image_data_size(): Compute the size of the [data] array */ +int image_data_size(image_t const *img) +{ + return (img->stride * img->height); +} diff --git a/src/modules/display/image/object.c b/src/modules/display/image/object.c new file mode 100644 index 0000000..6ceb241 --- /dev/null +++ b/src/modules/display/image/object.c @@ -0,0 +1,149 @@ +#include +#include + +#include +#include + +//--- +// Image-object API +//--- + +/* image_alloc(): Create a new (uninitialized) image */ +image_t *image_alloc(size_t width, size_t height, int format) +{ + image_t *img = image_create(width, height, format); + if(img == NULL) + return (NULL); + + if(IMAGE_IS_RGB16(format)) + img->stride = ((width + 1) >> 1) * 4; + else if(IMAGE_IS_P8(format)) + img->stride = width; + else if(IMAGE_IS_P4(format)) + img->stride = ((width + 1) >> 1); + + void *data = malloc(height * img->stride); + if(data == NULL) { + image_free(img); + return (NULL); + } + + img->data = data; + img->flags |= IMAGE_FLAGS_DATA_ALLOC; + return img; +} + +/* image_create(): Create a bare image with no data/palette */ +image_t *image_create(size_t width, size_t height, int format) +{ + if(!IMAGE_IS_RGB16(format) + && !IMAGE_IS_P8(format) + && !IMAGE_IS_P4(format)) { + return (NULL); + } + if(width > 0xffff || height > 0xffff) + return (NULL); + + image_t *img = calloc(1, sizeof(image_t)); + if(img == NULL) + return (NULL); + + img->format = format; + img->flags = 0; + img->color_count = 0; + img->width = width; + img->height = height; + img->stride = 0; + img->data = NULL; + img->palette = NULL; + + return (img); +} + +/* image_free(): Free and image and the data it owns */ +void image_free(image_t *img) +{ + if(img == NULL) + return; + + if(img->flags & IMAGE_FLAGS_DATA_ALLOC) + free(img->data); + if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) + free(img->palette); + + if (!(img->flags & IMAGE_FLAGS_RO)) + free(img); +} + +//--- +// Palette API +//--- + +/* image_set_palette(): Specify an external palette for an image */ +void image_set_palette(image_t *img, uint16_t *palette, size_t size, bool owns) +{ + if(img == NULL || !IMAGE_IS_INDEXED(img->format)) + return; + + if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) + free(img->palette); + + img->palette = palette; + img->color_count = size; + + if(owns) { + img->flags |= IMAGE_FLAGS_PALETTE_ALLOC; + } else { + img->flags &= ~IMAGE_FLAGS_PALETTE_ALLOC; + } +} + + +/* image_alloc_palette(): Allocate a new palette for an image */ +bool image_alloc_palette(image_t *img, int size) +{ + if(img == NULL || !IMAGE_IS_INDEXED(img->format)) + return (false); + + if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) + free(img->palette); + + if(IMAGE_IS_P8(img->format)) { + size = (size <= 0) ? 256 : min(size, 256); + } + if(IMAGE_IS_P4(img->format)) { + size = 16; + } + + img->palette = calloc(size, sizeof(uint16_t)); + img->color_count = 0; + img->flags &= ~IMAGE_FLAGS_PALETTE_ALLOC; + + if(img->palette == NULL) + return (false); + + memset(img->palette, 0, size * sizeof(uint16_t)); + img->color_count = size; + img->flags |= IMAGE_FLAGS_PALETTE_ALLOC; + return (true); +} + +/* image_copy_palette(): Copy another image's palette */ +bool image_copy_palette(image_t const *src, image_t *dst, int size) +{ + if(!image_valid(src) || dst == NULL) + return (false); + + if(!IMAGE_IS_INDEXED(dst->format)) + return (true); + + if(size < 0) + size = src->color_count; + if(!image_alloc_palette(dst, size)) + return (false); + + int N = min(src->color_count, dst->color_count); + memcpy(dst->palette, src->palette, N * sizeof(uint16_t)); + return (true); +} + diff --git a/src/modules/display/image/render.c b/src/modules/display/image/render.c new file mode 100644 index 0000000..330a1fe --- /dev/null +++ b/src/modules/display/image/render.c @@ -0,0 +1,278 @@ +#include +#include +#include + +//--- +// Internal functions +//--- + +struct imgbox { + struct { + struct { + int start; + int end; + } y; + struct { + int start; + int end; + } x; + } img; + struct { + uint16_t *vram; + int voff; + } surface; +}; + +/* dsubimage_default_render() : raw-format image render */ +static void dsubimage_default_render(image_t const *image, struct imgbox *box) +{ + (void)image; + (void)box; +#if 0 + uint16_t *vram; + uint16_t *vidxs; + uint16_t *vidx; + int voff; + + vram = surface->draw; + voff = box->surface.idx.offset; + vidxs = &vram[box->surface.idx.start]; + + for (int iy = box->img.y.start; iy < box->img.y.end; ++iy) { + vidx = vidxs; + for (int ix = box->img.x.start; ix < box->img.x.end; ++ix) { + *(vidx++) = image_decode_pixel( + image, + image_get_pixel(image, ix, iy) + ); + } + vidxs = &vidxs[voff]; + } +#endif +} + +/* dsubimage_alpha_render() : alpha-format image render */ +static void dsubimage_alpha_render(image_t const *image, struct imgbox *box) +{ + uint16_t *vram; + uint16_t *data; + uint16_t pixel; + uint16_t alpha; + int voff; + int c; + + voff = box->surface.voff; + vram = box->surface.vram; + alpha = image_alpha(image->format); + + for (int iy = box->img.y.start; iy < box->img.y.end; ++iy) { + data = vram; + c = 0; + for (int ix = box->img.x.start; ix < box->img.x.end; ++ix) { + pixel = image_get_pixel(image, ix, iy); + if (pixel != alpha) + data[c] = image_decode_pixel(image, pixel); + c += 1; + } + vram = &vram[voff]; + } +} + + +//--- +// Kernel-level API +//--- + +void dsubimage_render( + dsurface_t *surface, + int x, int y, + image_t const *image, + int left, int top, int width, int height +) { + uint16_t *vram; + int vxs; + int vys; + + /* precalculate culling information */ + if (x < 0) { + left -= x; + width += x; + x = 0; + } + if (y < 0) { + top -= y; + height += y; + y = 0; + } + + /* check error */ + if (top < 0 || left < 0 || width < 0 || height < 0) + return; + + /* check culling */ + if (y >= surface->y2 + || y + height < surface->y1 + || x >= surface->x2 + || x + width < surface->x1) { + return; + } + + /* precalculate geometry information (both image and vram) */ + vxs = x - surface->x1; + vys = y - surface->y1; + if (vxs < 0) { + vxs = 0; + left += (surface->x1 - x); + width -= (surface->x1 - x); + } + if (vys < 0) { + vys = 0; + top += (surface->y1 - y); + height -= (surface->y1 + y); + } + if ((size_t)(vxs + width) > surface->width) + width = surface->width - vxs; + if ((size_t)(vys + height) > surface->height) + height = surface->height - vys; + vram = surface->frag; + + + /* prepare box request */ + struct imgbox imgbox = { + .img = { + .y = { + .start = top, + .end = top + height + }, + .x = { + .start = left, + .end = left + width + } + }, + .surface = { + .vram = &vram[(surface->width * vys) + vxs], + .voff = surface->width + } + }; + +#if 0 + dclear(C_WHITE); + dprint(0, 0, C_BLACK, + "surface.x1 = %d\n" + "surface.x2 = %d\n" + "surface.y1 = %d\n" + "surface.y2 = %d\n" + "surface.width = %d\n" + "surface.height = %d\n" + "\n" + "img.y.start = %d\n" + "img.y.end = %d\n" + "img.x.start = %d\n" + "img.x.end = %d\n" + "\n" + "image.x = %d\n" + "image.y = %d\n" + "image.width = %d\n" + "image.heigt = %d\n", + surface->x1, + surface->x2, + surface->y1, + surface->y2, + surface->width, + surface->height, + imgbox.img.y.start, + imgbox.img.y.end, + imgbox.img.x.start, + imgbox.img.x.end, + x, + y, + width, + height + ); + dupdate(); + while(1) { __asm__ volatile ("sleep"); } +#endif + + /* send drawing request in the approriate optimized function */ + if (IMAGE_IS_ALPHA(image->format)) { + dsubimage_alpha_render(image, &imgbox); + return; + } + dsubimage_default_render(image, &imgbox); +} + +/* dimage_render(): Render a full image */ +void dimage_render(dsurface_t *surface, int x, int y, image_t const *img) +{ + dsubimage_render(surface, x, y, img, 0, 0, img->width, img->height); +} + + + +//--- +// Dstack API +//--- + +/* dimage_dstack(): dstack wrapper primitive */ +void dimage_dstack(dsurface_t *surface, uint32_t *args) +{ + dimage_render( + surface, + (int)args[0], + (int)args[1], + (image_t*)(uintptr_t)args[2] + ); +} + +/* dsubimage_dstack(): dstack wrapper primitive */ +void dsubimage_dstack(dsurface_t *surface, uint32_t *args) +{ + dsubimage_render( + surface, + (int)args[0], + (int)args[1], + (image_t*)(uintptr_t)args[2], + (int)args[3], + (int)args[4], + (int)args[5], + (int)args[6] + ); +} + + + +//--- +// User-level API +//--- + +/* dimage(): Render a full image */ +void dimage(image_t const *image, int x, int y, int mode) +{ + if (mode & DIMAGE_CENTER) { x -= image->width >> 1; } + if (mode & DIMAGE_RIGHT) { x -= image->width; } + if (mode & DIMAGE_MIDDLE) { y -= image->height >> 1; } + if (mode & DIMAGE_BOTTOM) { y -= image->height; } + dstack_add_action( + &DSTACK_CALL(&dimage_dstack, x, y, (uintptr_t)image), + NULL, + NULL + ); +} + +/* dsubimage(): Render a section of an image */ +void dsubimage( + int x, int y, + image_t const *image, + int left, int top, int width, int height +) { + dstack_add_action( + &DSTACK_CALL( + &dsubimage_dstack, + x, y, + (uintptr_t)image, + left, top, width, height + ), + NULL, + NULL + ); +} diff --git a/src/modules/display/text/dfont.c b/src/modules/display/text/dfont.c index 8ab30f4..13c7490 100644 --- a/src/modules/display/text/dfont.c +++ b/src/modules/display/text/dfont.c @@ -159,14 +159,8 @@ void dfont_text_render(struct dshader_surface *surface, uint32_t *arg) int sy; /* check culling */ - if ( - ( - (int)arg[3] < surface->y - && (int)arg[1] >= (int)(surface->y + surface->height) - ) || ( - (int)arg[2] < surface->x - && (int)arg[0] >= (int)(surface->x + surface->width) - )) { + if (((int)arg[3] < surface->y1 && (int)arg[1] > surface->y2) + || ((int)arg[2] < surface->x1 && (int)arg[0] > surface->x2)) { return; }