objects themselves.undefined
@@ -35,7 +35,7 @@ extern ANativeActivity *g_activity; | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(AndroidImageCodec, 100) | class AndroidImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
@@ -50,6 +50,8 @@ private: | |||||
jint *pixels; | jint *pixels; | ||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(AndroidImageCodec, 100) | |||||
bool AndroidImageCodec::Load(Image *image, char const *path) | bool AndroidImageCodec::Load(Image *image, char const *path) | ||||
{ | { | ||||
JNIEnv *env; | JNIEnv *env; | ||||
@@ -24,14 +24,15 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(DummyImageCodec, 0) | class DummyImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(Image *image, char const *path); | virtual bool Save(Image *image, char const *path); | ||||
virtual bool Close(); | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(DummyImageCodec, 0) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -63,10 +64,5 @@ bool DummyImageCodec::Save(Image *image, char const *path) | |||||
return true; | return true; | ||||
} | } | ||||
bool DummyImageCodec::Close() | |||||
{ | |||||
return true; | |||||
} | |||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -31,20 +31,15 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100) | class GdiPlusImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(Image *image, char const *path); | virtual bool Save(Image *image, char const *path); | ||||
virtual bool Close(); | |||||
virtual uint8_t *GetData() const; | |||||
private: | |||||
Gdiplus::Bitmap *m_bitmap; | |||||
Gdiplus::BitmapData m_bdata; | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -64,7 +59,7 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) | |||||
} | } | ||||
Array<String> pathlist = System::GetPathList(path); | Array<String> pathlist = System::GetPathList(path); | ||||
m_bitmap = nullptr; | Gdiplus::Bitmap *bitmap = nullptr; | ||||
for (auto fullpath : pathlist) | for (auto fullpath : pathlist) | ||||
{ | { | ||||
size_t len; | size_t len; | ||||
@@ -80,11 +75,11 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) | |||||
} | } | ||||
status = Gdiplus::Ok; | status = Gdiplus::Ok; | ||||
m_bitmap = Gdiplus::Bitmap::FromFile(wpath, 0); | bitmap = Gdiplus::Bitmap::FromFile(wpath, 0); | ||||
if (m_bitmap) | if (bitmap) | ||||
{ | { | ||||
status = m_bitmap->GetLastStatus(); | status = bitmap->GetLastStatus(); | ||||
if (status != Gdiplus::Ok) | if (status != Gdiplus::Ok) | ||||
{ | { | ||||
#if !LOL_BUILD_RELEASE | #if !LOL_BUILD_RELEASE | ||||
@@ -92,17 +87,17 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) | |||||
Log::Error("error %d loading %s\n", | Log::Error("error %d loading %s\n", | ||||
status, fullpath.C()); | status, fullpath.C()); | ||||
#endif | #endif | ||||
delete m_bitmap; | delete bitmap; | ||||
m_bitmap = nullptr; | bitmap = nullptr; | ||||
} | } | ||||
} | } | ||||
delete[] wpath; | delete[] wpath; | ||||
if (m_bitmap) | if (bitmap) | ||||
break; | break; | ||||
} | } | ||||
if (!m_bitmap) | if (!bitmap) | ||||
{ | { | ||||
#if !LOL_BUILD_RELEASE | #if !LOL_BUILD_RELEASE | ||||
Log::Error("could not load %s\n", path); | Log::Error("could not load %s\n", path); | ||||
@@ -110,33 +105,32 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) | |||||
return false; | return false; | ||||
} | } | ||||
image->m_data->m_size = ivec2(m_bitmap->GetWidth(), | ivec2 size(bitmap->GetWidth(), bitmap->GetHeight()); | ||||
m_bitmap->GetHeight()); | Gdiplus::Rect rect(0, 0, size.x, size.y); | ||||
image->m_data->m_format = PixelFormat::RGBA_8; | Gdiplus::BitmapData bdata; | ||||
if (bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, | |||||
Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y); | PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) | ||||
if (m_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, | |||||
PixelFormat32bppARGB, &m_bdata) != Gdiplus::Ok) | |||||
{ | { | ||||
#if !LOL_BUILD_RELEASE | #if !LOL_BUILD_RELEASE | ||||
Log::Error("could not lock bits in %s\n", path); | Log::Error("could not lock bits in %s\n", path); | ||||
#endif | #endif | ||||
delete m_bitmap; | delete bitmap; | ||||
return false; | return false; | ||||
} | } | ||||
/* FIXME: GDI+ doesn't know about RGBA, only ARGB. And OpenGL doesn't | /* FIXME: GDI+ doesn't know about RGBA, only ARGB. And OpenGL doesn't | ||||
* know about ARGB, only RGBA. So we swap bytes. We could also fix | * know about ARGB, only RGBA. So we swap bytes. We could also fix | ||||
* this in the shader. */ | * this in the shader. */ | ||||
uint8_t *p = static_cast<uint8_t *>(m_bdata.Scan0); | image->SetSize(size); | ||||
for (int y = 0; y < image->m_data->m_size.y; y++) | u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>(); | ||||
for (int x = 0; x < image->m_data->m_size.x; x++) | u8vec4 *source = static_cast<u8vec4 *>(bdata.Scan0); | ||||
{ | for (int y = 0; y < size.y; y++) | ||||
uint8_t tmp = p[2]; | for (int x = 0; x < size.x; x++) | ||||
p[2] = p[0]; | *pixels++ = (*source++).bgra; | ||||
p[0] = tmp; | image->Unlock(); | ||||
p += 4; | bitmap->UnlockBits(&bdata); | ||||
} | delete bitmap; | ||||
return true; | return true; | ||||
} | } | ||||
@@ -159,16 +153,16 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||||
else /* if (strstr(path, ".bmp")) */ | else /* if (strstr(path, ".bmp")) */ | ||||
fmt = L"image/bmp"; | fmt = L"image/bmp"; | ||||
unsigned int num = 0, size = 0; | unsigned int num = 0, encoder_size = 0; | ||||
Gdiplus::GetImageEncodersSize(&num, &size); | Gdiplus::GetImageEncodersSize(&num, &encoder_size); | ||||
if (num == 0) | if (num == 0) | ||||
{ | { | ||||
Log::Error("no GDI+ image encoders found\n"); | Log::Error("no GDI+ image encoders found\n"); | ||||
return false; | return false; | ||||
} | } | ||||
Gdiplus::ImageCodecInfo *codecs | Gdiplus::ImageCodecInfo *codecs | ||||
= (Gdiplus::ImageCodecInfo *)new uint8_t[size]; | = (Gdiplus::ImageCodecInfo *)new uint8_t[encoder_size]; | ||||
Gdiplus::GetImageEncoders(num, size, codecs); | Gdiplus::GetImageEncoders(num, encoder_size, codecs); | ||||
CLSID clsid; | CLSID clsid; | ||||
for (unsigned int i = 0; i < num; i++) | for (unsigned int i = 0; i < num; i++) | ||||
{ | { | ||||
@@ -190,15 +184,16 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||||
return false; | return false; | ||||
} | } | ||||
ivec2 size = image->GetSize(); | |||||
/* FIXME: we create a new image because there is no guarantee that | /* FIXME: we create a new image because there is no guarantee that | ||||
* the image comes from GDI. But the engine architecture doesn't | * the image comes from GDI. But the engine architecture doesn't | ||||
* allow us to save other images anyway. */ | * allow us to save other images anyway. */ | ||||
Gdiplus::Bitmap *b = new Gdiplus::Bitmap(image->m_data->m_size.x, | Gdiplus::Bitmap *b = new Gdiplus::Bitmap(size.x, size.y, | ||||
image->m_data->m_size.y, | |||||
PixelFormat32bppARGB); | PixelFormat32bppARGB); | ||||
Gdiplus::BitmapData bdata; | Gdiplus::BitmapData bdata; | ||||
Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y); | Gdiplus::Rect rect(0, 0, size.x, size.y); | ||||
if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite, | if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite, | ||||
PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) | PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) | ||||
@@ -210,14 +205,12 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||||
return false; | return false; | ||||
} | } | ||||
u8vec4 *psrc = static_cast<u8vec4 *>(m_bdata.Scan0); | u8vec4 *psrc = image->Lock<PixelFormat::RGBA_8>(); | ||||
u8vec4 *pdst = static_cast<u8vec4 *>(bdata.Scan0); | u8vec4 *pdst = static_cast<u8vec4 *>(bdata.Scan0); | ||||
for (int y = 0; y < image->m_data->m_size.y; y++) | for (int y = 0; y < size.y; y++) | ||||
for (int x = 0; x < image->m_data->m_size.x; x++) | for (int x = 0; x < size.x; x++) | ||||
{ | |||||
*pdst++ = (*psrc++).bgra; | *pdst++ = (*psrc++).bgra; | ||||
} | image->Unlock(); | ||||
b->UnlockBits(&bdata); | b->UnlockBits(&bdata); | ||||
if (b->Save(wpath, &clsid, NULL) != Gdiplus::Ok) | if (b->Save(wpath, &clsid, NULL) != Gdiplus::Ok) | ||||
@@ -235,19 +228,6 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||||
return true; | return true; | ||||
} | } | ||||
bool GdiPlusImageCodec::Close() | |||||
{ | |||||
m_bitmap->UnlockBits(&m_bdata); | |||||
delete m_bitmap; | |||||
return true; | |||||
} | |||||
uint8_t * GdiPlusImageCodec::GetData() const | |||||
{ | |||||
return static_cast<uint8_t *>(m_bdata.Scan0); | |||||
} | |||||
} /* namespace lol */ | } /* namespace lol */ | ||||
#endif /* defined USE_GDIPLUS */ | #endif /* defined USE_GDIPLUS */ | ||||
@@ -28,19 +28,15 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(IosImageCodec, 100) | class IosImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(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; | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(IosImageCodec, 100) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -30,7 +30,7 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(Ps3ImageCodec, 100) | class Ps3ImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
@@ -42,9 +42,10 @@ public: | |||||
private: | private: | ||||
static void* Malloc(uint32_t size, void* data) { return malloc(size); }; | static void* Malloc(uint32_t size, void* data) { return malloc(size); }; | ||||
static int32_t Free(void* ptr, void* data) { free(ptr); return 0; }; | static int32_t Free(void* ptr, void* data) { free(ptr); return 0; }; | ||||
uint8_t *pixels; | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(Ps3ImageCodec, 100) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -37,34 +37,34 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(SdlImageCodec, 50) | class SdlImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(Image *image, char const *path); | virtual bool Save(Image *image, char const *path); | ||||
virtual bool Close(); | |||||
static SDL_Surface *Create32BppSurface(ivec2 size); | static SDL_Surface *Create32BppSurface(ivec2 size); | ||||
private: | |||||
SDL_Surface *m_surface; | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(SdlImageCodec, 50) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
bool SdlImageCodec::Load(Image *image, char const *path) | bool SdlImageCodec::Load(Image *image, char const *path) | ||||
{ | { | ||||
SDL_Surface *surface = nullptr; | |||||
Array<String> pathlist = System::GetPathList(path); | Array<String> pathlist = System::GetPathList(path); | ||||
for (int i = 0; i < pathlist.Count(); i++) | for (int i = 0; i < pathlist.Count(); i++) | ||||
{ | { | ||||
m_surface = IMG_Load(pathlist[i].C()); | surface = IMG_Load(pathlist[i].C()); | ||||
if (m_surface) | if (surface) | ||||
break; | break; | ||||
} | } | ||||
if (!m_surface) | if (!surface) | ||||
{ | { | ||||
#if !LOL_BUILD_RELEASE | #if !LOL_BUILD_RELEASE | ||||
Log::Error("could not load image %s\n", path); | Log::Error("could not load image %s\n", path); | ||||
@@ -72,34 +72,39 @@ bool SdlImageCodec::Load(Image *image, char const *path) | |||||
return false; | return false; | ||||
} | } | ||||
if (m_surface->format->BytesPerPixel != 4) | ivec2 size(surface->w, surface->h); | ||||
if (surface->format->BytesPerPixel != 4) | |||||
{ | { | ||||
SDL_Surface *tmp = Create32BppSurface(image->GetSize()); | SDL_Surface *tmp = Create32BppSurface(size); | ||||
SDL_BlitSurface(m_surface, nullptr, tmp, nullptr); | SDL_BlitSurface(surface, nullptr, tmp, nullptr); | ||||
SDL_FreeSurface(m_surface); | SDL_FreeSurface(surface); | ||||
m_surface = tmp; | surface = tmp; | ||||
} | } | ||||
image->SetSize(ivec2(m_surface->w, m_surface->h)); | image->SetSize(size); | ||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | ||||
memcpy(data, m_surface->pixels, 4 * m_surface->w * m_surface->h); | memcpy(data, surface->pixels, 4 * size.x * size.y); | ||||
image->Unlock(); | image->Unlock(); | ||||
SDL_FreeSurface(surface); | |||||
return true; | return true; | ||||
} | } | ||||
bool SdlImageCodec::Save(Image *image, char const *path) | bool SdlImageCodec::Save(Image *image, char const *path) | ||||
{ | { | ||||
int ret = SDL_SaveBMP(m_surface, path); | ivec2 size = image->GetSize(); | ||||
SDL_Surface *surface = Create32BppSurface(size); | |||||
return ret == 0; | u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | ||||
} | memcpy(surface->pixels, data, 4 * size.x * size.y); | ||||
image->Unlock(); | |||||
bool SdlImageCodec::Close() | int ret = SDL_SaveBMP(surface, path); | ||||
{ | SDL_FreeSurface(surface); | ||||
SDL_FreeSurface(m_surface); | |||||
return true; | return ret == 0; | ||||
} | } | ||||
SDL_Surface *SdlImageCodec::Create32BppSurface(ivec2 size) | SDL_Surface *SdlImageCodec::Create32BppSurface(ivec2 size) | ||||
@@ -25,14 +25,12 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(ZedImageCodec, 0) | class ZedImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(Image *image, char const *path); | virtual bool Save(Image *image, char const *path); | ||||
virtual bool Close(); | |||||
virtual uint8_t *GetData() const; | |||||
virtual bool RetrieveTiles(Array<ivec2, ivec2>& tiles) | virtual bool RetrieveTiles(Array<ivec2, ivec2>& tiles) | ||||
{ | { | ||||
bool result = m_tiles.Count() > 0; | bool result = m_tiles.Count() > 0; | ||||
@@ -47,6 +45,8 @@ private: | |||||
Array<ivec2, ivec2> m_tiles; | Array<ivec2, ivec2> m_tiles; | ||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(ZedImageCodec, 0) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -307,15 +307,5 @@ bool ZedImageCodec::Save(Image *image, char const *path) | |||||
return true; | return true; | ||||
} | } | ||||
bool ZedImageCodec::Close() | |||||
{ | |||||
return true; | |||||
} | |||||
uint8_t * ZedImageCodec::GetData() const | |||||
{ | |||||
return m_pixels; | |||||
} | |||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -24,14 +24,15 @@ namespace lol | |||||
* Image implementation class | * Image implementation class | ||||
*/ | */ | ||||
DECLARE_IMAGE_CODEC(ZedPaletteImageCodec, 0) | class ZedPaletteImageCodec : public ImageCodec | ||||
{ | { | ||||
public: | public: | ||||
virtual bool Load(Image *image, char const *path); | virtual bool Load(Image *image, char const *path); | ||||
virtual bool Save(Image *image, char const *path); | virtual bool Save(Image *image, char const *path); | ||||
virtual bool Close(); | |||||
}; | }; | ||||
DECLARE_IMAGE_CODEC(ZedPaletteImageCodec, 0) | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
*/ | */ | ||||
@@ -86,10 +87,5 @@ bool ZedPaletteImageCodec::Save(Image *image, char const *path) | |||||
return true; | return true; | ||||
} | } | ||||
bool ZedPaletteImageCodec::Close() | |||||
{ | |||||
return true; | |||||
} | |||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -22,121 +22,50 @@ namespace lol | |||||
class ImageData | class ImageData | ||||
{ | { | ||||
friend class Image; | friend class Image; | ||||
friend class ImageBank; | |||||
friend class ImageCodec; | |||||
public: | public: | ||||
ImageData() | ImageData() | ||||
: m_size(0, 0), | : m_size(0, 0), | ||||
m_format(PixelFormat::Unknown), | m_format(PixelFormat::Unknown) | ||||
m_refcount(0), | |||||
m_codecdata(nullptr) | |||||
{} | {} | ||||
ivec2 m_size; | ivec2 m_size; | ||||
Map<int, void *> m_pixels; | Map<int, void *> m_pixels; | ||||
PixelFormat m_format; | PixelFormat m_format; | ||||
int m_refcount; | |||||
/* protected: */ /* FIXME: fix the ImageCodec subclasses access rights */ | |||||
class ImageCodecData *m_codecdata; | |||||
}; | }; | ||||
class ImageCodec | class ImageCodec | ||||
{ | { | ||||
friend class ImageBank; | |||||
friend class ImageCodecData; | |||||
public: | |||||
bool (*m_tryload)(Image *image, char const *path); | |||||
ImageCodec *m_next; | |||||
int m_priority; | |||||
static void RegisterCodec(ImageCodec *codec) | |||||
{ | |||||
Helper(codec); | |||||
} | |||||
private: | |||||
static void Load(Image *image, char const *path) | |||||
{ | |||||
ImageCodec *codec = Helper(nullptr); | |||||
bool success = false; | |||||
while (codec && !success) | |||||
{ | |||||
success = codec->m_tryload(image, path); | |||||
codec = codec->m_next; | |||||
} | |||||
} | |||||
static ImageCodec *Helper(ImageCodec *set) | |||||
{ | |||||
static ImageCodec *codec_list = nullptr; | |||||
if (!set) | |||||
return codec_list; | |||||
ImageCodec **codec = &codec_list; | |||||
while (*codec && (*codec)->m_priority > set->m_priority) | |||||
codec = &(*codec)->m_next; | |||||
set->m_next = *codec; | |||||
*codec = set; | |||||
return nullptr; | |||||
} | |||||
}; | |||||
/* | |||||
* Codec-specific data that is stored in some images | |||||
*/ | |||||
class ImageCodecData | |||||
{ | |||||
friend class Image; | |||||
friend class ImageBank; | |||||
public: | public: | ||||
inline ImageCodecData() {} | virtual bool Load(Image *image, char const *path) = 0; | ||||
virtual ~ImageCodecData() {} | virtual bool Save(Image *image, char const *path) = 0; | ||||
virtual bool Load(Image *, char const *) = 0; | |||||
virtual bool Save(Image *, char const *) = 0; | |||||
virtual bool RetrieveTiles(Array<ivec2, ivec2>& tiles) { return false; } | /* TODO: this should become more fine-grained */ | ||||
int m_priority; | |||||
}; | }; | ||||
#define REGISTER_IMAGE_CODEC(name) \ | #define REGISTER_IMAGE_CODEC(name) \ | ||||
extern void Register##name(); \ | extern ImageCodec *Register##name(); \ | ||||
Register##name(); | |||||
#define DECLARE_IMAGE_CODEC(name, prio) \ | |||||
template<typename T> class name##ImageCodec : public ImageCodec \ | |||||
{ \ | { \ | ||||
public: \ | /* Insert image codecs in a sorted list */ \ | ||||
name##ImageCodec() \ | ImageCodec *codec = Register##name(); \ | ||||
{ \ | int i = 0, prio = codec->m_priority; \ | ||||
static ImageCodec codec; \ | for ( ; i < codeclist.Count(); ++i) \ | ||||
codec.m_tryload = Load; \ | |||||
codec.m_priority = prio; \ | |||||
RegisterCodec(&codec); \ | |||||
} \ | |||||
static bool Load(Image *image, char const *path) \ | |||||
{ \ | { \ | ||||
T *codecdata = new T(); \ | if (codeclist[i]->m_priority <= prio) \ | ||||
bool ret = codecdata->Load(image, path); \ | break; \ | ||||
delete codecdata; \ | |||||
return ret; \ | |||||
} \ | } \ | ||||
}; \ | codeclist.Insert(codec, i); \ | ||||
class name; \ | } | ||||
name##ImageCodec<name> name##ImageCodecInstance; \ | #define DECLARE_IMAGE_CODEC(name, priority) \ | ||||
void Register##name() \ | ImageCodec *Register##name() \ | ||||
{ \ | { \ | ||||
(void)&name##ImageCodecInstance; \ | ImageCodec *ret = new name(); \ | ||||
} \ | ret->m_priority = priority; \ | ||||
class name : public ImageCodecData | return ret; \ | ||||
} | |||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -30,7 +30,7 @@ namespace lol | |||||
* The bug was reported to Microsoft and fixed by them, but the fix | * The bug was reported to Microsoft and fixed by them, but the fix | ||||
* is not yet available. | * is not yet available. | ||||
* https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ | * https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ | ||||
static bool RegisterAllCodecs() | static bool RegisterAllCodecs(Array<ImageCodec *> &codeclist) | ||||
{ | { | ||||
#if defined __ANDROID__ | #if defined __ANDROID__ | ||||
REGISTER_IMAGE_CODEC(AndroidImageCodec) | REGISTER_IMAGE_CODEC(AndroidImageCodec) | ||||
@@ -61,90 +61,74 @@ static bool RegisterAllCodecs() | |||||
static class ImageBank | static class ImageBank | ||||
{ | { | ||||
public: | public: | ||||
void Init(); | ImageBank(); | ||||
Image *Create(char const *path); | |||||
void Unref(Image *img); | bool Load(Image *image, char const *path); | ||||
bool Save(Image *image, char const *path); | |||||
private: | private: | ||||
Array<ImageCodec *> m_codecs; | |||||
Map<String, Image *> m_images; | Map<String, Image *> m_images; | ||||
} | } | ||||
g_image_bank; | g_image_bank; | ||||
void ImageBank::Init() | ImageBank::ImageBank() | ||||
{ | { | ||||
/* Initialise codecs (see above) */ | RegisterAllCodecs(m_codecs); | ||||
static bool init = RegisterAllCodecs(); | |||||
UNUSED(init); | |||||
} | } | ||||
Image *ImageBank::Create(char const *path) | bool ImageBank::Load(Image *image, char const *path) | ||||
{ | { | ||||
Init(); | /* FIXME: priority is ignored */ | ||||
for (auto codec : m_codecs) | |||||
/* Is our image already in the bank? If so, no need to create it. */ | if (codec->Load(image, path)) | ||||
if (!m_images.HasKey(path)) | return true; | ||||
{ | return false; | ||||
m_images[path] = new Image(); | |||||
ImageCodec::Load(m_images[path], path); | |||||
} | |||||
Image *img = m_images[path]; | |||||
++img->m_data->m_refcount; | |||||
return img; | |||||
} | } | ||||
void ImageBank::Unref(Image *img) | bool ImageBank::Save(Image *image, char const *path) | ||||
{ | { | ||||
ASSERT(img->m_data->m_refcount > 0); | /* FIXME: priority is ignored */ | ||||
if (--img->m_data->m_refcount == 0) | for (auto codec : m_codecs) | ||||
{ | if (codec->Save(image, path)) | ||||
/* FIXME: unload images etc. here */ | return true; | ||||
/* XXX: we’re gonna hit a problem here because we didn’t keep | return false; | ||||
* the image path within the object, so unless we store it | |||||
* ourselves we’re good for a O(n) lookup… which we can’t do | |||||
* on Map objects anyway. */ | |||||
/* TODO: 1. remove image from Map | |||||
2. delete img; */ | |||||
} | |||||
} | } | ||||
/* | /* | ||||
* Static image methods | * Public Image class | ||||
*/ | */ | ||||
Image *Image::Create(char const *path) | Image::Image() | ||||
: m_data(new ImageData()) | |||||
{ | { | ||||
return g_image_bank.Create(path); | |||||
} | } | ||||
/* | Image::Image(char const *path) | ||||
* Public Image class | : m_data(new ImageData()) | ||||
*/ | { | ||||
Load(path); | |||||
} | |||||
Image::Image() | Image::Image(ivec2 size) | ||||
: m_data(new ImageData()) | : m_data(new ImageData()) | ||||
{ | { | ||||
SetSize(size); | |||||
} | } | ||||
Image::~Image() | Image::~Image() | ||||
{ | { | ||||
delete m_data->m_codecdata; | |||||
delete m_data; | delete m_data; | ||||
} | } | ||||
void Image::Destroy() | bool Image::Load(char const *path) | ||||
{ | { | ||||
g_image_bank.Unref(this); | return g_image_bank.Load(this, path); | ||||
} | } | ||||
bool Image::Save(char const *path) | bool Image::Save(char const *path) | ||||
{ | { | ||||
/* FIXME: add autoloading of save codecs */ | return g_image_bank.Save(this, path); | ||||
if (!m_data->m_codecdata) | |||||
return false; | |||||
return m_data->m_codecdata->Save(this, path); | |||||
} | } | ||||
ivec2 Image::GetSize() const | ivec2 Image::GetSize() const | ||||
@@ -177,13 +161,6 @@ PixelFormat Image::GetFormat() const | |||||
return m_data->m_format; | return m_data->m_format; | ||||
} | } | ||||
void *Image::LockGeneric() | |||||
{ | |||||
ASSERT(m_data->m_format != PixelFormat::Unknown); | |||||
return m_data->m_pixels[(int)m_data->m_format]; | |||||
} | |||||
/* The Lock() method and its explicit specialisations */ | /* The Lock() method and its explicit specialisations */ | ||||
template<PixelFormat T> | template<PixelFormat T> | ||||
typename PixelType<T>::type *Image::Lock() | typename PixelType<T>::type *Image::Lock() | ||||
@@ -195,19 +172,28 @@ typename PixelType<T>::type *Image::Lock() | |||||
if (!m_data->m_pixels.HasKey((int)T)) | if (!m_data->m_pixels.HasKey((int)T)) | ||||
{ | { | ||||
m_data->m_pixels[(int)T] = | m_data->m_pixels[(int)T] = | ||||
new typename PixelType<T>::type(m_data->m_size.x * m_data->m_size.y); | 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]; | return (typename PixelType<T>::type *)m_data->m_pixels[(int)T]; | ||||
} | } | ||||
template typename PixelType<PixelFormat::Y_8>::type * | template PixelType<PixelFormat::Y_8>::type * | ||||
Image::Lock<PixelFormat::Y_8>(); | Image::Lock<PixelFormat::Y_8>(); | ||||
template typename PixelType<PixelFormat::RGB_8>::type * | template PixelType<PixelFormat::RGB_8>::type * | ||||
Image::Lock<PixelFormat::RGB_8>(); | Image::Lock<PixelFormat::RGB_8>(); | ||||
template typename PixelType<PixelFormat::RGBA_8>::type * | template PixelType<PixelFormat::RGBA_8>::type * | ||||
Image::Lock<PixelFormat::RGBA_8>(); | Image::Lock<PixelFormat::RGBA_8>(); | ||||
/* Special case for the "any" format */ | |||||
template<> | |||||
void *Image::Lock<PixelFormat::Unknown>() | |||||
{ | |||||
ASSERT(m_data->m_format != PixelFormat::Unknown); | |||||
return m_data->m_pixels[(int)m_data->m_format]; | |||||
} | |||||
void Image::Unlock() | void Image::Unlock() | ||||
{ | { | ||||
/* TODO: ensure we are actually unlocking something we locked */ | /* TODO: ensure we are actually unlocking something we locked */ | ||||
@@ -216,9 +202,9 @@ void Image::Unlock() | |||||
bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | ||||
{ | { | ||||
if (!m_data->m_codecdata) | /* TODO: re-implement this */ | ||||
return false; | //return m_data->m_codecdata->RetrieveTiles(tiles); | ||||
return m_data->m_codecdata->RetrieveTiles(tiles); | return false; | ||||
} | } | ||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -21,7 +21,6 @@ | |||||
namespace lol | namespace lol | ||||
{ | { | ||||
/* FIXME: is it possible to avoid the cast to int here? */ | |||||
template <PixelFormat T> struct PixelType { typedef void type; }; | template <PixelFormat T> struct PixelType { typedef void type; }; | ||||
template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; }; | template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; }; | ||||
template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; }; | template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; }; | ||||
@@ -29,31 +28,28 @@ template<> struct PixelType<PixelFormat::RGBA_8> { typedef u8vec4 type; }; | |||||
class Image | class Image | ||||
{ | { | ||||
friend class ImageBank; | |||||
friend class ImageCodec; | |||||
public: | public: | ||||
Image(); | Image(); | ||||
Image(ivec2 size); | |||||
/* XXX: use of this ctor should be discouraged, as it will not | |||||
* return information about a possible error. */ | |||||
Image(char const *path); | |||||
~Image(); | ~Image(); | ||||
static Image *Create(char const *path); | bool Load(char const *path); | ||||
bool Save(char const *path); | bool Save(char const *path); | ||||
void Destroy(); | ivec2 GetSize() const; | ||||
void SetSize(ivec2); | |||||
PixelFormat GetFormat() const; | PixelFormat GetFormat() const; | ||||
void *LockGeneric(); | |||||
template<PixelFormat T> typename PixelType<T>::type *Lock(); | template<PixelFormat T> typename PixelType<T>::type *Lock(); | ||||
void Unlock(); | void Unlock(); | ||||
ivec2 GetSize() const; | |||||
void SetSize(ivec2); | |||||
bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const; | bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const; | ||||
private: | private: | ||||
class ImageData *m_data; | class ImageData *m_data; | ||||
String m_path; | |||||
}; | }; | ||||
} /* namespace lol */ | } /* namespace lol */ | ||||
@@ -140,7 +140,7 @@ TileSet::TileSet(char const *path, Image* image, ivec2 size, ivec2 count) | |||||
void TileSet::Init(char const *path) | void TileSet::Init(char const *path) | ||||
{ | { | ||||
Init(path, Image::Create(path)); | Init(path, new Image(path)); | ||||
} | } | ||||
void TileSet::Init(char const *path, Image* image) | void TileSet::Init(char const *path, Image* image) | ||||
@@ -190,7 +190,7 @@ void TileSet::TickDraw(float seconds) | |||||
{ | { | ||||
if (m_data->m_image) | if (m_data->m_image) | ||||
{ | { | ||||
m_data->m_image->Destroy(); | delete m_data->m_image; | ||||
m_data->m_image = nullptr; | m_data->m_image = nullptr; | ||||
} | } | ||||
else | else | ||||
@@ -207,7 +207,7 @@ void TileSet::TickDraw(float seconds) | |||||
int w = m_data->m_texture_size.x; | int w = m_data->m_texture_size.x; | ||||
int h = m_data->m_texture_size.y; | int h = m_data->m_texture_size.y; | ||||
uint8_t *pixels = (uint8_t *)m_data->m_image->LockGeneric(); | uint8_t *pixels = (uint8_t *)m_data->m_image->Lock<PixelFormat::Unknown>(); | ||||
bool resized = false; | bool resized = false; | ||||
if (w != m_data->m_image_size.x || h != m_data->m_image_size.y) | if (w != m_data->m_image_size.x || h != m_data->m_image_size.y) | ||||
{ | { | ||||
@@ -225,7 +225,7 @@ void TileSet::TickDraw(float seconds) | |||||
if (resized) | if (resized) | ||||
delete[] pixels; | delete[] pixels; | ||||
m_data->m_image->Destroy(); | delete m_data->m_image; | ||||
m_data->m_image = nullptr; | m_data->m_image = nullptr; | ||||
} | } | ||||
} | } | ||||
@@ -24,13 +24,13 @@ LOLUNIT_FIXTURE(ImageTest) | |||||
{ | { | ||||
LOLUNIT_TEST(OpenImage) | LOLUNIT_TEST(OpenImage) | ||||
{ | { | ||||
Image *image = Image::Create("data/gradient.png"); | Image image("data/gradient.png"); | ||||
ivec2 size = image->GetSize(); | ivec2 size = image.GetSize(); | ||||
LOLUNIT_ASSERT_EQUAL(size.x, 256); | LOLUNIT_ASSERT_EQUAL(size.x, 256); | ||||
LOLUNIT_ASSERT_EQUAL(size.y, 16); | LOLUNIT_ASSERT_EQUAL(size.y, 16); | ||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | u8vec4 *data = image.Lock<PixelFormat::RGBA_8>(); | ||||
LOLUNIT_ASSERT(data); | LOLUNIT_ASSERT(data); | ||||
LOLUNIT_ASSERT_EQUAL((int)data[0].r, 0x00); | LOLUNIT_ASSERT_EQUAL((int)data[0].r, 0x00); | ||||
@@ -41,8 +41,7 @@ LOLUNIT_FIXTURE(ImageTest) | |||||
LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | ||||
LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | ||||
image->Unlock(); | image.Unlock(); | ||||
image->Destroy(); | |||||
} | } | ||||
}; | }; | ||||