Util: Start mImage/mColor APIs and tests

This commit is contained in:
Vicki Pfau 2023-03-22 01:42:27 -07:00
parent 646a0e9b33
commit e79ae2860b
5 changed files with 935 additions and 3 deletions

View File

@ -22,9 +22,9 @@ typedef uint32_t color_t;
#define M_G5(X) (((X) >> 5) & 0x1F) #define M_G5(X) (((X) >> 5) & 0x1F)
#define M_B5(X) (((X) >> 10) & 0x1F) #define M_B5(X) (((X) >> 10) & 0x1F)
#define M_R8(X) (((((X) << 3) & 0xF8) * 0x21) >> 2) #define M_R8(X) ((M_R5(X) * 0x21) >> 2)
#define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 2) #define M_G8(X) ((M_G5(X) * 0x21) >> 2)
#define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 2) #define M_B8(X) ((M_B5(X) * 0x21) >> 2)
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19)) #define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
#define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3)) #define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3))
@ -81,7 +81,54 @@ enum mColorFormat {
mCOLOR_ANY = -1 mCOLOR_ANY = -1
}; };
struct mImage {
void* data;
unsigned width;
unsigned height;
unsigned stride;
unsigned depth;
enum mColorFormat format;
};
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y);
void mImageSetPixel(struct mImage* image, unsigned x, unsigned y, uint32_t color);
void mImageSetPixelRaw(struct mImage* image, unsigned x, unsigned y, uint32_t color);
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to);
#ifndef PYCPARSE #ifndef PYCPARSE
static inline unsigned mColorFormatBytes(enum mColorFormat format) {
switch (format) {
case mCOLOR_XBGR8:
case mCOLOR_XRGB8:
case mCOLOR_BGRX8:
case mCOLOR_RGBX8:
case mCOLOR_ABGR8:
case mCOLOR_ARGB8:
case mCOLOR_BGRA8:
case mCOLOR_RGBA8:
return 4;
case mCOLOR_RGB5:
case mCOLOR_BGR5:
case mCOLOR_RGB565:
case mCOLOR_BGR565:
case mCOLOR_ARGB5:
case mCOLOR_ABGR5:
case mCOLOR_RGBA5:
case mCOLOR_BGRA5:
return 2;
case mCOLOR_RGB8:
case mCOLOR_BGR8:
return 3;
case mCOLOR_L8:
return 1;
case mCOLOR_ANY:
break;
}
return 0;
}
static inline color_t mColorFrom555(uint16_t value) { static inline color_t mColorFrom555(uint16_t value) {
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5 #ifdef COLOR_5_6_5

View File

@ -16,6 +16,7 @@ set(SOURCE_FILES
convolve.c convolve.c
elf-read.c elf-read.c
geometry.c geometry.c
image.c
image/export.c image/export.c
image/png-io.c image/png-io.c
patch.c patch.c
@ -34,7 +35,9 @@ set(GUI_FILES
gui/menu.c) gui/menu.c)
set(TEST_FILES set(TEST_FILES
test/color.c
test/geometry.c test/geometry.c
test/image.c
test/sfo.c test/sfo.c
test/string-parser.c test/string-parser.c
test/string-utf8.c test/string-utf8.c

269
src/util/image.c Normal file
View File

@ -0,0 +1,269 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/image.h>
#define PIXEL(IM, X, Y) \
(void*) (((IM)->stride * (Y) + (X)) * (IM)->depth + (uintptr_t) (IM)->data)
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y) {
if (x >= image->width || y >= image->height) {
return 0;
}
const void* pixel = PIXEL(image, x, y);
uint32_t color;
switch (image->depth) {
case 1:
color = *(const uint8_t*) pixel;
break;
case 2:
color = *(const uint16_t*) pixel;
break;
case 4:
color = *(const uint32_t*) pixel;
break;
case 3:
#ifdef __BIG_ENDIAN__
color = ((const uint8_t*) pixel)[0] << 16;
color |= ((const uint8_t*) pixel)[1] << 8;
color |= ((const uint8_t*) pixel)[2];
#else
color = ((const uint8_t*) pixel)[0];
color |= ((const uint8_t*) pixel)[1] << 8;
color |= ((const uint8_t*) pixel)[2] << 16;
#endif
break;
}
return color;
}
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y) {
return mColorConvert(mImageGetPixelRaw(image, x, y), image->format, mCOLOR_ARGB8);
}
void mImageSetPixelRaw(struct mImage* image, unsigned x, unsigned y, uint32_t color) {
if (x >= image->width || y >= image->height) {
return;
}
void* pixel = PIXEL(image, x, y);
switch (image->depth) {
case 1:
*(uint8_t*) pixel = color;
break;
case 2:
*(uint16_t*) pixel = color;
break;
case 4:
*(uint32_t*) pixel = color;
break;
case 3:
#ifdef __BIG_ENDIAN__
((uint8_t*) pixel)[0] = color >> 16;
((uint8_t*) pixel)[1] = color >> 8;
((uint8_t*) pixel)[2] = color;
#else
((uint8_t*) pixel)[0] = color;
((uint8_t*) pixel)[1] = color >> 8;
((uint8_t*) pixel)[2] = color >> 16;
#endif
break;
}
}
void mImageSetPixel(struct mImage* image, unsigned x, unsigned y, uint32_t color) {
mImageSetPixelRaw(image, x, y, mColorConvert(color, mCOLOR_ARGB8, image->format));
}
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to) {
if (from == to) {
return color;
}
int r;
int g;
int b;
int a = 0xFF;
switch (from) {
case mCOLOR_ARGB8:
a = color >> 24;
// Fall through
case mCOLOR_XRGB8:
case mCOLOR_RGB8:
r = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
b = color & 0xFF;
break;
case mCOLOR_ABGR8:
a = color >> 24;
// Fall through
case mCOLOR_XBGR8:
case mCOLOR_BGR8:
b = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
r = color & 0xFF;
break;
case mCOLOR_RGBA8:
a = color & 0xFF;
// Fall through
case mCOLOR_RGBX8:
r = (color >> 24) & 0xFF;
g = (color >> 16) & 0xFF;
b = (color >> 8) & 0xFF;
break;
case mCOLOR_BGRA8:
a = color & 0xFF;
// Fall through
case mCOLOR_BGRX8:
b = (color >> 24) & 0xFF;
g = (color >> 16) & 0xFF;
r = (color >> 8) & 0xFF;
break;
case mCOLOR_ARGB5:
a = (color >> 15) * 0xFF;
// Fall through
case mCOLOR_RGB5:
r = (((color >> 10) & 0x1F) * 0x21) >> 2;
g = (((color >> 5) & 0x1F) * 0x21) >> 2;
b = ((color & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_ABGR5:
a = (color >> 15) * 0xFF;
// Fall through
case mCOLOR_BGR5:
b = (((color >> 10) & 0x1F) * 0x21) >> 2;
g = (((color >> 5) & 0x1F) * 0x21) >> 2;
r = ((color & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_RGBA5:
a = (color & 1) * 0xFF;
r = (((color >> 11) & 0x1F) * 0x21) >> 2;
g = (((color >> 6) & 0x1F) * 0x21) >> 2;
b = (((color >> 1) & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_BGRA5:
a = (color & 1) * 0xFF;
b = (((color >> 11) & 0x1F) * 0x21) >> 2;
g = (((color >> 6) & 0x1F) * 0x21) >> 2;
r = (((color >> 1) & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_RGB565:
r = (((color >> 10) & 0x1F) * 0x21) >> 2;
g = (((color >> 5) & 0x3F) * 0x41) >> 4;
b = ((color & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_BGR565:
b = (((color >> 10) & 0x1F) * 0x21) >> 2;
g = (((color >> 5) & 0x3F) * 0x41) >> 4;
r = ((color & 0x1F) * 0x21) >> 2;
break;
case mCOLOR_L8:
r = color;
g = color;
b = color;
break;
case mCOLOR_ANY:
return 0;
}
color = 0;
switch (to) {
case mCOLOR_XRGB8:
a = 0xFF;
// Fall through
case mCOLOR_ARGB8:
color |= a << 24;
// Fall through
case mCOLOR_RGB8:
color |= r << 16;
color |= g << 8;
color |= b;
break;
case mCOLOR_XBGR8:
a = 0xFF;
// Fall through
case mCOLOR_ABGR8:
color |= a << 24;
// Fall through
case mCOLOR_BGR8:
color |= b << 16;
color |= g << 8;
color |= r;
break;
case mCOLOR_RGBX8:
a = 0xFF;
// Fall through
case mCOLOR_RGBA8:
color |= a;
color |= r << 24;
color |= g << 16;
color |= b << 8;
break;
case mCOLOR_BGRX8:
a = 0xFF;
// Fall through
case mCOLOR_BGRA8:
color |= a;
color |= b << 24;
color |= g << 16;
color |= r << 8;
break;
case mCOLOR_ARGB5:
color |= (!!a << 15);
// Fall through
case mCOLOR_RGB5:
color |= (r >> 3) << 10;
color |= (g >> 3) << 5;
color |= b >> 3;
break;
case mCOLOR_ABGR5:
color |= (!!a << 15);
// Fall through
case mCOLOR_BGR5:
color |= (b >> 3) << 10;
color |= (g >> 3) << 5;
color |= r >> 3;
break;
case mCOLOR_RGBA5:
color |= !!a;
color |= (r >> 3) << 11;
color |= (g >> 3) << 6;
color |= (b >> 3) << 1;
break;
case mCOLOR_BGRA5:
color |= !!a;
color |= (b >> 3) << 11;
color |= (g >> 3) << 6;
color |= (r >> 3) << 1;
break;
case mCOLOR_RGB565:
color |= (r >> 3) << 11;
color |= (g >> 2) << 5;
color |= b >> 3;
break;
case mCOLOR_BGR565:
color |= (b >> 3) << 11;
color |= (g >> 2) << 5;
color |= r >> 3;
break;
case mCOLOR_L8:
// sRGB primaries in fixed point, roughly fudged to saturate to 0xFFFF
color = (55 * r + 184 * g + 18 * b) >> 8;
break;
case mCOLOR_ANY:
return 0;
}
return color;
}

163
src/util/test/color.c Normal file
View File

@ -0,0 +1,163 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba-util/image.h>
M_TEST_DEFINE(channelSwap32) {
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ARGB8, mCOLOR_ABGR8), 0xFFCCBBAA);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ABGR8, mCOLOR_ARGB8), 0xFFCCBBAA);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_XRGB8, mCOLOR_XBGR8), 0xFFCCBBAA);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_XBGR8, mCOLOR_XRGB8), 0xFFCCBBAA);
assert_int_equal(mColorConvert(0xAABBCC, mCOLOR_RGB8, mCOLOR_BGR8), 0xCCBBAA);
assert_int_equal(mColorConvert(0xAABBCC, mCOLOR_BGR8, mCOLOR_RGB8), 0xCCBBAA);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ARGB8, mCOLOR_RGBA8), 0xAABBCCFF);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ABGR8, mCOLOR_BGRA8), 0xAABBCCFF);
assert_int_equal(mColorConvert(0xAABBCCFF, mCOLOR_RGBA8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xAABBCCFF, mCOLOR_BGRA8, mCOLOR_ABGR8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ARGB8, mCOLOR_BGRA8), 0xCCBBAAFF);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ABGR8, mCOLOR_RGBA8), 0xCCBBAAFF);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_RGBA8, mCOLOR_ABGR8), 0xCCBBAAFF);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_BGRA8, mCOLOR_ARGB8), 0xCCBBAAFF);
}
M_TEST_DEFINE(channelSwap16) {
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_ARGB5, mCOLOR_ABGR5), 0x83FF);
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_ABGR5, mCOLOR_ARGB5), 0x83FF);
assert_int_equal(mColorConvert(0x7FE0, mCOLOR_RGB5, mCOLOR_BGR5), 0x03FF);
assert_int_equal(mColorConvert(0x7FE0, mCOLOR_BGR5, mCOLOR_RGB5), 0x03FF);
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_RGB565, mCOLOR_BGR565), 0x07FF);
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_BGR565, mCOLOR_RGB565), 0x07FF);
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_ARGB5, mCOLOR_RGBA5), 0xFFC1);
assert_int_equal(mColorConvert(0xFFE0, mCOLOR_RGBA5, mCOLOR_ARGB5), 0x7FF0);
}
M_TEST_DEFINE(convertQuantizeOpaque) {
assert_int_equal(mColorConvert(0xA0B0C0, mCOLOR_XRGB8, mCOLOR_RGB5), (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xA1B1C1, mCOLOR_XRGB8, mCOLOR_RGB5), (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xA2B2C2, mCOLOR_XRGB8, mCOLOR_RGB5), (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xA4B4C4, mCOLOR_XRGB8, mCOLOR_RGB5), (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xA8B8C8, mCOLOR_XRGB8, mCOLOR_RGB5), (0xA << 11) | (0x1B << 6) | (0x1C << 1) | 1);
assert_int_equal(mColorConvert(0xA0B0C0, mCOLOR_XRGB8, mCOLOR_BGR5), (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xA1B1C1, mCOLOR_XRGB8, mCOLOR_BGR5), (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xA2B2C2, mCOLOR_XRGB8, mCOLOR_BGR5), (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xA4B4C4, mCOLOR_XRGB8, mCOLOR_BGR5), (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xA8B8C8, mCOLOR_XRGB8, mCOLOR_BGR5), (0xC << 11) | (0x1B << 6) | (0x1A << 1) | 1);
assert_int_equal(mColorConvert(0xA0B0C0, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0xB << 7) | (0xC << 1));
assert_int_equal(mColorConvert(0xA1B1C1, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0xB << 7) | (0xC << 1));
assert_int_equal(mColorConvert(0xA2B2C2, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0xB << 7) | (0xC << 1));
assert_int_equal(mColorConvert(0xA4B4C4, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0xB << 7) | (0x1C << 1));
assert_int_equal(mColorConvert(0xA8B8C8, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0x1B << 7) | (0x2C << 1) | 1);
assert_int_equal(mColorConvert(0xACBCCC, mCOLOR_XRGB8, mCOLOR_RGB565), (0xA << 12) | (0x1B << 7) | (0x3C << 1) | 1);
assert_int_equal(mColorConvert(0xA0B0C0, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0xB << 7) | (0xA << 1));
assert_int_equal(mColorConvert(0xA1B1C1, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0xB << 7) | (0xA << 1));
assert_int_equal(mColorConvert(0xA2B2C2, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0xB << 7) | (0xA << 1));
assert_int_equal(mColorConvert(0xA4B4C4, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0xB << 7) | (0x1A << 1));
assert_int_equal(mColorConvert(0xA8B8C8, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0x1B << 7) | (0x2A << 1) | 1);
assert_int_equal(mColorConvert(0xACBCCC, mCOLOR_XRGB8, mCOLOR_BGR565), (0xC << 12) | (0x1B << 7) | (0x3A << 1) | 1);
}
M_TEST_DEFINE(convertQuantizeTransparent) {
assert_int_equal(mColorConvert(0xFFA0B0C0, mCOLOR_ARGB8, mCOLOR_ARGB5), 0x8000 | (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0x00A0B0C0, mCOLOR_ARGB8, mCOLOR_ARGB5), (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xFEA0B0C0, mCOLOR_ARGB8, mCOLOR_ARGB5), 0x8000 | (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0x01A0B0C0, mCOLOR_ARGB8, mCOLOR_ARGB5), 0x8000 | (0xA << 11) | (0xB << 6) | (0xC << 1));
assert_int_equal(mColorConvert(0xFFA0B0C0, mCOLOR_ARGB8, mCOLOR_ABGR5), 0x8000 | (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0x00A0B0C0, mCOLOR_ARGB8, mCOLOR_ABGR5), (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xFEA0B0C0, mCOLOR_ARGB8, mCOLOR_ABGR5), 0x8000 | (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0x01A0B0C0, mCOLOR_ARGB8, mCOLOR_ABGR5), 0x8000 | (0xC << 11) | (0xB << 6) | (0xA << 1));
assert_int_equal(mColorConvert(0xFFA0B0C0, mCOLOR_ARGB8, mCOLOR_RGBA5), 1 | (0xA << 12) | (0xB << 7) | (0xC << 2));
assert_int_equal(mColorConvert(0x00A0B0C0, mCOLOR_ARGB8, mCOLOR_RGBA5), (0xA << 12) | (0xB << 7) | (0xC << 2));
assert_int_equal(mColorConvert(0xFEA0B0C0, mCOLOR_ARGB8, mCOLOR_RGBA5), 1 | (0xA << 12) | (0xB << 7) | (0xC << 2));
assert_int_equal(mColorConvert(0x01A0B0C0, mCOLOR_ARGB8, mCOLOR_RGBA5), 1 | (0xA << 12) | (0xB << 7) | (0xC << 2));
assert_int_equal(mColorConvert(0xFFA0B0C0, mCOLOR_ARGB8, mCOLOR_BGRA5), 1 | (0xC << 12) | (0xB << 7) | (0xA << 2));
assert_int_equal(mColorConvert(0x00A0B0C0, mCOLOR_ARGB8, mCOLOR_BGRA5), (0xC << 12) | (0xB << 7) | (0xA << 2));
assert_int_equal(mColorConvert(0xFEA0B0C0, mCOLOR_ARGB8, mCOLOR_BGRA5), 1 | (0xC << 12) | (0xB << 7) | (0xA << 2));
assert_int_equal(mColorConvert(0x01A0B0C0, mCOLOR_ARGB8, mCOLOR_BGRA5), 1 | (0xC << 12) | (0xB << 7) | (0xA << 2));
}
M_TEST_DEFINE(convertToOpaque) {
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ARGB8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFEAABBCC, mCOLOR_ARGB8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x01AABBCC, mCOLOR_ARGB8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x00AABBCC, mCOLOR_ARGB8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_ARGB8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0xFEAABBCC, mCOLOR_ARGB8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0x01AABBCC, mCOLOR_ARGB8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0x00AABBCC, mCOLOR_ARGB8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0xAABBCCFF, mCOLOR_RGBA8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xAABBCCFE, mCOLOR_RGBA8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xAABBCC01, mCOLOR_RGBA8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xAABBCC00, mCOLOR_RGBA8, mCOLOR_XRGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xAABBCCFF, mCOLOR_RGBA8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0xAABBCCFE, mCOLOR_RGBA8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0xAABBCC01, mCOLOR_RGBA8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0xAABBCC00, mCOLOR_RGBA8, mCOLOR_RGB8), 0xAABBCC);
assert_int_equal(mColorConvert(0x7FFF, mCOLOR_ARGB5, mCOLOR_RGB5), 0x7FFF);
assert_int_equal(mColorConvert(0xFFFF, mCOLOR_ARGB5, mCOLOR_RGB5), 0x7FFF);
assert_int_equal(mColorConvert(0xFFFE, mCOLOR_RGBA5, mCOLOR_RGB5), 0x7FFF);
assert_int_equal(mColorConvert(0xFFFF, mCOLOR_RGBA5, mCOLOR_RGB5), 0x7FFF);
}
M_TEST_DEFINE(convertToAlpha) {
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_XRGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFEAABBCC, mCOLOR_XRGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x01AABBCC, mCOLOR_XRGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x00AABBCC, mCOLOR_XRGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFFAABBCC, mCOLOR_RGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0xFEAABBCC, mCOLOR_RGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x01AABBCC, mCOLOR_RGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x00AABBCC, mCOLOR_RGB8, mCOLOR_ARGB8), 0xFFAABBCC);
assert_int_equal(mColorConvert(0x7FFF, mCOLOR_RGB5, mCOLOR_ARGB5), 0xFFFF);
assert_int_equal(mColorConvert(0xFFFF, mCOLOR_RGB5, mCOLOR_ARGB5), 0xFFFF);
assert_int_equal(mColorConvert(0x7FFF, mCOLOR_RGB5, mCOLOR_RGBA5), 0xFFFF);
assert_int_equal(mColorConvert(0xFFFF, mCOLOR_RGB5, mCOLOR_RGBA5), 0xFFFF);
}
M_TEST_DEFINE(convertFromGray) {
int i;
for (i = 0; i < 256; ++i) {
assert_int_equal(mColorConvert(i, mCOLOR_L8, mCOLOR_RGB8), (i << 16) | (i << 8) | i);
assert_int_equal(mColorConvert(i, mCOLOR_L8, mCOLOR_ARGB8), 0xFF000000 | (i << 16) | (i << 8) | i);
}
}
M_TEST_DEFINE(convertToGray) {
int i;
for (i = 0; i < 256; ++i) {
assert_int_equal(mColorConvert((i << 16) | (i << 8) | i, mCOLOR_RGB8, mCOLOR_L8), i);
assert_int_equal(mColorConvert((i << 16) | (i << 8) | i, mCOLOR_ARGB8, mCOLOR_L8), i);
assert_int_equal(mColorConvert(0xFF000000 | (i << 16) | (i << 8) | i, mCOLOR_ARGB8, mCOLOR_L8), i);
}
}
M_TEST_SUITE_DEFINE(Color,
cmocka_unit_test(channelSwap32),
cmocka_unit_test(channelSwap16),
cmocka_unit_test(convertQuantizeOpaque),
cmocka_unit_test(convertQuantizeTransparent),
cmocka_unit_test(convertToOpaque),
cmocka_unit_test(convertToAlpha),
cmocka_unit_test(convertFromGray),
cmocka_unit_test(convertToGray),
)

