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:
parent
221dacae2e
commit
1ebab24090
|
@ -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 */
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#include <vhex/display/image/object.h>
|
||||
#include <vhex/display/image/information.h>
|
||||
#include <vhex/display/image/render.h>
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue