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