450
src/util/test/image.c Normal file
View File

@ -0,0 +1,450 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba-util/image.h>
M_TEST_DEFINE(pitchRead) {
static uint8_t buffer[12] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB
};
struct mImage image = {
.data = buffer,
.height = 1
};
int i;
image.depth = 1;
image.width = 12;
image.format = mCOLOR_L8;
for (i = 0; i < 12; ++i) {
assert_int_equal(mImageGetPixelRaw(&image, i, 0), i);
}
image.depth = 2;
image.width = 6;
image.format = mCOLOR_RGB5;
for (i = 0; i < 6; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 2) << 8 | (i * 2 + 1));
#else
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 2 + 1) << 8 | (i * 2));
#endif
}
image.depth = 3;
image.width = 4;
image.format = mCOLOR_RGB8;
for (i = 0; i < 4; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 3) << 16 | (i * 3 + 1) << 8 | (i * 3 + 2));
#else
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 3 + 2) << 16 | (i * 3 + 1) << 8 | (i * 3));
#endif
}
image.depth = 4;
image.width = 3;
image.format = mCOLOR_ARGB8;
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 4) << 24 | (i * 4 + 1) << 16 | (i * 4 + 2) << 8 | (i * 4 + 3));
#else
assert_int_equal(mImageGetPixelRaw(&image, i, 0), (i * 4 + 3) << 24 | (i * 4 + 2) << 16 | (i * 4 + 1) << 8 | (i * 4));
#endif
}
}
M_TEST_DEFINE(strideRead) {
static uint8_t buffer[12] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB
};
struct mImage image = {
.data = buffer,
.width = 1
};
int i;
image.depth = 1;
image.stride = 1;
image.height = 12;
image.format = mCOLOR_L8;
for (i = 0; i < 12; ++i) {
assert_int_equal(mImageGetPixelRaw(&image, 0, i), i);
}
image.depth = 1;
image.stride = 2;
image.height = 6;
image.format = mCOLOR_L8;
for (i = 0; i < 6; ++i) {
assert_int_equal(mImageGetPixelRaw(&image, 0, i), i * 2);
}
image.depth = 2;
image.stride = 1;
image.height = 6;
image.format = mCOLOR_RGB5;
for (i = 0; i < 6; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 2) << 8 | (i * 2 + 1));
#else
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 2 + 1) << 8 | (i * 2));
#endif
}
image.depth = 2;
image.stride = 2;
image.height = 3;
image.format = mCOLOR_RGB5;
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 4) << 8 | (i * 4 + 1));
#else
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 4 + 1) << 8 | (i * 4));
#endif
}
image.depth = 3;
image.stride = 1;
image.height = 4;
image.format = mCOLOR_RGB8;
for (i = 0; i < 4; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 3) << 16 | (i * 3 + 1) << 8 | (i * 3 + 2));
#else
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 3 + 2) << 16 | (i * 3 + 1) << 8 | (i * 3));
#endif
}
image.depth = 3;
image.stride = 2;
image.height = 2;
image.format = mCOLOR_RGB8;
for (i = 0; i < 2; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 6) << 16 | (i * 6 + 1) << 8 | (i * 6 + 2));
#else
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 6 + 2) << 16 | (i * 6 + 1) << 8 | (i * 6));
#endif
}
image.depth = 4;
image.stride = 1;
image.height = 3;
image.format = mCOLOR_ARGB8;
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 4) << 24 | (i * 4 + 1) << 16 | (i * 4 + 2) << 8 | (i * 4 + 3));
#else
assert_int_equal(mImageGetPixelRaw(&image, 0, i), (i * 4 + 3) << 24 | (i * 4 + 2) << 16 | (i * 4 + 1) << 8 | (i * 4));
#endif
}
}
M_TEST_DEFINE(oobRead) {
static uint8_t buffer[8] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
struct mImage image = {
.data = buffer,
.width = 1,
.height = 1,
.stride = 1
};
image.depth = 1;
image.format = mCOLOR_L8;
assert_int_equal(mImageGetPixelRaw(&image, 0, 0), 0xFF);
assert_int_equal(mImageGetPixelRaw(&image, 1, 0), 0);
assert_int_equal(mImageGetPixelRaw(&image, 0, 1), 0);
assert_int_equal(mImageGetPixelRaw(&image, 1, 1), 0);
image.depth = 2;
image.format = mCOLOR_RGB5;
assert_int_equal(mImageGetPixelRaw(&image, 0, 0), 0xFFFF);
assert_int_equal(mImageGetPixelRaw(&image, 1, 0), 0);
assert_int_equal(mImageGetPixelRaw(&image, 0, 1), 0);
assert_int_equal(mImageGetPixelRaw(&image, 1, 1), 0);
image.depth = 3;
image.format = mCOLOR_RGB8;
assert_int_equal(mImageGetPixelRaw(&image, 0, 0), 0xFFFFFF);
assert_int_equal(mImageGetPixelRaw(&image, 1, 0), 0);
assert_int_equal(mImageGetPixelRaw(&image, 0, 1), 0);
assert_int_equal(mImageGetPixelRaw(&image, 1, 1), 0);
image.depth = 4;
image.format = mCOLOR_ARGB8;
assert_int_equal(mImageGetPixelRaw(&image, 0, 0), 0xFFFFFFFF);
assert_int_equal(mImageGetPixelRaw(&image, 1, 0), 0);
assert_int_equal(mImageGetPixelRaw(&image, 0, 1), 0);
assert_int_equal(mImageGetPixelRaw(&image, 1, 1), 0);
}
M_TEST_DEFINE(pitchWrite) {
static const uint8_t baseline[12] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB
};
uint8_t buffer[12];
struct mImage image = {
.data = buffer,
.height = 1
};
int i;
image.depth = 1;
image.width = 12;
image.format = mCOLOR_L8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 12; ++i) {
mImageSetPixelRaw(&image, i, 0, i);
}
assert_memory_equal(baseline, buffer, sizeof(baseline));
image.depth = 2;
image.width = 6;
image.format = mCOLOR_RGB5;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 6; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, i, 0, (i * 2) << 8 | (i * 2 + 1));
#else
mImageSetPixelRaw(&image, i, 0, (i * 2 + 1) << 8 | (i * 2));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(baseline));
image.depth = 3;
image.width = 4;
image.format = mCOLOR_RGB8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 4; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, i, 0, (i * 3) << 16 | (i * 3 + 1) << 8 | (i * 3 + 2));
#else
mImageSetPixelRaw(&image, i, 0, (i * 3 + 2) << 16 | (i * 3 + 1) << 8 | (i * 3));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(baseline));
image.depth = 4;
image.width = 3;
image.format = mCOLOR_ARGB8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, i, 0, (i * 4) << 24 | (i * 4 + 1) << 16 | (i * 4 + 2) << 8 | (i * 4 + 3));
#else
mImageSetPixelRaw(&image, i, 0, (i * 4 + 3) << 24 | (i * 4 + 2) << 16 | (i * 4 + 1) << 8 | (i * 4));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(baseline));
}
M_TEST_DEFINE(strideWrite) {
static const uint8_t baseline[12] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB
};
static const uint8_t baseline2x1[12] = {
0x0, 0x0, 0x2, 0x0, 0x4, 0x0, 0x6, 0x0, 0x8, 0x0, 0xA, 0x0
};
static const uint8_t baseline2x2[12] = {
0x0, 0x1, 0x0, 0x0, 0x4, 0x5, 0x0, 0x0, 0x8, 0x9, 0x0, 0x0
};
static const uint8_t baseline3x2[12] = {
0x0, 0x1, 0x2, 0x0, 0x0, 0x0, 0x6, 0x7, 0x8, 0x0, 0x0, 0x0
};
uint8_t buffer[12];
struct mImage image = {
.data = buffer,
.width = 1
};
int i;
image.depth = 1;
image.stride = 1;
image.height = 12;
image.format = mCOLOR_L8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 12; ++i) {
mImageSetPixelRaw(&image, 0, i, i);
}
assert_memory_equal(baseline, buffer, sizeof(buffer));
image.depth = 1;
image.stride = 2;
image.height = 6;
image.format = mCOLOR_L8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 6; ++i) {
mImageSetPixelRaw(&image, 0, i, i * 2);
}
assert_memory_equal(baseline2x1, buffer, sizeof(buffer));
image.depth = 2;
image.stride = 1;
image.height = 6;
image.format = mCOLOR_RGB5;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 6; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, 0, i, (i * 2) << 8 | (i * 2 + 1));
#else
mImageSetPixelRaw(&image, 0, i, (i * 2 + 1) << 8 | (i * 2));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(buffer));
image.depth = 2;
image.stride = 2;
image.height = 3;
image.format = mCOLOR_RGB5;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, 0, i, (i * 4) << 8 | (i * 4 + 1));
#else
mImageSetPixelRaw(&image, 0, i, (i * 4 + 1) << 8 | (i * 4));
#endif
}
assert_memory_equal(baseline2x2, buffer, sizeof(buffer));
image.depth = 3;
image.stride = 1;
image.height = 4;
image.format = mCOLOR_RGB8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 4; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, 0, i, (i * 3) << 16 | (i * 3 + 1) << 8 | (i * 3 + 2));
#else
mImageSetPixelRaw(&image, 0, i, (i * 3 + 2) << 16 | (i * 3 + 1) << 8 | (i * 3));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(buffer));
image.depth = 3;
image.stride = 2;
image.height = 2;
image.format = mCOLOR_RGB8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 2; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, 0, i, (i * 6) << 16 | (i * 6 + 1) << 8 | (i * 6 + 2));
#else
mImageSetPixelRaw(&image, 0, i, (i * 6 + 2) << 16 | (i * 6 + 1) << 8 | (i * 6));
#endif
}
assert_memory_equal(baseline3x2, buffer, sizeof(buffer));
image.depth = 4;
image.stride = 1;
image.height = 3;
image.format = mCOLOR_ARGB8;
memset(buffer, 0, sizeof(buffer));
for (i = 0; i < 3; ++i) {
#ifdef __BIG_ENDIAN__
mImageSetPixelRaw(&image, 0, i, (i * 4) << 24 | (i * 4 + 1) << 16 | (i * 4 + 2) << 8 | (i * 4 + 3));
#else
mImageSetPixelRaw(&image, 0, i, (i * 4 + 3) << 24 | (i * 4 + 2) << 16 | (i * 4 + 1) << 8 | (i * 4));
#endif
}
assert_memory_equal(baseline, buffer, sizeof(buffer));
}
M_TEST_DEFINE(oobWrite) {
static uint8_t buffer[8];
struct mImage image = {
.data = buffer,
.width = 1,
.height = 1,
.stride = 1
};
image.depth = 1;
image.format = mCOLOR_L8;
memset(buffer, 0, sizeof(buffer));
mImageSetPixelRaw(&image, 0, 0, 0xFF);
mImageSetPixelRaw(&image, 1, 0, 0);
mImageSetPixelRaw(&image, 0, 1, 0);
mImageSetPixelRaw(&image, 1, 1, 0);
assert_memory_equal(buffer, (&(uint8_t[8]) { 0xFF }), sizeof(buffer));
image.depth = 2;
image.format = mCOLOR_RGB5;
memset(buffer, 0, sizeof(buffer));
mImageSetPixelRaw(&image, 0, 0, 0xFFFF);
mImageSetPixelRaw(&image, 1, 0, 0);
mImageSetPixelRaw(&image, 0, 1, 0);
mImageSetPixelRaw(&image, 1, 1, 0);
assert_memory_equal(buffer, (&(uint8_t[8]) { 0xFF, 0xFF }), sizeof(buffer));
image.depth = 3;
image.format = mCOLOR_RGB8;
memset(buffer, 0, sizeof(buffer));
mImageSetPixelRaw(&image, 0, 0, 0xFFFFFF);
mImageSetPixelRaw(&image, 1, 0, 0);
mImageSetPixelRaw(&image, 0, 1, 0);
mImageSetPixelRaw(&image, 1, 1, 0);
assert_memory_equal(buffer, (&(uint8_t[8]) { 0xFF, 0xFF, 0xFF }), sizeof(buffer));
image.depth = 4;
image.format = mCOLOR_ARGB8;
memset(buffer, 0, sizeof(buffer));
mImageSetPixelRaw(&image, 0, 0, 0xFFFFFFFF);
mImageSetPixelRaw(&image, 1, 0, 0);
mImageSetPixelRaw(&image, 0, 1, 0);
mImageSetPixelRaw(&image, 1, 1, 0);
assert_memory_equal(buffer, (&(uint8_t[8]) { 0xFF, 0xFF, 0xFF, 0xFF }), sizeof(buffer));
}
M_TEST_SUITE_DEFINE(Image,
cmocka_unit_test(pitchRead),
cmocka_unit_test(strideRead),
cmocka_unit_test(oobRead),
cmocka_unit_test(pitchWrite),
cmocka_unit_test(strideWrite),
cmocka_unit_test(oobWrite),
)