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; | |||||
| m_bitmap = nullptr; | |||||
| delete bitmap; | |||||
| 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(), | |||||
| m_bitmap->GetHeight()); | |||||
| image->m_data->m_format = PixelFormat::RGBA_8; | |||||
| 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) | |||||
| ivec2 size(bitmap->GetWidth(), bitmap->GetHeight()); | |||||
| Gdiplus::Rect rect(0, 0, size.x, size.y); | |||||
| Gdiplus::BitmapData bdata; | |||||
| if (bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, | |||||
| PixelFormat32bppARGB, &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); | |||||
| 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]; | |||||
| p[0] = tmp; | |||||
| p += 4; | |||||
| } | |||||
| image->SetSize(size); | |||||
| u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>(); | |||||
| u8vec4 *source = static_cast<u8vec4 *>(bdata.Scan0); | |||||
| for (int y = 0; y < size.y; y++) | |||||
| for (int x = 0; x < size.x; x++) | |||||
| *pixels++ = (*source++).bgra; | |||||
| image->Unlock(); | |||||
| 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; | |||||
| Gdiplus::GetImageEncodersSize(&num, &size); | |||||
| unsigned int num = 0, encoder_size = 0; | |||||
| 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::GetImageEncoders(num, size, codecs); | |||||
| = (Gdiplus::ImageCodecInfo *)new uint8_t[encoder_size]; | |||||
| 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, | |||||
| image->m_data->m_size.y, | |||||
| Gdiplus::Bitmap *b = new Gdiplus::Bitmap(size.x, 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 x = 0; x < image->m_data->m_size.x; x++) | |||||
| { | |||||
| for (int y = 0; y < size.y; y++) | |||||
| 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()); | |||||
| if (m_surface) | |||||
| surface = IMG_Load(pathlist[i].C()); | |||||
| 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_BlitSurface(m_surface, nullptr, tmp, nullptr); | |||||
| SDL_FreeSurface(m_surface); | |||||
| m_surface = tmp; | |||||
| SDL_Surface *tmp = Create32BppSurface(size); | |||||
| SDL_BlitSurface(surface, nullptr, tmp, nullptr); | |||||
| SDL_FreeSurface(surface); | |||||
| 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() | |||||
| { | |||||
| SDL_FreeSurface(m_surface); | |||||
| int ret = SDL_SaveBMP(surface, path); | |||||
| SDL_FreeSurface(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_refcount(0), | |||||
| m_codecdata(nullptr) | |||||
| m_format(PixelFormat::Unknown) | |||||
| {} | {} | ||||
| 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 ~ImageCodecData() {} | |||||
| virtual bool Load(Image *, char const *) = 0; | |||||
| virtual bool Save(Image *, char const *) = 0; | |||||
| virtual bool Load(Image *image, char const *path) = 0; | |||||
| virtual bool Save(Image *image, char const *path) = 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(); \ | |||||
| Register##name(); | |||||
| #define DECLARE_IMAGE_CODEC(name, prio) \ | |||||
| template<typename T> class name##ImageCodec : public ImageCodec \ | |||||
| extern ImageCodec *Register##name(); \ | |||||
| { \ | { \ | ||||
| public: \ | |||||
| name##ImageCodec() \ | |||||
| { \ | |||||
| static ImageCodec codec; \ | |||||
| codec.m_tryload = Load; \ | |||||
| codec.m_priority = prio; \ | |||||
| RegisterCodec(&codec); \ | |||||
| } \ | |||||
| static bool Load(Image *image, char const *path) \ | |||||
| /* Insert image codecs in a sorted list */ \ | |||||
| ImageCodec *codec = Register##name(); \ | |||||
| int i = 0, prio = codec->m_priority; \ | |||||
| for ( ; i < codeclist.Count(); ++i) \ | |||||
| { \ | { \ | ||||
| T *codecdata = new T(); \ | |||||
| bool ret = codecdata->Load(image, path); \ | |||||
| delete codecdata; \ | |||||
| return ret; \ | |||||
| if (codeclist[i]->m_priority <= prio) \ | |||||
| break; \ | |||||
| } \ | } \ | ||||
| }; \ | |||||
| class name; \ | |||||
| name##ImageCodec<name> name##ImageCodecInstance; \ | |||||
| void Register##name() \ | |||||
| codeclist.Insert(codec, i); \ | |||||
| } | |||||
| #define DECLARE_IMAGE_CODEC(name, priority) \ | |||||
| ImageCodec *Register##name() \ | |||||
| { \ | { \ | ||||
| (void)&name##ImageCodecInstance; \ | |||||
| } \ | |||||
| class name : public ImageCodecData | |||||
| ImageCodec *ret = new name(); \ | |||||
| ret->m_priority = priority; \ | |||||
| 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(); | |||||
| Image *Create(char const *path); | |||||
| ImageBank(); | |||||
| 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) */ | |||||
| static bool init = RegisterAllCodecs(); | |||||
| UNUSED(init); | |||||
| RegisterAllCodecs(m_codecs); | |||||
| } | } | ||||
| Image *ImageBank::Create(char const *path) | |||||
| bool ImageBank::Load(Image *image, char const *path) | |||||
| { | { | ||||
| Init(); | |||||
| /* Is our image already in the bank? If so, no need to create it. */ | |||||
| if (!m_images.HasKey(path)) | |||||
| { | |||||
| m_images[path] = new Image(); | |||||
| ImageCodec::Load(m_images[path], path); | |||||
| } | |||||
| Image *img = m_images[path]; | |||||
| ++img->m_data->m_refcount; | |||||
| return img; | |||||
| /* FIXME: priority is ignored */ | |||||
| for (auto codec : m_codecs) | |||||
| if (codec->Load(image, path)) | |||||
| return true; | |||||
| return false; | |||||
| } | } | ||||
| void ImageBank::Unref(Image *img) | |||||
| bool ImageBank::Save(Image *image, char const *path) | |||||
| { | { | ||||
| ASSERT(img->m_data->m_refcount > 0); | |||||
| if (--img->m_data->m_refcount == 0) | |||||
| { | |||||
| /* FIXME: unload images etc. here */ | |||||
| /* XXX: we’re gonna hit a problem here because we didn’t keep | |||||
| * 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; */ | |||||
| } | |||||
| /* FIXME: priority is ignored */ | |||||
| for (auto codec : m_codecs) | |||||
| if (codec->Save(image, path)) | |||||
| return true; | |||||
| return false; | |||||
| } | } | ||||
| /* | /* | ||||
| * Static image methods | |||||
| * Public Image class | |||||
| */ | */ | ||||
| Image *Image::Create(char const *path) | |||||
| Image::Image() | |||||
| : m_data(new ImageData()) | |||||
| { | { | ||||
| return g_image_bank.Create(path); | |||||
| } | } | ||||
| /* | |||||
| * Public Image class | |||||
| */ | |||||
| Image::Image(char const *path) | |||||
| : 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 */ | |||||
| if (!m_data->m_codecdata) | |||||
| return false; | |||||
| return m_data->m_codecdata->Save(this, path); | |||||
| return g_image_bank.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) | |||||
| return false; | |||||
| return m_data->m_codecdata->RetrieveTiles(tiles); | |||||
| /* TODO: re-implement this */ | |||||
| //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->Destroy(); | |||||
| image.Unlock(); | |||||
| } | } | ||||
| }; | }; | ||||