From f455f0e2d1f7b25630b6f3a2ff8da3f8f3217062 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Mon, 16 Jun 2014 07:20:18 +0000 Subject: [PATCH] image: start reworking the Image class to properly split the pixel handling and the codec logic. --- src/image/codec/android-image.cpp | 14 +-- src/image/codec/dummy-image.cpp | 40 +++---- src/image/codec/gdiplus-image.cpp | 34 +++--- src/image/codec/ios-image.cpp | 14 +-- src/image/codec/ps3-image.cpp | 18 ++-- src/image/codec/sdl-image.cpp | 51 ++++----- src/image/codec/zed-image.cpp | 26 +++-- src/image/codec/zed-palette-image.cpp | 42 +++----- src/image/image-private.h | 119 ++++++++++---------- src/image/image.cpp | 149 ++++++++++++++------------ src/lol/gpu/texture.h | 48 ++++----- src/lol/image/image.h | 30 +++--- src/tileset.cpp | 8 +- test/unit/image.cpp | 15 +-- 14 files changed, 302 insertions(+), 306 deletions(-) diff --git a/src/image/codec/android-image.cpp b/src/image/codec/android-image.cpp index f615a8fc..70ecc3a6 100644 --- a/src/image/codec/android-image.cpp +++ b/src/image/codec/android-image.cpp @@ -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; } diff --git a/src/image/codec/dummy-image.cpp b/src/image/codec/dummy-image.cpp index ab3a98f4..e9ded0a2 100644 --- a/src/image/codec/dummy-image.cpp +++ b/src/image/codec/dummy-image.cpp @@ -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(); 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 */ diff --git a/src/image/codec/gdiplus-image.cpp b/src/image/codec/gdiplus-image.cpp index 18e3f0ce..97592c6d 100644 --- a/src/image/codec/gdiplus-image.cpp +++ b/src/image/codec/gdiplus-image.cpp @@ -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(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(m_bdata.Scan0); u8vec4 *pdst = static_cast(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(m_bdata.Scan0); } diff --git a/src/image/codec/ios-image.cpp b/src/image/codec/ios-image.cpp index 0d6d4810..d052938d 100644 --- a/src/image/codec/ios-image.cpp +++ b/src/image/codec/ios-image.cpp @@ -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; } diff --git a/src/image/codec/ps3-image.cpp b/src/image/codec/ps3-image.cpp index ac3cb322..55202691 100644 --- a/src/image/codec/ps3-image.cpp +++ b/src/image/codec/ps3-image.cpp @@ -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; } diff --git a/src/image/codec/sdl-image.cpp b/src/image/codec/sdl-image.cpp index 640919db..7e732140 100644 --- a/src/image/codec/sdl-image.cpp +++ b/src/image/codec/sdl-image.cpp @@ -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 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(); + 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 diff --git a/src/image/codec/zed-image.cpp b/src/image/codec/zed-image.cpp index 45bc7107..0dc3bb77 100644 --- a/src/image/codec/zed-image.cpp +++ b/src/image/codec/zed-image.cpp @@ -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(); //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; } diff --git a/src/image/codec/zed-palette-image.cpp b/src/image/codec/zed-palette-image.cpp index 64405713..f464fd0c 100644 --- a/src/image/codec/zed-palette-image.cpp +++ b/src/image/codec/zed-palette-image.cpp @@ -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(); 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 */ diff --git a/src/image/image-private.h b/src/image/image-private.h index 236a0619..3fa2a25a 100644 --- a/src/image/image-private.h +++ b/src/image/image-private.h @@ -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 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& 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 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 */ diff --git a/src/image/image.cpp b/src/image/image.cpp index 9dac7c27..c9e0d401 100644 --- a/src/image/image.cpp +++ b/src/image/image.cpp @@ -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(); + 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 +typename PixelType::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::type(m_data->m_size.x * m_data->m_size.y); + } + + return (typename PixelType::type *)m_data->m_pixels[(int)T]; } -bool Image::RetrieveTiles(Array& tiles) const +template typename PixelType::type * +Image::Lock(); +template typename PixelType::type * +Image::Lock(); +template typename PixelType::type * +Image::Lock(); + +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& tiles) const { - m_data->Close(); - delete m_data; + if (!m_data->m_codecdata) + return false; + return m_data->m_codecdata->RetrieveTiles(tiles); } } /* namespace lol */ diff --git a/src/lol/gpu/texture.h b/src/lol/gpu/texture.h index 906f3a34..ec2a3fa0 100644 --- a/src/lol/gpu/texture.h +++ b/src/lol/gpu/texture.h @@ -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; } }; diff --git a/src/lol/image/image.h b/src/lol/image/image.h index 8da761e0..a3a59891 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -21,31 +21,37 @@ namespace lol { +/* FIXME: is it possible to avoid the cast to int here? */ +template struct PixelType { typedef void type; }; +template<> struct PixelType { typedef uint8_t type; }; +template<> struct PixelType { typedef u8vec3 type; }; +template<> struct PixelType { 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 typename PixelType::type *Lock(); + void Unlock(); + + ivec2 GetSize() const; + void SetSize(ivec2); + bool RetrieveTiles(Array& tiles) const; private: - Image(char const* path); - ~Image(); - class ImageData *m_data; String m_path; }; diff --git a/src/tileset.cpp b/src/tileset.cpp index 319bcdf0..58a8cd2a 100644 --- a/src/tileset.cpp +++ b/src/tileset.cpp @@ -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; diff --git a/test/unit/image.cpp b/test/unit/image.cpp index 37557133..14e3f245 100644 --- a/test/unit/image.cpp +++ b/test/unit/image.cpp @@ -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(); 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(); } };