From 5a69e44078f4c1f67ae8efefffb0bb429b600124 Mon Sep 17 00:00:00 2001 From: Lephe Date: Fri, 13 May 2022 23:30:52 +0100 Subject: [PATCH] image: new image format and base for the image library --- CMakeLists.txt | 17 + include/gint/image.h | 563 +++++++++++++++++++++++------- src/image/image_alloc.c | 45 +++ src/image/image_clear.c | 9 + src/image/image_copy.c | 49 +++ src/image/image_copy_palette.c | 16 + src/image/image_create.c | 28 ++ src/image/image_create_vram.c | 13 + src/image/image_data_size.c | 6 + src/image/image_decode_pixel.c | 12 + src/image/image_fill.c | 26 ++ src/image/image_free.c | 16 + src/image/image_get_pixel.c | 25 ++ src/image/image_palette_size.c | 7 + src/image/image_set_pixel.c | 26 ++ src/image/image_sub.c | 40 +++ src/image/image_target.c | 39 +++ src/image/image_valid.c | 17 + src/render-cg/dsubimage.c | 7 +- src/render-cg/image/image.c | 22 +- src/render-cg/image/image_p4.S | 5 +- src/render-cg/image/image_p4.c | 2 +- src/render-cg/image/image_p8.c | 2 +- src/render-cg/image/image_rgb16.S | 6 +- src/render-cg/image/image_rgb16.c | 2 +- 25 files changed, 847 insertions(+), 153 deletions(-) create mode 100644 src/image/image_alloc.c create mode 100644 src/image/image_clear.c create mode 100644 src/image/image_copy.c create mode 100644 src/image/image_copy_palette.c create mode 100644 src/image/image_create.c create mode 100644 src/image/image_create_vram.c create mode 100644 src/image/image_data_size.c create mode 100644 src/image/image_decode_pixel.c create mode 100644 src/image/image_fill.c create mode 100644 src/image/image_free.c create mode 100644 src/image/image_get_pixel.c create mode 100644 src/image/image_palette_size.c create mode 100644 src/image/image_set_pixel.c create mode 100644 src/image/image_sub.c create mode 100644 src/image/image_target.c create mode 100644 src/image/image_valid.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 23efdfe..4df5e69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,23 @@ set(SOURCES_FX set(SOURCES_CG # R61524 driver src/r61524/r61524.c + # Image library + src/image/image_alloc.c + src/image/image_clear.c + src/image/image_copy.c + src/image/image_copy_palette.c + src/image/image_create.c + src/image/image_create_vram.c + src/image/image_data_size.c + src/image/image_decode_pixel.c + src/image/image_fill.c + src/image/image_free.c + src/image/image_get_pixel.c + src/image/image_palette_size.c + src/image/image_set_pixel.c + src/image/image_sub.c + src/image/image_target.c + src/image/image_valid.c # Rendering src/render-cg/dclear.c src/render-cg/dpixel.c diff --git a/include/gint/image.h b/include/gint/image.h index 49f9eba..20d5731 100644 --- a/include/gint/image.h +++ b/include/gint/image.h @@ -10,9 +10,9 @@ // often the bottleneck, and image rendering is much faster in Azur (which is // what the renderer was initially designed for). // -// We support 3 bit depths: full-color 16-bit (RGB565), indexed 8-bit (P8) and -// indexed 4-bit (P4). All three have an "alpha" variation where one color is -// treated as transparent, leading to 6 total formats. +// This module supports 3 bit depths: full-color 16-bit (RGB565), indexed 8-bit +// (P8) and indexed 4-bit (P4). All three have an "alpha" variation where one +// color is treated as transparent, leading to 6 total formats. // // The image renderers support so-called *dynamic effects*, which are image // transformations performed on-the-fly while rendering, without generating an @@ -20,7 +20,11 @@ // achieve similar performance to straight rendering and can be combined to // some extent, which makes them reliable whenever applicable. // -// TODO: Switch to libimg-style image refs. +// For images of the RGB16 and P8 bit depths, the module supports a rich API +// with image subsurfaces and a fairly large sets of geometric and color +// transforms, including some in-place. P4 is not supported in most of these +// functions because the dense bit packing is both impractical and slower for +// these applications. //--- #ifndef GINT_IMAGE @@ -45,14 +49,38 @@ extern "C" { rendering method, as a transparent background can always be added or removed by a dynamic effect 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 */ + 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 */ - IMAGE_DEPRECATED_P8 = 2, + IMAGE_DEPRECATED_P8 = 2, +}; + +/* 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_t: gint's native bitmap image format @@ -60,31 +88,37 @@ enum { using the fxSDK's built-in image converters with fxconv. */ typedef struct { - /* Color format, one of the IMAGE_* values defined above. */ - uint16_t profile; - /* For formats with alpha, value or index used for transparency. */ - uint16_t alpha; - /* Full width and height, in pixels */ - uint16_t width; - uint16_t height; + /* Color format, one of the IMAGE_* values defined above */ + uint8_t format; + /* Additional flags, a combination of IMAGE_FLAGS_* values */ + uint8_t flags; + /* For formats with alpha, value or index used for transparency */ + uint16_t alpha; + /* Full width and height, in pixels */ + uint16_t width; + uint16_t height; + /* Byte stride between lines */ + int stride; - /* Here we lose structure because of the flexible array. + /* Pixel data in row-major order, left to right. - RGB565, RGB565A: - * Pixels in row-major order, 16 bits per pixel - P8: - * Palette with 256 entries (512 bytes total) - * Pixels in row-major order, 8 bits per pixel - P8_RGB565A, P8_RGB565: - * Number of entries in palette, N (2 bytes) - * Palette with N entries (2N bytes) - * Pixels in row-major order, 8 bits per pixel (signed indices in - an uint16_t array starting at +<256 bytes>) - P4/P4_RGB565A, P4_RGB565: - * Palette with 16 entries (32 bytes total) - * Pixels in row-major order, 4 bits per pixel, each row - byte-padded */ - uint16_t data[]; + RGB16: + - 2 bytes per entry, each row padded to 4 bytes for alignment + - Each 2-byte value is an RGB565 color + P8: + - 1 byte per entry + - Each byte is a shifted palette index (to access the palette, use: + palette.colors[+128]) + P4: + - 4 bits per entry, each row padded to a full byte + - Each entry is a palette index (0...15) */ + void *data; + + /* For P8 and P4, palette. The color count does not account for alpha + (which is usually the last entry, but not materialized) and instead + indicates how much memory is allocated. */ + int color_count; + uint16_t *palette; } GPACKED(4) image_t; @@ -93,55 +127,279 @@ typedef struct - HFLIP and VFLIP can both be added regardless of any other effect - At most one color effect can be applied */ enum { - /* Value 0x01 is reserved, because it is DIMAGE_NOCLIP, which although - part of the old API still needs to be supported. */ + /* Value 0x01 is reserved, because it is DIMAGE_NOCLIP, which although + part of the old API still needs to be supported. */ - /* [Any]: Skip clipping the command against the source image */ - IMAGE_NOCLIP_INPUT = 0x04, - /* [Any]: Skip clipping the command against the output VRAM */ - IMAGE_NOCLIP_OUTPUT = 0x08, - /* [Any]: Skip clipping both */ - IMAGE_NOCLIP = IMAGE_NOCLIP_INPUT | IMAGE_NOCLIP_OUTPUT, + /* [Any]: Skip clipping the command against the source image */ + IMAGE_NOCLIP_INPUT = 0x04, + /* [Any]: Skip clipping the command against the output VRAM */ + IMAGE_NOCLIP_OUTPUT = 0x08, + /* [Any]: Skip clipping both */ + IMAGE_NOCLIP = IMAGE_NOCLIP_INPUT | IMAGE_NOCLIP_OUTPUT, - // Geometric effects. These values should remain at exactly bit 8 and - // following, or change gint_image_mkcmd() along with it. + // Geometric effects. These values should remain at exactly bit 8 and + // following, or change gint_image_mkcmd() along with it. - /* [Any]: Flip image vertically */ - IMAGE_VFLIP = 0x0100, - /* [Any]: Flip image horizontally */ - IMAGE_HFLIP = 0x0200, + /* [Any]: Flip image vertically */ + IMAGE_VFLIP = 0x0100, + /* [Any]: Flip image horizontally */ + IMAGE_HFLIP = 0x0200, - // Color effects + // Color effects - /* [RGB565, P8_RGB565, P4_RGB565]: Make a color transparent - Adds one argument: - * Color to clear (RGB16: 16-bit value; P8/P4: palette index) */ - IMAGE_CLEARBG = 0x10, - /* [RGB565, P8_RGB565, P4_RGB565]: Turn a color into another - Adds two arguments: - * Color to replace (RGB16: 16-bit value; P8/P4: palette index) - * Replacement color (16-bit value) */ - IMAGE_SWAPCOLOR = 0x20, - /* [RGB565A, P8_RGB565A, P4_RGB565A]: Add a background - Adds one argument: - * Background color (16-bit value) */ - IMAGE_ADDBG = 0x40, - /* [RGB565A, P8_RGB565A, P4_RGB565A]: Dye all non-transparent pixels - Adds one argument: - * Dye color (16-bit value) */ - IMAGE_DYE = 0x80, + /* [RGB565, P8_RGB565, P4_RGB565]: Make a color transparent + Adds one argument: + * Color to clear (RGB16: 16-bit value; P8/P4: palette index) */ + IMAGE_CLEARBG = 0x10, + /* [RGB565, P8_RGB565, P4_RGB565]: Turn a color into another + Adds two arguments: + * Color to replace (RGB16: 16-bit value; P8/P4: palette index) + * Replacement color (16-bit value) */ + IMAGE_SWAPCOLOR = 0x20, + /* [RGB565A, P8_RGB565A, P4_RGB565A]: Add a background + Adds one argument: + * Background color (16-bit value) */ + IMAGE_ADDBG = 0x40, + /* [RGB565A, P8_RGB565A, P4_RGB565A]: Dye all non-transparent pixels + Adds one argument: + * Dye color (16-bit value) */ + IMAGE_DYE = 0x80, }; //--- -// Image access and information +// Image creation and destruction //--- -/* TODO: Expand */ +/* 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, though a palette from another image + can be reused. (If you need to reuse a data array, see image_create() below + or use img_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. + + By default, a new palette is allocated for formats with palettes; the new + image owns the palette and frees it when freed. This can be overriden by + setting the [palette] pointer to the desired palette; in this case, the new + image does not own the palette and does not free it when freed. + + Regardless of whether the palette is allocated or specified by hand, for P8 + the palette size must be indicated. A value of -1 can be specified to use + the default (256 colors). For all other formats, set a value of -1. + + For images with alpha, the last parameter [alpha] indicates the palette + index or color value that denotes transparent pixels. + + 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 + @palette If not NULL, specifies the palette instead of allocating it + @palette_size For P8, indicates the palette size to use + @alpha For formats with alpha, color or index denoting alpha */ +image_t *image_alloc(int width, int height, int format, + void *palette, int palette_size, int alpha); + +/* 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, and [color_count] is either 0 or + -1 depending on whether the format normally has a palette. + + This function is useful to create images that reuse externally-provided + information. It is intended that the user of this function sets the [data], + [stride] and [palette] and [color_count] members themselves. The + IMAGE_FLAGS_DATA_ALLOC and the IMAGE_FLAGS_PALETTE_ALLOC flags can be set on + the image if the user wishes for the image to free its data and palette when + freed. + + For images with alpha, the last parameter [alpha] indicates the palette + index or color value that denotes transparent pixels. + + The returned image structure must be freed with image_free() after use. */ +image_t *image_create(int width, int height, int format, int alpha); + +/* image_create_vram(): Create a reference to gint_vram + + This function creates a new RGB565 image that references gint_vram. Using + this image as target for transformation functions can effectively render + transformed images to VRAM. + + The value of gint_vram is captured when this function is called, and does + not update after dupdate() when triple-buffering is used. The user should + account for this option. (Using this function twice then replacing one of + the [data] pointers is allowed.) + + The VRAM image ows no data but it does own its own structure so it must + still be freed with image_free() after use. */ +image_t *image_create_vram(void); + +/* 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. */ +void image_free(image_t *img); + +/* image_copy_palette(): Duplicate an image's palette + + This function duplicates the palette and returns a new one allocated with + malloc(). If the input image is not in a palette format or has no palette + assigned, returns NULL. If the returned pointer is not NULL, free() after + use or set the IMAGE_FLAGS_PALETTE_ALLOC flag on the image holding it so + that free() is automatically called when the image is freed. */ +uint16_t *image_copy_palette(image_t const *img); + +//--- +// 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. */ +bool image_valid(image_t const *img); + +/* 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. */ 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. */ 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. */ +int image_data_size(image_t const *img); + +/* image_palette_size(): Compute the size of the [palette] array + This function returns the size of the palette array, in bytes. This can be + used to duplicate the palette. For images without a palette, returns -1. */ +int image_palette_size(image_t const *img); + +//--- +// Basic image modifications +//--- + +/* image_set_pixel(): Set a pixel in the data array + + This function writes a pixel into the image's data array at position (x,y). + The pixel value must be of the proper format, as specified in the definition + of the [data] field of image_t. + + Formats: RGB16, P8, P4 */ +void image_set_pixel(image_t const *img, int x, int y, int value); + +/* image_copy(): Copy an image into another one + + This function copies an image into another image. The target must be a valid + image with the same format as the source, otherwise this function is a + no-op. Unlike transforms, this function does clip, so there are no + conditions on the size of the target. + + If [copy_alpha] is true, transparent pixels are copied verbatim, which + effectively replaces the top-left corner of [dst] with [src]. If it's false, + transparent pixels of [src] are skipped, effectively rendering [src] over + the top-left corner of [src]. + + The color scheme of src and dst should normally match. In RGB16, if + src->alpha and dst->alpha differ, this function adopts a resonable behavior; + inputs of value src->alpha are turned into dst->alpha if copy_alpha is true, + ignored otherwise; and opaque inputs of value dst->alpha are turned into + [dst->alpha ^ 1] to keep the visuals consistent. In P8, no attempt is made + to merge the palettes. + + Formats: RGB16, P8 */ +void image_copy(image_t const *src, image_t *dst, bool copy_alpha); + +/* image_fill(): Fill an image with a single pixel value */ +void image_fill(image_t *img, int value); + +/* image_clear(): Fill a transparent image with its transparent value */ +void image_clear(image_t *img); + +/* TODO: Expand by taking from libimg */ + +//--- +// Sub-image extraction +//--- + +/* image_sub(): Build a reference to a sub-image + + This function is used to create references to sub-images of RGB16 and P8 + images. The [data] pointer of the sub-image points somewhere within the data + array of the source, and its [palette] pointer is identical to the source's. + + The last parameter is a pointer to a preallocated image_t structure (usually + on the stack) that gets filled with the data. Doing this instead of + allocating a new object with malloc() means that there is no need to + image_free() the sub-image, and thus it can be used inline: + + image_t tmp; + image_hflip(src, image_sub(dst, x, y, w, h, &tmp)); + + A preprocessor macro is used to make the last parameter optional. If it's + not specified, a pointer to a static image_t will be returned instead. This + is useful in inline calls as shown above, which then simplify to: + + image_hflip(src, image_sub(dst, x, y, w, h)); + + However, another call to image_sub() or image_at() will override the + sub-image, so you should only use this in such temporary settings. + + If the requested rectangle does not intersect the source, the sub-image will + be of dimension 0x0. If the image format does not support sub-images (P4), + the sub-image will test invalid with image_valid(). */ +image_t *image_sub(image_t const *src, int x, int y, int w, int h, + image_t *dst); + +/* Make the last parameter optional */ +#define image_sub1(src, x, y, w, h, dst, ...) image_sub(src, x, y, w, h, dst) +#define image_sub(...) image_sub(__VA_ARGS__, NULL) + +/* image_at(): Build a reference to a position within a sub-image */ +#define image_at(img, x, y) image_sub(img, x, y, -1, -1) + +//--- +// Geometric image transforms +//--- + +/* TODO: Geometric transforms */ + +//--- +// Color transforms +//--- + +/* TODO: Color transforms */ + //--- // Image rendering functions // @@ -168,21 +426,21 @@ int image_decode_pixel(image_t const *img, int pixel); /* dimage_effect(): Generalized dimage() supporting dynamic effects */ #define dimage_effect(x, y, img, eff, ...) \ - dsubimage_effect(x, y, img, 0, 0, (img)->width, (img)->height, eff, \ - ##__VA_ARGS__) + dsubimage_effect(x, y, img, 0, 0, (img)->width, (img)->height, eff, \ + ##__VA_ARGS__) /* dsubimage_effect(): Generalized dsubimage() supporting dynamic effects */ void dsubimage_effect(int x, int y, image_t const *img, - int left, int top, int w, int h, int effects, ...); + int left, int top, int w, int h, int effects, ...); /* Specific versions for each format */ #define DIMAGE_SIG1(NAME, ...) \ - void dimage_ ## NAME(int x, int y, image_t const *img,##__VA_ARGS__); \ - void dsubimage_ ## NAME(int x, int y, image_t const *img, \ - int left, int top, int w, int h, ##__VA_ARGS__); + void dimage_ ## NAME(int x, int y, image_t const *img,##__VA_ARGS__); \ + void dsubimage_ ## NAME(int x, int y, image_t const *img, \ + int left, int top, int w, int h, ##__VA_ARGS__); #define DIMAGE_SIG(NAME, ...) \ - DIMAGE_SIG1(rgb16 ## NAME, ##__VA_ARGS__) \ - DIMAGE_SIG1(p8 ## NAME, ##__VA_ARGS__) \ - DIMAGE_SIG1(p4 ## NAME, ##__VA_ARGS__) + DIMAGE_SIG1(rgb16 ## NAME, ##__VA_ARGS__) \ + DIMAGE_SIG1(p8 ## NAME, ##__VA_ARGS__) \ + DIMAGE_SIG1(p4 ## NAME, ##__VA_ARGS__) /* d[sub]image_{rgb16,p8,p4}_effect(..., effects, ) */ DIMAGE_SIG(_effect, int effects, ...) @@ -204,14 +462,14 @@ DIMAGE_SIG(_dye, int effects, int dye_color) DIMAGE_SIG1(p4_clearbg_alt, int effects, int bg_index) #define dimage_rgb16_effect(x, y, img, eff, ...) \ - dsubimage_rgb16_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ - eff, ##__VA_ARGS__) + dsubimage_rgb16_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ + eff, ##__VA_ARGS__) #define dimage_p8_effect(x, y, img, eff, ...) \ - dsubimage_p8_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ - eff, ##__VA_ARGS__) + dsubimage_p8_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ + eff, ##__VA_ARGS__) #define dimage_p4_effect(x, y, img, eff, ...) \ - dsubimage_p4_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ - eff, ##__VA_ARGS__) + dsubimage_p4_effect(x, y, img, 0, 0, (img)->width, (img)->height, \ + eff, ##__VA_ARGS__) #undef DIMAGE_SIG #undef DIMAGE_SIG1 @@ -223,12 +481,12 @@ DIMAGE_SIG1(p4_clearbg_alt, int effects, int bg_index) /* Double box specifying both a source and target area */ struct gint_image_box { - /* Target location of top-left corner */ - int x, y; - /* Width and height of rendered sub-image */ - int w, h; - /* Source bounding box (low included, high excluded) */ - int left, top; + /* Target location of top-left corner */ + int x, y; + /* Width and height of rendered sub-image */ + int w, h; + /* Source bounding box (low included, high excluded) */ + int left, top; }; /* Clip the provided box against the input. If, after clipping, the box no @@ -261,50 +519,50 @@ void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h); the image and resume the rendering later. This is used in Azur. */ struct gint_image_cmd { - /* Shader ID. This is used in Azur, and ignored in gint */ - uint8_t shader_id; - /* Dynamic effects not already dispatched by renderer - Bit 0: VFLIP - Bit 1: HFLIP */ - uint8_t effect; + /* Shader ID. This is used in Azur, and ignored in gint */ + uint8_t shader_id; + /* Dynamic effects not already dispatched by renderer + Bit 0: VFLIP + Bit 1: HFLIP */ + uint8_t effect; - /* Number of pixels to render per line. For formats that force either x - or width alignment (most of them), this is already adjusted to a - suitable multiple (usually a multiple of 2). */ - int16_t columns; + /* Number of pixels to render per line. For formats that force either x + or width alignment (most of them), this is already adjusted to a + suitable multiple (usually a multiple of 2). */ + int16_t columns; - /* Stride of the input image (number of pixels between each row), in - pixels, without subtracting the number of columns */ - int16_t input_stride; + /* Stride of the input image (number of pixels between each row), in + pixels, without subtracting the number of columns */ + int16_t input_stride; - /* Number of lines in the command. This can be adjusted freely, and is - particularly useful in Azur for fragmented rendering. */ - uint8_t lines; + /* Number of lines in the command. This can be adjusted freely, and is + particularly useful in Azur for fragmented rendering. */ + uint8_t lines; - /* [Any effect]: Offset of first edge */ - int8_t edge_1; + /* [Any effect]: Offset of first edge */ + int8_t edge_1; - /* Core loop; this is an internal label of the renderer */ - void const *loop; - /* Output pixel array, offset by target x/y */ - void const *output; - /* Input pixel array, offset by source x/y. For formats that force x - alignment, this is already adjusted. */ - void const *input; - /* Palette, when applicable */ - uint16_t const *palette; + /* Core loop; this is an internal label of the renderer */ + void const *loop; + /* Output pixel array, offset by target x/y */ + void const *output; + /* Input pixel array, offset by source x/y. For formats that force x + alignment, this is already adjusted. */ + void const *input; + /* Palette, when applicable */ + uint16_t const *palette; - /* [Any effect]: Offset of right edge */ - int16_t edge_2; - /* [CLEARBG, SWAPCOLOR]: Source color */ - uint16_t color_1; - /* [SWAPCOLOR]: Destination color */ - uint16_t color_2; + /* [Any effect]: Offset of right edge */ + int16_t edge_2; + /* [CLEARBG, SWAPCOLOR]: Source color */ + uint16_t color_1; + /* [SWAPCOLOR]: Destination color */ + uint16_t color_2; - /* Remaining height (for updates between fragments) */ - int16_t height; - /* Local x position (for updates between fragments) */ - int16_t x; + /* Remaining height (for updates between fragments) */ + int16_t height; + /* Local x position (for updates between fragments) */ + int16_t x; }; /* gint_image_mkcmd(): Prepare a rendering command with dynamic effects @@ -331,8 +589,8 @@ struct gint_image_cmd case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect the final box after clipping but not accounting for edges. */ bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img, - int effects, bool left_edge, bool right_edge, - struct gint_image_cmd *cmd, int out_width, int out_height); + int effects, bool left_edge, bool right_edge, + struct gint_image_cmd *cmd, int out_width, int out_height); /* Entry point of the renderers. These functions can be called normally as long as you can build the commands (eg. by using gint_image_mkcmd() then filling @@ -362,6 +620,55 @@ void gint_image_p4_clearbg_alt(void); void gint_image_p4_swapcolor(void); void gint_image_p4_dye(void); +//--- +// Image library utilities +// +// The following functions and macros are mostly internal utilities; they are +// exposed here in case user applications want to extend the set of image +// transforms with custom additions. +//--- + +/* image_target(): Check if an image can be used as target for a transform + + This function is used to quickly check whether a transform from [src] to + [dst] is possible. It requires image_valid(src) and image_valid(dst), plus + any optional constraints specified as variadic arguments. These constraints + can be: + + * NOT_P4: fails if [dst] is P4. + * DATA_RW: fails if [dst] is not data-writable. + * PALETTE_RW: fails if [dst] is not palette-writable. + * SAME_SIZE: fails if [dst] is not at least as large as [src]. + + For example, in image_hflip(), we write: + if(!image_target(src, dst, NOT_P4, DATA_RW, SAME_SIZE)) return; */ + +enum { + IMAGE_TARGET_NONE, + IMAGE_TARGET_NOT_P4, + IMAGE_TARGET_DATA_RW, + IMAGE_TARGET_PALETTE_RW, + IMAGE_TARGET_SAME_SIZE, + IMAGE_TARGET_SAME_FORMAT, + IMAGE_TARGET_SAME_DEPTH, +}; +bool image_target(image_t const *src, image_t *dst, ...); + +#define image_target(src, dst, ...) \ + image_target(src, dst, image_target_arg1(__VA_ARGS__ __VA_OPT__(,) NONE)) +#define image_target_arg1(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg2(__VA_ARGS__)) +#define image_target_arg2(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg3(__VA_ARGS__)) +#define image_target_arg3(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg4(__VA_ARGS__)) +#define image_target_arg4(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg5(__VA_ARGS__)) +#define image_target_arg5(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg6(__VA_ARGS__)) +#define image_target_arg6(c, ...) \ + IMAGE_TARGET_ ## c __VA_OPT__(, image_target_too_many_args(__VA_ARGS__)) + #endif /* FXCG50 */ #ifdef __cplusplus diff --git a/src/image/image_alloc.c b/src/image/image_alloc.c new file mode 100644 index 0000000..940089a --- /dev/null +++ b/src/image/image_alloc.c @@ -0,0 +1,45 @@ +#include +#include +#include + +image_t *image_alloc(int width, int height, int format, + void *palette, int palette_size, int alpha) +{ + image_t *img = image_create(width, height, format, alpha); + if(!img) + return NULL; + + if(IMAGE_IS_RGB16(format)) { + img->stride = ((width + 1) >> 1) * 4; + palette_size = -1; + } + else if(IMAGE_IS_P8(format)) { + img->stride = width; + palette_size = max(0, min(256, palette_size)); + } + else if(IMAGE_IS_P4(format)) { + img->stride = ((width + 1) >> 1); + palette_size = 32; + } + + void *data = malloc(height * img->stride); + if(!data) { + image_free(img); + return NULL; + } + + img->data = data; + img->flags |= IMAGE_FLAGS_DATA_ALLOC; + + if(!palette && palette_size > 0) { + palette = malloc(palette_size * 2); + if(!palette) { + image_free(img); + return NULL; + } + } + + img->palette = palette; + img->color_count = palette_size; + return img; +} diff --git a/src/image/image_clear.c b/src/image/image_clear.c new file mode 100644 index 0000000..e5b86a4 --- /dev/null +++ b/src/image/image_clear.c @@ -0,0 +1,9 @@ +#include + +void image_clear(image_t *img) +{ + if(!IMAGE_IS_ALPHA(img->format)) + return; + + image_fill(img, img->alpha); +} diff --git a/src/image/image_copy.c b/src/image/image_copy.c new file mode 100644 index 0000000..f813227 --- /dev/null +++ b/src/image/image_copy.c @@ -0,0 +1,49 @@ +#include +#include + +void image_copy(image_t const *src, image_t *dst, bool copy_alpha) +{ + if(!image_target(src, dst, NOT_P4, DATA_RW, SAME_DEPTH)) + return; + if(!IMAGE_IS_ALPHA(src->format)) + copy_alpha = true; + + /* Clip the input to match the size of the output */ + int w = min(src->width, dst->width); + int h = min(src->height, dst->height); + if(w <= 0 || h <= 0) + return; + + void *src_px = src->data; + void *dst_px = dst->data; + + if(IMAGE_IS_RGB16(src->format)) { + int src_alpha = copy_alpha ? -1 : src->alpha; + int dst_alpha = IMAGE_IS_ALPHA(dst->format) ? -1 : dst->alpha; + + do { + for(int x = 0; x < w; x++) { + int px = ((uint16_t *)src_px)[x]; + if(px != src_alpha) { + px ^= (px == dst_alpha); + ((uint16_t *)dst_px)[x] = px; + } + } + src_px += src->stride; + dst_px += dst->stride; + } while(--h > 0); + } + else if(IMAGE_IS_P8(src->format)) { + int src_alpha = copy_alpha ? 256 : src->alpha; + + do { + for(int x = 0; x < w; x++) { + int px = ((int8_t *)src_px)[x]; + if(px != src_alpha) + ((int8_t *)dst_px)[x] = px; + } + src_px += src->stride; + dst_px += dst->stride; + } while(--h > 0); + } +} diff --git a/src/image/image_copy_palette.c b/src/image/image_copy_palette.c new file mode 100644 index 0000000..d2a709e --- /dev/null +++ b/src/image/image_copy_palette.c @@ -0,0 +1,16 @@ +#include +#include +#include + +uint16_t *image_copy_palette(image_t const *img) +{ + int size = image_palette_size(img); + if(size < 0 || !img->palette) + return NULL; + + void *palette = malloc(size); + if(!palette) + return NULL; + + return memcpy(palette, img->palette, size); +} diff --git a/src/image/image_create.c b/src/image/image_create.c new file mode 100644 index 0000000..82332f8 --- /dev/null +++ b/src/image/image_create.c @@ -0,0 +1,28 @@ +#include +#include + +image_t *image_create(int width, int height, int format, int alpha) +{ + if(!IMAGE_IS_RGB16(format) && !IMAGE_IS_P8(format) && !IMAGE_IS_P4(format)) + return NULL; + if(width <= 0 || width > 0xffff || height <= 0 || height > 0xffff) + return NULL; + + image_t *img = malloc(sizeof *img); + if(!img) + return NULL; + + img->format = format; + img->flags = 0; + img->alpha = alpha; + + img->width = width; + img->height = height; + + img->stride = 0; + img->data = NULL; + img->color_count = IMAGE_IS_INDEXED(format) ? 0 : -1; + img->palette = NULL; + + return img; +} diff --git a/src/image/image_create_vram.c b/src/image/image_create_vram.c new file mode 100644 index 0000000..e7c697b --- /dev/null +++ b/src/image/image_create_vram.c @@ -0,0 +1,13 @@ +#include +#include + +image_t *image_create_vram(void) +{ + image_t *img = image_create(DWIDTH, DHEIGHT, IMAGE_RGB565, 0); + if(!img) + return NULL; + + img->stride = 2 * DWIDTH; + img->data = gint_vram; + return img; +} diff --git a/src/image/image_data_size.c b/src/image/image_data_size.c new file mode 100644 index 0000000..12e9953 --- /dev/null +++ b/src/image/image_data_size.c @@ -0,0 +1,6 @@ +#include + +int image_data_size(image_t const *img) +{ + return img->stride * img->height; +} diff --git a/src/image/image_decode_pixel.c b/src/image/image_decode_pixel.c new file mode 100644 index 0000000..c152b0a --- /dev/null +++ b/src/image/image_decode_pixel.c @@ -0,0 +1,12 @@ +#include + +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+128]; + else if(IMAGE_IS_P4(img->format)) + return img->palette[pixel]; + return -1; +} diff --git a/src/image/image_fill.c b/src/image/image_fill.c new file mode 100644 index 0000000..ecd0534 --- /dev/null +++ b/src/image/image_fill.c @@ -0,0 +1,26 @@ +#include + +void image_fill(image_t *img, int value) +{ + if(!image_target(img, img, NOT_P4, DATA_RW)) + return; + + void *img_px = img->data; + + if(IMAGE_IS_RGB16(img->format)) { + for(int y = 0; y < img->height; y++) { + for(int x = 0; x < img->width; x++) { + ((uint16_t *)img_px)[x] = value; + } + img_px += img->stride; + } + } + else if(IMAGE_IS_P8(img->format)) { + for(int y = 0; y < img->height; y++) { + for(int x = 0; x < img->width; x++) { + ((int8_t *)img_px)[x] = value; + } + img_px += img->stride; + } + } +} diff --git a/src/image/image_free.c b/src/image/image_free.c new file mode 100644 index 0000000..64c470f --- /dev/null +++ b/src/image/image_free.c @@ -0,0 +1,16 @@ +#include +#include +#include + +void image_free(image_t *img) +{ + if(!img || mmu_is_rom(img)) + return; + + if(img->flags & IMAGE_FLAGS_DATA_ALLOC) + free(img->data); + if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) + free(img->palette); + + free(img); +} diff --git a/src/image/image_get_pixel.c b/src/image/image_get_pixel.c new file mode 100644 index 0000000..ab9fb4f --- /dev/null +++ b/src/image/image_get_pixel.c @@ -0,0 +1,25 @@ +#include + +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 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; +} diff --git a/src/image/image_palette_size.c b/src/image/image_palette_size.c new file mode 100644 index 0000000..2953059 --- /dev/null +++ b/src/image/image_palette_size.c @@ -0,0 +1,7 @@ +#include + +int image_palette_size(image_t const *img) +{ + return (img->color_count >= 0) ? img->color_count* 2 : -1; +} + diff --git a/src/image/image_set_pixel.c b/src/image/image_set_pixel.c new file mode 100644 index 0000000..a8dfb15 --- /dev/null +++ b/src/image/image_set_pixel.c @@ -0,0 +1,26 @@ +#include + +void image_set_pixel(image_t const *img, int x, int y, int value) +{ + if(!image_valid(img)) + return; + if((unsigned)x >= img->width || (unsigned)y >= img->height) + return; + + void *data = img->data + y * img->stride; + uint8_t *data_u8 = data; + uint16_t *data_u16 = data; + + if(IMAGE_IS_RGB16(img->format)) { + data_u16[x] = value; + } + else if(IMAGE_IS_P8(img->format)) { + data_u8[x] = value; + } + else if(IMAGE_IS_P4(img->format)) { + if(x & 1) + data_u8[x >> 1] = (data_u8[x >> 1] & 0xf0) | (value & 0x0f); + else + data_u8[x >> 1] = (data_u8[x >> 1] & 0x0f) | (value << 4); + } +} diff --git a/src/image/image_sub.c b/src/image/image_sub.c new file mode 100644 index 0000000..f548c67 --- /dev/null +++ b/src/image/image_sub.c @@ -0,0 +1,40 @@ +#include +#include +#undef image_sub + +static image_t image_sub_default; + +image_t *image_sub(image_t const *src, int left, int top, int w, int h, + image_t *dst) +{ + if(!dst) + dst = &image_sub_default; + if(w < 0) + w = src->width - left; + if(h < 0) + h = src->height - top; + + struct gint_image_box box = { 0, 0, w, h, left, top }; + if(!image_valid(src) || IMAGE_IS_P4(src->format) || + !gint_image_clip_input(src, &box, w, h)) { + memset(dst, 0, sizeof *dst); + return dst; + } + + int const ro_flags = IMAGE_FLAGS_DATA_RO | IMAGE_FLAGS_PALETTE_RO; + dst->format = src->format; + dst->flags = src->flags & ro_flags; + dst->alpha = src->alpha; + dst->stride = src->stride; + dst->width = box.w; + dst->height = box.h; + + if(IMAGE_IS_RGB16(src->format)) + dst->data = src->data + box.top * src->stride + 2 * box.left; + else if(IMAGE_IS_P8(src->format)) + dst->data = src->data + box.top * src->stride + box.left; + + dst->color_count = src->color_count; + dst->palette = src->palette; + return dst; +} diff --git a/src/image/image_target.c b/src/image/image_target.c new file mode 100644 index 0000000..ccd6e15 --- /dev/null +++ b/src/image/image_target.c @@ -0,0 +1,39 @@ +#include +#undef image_target + +bool image_target(image_t const *src, image_t *dst, ...) +{ + if(!image_valid(src) || !image_valid(dst)) + return false; + + va_list args; + va_start(args, dst); + + int req = -1; + while((req = va_arg(args, int)) != IMAGE_TARGET_NONE) { + if(req == IMAGE_TARGET_NOT_P4 && IMAGE_IS_P4(dst->format)) + return false; + if(req == IMAGE_TARGET_DATA_RW && (dst->flags & IMAGE_FLAGS_DATA_RO)) + return false; + if(req == IMAGE_TARGET_PALETTE_RW && (dst->flags & + IMAGE_FLAGS_PALETTE_RO)) + return false; + if(req == IMAGE_TARGET_SAME_SIZE && + (dst->width < src->width || dst->height < src->height)) + return false; + if(req == IMAGE_TARGET_SAME_FORMAT && src->format != dst->format) + return false; + if(req == IMAGE_TARGET_SAME_DEPTH) { + if(IMAGE_IS_RGB16(src->format) && IMAGE_IS_RGB16(dst->format)) + continue; + if(IMAGE_IS_P8(src->format) && IMAGE_IS_P8(dst->format)) + continue; + if(IMAGE_IS_P4(src->format) && IMAGE_IS_P4(dst->format)) + continue; + return false; + } + } + + va_end(args); + return true; +} diff --git a/src/image/image_valid.c b/src/image/image_valid.c new file mode 100644 index 0000000..861df87 --- /dev/null +++ b/src/image/image_valid.c @@ -0,0 +1,17 @@ +#include + +bool image_valid(image_t const *img) +{ + if(!img) + 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); + } + + /* Invalid format */ + return false; +} diff --git a/src/render-cg/dsubimage.c b/src/render-cg/dsubimage.c index 59a9493..f3d1379 100644 --- a/src/render-cg/dsubimage.c +++ b/src/render-cg/dsubimage.c @@ -4,11 +4,10 @@ void dsubimage(int x, int y, image_t const *img, int left, int top, int w, int h, int flags) { - int p = img->profile; - if(p == IMAGE_RGB565 || p == IMAGE_RGB565A) + if(IMAGE_IS_RGB16(img->format)) return dsubimage_rgb16(x, y, img, left, top, w, h, flags); - if(p == IMAGE_P8_RGB565 || p == IMAGE_P8_RGB565A) + else if(IMAGE_IS_P8(img->format)) return dsubimage_p8(x, y, img, left, top, w, h, flags); - if(p == IMAGE_P4_RGB565 || p == IMAGE_P4_RGB565A) + else if(IMAGE_IS_P4(img->format)) return dsubimage_p4(x, y, img, left, top, w, h, flags); } diff --git a/src/render-cg/image/image.c b/src/render-cg/image/image.c index 794f249..994e0e1 100644 --- a/src/render-cg/image/image.c +++ b/src/render-cg/image/image.c @@ -47,28 +47,28 @@ bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img, cmd->effect = (effects & (IMAGE_VFLIP | IMAGE_HFLIP)) >> 8; cmd->columns = box->w; - cmd->input_stride = img->width; + cmd->input_stride = img->stride; cmd->x = box->x; cmd->edge_1 = -1; cmd->edge_2 = -1; - int p = img->profile; + int f = img->format; int input_row = (effects & IMAGE_VFLIP) ? box->top+box->h-1 : box->top; - if(p == IMAGE_RGB565 || p == IMAGE_RGB565A) { + if(IMAGE_IS_RGB16(f)) { cmd->input_stride += (cmd->input_stride & 1); cmd->input = (void *)img->data + - (input_row * cmd->input_stride + box->left) * 2; + input_row * img->stride + (box->left * 2); } - else if(p == IMAGE_P8_RGB565 || p == IMAGE_P8_RGB565A) { - cmd->input = (void *)img->data + img->data[0] * 2 + 2 + - (input_row * img->width + box->left); - cmd->palette = (void *)img->data + 258; + else if(IMAGE_IS_P8(f)) { + cmd->input = (void *)img->data + + (input_row * img->stride) + box->left; + cmd->palette = (void *)img->palette + 256; } else { - cmd->input = (void *)img->data + 32 + - input_row * ((img->width + 1) >> 1) + (box->left >> 1); - cmd->palette = img->data; + cmd->input = (void *)img->data + + input_row * img->stride + (box->left >> 1); + cmd->palette = (void *)img->palette; /* By default, use edge_1 to indicate (box->left & 1), so that functions that don't use edge_1 can still work properly */ if(!left_edge) diff --git a/src/render-cg/image/image_p4.S b/src/render-cg/image/image_p4.S index 71240b7..9ff67f4 100644 --- a/src/render-cg/image/image_p4.S +++ b/src/render-cg/image/image_p4.S @@ -54,7 +54,7 @@ _gint_image_p4_loop: add r6, r6 mov.b @r8+, r1 /* cmd.lines */ - shlr r4 + nop mov.l r10, @-r15 extu.b r1, r1 @@ -62,9 +62,6 @@ _gint_image_p4_loop: mov.b @r8+, r10 /* cmd.edge_1 */ nop - mov #0, r9 - addc r9, r4 /* r4 = (img.width + 1) >> 1 */ - mov.l @r8+, r9 shlr r0 /* T bit is now VFLIP */ diff --git a/src/render-cg/image/image_p4.c b/src/render-cg/image/image_p4.c index e193f98..e637c0d 100644 --- a/src/render-cg/image/image_p4.c +++ b/src/render-cg/image/image_p4.c @@ -9,7 +9,7 @@ void dimage_p4(int x, int y, image_t const *img, int eff) void dsubimage_p4(int x, int y, image_t const *img, int left, int top, int w, int h, int eff) { - if(img->profile == IMAGE_P4_RGB565A) + if(img->format == IMAGE_P4_RGB565A) return dsubimage_p4_clearbg(x, y, img, left, top, w, h, eff, img->alpha); diff --git a/src/render-cg/image/image_p8.c b/src/render-cg/image/image_p8.c index c6f5f8b..ae62ec5 100644 --- a/src/render-cg/image/image_p8.c +++ b/src/render-cg/image/image_p8.c @@ -9,7 +9,7 @@ void dimage_p8(int x, int y, image_t const *img, int eff) void dsubimage_p8(int x, int y, image_t const *img, int left, int top, int w, int h, int eff) { - if(img->profile == IMAGE_P8_RGB565A) + if(img->format == IMAGE_P8_RGB565A) return dsubimage_p8_clearbg(x, y, img, left, top, w, h, eff, img->alpha); diff --git a/src/render-cg/image/image_rgb16.S b/src/render-cg/image/image_rgb16.S index 4ee1b2f..c6f5c7d 100644 --- a/src/render-cg/image/image_rgb16.S +++ b/src/render-cg/image/image_rgb16.S @@ -1,6 +1,6 @@ .global _gint_image_rgb16_loop -/* gint's image renderer: 16-bit RGB entry piont +/* gint's image renderer: 16-bit RGB entry point These formats are the simplest of the bunch. RGB565 can use longword access in cases when alignment is favorable and no geometric effect is applied. In @@ -52,10 +52,10 @@ _gint_image_rgb16_loop: nop mov.l @r8+, r3 /* cmd.input */ - nop + add #4, r8 /* cmd.palette (don't care) */ bf.s _NO_VFLIP - add #4, r8 /* cmd.palette (don't care) */ + shlr r4 _VFLIP: neg r4, r4 diff --git a/src/render-cg/image/image_rgb16.c b/src/render-cg/image/image_rgb16.c index 98196d6..9b9e6b2 100644 --- a/src/render-cg/image/image_rgb16.c +++ b/src/render-cg/image/image_rgb16.c @@ -9,7 +9,7 @@ void dimage_rgb16(int x, int y, image_t const *img, int eff) void dsubimage_rgb16(int x, int y, image_t const *img, int left, int top, int w, int h, int eff) { - if(img->profile == IMAGE_RGB565A) + if(img->format == IMAGE_RGB565A) return dsubimage_rgb16_clearbg(x, y, img, left, top, w, h, eff, img->alpha);