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
This commit is contained in:
Yann MAGNIN 2022-06-19 20:11:52 +02:00
parent 221dacae2e
commit 1ebab24090
18 changed files with 847 additions and 98 deletions

View File

@ -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 */

View File

@ -0,0 +1,3 @@
#include <vhex/display/image/object.h>
#include <vhex/display/image/information.h>
#include <vhex/display/image/render.h>

View File

@ -0,0 +1,58 @@
#ifndef __VHEX_DISPLAY_DRAW_IMAGE_INFORMATION__
# define __VHEX_DISPLAY_DRAW_IMAGE_INFORMATION__
#include <vhex/display/image/types.h>
//---
// 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__ */

View File

@ -0,0 +1,91 @@
#ifndef __VHEX_DISPLAY_DRAW_IMAGE_OBJECT__
# define __VHEX_DISPLAY_DRAW_IMAGE_OBJECT__
#include <vhex/display/image/types.h>
//---
// 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__ */

View File

@ -0,0 +1,42 @@
#ifndef __VHEX_DISPLAY_DRAW_IMAGE_RENDER__
# define __VHEX_DISPLAY_DRAW_IMAGE_RENDER__
#include <vhex/display/image/types.h>
/* 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__ */

View File

@ -0,0 +1,82 @@
#ifndef __VHEX_DISPLAY_DRAW_IMAGE_TYPES__
# define __VHEX_DISPLAY_DRAW_IMAGE_TYPES__
#include <vhex/defs/attributes.h>
#include <vhex/defs/types.h>
/* 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[<value>+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__ */

View File

@ -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;

View File

@ -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 <gint/intc.h>. */
/* 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 };

View File

@ -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]);
}

View File

@ -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)

View File

@ -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),

View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -0,0 +1,84 @@
#include <vhex/display/image.h>
#include <vhex/defs/utils.h>
/* 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);
}

View File

@ -0,0 +1,149 @@
#include <vhex/display/image.h>
#include <vhex/defs/utils.h>
#include <stdlib.h>
#include <string.h>
//---
// 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);
}

View File

@ -0,0 +1,278 @@
#include <vhex/display/image.h>
#include <vhex/display/shader.h>
#include <vhex/display.h>
//---
// 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
);
}

View File

@ -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;
}