and the codec logic.undefined
@@ -35,11 +35,11 @@ extern ANativeActivity *g_activity; | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(AndroidImageData, 100) | |||
DECLARE_IMAGE_CODEC(AndroidImageCodec, 100) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
@@ -50,7 +50,7 @@ private: | |||
jint *pixels; | |||
}; | |||
bool AndroidImageData::Open(char const *path) | |||
bool AndroidImageCodec::Load(Image *image, char const *path) | |||
{ | |||
JNIEnv *env; | |||
jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2); | |||
@@ -109,14 +109,14 @@ bool AndroidImageData::Open(char const *path) | |||
return true; | |||
} | |||
bool AndroidImageData::Save(char const *path) | |||
bool AndroidImageCodec::Save(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
/* TODO: unimplemented */ | |||
} | |||
bool AndroidImageData::Close() | |||
bool AndroidImageCodec::Close() | |||
{ | |||
JNIEnv *env; | |||
jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2); | |||
@@ -141,7 +141,7 @@ bool AndroidImageData::Close() | |||
return true; | |||
} | |||
uint8_t *AndroidImageData::GetData() const | |||
uint8_t *AndroidImageCodec::GetData() const | |||
{ | |||
return (uint8_t *)pixels; | |||
} | |||
@@ -24,61 +24,49 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(DummyImageData, 0) | |||
DECLARE_IMAGE_CODEC(DummyImageCodec, 0) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
private: | |||
uint8_t *pixels; | |||
}; | |||
/* | |||
* Public Image class | |||
*/ | |||
bool DummyImageData::Open(char const *path) | |||
bool DummyImageCodec::Load(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
m_size = ivec2(256); | |||
m_format = PixelFormat::RGBA_8; | |||
pixels = new uint8_t[256 * 256 * 4 * sizeof(*pixels)]; | |||
uint8_t *parser = pixels; | |||
image->SetSize(ivec2(256)); | |||
u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>(); | |||
for (int j = 0; j < 256; j++) | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
*parser++ = ((i ^ j) & 1) * 0xff; | |||
*parser++ = (uint8_t)i; | |||
*parser++ = (uint8_t)j; | |||
*parser++ = (((i >> 4) ^ (j >> 4)) & 1) * 0xff; | |||
pixels->r = ((i ^ j) & 1) * 0xff; | |||
pixels->g = (uint8_t)i; | |||
pixels->b = (uint8_t)j; | |||
pixels->a = (((i >> 4) ^ (j >> 4)) & 1) * 0xff; | |||
++pixels; | |||
} | |||
image->Unlock(); | |||
return true; | |||
} | |||
bool DummyImageData::Save(char const *path) | |||
bool DummyImageCodec::Save(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
return true; | |||
} | |||
bool DummyImageData::Close() | |||
bool DummyImageCodec::Close() | |||
{ | |||
delete[] pixels; | |||
return true; | |||
} | |||
uint8_t * DummyImageData::GetData() const | |||
{ | |||
return pixels; | |||
} | |||
} /* namespace lol */ | |||
@@ -31,11 +31,11 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(GdiPlusImageData, 100) | |||
DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
@@ -49,7 +49,7 @@ private: | |||
* Public Image class | |||
*/ | |||
bool GdiPlusImageData::Open(char const *path) | |||
bool GdiPlusImageCodec::Load(Image *image, char const *path) | |||
{ | |||
Gdiplus::Status status; | |||
ULONG_PTR token; | |||
@@ -110,10 +110,11 @@ bool GdiPlusImageData::Open(char const *path) | |||
return false; | |||
} | |||
m_size = ivec2(m_bitmap->GetWidth(), m_bitmap->GetHeight()); | |||
m_format = PixelFormat::RGBA_8; | |||
image->m_data->m_size = ivec2(m_bitmap->GetWidth(), | |||
m_bitmap->GetHeight()); | |||
image->m_data->m_format = PixelFormat::RGBA_8; | |||
Gdiplus::Rect rect(0, 0, m_size.x, m_size.y); | |||
Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y); | |||
if (m_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, | |||
PixelFormat32bppARGB, &m_bdata) != Gdiplus::Ok) | |||
{ | |||
@@ -128,8 +129,8 @@ bool GdiPlusImageData::Open(char const *path) | |||
* know about ARGB, only RGBA. So we swap bytes. We could also fix | |||
* this in the shader. */ | |||
uint8_t *p = static_cast<uint8_t *>(m_bdata.Scan0); | |||
for (int y = 0; y < m_size.y; y++) | |||
for (int x = 0; x < m_size.x; x++) | |||
for (int y = 0; y < image->m_data->m_size.y; y++) | |||
for (int x = 0; x < image->m_data->m_size.x; x++) | |||
{ | |||
uint8_t tmp = p[2]; | |||
p[2] = p[0]; | |||
@@ -140,7 +141,7 @@ bool GdiPlusImageData::Open(char const *path) | |||
return true; | |||
} | |||
bool GdiPlusImageData::Save(char const *path) | |||
bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||
{ | |||
ULONG_PTR token; | |||
Gdiplus::GdiplusStartupInput input; | |||
@@ -192,11 +193,12 @@ bool GdiPlusImageData::Save(char const *path) | |||
/* FIXME: we create a new image because there is no guarantee that | |||
* the image comes from GDI. But the engine architecture doesn't | |||
* allow us to save other images anyway. */ | |||
Gdiplus::Bitmap *b = new Gdiplus::Bitmap(m_size.x, m_size.y, | |||
Gdiplus::Bitmap *b = new Gdiplus::Bitmap(image->m_data->m_size.x, | |||
image->m_data->m_size.y, | |||
PixelFormat32bppARGB); | |||
Gdiplus::BitmapData bdata; | |||
Gdiplus::Rect rect(0, 0, m_size.x, m_size.y); | |||
Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y); | |||
if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite, | |||
PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) | |||
@@ -210,8 +212,8 @@ bool GdiPlusImageData::Save(char const *path) | |||
u8vec4 *psrc = static_cast<u8vec4 *>(m_bdata.Scan0); | |||
u8vec4 *pdst = static_cast<u8vec4 *>(bdata.Scan0); | |||
for (int y = 0; y < m_size.y; y++) | |||
for (int x = 0; x < m_size.x; x++) | |||
for (int y = 0; y < image->m_data->m_size.y; y++) | |||
for (int x = 0; x < image->m_data->m_size.x; x++) | |||
{ | |||
*pdst++ = (*psrc++).bgra; | |||
} | |||
@@ -233,7 +235,7 @@ bool GdiPlusImageData::Save(char const *path) | |||
return true; | |||
} | |||
bool GdiPlusImageData::Close() | |||
bool GdiPlusImageCodec::Close() | |||
{ | |||
m_bitmap->UnlockBits(&m_bdata); | |||
delete m_bitmap; | |||
@@ -241,7 +243,7 @@ bool GdiPlusImageData::Close() | |||
return true; | |||
} | |||
uint8_t * GdiPlusImageData::GetData() const | |||
uint8_t * GdiPlusImageCodec::GetData() const | |||
{ | |||
return static_cast<uint8_t *>(m_bdata.Scan0); | |||
} | |||
@@ -28,11 +28,11 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(IosImageData, 100) | |||
DECLARE_IMAGE_CODEC(IosImageCodec, 100) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
@@ -45,7 +45,7 @@ private: | |||
* Public Image class | |||
*/ | |||
bool IosImageData::Open(char const *path) | |||
bool IosImageCodec::Load(Image *image, char const *path) | |||
{ | |||
NSString *fullpath = [NSString stringWithUTF8String:path]; | |||
NSArray *chunks = [fullpath componentsSeparatedByString: @"/"]; | |||
@@ -84,7 +84,7 @@ bool IosImageData::Open(char const *path) | |||
return true; | |||
} | |||
bool IosImageData::Save(char const *path) | |||
bool IosImageCodec::Save(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
@@ -92,14 +92,14 @@ bool IosImageData::Save(char const *path) | |||
return true; | |||
} | |||
bool IosImageData::Close() | |||
bool IosImageCodec::Close() | |||
{ | |||
free(pixels); | |||
return true; | |||
} | |||
uint8_t * IosImageData::GetData() const | |||
uint8_t * IosImageCodec::GetData() const | |||
{ | |||
return pixels; | |||
} | |||
@@ -30,11 +30,11 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(Ps3ImageData, 100) | |||
DECLARE_IMAGE_CODEC(Ps3ImageCodec, 100) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
@@ -49,7 +49,7 @@ private: | |||
* Public Image class | |||
*/ | |||
bool Ps3ImageData::Open(char const *path) | |||
bool Ps3ImageCodec::Load(Image *image, char const *path) | |||
{ | |||
int32_t err; | |||
@@ -74,9 +74,9 @@ bool Ps3ImageData::Open(char const *path) | |||
in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE; | |||
in_param.ppuThreadPriority = 1000; | |||
in_param.spuThreadPriority = 200; | |||
in_param.cbCtrlMallocFunc = Ps3ImageData::Malloc; | |||
in_param.cbCtrlMallocFunc = Ps3ImageCodec::Malloc; | |||
in_param.cbCtrlMallocArg = nullptr; | |||
in_param.cbCtrlFreeFunc = Ps3ImageData::Free; | |||
in_param.cbCtrlFreeFunc = Ps3ImageCodec::Free; | |||
in_param.cbCtrlFreeArg = nullptr; | |||
CellPngDecThreadOutParam out_param; | |||
err = cellPngDecCreate(&hmain, &in_param, &out_param); | |||
@@ -174,7 +174,7 @@ bool Ps3ImageData::Open(char const *path) | |||
return true; | |||
} | |||
bool Ps3ImageData::Open(char const *path) | |||
bool Ps3ImageCodec::Load(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
@@ -182,14 +182,14 @@ bool Ps3ImageData::Open(char const *path) | |||
return true; | |||
} | |||
bool Ps3ImageData::Close() | |||
bool Ps3ImageCodec::Close() | |||
{ | |||
free(pixels); | |||
return true; | |||
} | |||
uint8_t * Ps3ImageData::GetData() const | |||
uint8_t * Ps3ImageCodec::GetData() const | |||
{ | |||
return pixels; | |||
} | |||
@@ -37,36 +37,34 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(SdlImageData, 50) | |||
DECLARE_IMAGE_CODEC(SdlImageCodec, 50) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
static SDL_Surface *Create32BppSurface(ivec2 size); | |||
private: | |||
SDL_Surface *m_img; | |||
SDL_Surface *m_surface; | |||
}; | |||
/* | |||
* Public Image class | |||
*/ | |||
bool SdlImageData::Open(char const *path) | |||
bool SdlImageCodec::Load(Image *image, char const *path) | |||
{ | |||
Array<String> pathlist = System::GetPathList(path); | |||
for (int i = 0; i < pathlist.Count(); i++) | |||
{ | |||
m_img = IMG_Load(pathlist[i].C()); | |||
if (m_img) | |||
m_surface = IMG_Load(pathlist[i].C()); | |||
if (m_surface) | |||
break; | |||
} | |||
if (!m_img) | |||
if (!m_surface) | |||
{ | |||
#if !LOL_BUILD_RELEASE | |||
Log::Error("could not load image %s\n", path); | |||
@@ -74,42 +72,37 @@ bool SdlImageData::Open(char const *path) | |||
return false; | |||
} | |||
m_size = ivec2(m_img->w, m_img->h); | |||
if (m_img->format->BytesPerPixel != 4) | |||
if (m_surface->format->BytesPerPixel != 4) | |||
{ | |||
SDL_Surface *tmp = Create32BppSurface(m_size); | |||
SDL_BlitSurface(m_img, nullptr, tmp, nullptr); | |||
SDL_FreeSurface(m_img); | |||
m_img = tmp; | |||
SDL_Surface *tmp = Create32BppSurface(image->GetSize()); | |||
SDL_BlitSurface(m_surface, nullptr, tmp, nullptr); | |||
SDL_FreeSurface(m_surface); | |||
m_surface = tmp; | |||
} | |||
m_format = m_img->format->Amask ? PixelFormat::RGBA_8 | |||
: PixelFormat::RGB_8; | |||
image->SetSize(ivec2(m_surface->w, m_surface->h)); | |||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||
memcpy(data, m_surface->pixels, 4 * m_surface->w * m_surface->h); | |||
image->Unlock(); | |||
return true; | |||
} | |||
bool SdlImageData::Save(char const *path) | |||
bool SdlImageCodec::Save(Image *image, char const *path) | |||
{ | |||
int ret = SDL_SaveBMP(m_img, path); | |||
int ret = SDL_SaveBMP(m_surface, path); | |||
return ret == 0; | |||
} | |||
bool SdlImageData::Close() | |||
bool SdlImageCodec::Close() | |||
{ | |||
SDL_FreeSurface(m_img); | |||
SDL_FreeSurface(m_surface); | |||
return true; | |||
} | |||
uint8_t * SdlImageData::GetData() const | |||
{ | |||
return (uint8_t *)m_img->pixels; | |||
} | |||
SDL_Surface *SdlImageData::Create32BppSurface(ivec2 size) | |||
SDL_Surface *SdlImageCodec::Create32BppSurface(ivec2 size) | |||
{ | |||
uint32_t rmask, gmask, bmask, amask; | |||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN | |||
@@ -25,11 +25,11 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(ZedImageData, 0) | |||
DECLARE_IMAGE_CODEC(ZedImageCodec, 0) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
@@ -51,7 +51,7 @@ private: | |||
* Public Image class | |||
*/ | |||
bool ZedImageData::Open(char const *path) | |||
bool ZedImageCodec::Load(Image *image, char const *path) | |||
{ | |||
if (!lol::String(path).EndsWith(".RSC")) | |||
return false; | |||
@@ -239,10 +239,8 @@ bool ZedImageData::Open(char const *path) | |||
tex_size <<= 1; | |||
//Prepare final imqge | |||
m_size = ivec2(tex_size); | |||
m_format = PixelFormat::Y_8; | |||
m_pixels = new uint8_t[tex_size * tex_size * 1 * sizeof(*m_pixels)]; | |||
uint8_t *image = m_pixels; | |||
image->SetSize(ivec2(tex_size)); | |||
uint8_t *pixels = image->Lock<PixelFormat::Y_8>(); | |||
//Data refactor stage | |||
ivec2 pos = ivec2(0); | |||
@@ -278,7 +276,7 @@ bool ZedImageData::Open(char const *path) | |||
for (int x_cur = 0; x_cur < t_size.x / 4; x_cur++) | |||
{ | |||
int32_t img_pos = img_off + pass + 4 * x_cur + y_cur * (int32_t)tex_size; | |||
image[img_pos] = file_convert[file_off++]; | |||
pixels[img_pos] = file_convert[file_off++]; | |||
} | |||
} | |||
} | |||
@@ -297,24 +295,24 @@ bool ZedImageData::Open(char const *path) | |||
j++; | |||
} | |||
} | |||
image->Unlock(); | |||
return true; | |||
} | |||
bool ZedImageData::Save(char const *path) | |||
bool ZedImageCodec::Save(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
/* FIXME: do we need to implement this? */ | |||
return true; | |||
} | |||
bool ZedImageData::Close() | |||
bool ZedImageCodec::Close() | |||
{ | |||
delete[] m_pixels; | |||
return true; | |||
} | |||
uint8_t * ZedImageData::GetData() const | |||
uint8_t * ZedImageCodec::GetData() const | |||
{ | |||
return m_pixels; | |||
} | |||
@@ -24,24 +24,19 @@ namespace lol | |||
* Image implementation class | |||
*/ | |||
DECLARE_IMAGE_LOADER(ZedPaletteImageData, 0) | |||
DECLARE_IMAGE_CODEC(ZedPaletteImageCodec, 0) | |||
{ | |||
public: | |||
virtual bool Open(char const *); | |||
virtual bool Save(char const *); | |||
virtual bool Load(Image *image, char const *path); | |||
virtual bool Save(Image *image, char const *path); | |||
virtual bool Close(); | |||
virtual uint8_t *GetData() const; | |||
private: | |||
uint8_t *m_pixels; | |||
}; | |||
/* | |||
* Public Image class | |||
*/ | |||
bool ZedPaletteImageData::Open(char const *path) | |||
bool ZedPaletteImageCodec::Load(Image *image, char const *path) | |||
{ | |||
if (!lol::String(path).EndsWith(".pal")) | |||
return false; | |||
@@ -61,47 +56,40 @@ bool ZedPaletteImageData::Open(char const *path) | |||
int32_t tex_size = 2; | |||
while (tex_size < tex_sqrt) | |||
tex_size <<= 1; | |||
m_size = ivec2(tex_size); | |||
image->m_data->m_size = ivec2(tex_size); | |||
#else | |||
int32_t tex_sqrt = file_size / 3; | |||
int32_t tex_size = 2; | |||
while (tex_size < tex_sqrt) | |||
tex_size <<= 1; | |||
m_size = ivec2(tex_size, 1); | |||
image->SetSize(ivec2(tex_size, 1)); | |||
#endif | |||
m_format = PixelFormat::RGBA_8; | |||
m_pixels = new uint8_t[tex_size * tex_size * 4 * sizeof(*m_pixels)]; | |||
uint8_t *parser = m_pixels; | |||
u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>(); | |||
for (int i = 0; i < file_buffer.Count();) | |||
{ | |||
*parser++ = file_buffer[i++]; | |||
*parser++ = file_buffer[i++]; | |||
*parser++ = file_buffer[i++]; | |||
*parser++ = (i == 0) ? 0 : 255; | |||
pixels->r = file_buffer[i++]; | |||
pixels->g = file_buffer[i++]; | |||
pixels->b = file_buffer[i++]; | |||
pixels->a = (i == 0) ? 0 : 255; | |||
++pixels; | |||
} | |||
image->Unlock(); | |||
return true; | |||
} | |||
bool ZedPaletteImageData::Save(char const *path) | |||
bool ZedPaletteImageCodec::Save(Image *image, char const *path) | |||
{ | |||
UNUSED(path); | |||
/* FIXME: do we need to implement this? */ | |||
return true; | |||
} | |||
bool ZedPaletteImageData::Close() | |||
bool ZedPaletteImageCodec::Close() | |||
{ | |||
delete[] m_pixels; | |||
return true; | |||
} | |||
uint8_t * ZedPaletteImageData::GetData() const | |||
{ | |||
return m_pixels; | |||
} | |||
} /* namespace lol */ | |||
@@ -9,8 +9,8 @@ | |||
// | |||
// | |||
// The ImageData class | |||
// ------------------- | |||
// The ImageCodecData class | |||
// ------------------------ | |||
// | |||
#if !defined __LOL_IMAGE_PRIVATE_H__ | |||
@@ -19,103 +19,114 @@ | |||
namespace lol | |||
{ | |||
class ImageData | |||
{ | |||
friend class Image; | |||
friend class ImageBank; | |||
friend class ImageCodec; | |||
public: | |||
ImageData() | |||
: m_size(0, 0), | |||
m_format(PixelFormat::Unknown), | |||
m_refcount(0), | |||
m_codecdata(nullptr) | |||
{} | |||
ivec2 m_size; | |||
Map<int, void *> m_pixels; | |||
PixelFormat m_format; | |||
int m_refcount; | |||
/* protected: */ /* FIXME: fix the ImageCodec subclasses access rights */ | |||
class ImageCodecData *m_codecdata; | |||
}; | |||
class ImageCodec | |||
{ | |||
friend class ImageBank; | |||
friend class ImageData; | |||
friend class ImageCodecData; | |||
public: | |||
ImageData *(*fun)(char const *path); | |||
ImageCodec *next; | |||
int priority; | |||
bool (*m_tryload)(Image *image, char const *path); | |||
ImageCodec *m_next; | |||
int m_priority; | |||
static void RegisterLoader(ImageCodec *loader) | |||
static void RegisterCodec(ImageCodec *codec) | |||
{ | |||
Helper(loader); | |||
Helper(codec); | |||
} | |||
private: | |||
static ImageData *Load(char const *path) | |||
static void Load(Image *image, char const *path) | |||
{ | |||
ImageCodec *parser = Helper(nullptr); | |||
ImageData *ret = nullptr; | |||
ImageCodec *codec = Helper(nullptr); | |||
bool success = false; | |||
while (parser && !ret) | |||
while (codec && !success) | |||
{ | |||
ret = parser->fun(path); | |||
parser = parser->next; | |||
success = codec->m_tryload(image, path); | |||
codec = codec->m_next; | |||
} | |||
return ret; | |||
} | |||
static ImageCodec *Helper(ImageCodec *set) | |||
{ | |||
static ImageCodec *loaders = nullptr; | |||
static ImageCodec *codec_list = nullptr; | |||
if (!set) | |||
return loaders; | |||
return codec_list; | |||
ImageCodec **parser = &loaders; | |||
while (*parser && (*parser)->priority > set->priority) | |||
parser = &(*parser)->next; | |||
set->next = *parser; | |||
*parser = set; | |||
ImageCodec **codec = &codec_list; | |||
while (*codec && (*codec)->m_priority > set->m_priority) | |||
codec = &(*codec)->m_next; | |||
set->m_next = *codec; | |||
*codec = set; | |||
return nullptr; | |||
} | |||
}; | |||
class ImageData | |||
/* | |||
* Codec-specific data that is stored in some images | |||
*/ | |||
class ImageCodecData | |||
{ | |||
friend class Image; | |||
friend class ImageBank; | |||
public: | |||
inline ImageData() | |||
: m_size(0, 0), | |||
m_format(PixelFormat::Unknown), | |||
m_refcount(0) | |||
{ } | |||
virtual ~ImageData() {} | |||
inline ImageCodecData() {} | |||
virtual ~ImageCodecData() {} | |||
virtual bool Open(char const *) = 0; | |||
virtual bool Save(char const *) = 0; | |||
virtual bool Close() = 0; | |||
virtual bool Load(Image *, char const *) = 0; | |||
virtual bool Save(Image *, char const *) = 0; | |||
virtual uint8_t *GetData() const = 0; | |||
virtual bool RetrieveTiles(Array<ivec2, ivec2>& tiles) { return false; } | |||
protected: | |||
ivec2 m_size; | |||
PixelFormat m_format; | |||
int m_refcount; | |||
}; | |||
#define REGISTER_IMAGE_LOADER(name) \ | |||
#define REGISTER_IMAGE_CODEC(name) \ | |||
extern void Register##name(); \ | |||
Register##name(); | |||
#define DECLARE_IMAGE_LOADER(name, prio) \ | |||
#define DECLARE_IMAGE_CODEC(name, prio) \ | |||
template<typename T> class name##ImageCodec : public ImageCodec \ | |||
{ \ | |||
public: \ | |||
name##ImageCodec() \ | |||
{ \ | |||
static ImageCodec loader; \ | |||
loader.fun = Load; \ | |||
loader.priority = prio; \ | |||
RegisterLoader(&loader); \ | |||
static ImageCodec codec; \ | |||
codec.m_tryload = Load; \ | |||
codec.m_priority = prio; \ | |||
RegisterCodec(&codec); \ | |||
} \ | |||
static ImageData *Load(char const *path) \ | |||
static bool Load(Image *image, char const *path) \ | |||
{ \ | |||
T *ret = new T(); \ | |||
if (!ret->Open(path)) \ | |||
{ \ | |||
delete ret; \ | |||
return nullptr; \ | |||
} \ | |||
T *codecdata = new T(); \ | |||
bool ret = codecdata->Load(image, path); \ | |||
delete codecdata; \ | |||
return ret; \ | |||
} \ | |||
}; \ | |||
@@ -125,7 +136,7 @@ protected: | |||
{ \ | |||
(void)&name##ImageCodecInstance; \ | |||
} \ | |||
class name : public ImageData | |||
class name : public ImageCodecData | |||
} /* namespace lol */ | |||
@@ -21,7 +21,7 @@ namespace lol | |||
{ | |||
/* HACK: We cannot make this an ImageCodec member function, because the | |||
* REGISTER_IMAGE_LOADER macro forward-declares free functions from | |||
* REGISTER_IMAGE_CODEC macro forward-declares free functions from | |||
* the "lol" namespace. An apparent bug in Visual Studio's compiler | |||
* makes it think these functions are actually in the top-level | |||
* namespace when the forward declaration is in a class member function. | |||
@@ -30,26 +30,26 @@ namespace lol | |||
* The bug was reported to Microsoft and fixed by them, but the fix | |||
* is not yet available. | |||
* https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ | |||
static bool RegisterAllLoaders() | |||
static bool RegisterAllCodecs() | |||
{ | |||
#if defined __ANDROID__ | |||
REGISTER_IMAGE_LOADER(AndroidImageData) | |||
REGISTER_IMAGE_CODEC(AndroidImageCodec) | |||
#endif | |||
REGISTER_IMAGE_LOADER(DummyImageData) | |||
REGISTER_IMAGE_CODEC(DummyImageCodec) | |||
#if defined USE_GDIPLUS | |||
REGISTER_IMAGE_LOADER(GdiPlusImageData) | |||
REGISTER_IMAGE_CODEC(GdiPlusImageCodec) | |||
#endif | |||
#if defined __APPLE__ && defined __MACH__ && defined __arm__ | |||
REGISTER_IMAGE_LOADER(IosImageData) | |||
REGISTER_IMAGE_CODEC(IosImageCodec) | |||
#endif | |||
#if defined __CELLOS_LV2__ | |||
REGISTER_IMAGE_LOADER(Ps3ImageData) | |||
REGISTER_IMAGE_CODEC(Ps3ImageCodec) | |||
#endif | |||
#if defined USE_SDL_IMAGE | |||
REGISTER_IMAGE_LOADER(SdlImageData) | |||
REGISTER_IMAGE_CODEC(SdlImageCodec) | |||
#endif | |||
REGISTER_IMAGE_LOADER(ZedImageData) | |||
REGISTER_IMAGE_LOADER(ZedPaletteImageData) | |||
REGISTER_IMAGE_CODEC(ZedImageCodec) | |||
REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) | |||
return true; | |||
} | |||
@@ -63,8 +63,6 @@ static class ImageBank | |||
public: | |||
void Init(); | |||
Image *Create(char const *path); | |||
bool Store(Image *img); | |||
Image *Load(char const *path); | |||
void Unref(Image *img); | |||
@@ -75,51 +73,27 @@ g_image_bank; | |||
void ImageBank::Init() | |||
{ | |||
/* Initialise loaders (see above) */ | |||
static bool init = RegisterAllLoaders(); | |||
/* Initialise codecs (see above) */ | |||
static bool init = RegisterAllCodecs(); | |||
UNUSED(init); | |||
} | |||
Image *ImageBank::Create(char const *path) | |||
{ | |||
/* Is our image already in the bank? If so, no need to create it. */ | |||
Image *img; | |||
Init(); | |||
if (m_images.HasKey(path)) | |||
{ | |||
img = m_images[path]; | |||
} | |||
else | |||
/* Is our image already in the bank? If so, no need to create it. */ | |||
if (!m_images.HasKey(path)) | |||
{ | |||
img = Load(path); | |||
m_images[path] = img; | |||
m_images[path] = new Image(); | |||
ImageCodec::Load(m_images[path], path); | |||
} | |||
Image *img = m_images[path]; | |||
++img->m_data->m_refcount; | |||
return img; | |||
} | |||
Image *ImageBank::Load(char const *path) | |||
{ | |||
Init(); | |||
Image *img = new Image(path); | |||
img->m_data = ImageCodec::Load(img->GetPath().C()); | |||
return img; | |||
} | |||
bool ImageBank::Store(Image *img) | |||
{ | |||
/* Is our image already in the bank? If so, no need to create it. */ | |||
if (m_images.HasKey(img->GetPath().C())) | |||
return false; | |||
m_images[img->GetPath().C()] = img; | |||
++img->m_data->m_refcount; | |||
return true; | |||
} | |||
void ImageBank::Unref(Image *img) | |||
{ | |||
ASSERT(img->m_data->m_refcount > 0); | |||
@@ -145,24 +119,19 @@ Image *Image::Create(char const *path) | |||
return g_image_bank.Create(path); | |||
} | |||
Image *Image::Load(char const *path) | |||
{ | |||
return g_image_bank.Load(path); | |||
} | |||
bool Image::Store(Image *img) | |||
{ | |||
return g_image_bank.Store(img); | |||
} | |||
/* | |||
* Public Image class | |||
*/ | |||
Image::Image(char const* path) | |||
: m_data(nullptr) | |||
Image::Image() | |||
: m_data(new ImageData()) | |||
{ | |||
m_path = path; | |||
} | |||
Image::~Image() | |||
{ | |||
delete m_data->m_codecdata; | |||
delete m_data; | |||
} | |||
void Image::Destroy() | |||
@@ -172,7 +141,10 @@ void Image::Destroy() | |||
bool Image::Save(char const *path) | |||
{ | |||
return m_data->Save(path); | |||
/* FIXME: add autoloading of save codecs */ | |||
if (!m_data->m_codecdata) | |||
return false; | |||
return m_data->m_codecdata->Save(this, path); | |||
} | |||
ivec2 Image::GetSize() const | |||
@@ -180,30 +152,73 @@ ivec2 Image::GetSize() const | |||
return m_data->m_size; | |||
} | |||
void Image::SetSize(ivec2 size) | |||
{ | |||
if (m_data->m_size != size) | |||
{ | |||
/* FIXME: delete data or resize it */ | |||
if (m_data->m_format != PixelFormat::Unknown) | |||
{ | |||
m_data->m_pixels.Remove((int)m_data->m_format); | |||
} | |||
} | |||
m_data->m_size = size; | |||
/* FIXME: don’t do this! */ | |||
if (m_data->m_format != PixelFormat::Unknown) | |||
{ | |||
Lock<PixelFormat::RGBA_8>(); | |||
Unlock(); | |||
} | |||
} | |||
PixelFormat Image::GetFormat() const | |||
{ | |||
return m_data->m_format; | |||
} | |||
uint8_t *Image::GetData() const | |||
void *Image::LockGeneric() | |||
{ | |||
return m_data->GetData(); | |||
ASSERT(m_data->m_format != PixelFormat::Unknown); | |||
return m_data->m_pixels[(int)m_data->m_format]; | |||
} | |||
String Image::GetPath() const | |||
/* The Lock() method and its explicit specialisations */ | |||
template<PixelFormat T> | |||
typename PixelType<T>::type *Image::Lock() | |||
{ | |||
return m_path; | |||
/* TODO: convert data if this doesn’t match */ | |||
ASSERT(m_data->m_format == T || m_data->m_format == PixelFormat::Unknown); | |||
m_data->m_format = (PixelFormat)T; | |||
if (!m_data->m_pixels.HasKey((int)T)) | |||
{ | |||
m_data->m_pixels[(int)T] = | |||
new typename PixelType<T>::type(m_data->m_size.x * m_data->m_size.y); | |||
} | |||
return (typename PixelType<T>::type *)m_data->m_pixels[(int)T]; | |||
} | |||
bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | |||
template typename PixelType<PixelFormat::Y_8>::type * | |||
Image::Lock<PixelFormat::Y_8>(); | |||
template typename PixelType<PixelFormat::RGB_8>::type * | |||
Image::Lock<PixelFormat::RGB_8>(); | |||
template typename PixelType<PixelFormat::RGBA_8>::type * | |||
Image::Lock<PixelFormat::RGBA_8>(); | |||
void Image::Unlock() | |||
{ | |||
return m_data->RetrieveTiles(tiles); | |||
/* TODO: ensure we are actually unlocking something we locked */ | |||
ASSERT(m_data->m_pixels.HasKey((int)m_data->m_format)); | |||
} | |||
Image::~Image() | |||
bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | |||
{ | |||
m_data->Close(); | |||
delete m_data; | |||
if (!m_data->m_codecdata) | |||
return false; | |||
return m_data->m_codecdata->RetrieveTiles(tiles); | |||
} | |||
} /* namespace lol */ | |||
@@ -19,38 +19,30 @@ | |||
namespace lol | |||
{ | |||
struct PixelFormat | |||
enum class PixelFormat | |||
{ | |||
/* XXX: make sure to update texture.cpp when this changes */ | |||
enum Value | |||
{ | |||
Unknown = 0, | |||
RGB_8, | |||
RGBA_8, | |||
ARGB_8, | |||
ABGR_8, | |||
Y_8, | |||
} | |||
m_value; | |||
inline PixelFormat() : m_value(Unknown) {} | |||
inline PixelFormat(Value v) : m_value(v) {} | |||
inline operator Value() { return m_value; } | |||
Unknown = 0, | |||
RGB_8, | |||
RGBA_8, | |||
ARGB_8, | |||
ABGR_8, | |||
Y_8, | |||
}; | |||
inline uint8_t BytesPerPixel() | |||
static inline uint8_t BytesPerPixel(PixelFormat format) | |||
{ | |||
switch (format) | |||
{ | |||
switch (m_value) | |||
{ | |||
case Y_8: | |||
return 1; | |||
case RGB_8: | |||
return 3; | |||
case RGBA_8: | |||
case ARGB_8: | |||
case ABGR_8: | |||
default: | |||
return 4; | |||
} | |||
case PixelFormat::Y_8: | |||
return 1; | |||
case PixelFormat::RGB_8: | |||
return 3; | |||
case PixelFormat::RGBA_8: | |||
case PixelFormat::ARGB_8: | |||
case PixelFormat::ABGR_8: | |||
default: | |||
return 4; | |||
} | |||
}; | |||
@@ -21,31 +21,37 @@ | |||
namespace lol | |||
{ | |||
/* FIXME: is it possible to avoid the cast to int here? */ | |||
template <PixelFormat T> struct PixelType { typedef void type; }; | |||
template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; }; | |||
template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; }; | |||
template<> struct PixelType<PixelFormat::RGBA_8> { typedef u8vec4 type; }; | |||
class Image | |||
{ | |||
friend class ImageBank; | |||
friend class ImageCodec; | |||
public: | |||
//Create/Load/Store image into bank. THREAD: NOT SAFE | |||
static Image* Create(char const *path); | |||
//Create/Load image into bank. THREAD: SAFE | |||
static Image* Load(char const *path); | |||
//Store image into bank. THREAD: NOT SAFE | |||
static bool Store(Image *img); | |||
Image(); | |||
~Image(); | |||
static Image *Create(char const *path); | |||
bool Save(char const *path); | |||
void Destroy(); | |||
ivec2 GetSize() const; | |||
PixelFormat GetFormat() const; | |||
uint8_t *GetData() const; | |||
String GetPath() const; | |||
void *LockGeneric(); | |||
template<PixelFormat T> typename PixelType<T>::type *Lock(); | |||
void Unlock(); | |||
ivec2 GetSize() const; | |||
void SetSize(ivec2); | |||
bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const; | |||
private: | |||
Image(char const* path); | |||
~Image(); | |||
class ImageData *m_data; | |||
String m_path; | |||
}; | |||
@@ -202,12 +202,13 @@ void TileSet::TickDraw(float seconds) | |||
else if (m_data->m_image) | |||
{ | |||
PixelFormat format = m_data->m_image->GetFormat(); | |||
int planes = format.BytesPerPixel(); | |||
int planes = BytesPerPixel(format); | |||
int w = m_data->m_texture_size.x; | |||
int h = m_data->m_texture_size.y; | |||
uint8_t *pixels = m_data->m_image->GetData(); | |||
uint8_t *pixels = (uint8_t *)m_data->m_image->LockGeneric(); | |||
bool resized = false; | |||
if (w != m_data->m_image_size.x || h != m_data->m_image_size.y) | |||
{ | |||
uint8_t *tmp = new uint8_t[planes * w * h]; | |||
@@ -216,12 +217,13 @@ void TileSet::TickDraw(float seconds) | |||
pixels + planes * m_data->m_image_size.x * line, | |||
planes * m_data->m_image_size.x); | |||
pixels = tmp; | |||
resized = false; | |||
} | |||
m_data->m_texture = new Texture(ivec2(w, h), format); | |||
m_data->m_texture->SetData(pixels); | |||
if (pixels != m_data->m_image->GetData()) | |||
if (resized) | |||
delete[] pixels; | |||
m_data->m_image->Destroy(); | |||
m_data->m_image = nullptr; | |||
@@ -30,17 +30,18 @@ LOLUNIT_FIXTURE(ImageTest) | |||
LOLUNIT_ASSERT_EQUAL(size.x, 256); | |||
LOLUNIT_ASSERT_EQUAL(size.y, 16); | |||
uint8_t *data = image->GetData(); | |||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||
LOLUNIT_ASSERT(data); | |||
LOLUNIT_ASSERT_EQUAL((int)data[0], 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[1], 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[2], 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[0].r, 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[0].g, 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[0].b, 0x00); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 0], 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 1], 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 2], 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255].r, 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | |||
image->Unlock(); | |||
image->Destroy(); | |||
} | |||
}; | |||