Util: Add image loading API

This commit is contained in:
Vicki Pfau 2023-03-26 17:49:15 -07:00
parent cdb0c4193b
commit 2cba34d83a
3 changed files with 167 additions and 0 deletions

View File

@ -90,6 +90,12 @@ struct mImage {
enum mColorFormat format;
};
struct VFile;
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
struct mImage* mImageLoad(const char* path);
struct mImage* mImageLoadVF(struct VFile* vf);
void mImageDestroy(struct mImage*);
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);

View File

@ -5,9 +5,108 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/image.h>
#include <mgba-util/image/png-io.h>
#include <mgba-util/vfs.h>
#define PIXEL(IM, X, Y) \
(void*) (((IM)->stride * (Y) + (X)) * (IM)->depth + (uintptr_t) (IM)->data)
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format) {
struct mImage* image = calloc(1, sizeof(struct mImage));
if (!image) {
return NULL;
}
image->width = width;
image->height = height;
image->stride = width;
image->format = format;
image->depth = mColorFormatBytes(format);
image->data = calloc(width * height, image->depth);
if (!image->data) {
free(image);
return NULL;
}
return image;
}
struct mImage* mImageLoad(const char* path) {
struct VFile* vf = VFileOpen(path, O_RDONLY);
if (!vf) {
return NULL;
}
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
return image;
}
static struct mImage* mImageLoadPNG(struct VFile* vf) {
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (!png || !info || !end) {
PNGReadClose(png, info, end);
return NULL;
}
if (!PNGReadHeader(png, info)) {
PNGReadClose(png, info, end);
return NULL;
}
unsigned width = png_get_image_width(png, info);
unsigned height = png_get_image_height(png, info);
struct mImage* image = calloc(1, sizeof(*image));
image->width = width;
image->height = height;
image->stride = width;
switch (png_get_channels(png, info)) {
case 3:
image->format = mCOLOR_XBGR8;
image->depth = 4;
image->data = malloc(width * height * 4);
if (!PNGReadPixels(png, info, image->data, width, height, width)) {
free(image->data);
free(image);
PNGReadClose(png, info, end);
return NULL;
}
break;
case 4:
image->format = mCOLOR_ABGR8;
image->depth = 4;
image->data = malloc(width * height * 4);
if (!PNGReadPixelsA(png, info, image->data, width, height, width)) {
free(image->data);
free(image);
PNGReadClose(png, info, end);
return NULL;
}
break;
default:
// Not supported yet
free(image);
PNGReadClose(png, info, end);
return NULL;
}
return image;
}
struct mImage* mImageLoadVF(struct VFile* vf) {
vf->seek(vf, 0, SEEK_SET);
if (isPNG(vf)) {
return mImageLoadPNG(vf);
}
vf->seek(vf, 0, SEEK_SET);
return NULL;
}
void mImageDestroy(struct mImage* image) {
free(image->data);
free(image);
}
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y) {
if (x >= image->width || y >= image->height) {
return 0;

View File

@ -6,6 +6,7 @@
#include "util/test/suite.h"
#include <mgba-util/image.h>
#include <mgba-util/vfs.h>
M_TEST_DEFINE(pitchRead) {
static uint8_t buffer[12] = {
@ -440,6 +441,65 @@ M_TEST_DEFINE(oobWrite) {
assert_memory_equal(buffer, (&(uint8_t[8]) { 0xFF, 0xFF, 0xFF, 0xFF }), sizeof(buffer));
}
M_TEST_DEFINE(loadPng24) {
const uint8_t data[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x08, 0x02, 0x00, 0x00, 0x00, 0xfd, 0xd4, 0x9a, 0x73, 0x00, 0x00, 0x00,
0x12, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x63, 0xf8, 0xff, 0xff, 0x3f,
0x03, 0x03, 0x03, 0x03, 0x84, 0x00, 0x00, 0x2a, 0xe3, 0x04, 0xfc, 0xe8,
0x51, 0xc0, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82
};
size_t len = 75;
struct VFile* vf = VFileFromConstMemory(data, len);
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
assert_non_null(image);
assert_int_equal(image->width, 2);
assert_int_equal(image->height, 2);
assert_int_equal(image->format, mCOLOR_XBGR8);
assert_int_equal(mImageGetPixel(image, 0, 0) & 0xFFFFFF, 0xFFFFFF);
assert_int_equal(mImageGetPixel(image, 1, 0) & 0xFFFFFF, 0x000000);
assert_int_equal(mImageGetPixel(image, 0, 1) & 0xFFFFFF, 0xFF0000);
assert_int_equal(mImageGetPixel(image, 1, 1) & 0xFFFFFF, 0x0000FF);
mImageDestroy(image);
}
M_TEST_DEFINE(loadPng32) {
unsigned char data[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x08, 0x06, 0x00, 0x00, 0x00, 0x72, 0xb6, 0x0d, 0x24, 0x00, 0x00, 0x00,
0x1a, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x05, 0xc1, 0x31, 0x01, 0x00,
0x00, 0x08, 0xc0, 0x20, 0x6c, 0x66, 0x25, 0xfb, 0x1f, 0x13, 0xa6, 0x0a,
0xa7, 0x5a, 0x78, 0x58, 0x7b, 0x07, 0xac, 0xe9, 0x00, 0x3d, 0x95, 0x00,
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int len = 83;
struct VFile* vf = VFileFromConstMemory(data, len);
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
assert_non_null(image);
assert_int_equal(image->width, 2);
assert_int_equal(image->height, 2);
assert_int_equal(image->format, mCOLOR_ABGR8);
assert_int_equal(mImageGetPixel(image, 0, 0) >> 24, 0xFF);
assert_int_equal(mImageGetPixel(image, 1, 0) >> 24, 0x70);
assert_int_equal(mImageGetPixel(image, 0, 1) >> 24, 0x40);
assert_int_equal(mImageGetPixel(image, 1, 1) >> 24, 0x00);
mImageDestroy(image);
}
M_TEST_SUITE_DEFINE(Image,
cmocka_unit_test(pitchRead),
cmocka_unit_test(strideRead),
@ -447,4 +507,6 @@ M_TEST_SUITE_DEFINE(Image,
cmocka_unit_test(pitchWrite),
cmocka_unit_test(strideWrite),
cmocka_unit_test(oobWrite),
cmocka_unit_test(loadPng24),
cmocka_unit_test(loadPng32),
)