Browse Source

image: start reworking the Image class to properly split the pixel handling

and the codec logic.
undefined
Sam Hocevar 10 years ago
parent
commit
f455f0e2d1
14 changed files with 302 additions and 306 deletions
  1. +7
    -7
      src/image/codec/android-image.cpp
  2. +14
    -26
      src/image/codec/dummy-image.cpp
  3. +18
    -16
      src/image/codec/gdiplus-image.cpp
  4. +7
    -7
      src/image/codec/ios-image.cpp
  5. +9
    -9
      src/image/codec/ps3-image.cpp
  6. +22
    -29
      src/image/codec/sdl-image.cpp
  7. +12
    -14
      src/image/codec/zed-image.cpp
  8. +15
    -27
      src/image/codec/zed-palette-image.cpp
  9. +65
    -54
      src/image/image-private.h
  10. +82
    -67
      src/image/image.cpp
  11. +20
    -28
      src/lol/gpu/texture.h
  12. +18
    -12
      src/lol/image/image.h
  13. +5
    -3
      src/tileset.cpp
  14. +8
    -7
      test/unit/image.cpp

+ 7
- 7
src/image/codec/android-image.cpp View File

@@ -35,11 +35,11 @@ extern ANativeActivity *g_activity;
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(AndroidImageData, 100)
DECLARE_IMAGE_CODEC(AndroidImageCodec, 100)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const; virtual uint8_t *GetData() const;
@@ -50,7 +50,7 @@ private:
jint *pixels; jint *pixels;
}; };


bool AndroidImageData::Open(char const *path)
bool AndroidImageCodec::Load(Image *image, char const *path)
{ {
JNIEnv *env; JNIEnv *env;
jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2); jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2);
@@ -109,14 +109,14 @@ bool AndroidImageData::Open(char const *path)
return true; return true;
} }


bool AndroidImageData::Save(char const *path)
bool AndroidImageCodec::Save(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);


/* TODO: unimplemented */ /* TODO: unimplemented */
} }


bool AndroidImageData::Close()
bool AndroidImageCodec::Close()
{ {
JNIEnv *env; JNIEnv *env;
jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2); jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2);
@@ -141,7 +141,7 @@ bool AndroidImageData::Close()
return true; return true;
} }


uint8_t *AndroidImageData::GetData() const
uint8_t *AndroidImageCodec::GetData() const
{ {
return (uint8_t *)pixels; return (uint8_t *)pixels;
} }


+ 14
- 26
src/image/codec/dummy-image.cpp View File

@@ -24,61 +24,49 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(DummyImageData, 0)
DECLARE_IMAGE_CODEC(DummyImageCodec, 0)
{ {
public: 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 bool Close();

virtual uint8_t *GetData() const;

private:
uint8_t *pixels;
}; };


/* /*
* Public Image class * Public Image class
*/ */


bool DummyImageData::Open(char const *path)
bool DummyImageCodec::Load(Image *image, char const *path)
{ {
UNUSED(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 j = 0; j < 256; j++)
for (int i = 0; i < 256; i++) 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; return true;
} }


bool DummyImageData::Save(char const *path)
bool DummyImageCodec::Save(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);


return true; return true;
} }


bool DummyImageData::Close()
bool DummyImageCodec::Close()
{ {
delete[] pixels;

return true; return true;
} }


uint8_t * DummyImageData::GetData() const
{
return pixels;
}

} /* namespace lol */ } /* namespace lol */



+ 18
- 16
src/image/codec/gdiplus-image.cpp View File

@@ -31,11 +31,11 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(GdiPlusImageData, 100)
DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const; virtual uint8_t *GetData() const;
@@ -49,7 +49,7 @@ private:
* Public Image class * Public Image class
*/ */


bool GdiPlusImageData::Open(char const *path)
bool GdiPlusImageCodec::Load(Image *image, char const *path)
{ {
Gdiplus::Status status; Gdiplus::Status status;
ULONG_PTR token; ULONG_PTR token;
@@ -110,10 +110,11 @@ bool GdiPlusImageData::Open(char const *path)
return false; 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, if (m_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead,
PixelFormat32bppARGB, &m_bdata) != Gdiplus::Ok) 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 * 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); 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]; uint8_t tmp = p[2];
p[2] = p[0]; p[2] = p[0];
@@ -140,7 +141,7 @@ bool GdiPlusImageData::Open(char const *path)
return true; return true;
} }


bool GdiPlusImageData::Save(char const *path)
bool GdiPlusImageCodec::Save(Image *image, char const *path)
{ {
ULONG_PTR token; ULONG_PTR token;
Gdiplus::GdiplusStartupInput input; 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 /* 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(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); PixelFormat32bppARGB);


Gdiplus::BitmapData bdata; 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, if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite,
PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) PixelFormat32bppARGB, &bdata) != Gdiplus::Ok)
@@ -210,8 +212,8 @@ bool GdiPlusImageData::Save(char const *path)


u8vec4 *psrc = static_cast<u8vec4 *>(m_bdata.Scan0); u8vec4 *psrc = static_cast<u8vec4 *>(m_bdata.Scan0);
u8vec4 *pdst = static_cast<u8vec4 *>(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; *pdst++ = (*psrc++).bgra;
} }
@@ -233,7 +235,7 @@ bool GdiPlusImageData::Save(char const *path)
return true; return true;
} }


bool GdiPlusImageData::Close()
bool GdiPlusImageCodec::Close()
{ {
m_bitmap->UnlockBits(&m_bdata); m_bitmap->UnlockBits(&m_bdata);
delete m_bitmap; delete m_bitmap;
@@ -241,7 +243,7 @@ bool GdiPlusImageData::Close()
return true; return true;
} }


uint8_t * GdiPlusImageData::GetData() const
uint8_t * GdiPlusImageCodec::GetData() const
{ {
return static_cast<uint8_t *>(m_bdata.Scan0); return static_cast<uint8_t *>(m_bdata.Scan0);
} }


+ 7
- 7
src/image/codec/ios-image.cpp View File

@@ -28,11 +28,11 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(IosImageData, 100)
DECLARE_IMAGE_CODEC(IosImageCodec, 100)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const; virtual uint8_t *GetData() const;
@@ -45,7 +45,7 @@ private:
* Public Image class * Public Image class
*/ */


bool IosImageData::Open(char const *path)
bool IosImageCodec::Load(Image *image, char const *path)
{ {
NSString *fullpath = [NSString stringWithUTF8String:path]; NSString *fullpath = [NSString stringWithUTF8String:path];
NSArray *chunks = [fullpath componentsSeparatedByString: @"/"]; NSArray *chunks = [fullpath componentsSeparatedByString: @"/"];
@@ -84,7 +84,7 @@ bool IosImageData::Open(char const *path)
return true; return true;
} }


bool IosImageData::Save(char const *path)
bool IosImageCodec::Save(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);


@@ -92,14 +92,14 @@ bool IosImageData::Save(char const *path)
return true; return true;
} }


bool IosImageData::Close()
bool IosImageCodec::Close()
{ {
free(pixels); free(pixels);


return true; return true;
} }


uint8_t * IosImageData::GetData() const
uint8_t * IosImageCodec::GetData() const
{ {
return pixels; return pixels;
} }


+ 9
- 9
src/image/codec/ps3-image.cpp View File

@@ -30,11 +30,11 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(Ps3ImageData, 100)
DECLARE_IMAGE_CODEC(Ps3ImageCodec, 100)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const; virtual uint8_t *GetData() const;
@@ -49,7 +49,7 @@ private:
* Public Image class * Public Image class
*/ */


bool Ps3ImageData::Open(char const *path)
bool Ps3ImageCodec::Load(Image *image, char const *path)
{ {
int32_t err; int32_t err;


@@ -74,9 +74,9 @@ bool Ps3ImageData::Open(char const *path)
in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE; in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE;
in_param.ppuThreadPriority = 1000; in_param.ppuThreadPriority = 1000;
in_param.spuThreadPriority = 200; in_param.spuThreadPriority = 200;
in_param.cbCtrlMallocFunc = Ps3ImageData::Malloc;
in_param.cbCtrlMallocFunc = Ps3ImageCodec::Malloc;
in_param.cbCtrlMallocArg = nullptr; in_param.cbCtrlMallocArg = nullptr;
in_param.cbCtrlFreeFunc = Ps3ImageData::Free;
in_param.cbCtrlFreeFunc = Ps3ImageCodec::Free;
in_param.cbCtrlFreeArg = nullptr; in_param.cbCtrlFreeArg = nullptr;
CellPngDecThreadOutParam out_param; CellPngDecThreadOutParam out_param;
err = cellPngDecCreate(&hmain, &in_param, &out_param); err = cellPngDecCreate(&hmain, &in_param, &out_param);
@@ -174,7 +174,7 @@ bool Ps3ImageData::Open(char const *path)
return true; return true;
} }


bool Ps3ImageData::Open(char const *path)
bool Ps3ImageCodec::Load(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);


@@ -182,14 +182,14 @@ bool Ps3ImageData::Open(char const *path)
return true; return true;
} }


bool Ps3ImageData::Close()
bool Ps3ImageCodec::Close()
{ {
free(pixels); free(pixels);


return true; return true;
} }


uint8_t * Ps3ImageData::GetData() const
uint8_t * Ps3ImageCodec::GetData() const
{ {
return pixels; return pixels;
} }


+ 22
- 29
src/image/codec/sdl-image.cpp View File

@@ -37,36 +37,34 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(SdlImageData, 50)
DECLARE_IMAGE_CODEC(SdlImageCodec, 50)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const;

static SDL_Surface *Create32BppSurface(ivec2 size); static SDL_Surface *Create32BppSurface(ivec2 size);


private: private:
SDL_Surface *m_img;
SDL_Surface *m_surface;
}; };


/* /*
* Public Image class * Public Image class
*/ */


bool SdlImageData::Open(char const *path)
bool SdlImageCodec::Load(Image *image, char const *path)
{ {
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_img = IMG_Load(pathlist[i].C());
if (m_img)
m_surface = IMG_Load(pathlist[i].C());
if (m_surface)
break; break;
} }


if (!m_img)
if (!m_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);
@@ -74,42 +72,37 @@ bool SdlImageData::Open(char const *path)
return false; 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; 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; return ret == 0;
} }


bool SdlImageData::Close()
bool SdlImageCodec::Close()
{ {
SDL_FreeSurface(m_img);
SDL_FreeSurface(m_surface);


return true; 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; uint32_t rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN #if SDL_BYTEORDER == SDL_BIG_ENDIAN


+ 12
- 14
src/image/codec/zed-image.cpp View File

@@ -25,11 +25,11 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(ZedImageData, 0)
DECLARE_IMAGE_CODEC(ZedImageCodec, 0)
{ {
public: 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 bool Close();


virtual uint8_t *GetData() const; virtual uint8_t *GetData() const;
@@ -51,7 +51,7 @@ private:
* Public Image class * Public Image class
*/ */


bool ZedImageData::Open(char const *path)
bool ZedImageCodec::Load(Image *image, char const *path)
{ {
if (!lol::String(path).EndsWith(".RSC")) if (!lol::String(path).EndsWith(".RSC"))
return false; return false;
@@ -239,10 +239,8 @@ bool ZedImageData::Open(char const *path)
tex_size <<= 1; tex_size <<= 1;


//Prepare final imqge //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 //Data refactor stage
ivec2 pos = ivec2(0); 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++) 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; 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++; j++;
} }
} }
image->Unlock();

return true; return true;
} }


bool ZedImageData::Save(char const *path)
bool ZedImageCodec::Save(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);
/* FIXME: do we need to implement this? */ /* FIXME: do we need to implement this? */
return true; return true;
} }


bool ZedImageData::Close()
bool ZedImageCodec::Close()
{ {
delete[] m_pixels;

return true; return true;
} }


uint8_t * ZedImageData::GetData() const
uint8_t * ZedImageCodec::GetData() const
{ {
return m_pixels; return m_pixels;
} }


+ 15
- 27
src/image/codec/zed-palette-image.cpp View File

@@ -24,24 +24,19 @@ namespace lol
* Image implementation class * Image implementation class
*/ */


DECLARE_IMAGE_LOADER(ZedPaletteImageData, 0)
DECLARE_IMAGE_CODEC(ZedPaletteImageCodec, 0)
{ {
public: 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 bool Close();

virtual uint8_t *GetData() const;

private:
uint8_t *m_pixels;
}; };


/* /*
* Public Image class * Public Image class
*/ */


bool ZedPaletteImageData::Open(char const *path)
bool ZedPaletteImageCodec::Load(Image *image, char const *path)
{ {
if (!lol::String(path).EndsWith(".pal")) if (!lol::String(path).EndsWith(".pal"))
return false; return false;
@@ -61,47 +56,40 @@ bool ZedPaletteImageData::Open(char const *path)
int32_t tex_size = 2; int32_t tex_size = 2;
while (tex_size < tex_sqrt) while (tex_size < tex_sqrt)
tex_size <<= 1; tex_size <<= 1;
m_size = ivec2(tex_size);
image->m_data->m_size = ivec2(tex_size);
#else #else
int32_t tex_sqrt = file_size / 3; int32_t tex_sqrt = file_size / 3;
int32_t tex_size = 2; int32_t tex_size = 2;
while (tex_size < tex_sqrt) while (tex_size < tex_sqrt)
tex_size <<= 1; tex_size <<= 1;
m_size = ivec2(tex_size, 1);
image->SetSize(ivec2(tex_size, 1));
#endif #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();) 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; return true;
} }


bool ZedPaletteImageData::Save(char const *path)
bool ZedPaletteImageCodec::Save(Image *image, char const *path)
{ {
UNUSED(path); UNUSED(path);
/* FIXME: do we need to implement this? */ /* FIXME: do we need to implement this? */
return true; return true;
} }


bool ZedPaletteImageData::Close()
bool ZedPaletteImageCodec::Close()
{ {
delete[] m_pixels;

return true; return true;
} }


uint8_t * ZedPaletteImageData::GetData() const
{
return m_pixels;
}

} /* namespace lol */ } /* namespace lol */



+ 65
- 54
src/image/image-private.h View File

@@ -9,8 +9,8 @@
// //


// //
// The ImageData class
// -------------------
// The ImageCodecData class
// ------------------------
// //


#if !defined __LOL_IMAGE_PRIVATE_H__ #if !defined __LOL_IMAGE_PRIVATE_H__
@@ -19,103 +19,114 @@
namespace lol 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 class ImageCodec
{ {
friend class ImageBank; friend class ImageBank;
friend class ImageData;
friend class ImageCodecData;


public: 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: 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 *Helper(ImageCodec *set)
{ {
static ImageCodec *loaders = nullptr;
static ImageCodec *codec_list = nullptr;


if (!set) 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; return nullptr;
} }
}; };


class ImageData
/*
* Codec-specific data that is stored in some images
*/
class ImageCodecData
{ {
friend class Image; friend class Image;
friend class ImageBank; friend class ImageBank;


public: 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; } 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(); \ extern void Register##name(); \
Register##name(); Register##name();


#define DECLARE_IMAGE_LOADER(name, prio) \
#define DECLARE_IMAGE_CODEC(name, prio) \
template<typename T> class name##ImageCodec : public ImageCodec \ template<typename T> class name##ImageCodec : public ImageCodec \
{ \ { \
public: \ public: \
name##ImageCodec() \ 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; \ return ret; \
} \ } \
}; \ }; \
@@ -125,7 +136,7 @@ protected:
{ \ { \
(void)&name##ImageCodecInstance; \ (void)&name##ImageCodecInstance; \
} \ } \
class name : public ImageData
class name : public ImageCodecData


} /* namespace lol */ } /* namespace lol */




+ 82
- 67
src/image/image.cpp View File

@@ -21,7 +21,7 @@ namespace lol
{ {


/* HACK: We cannot make this an ImageCodec member function, because the /* 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 * the "lol" namespace. An apparent bug in Visual Studio's compiler
* makes it think these functions are actually in the top-level * makes it think these functions are actually in the top-level
* namespace when the forward declaration is in a class member function. * 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 * 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 RegisterAllLoaders()
static bool RegisterAllCodecs()
{ {
#if defined __ANDROID__ #if defined __ANDROID__
REGISTER_IMAGE_LOADER(AndroidImageData)
REGISTER_IMAGE_CODEC(AndroidImageCodec)
#endif #endif
REGISTER_IMAGE_LOADER(DummyImageData)
REGISTER_IMAGE_CODEC(DummyImageCodec)
#if defined USE_GDIPLUS #if defined USE_GDIPLUS
REGISTER_IMAGE_LOADER(GdiPlusImageData)
REGISTER_IMAGE_CODEC(GdiPlusImageCodec)
#endif #endif
#if defined __APPLE__ && defined __MACH__ && defined __arm__ #if defined __APPLE__ && defined __MACH__ && defined __arm__
REGISTER_IMAGE_LOADER(IosImageData)
REGISTER_IMAGE_CODEC(IosImageCodec)
#endif #endif
#if defined __CELLOS_LV2__ #if defined __CELLOS_LV2__
REGISTER_IMAGE_LOADER(Ps3ImageData)
REGISTER_IMAGE_CODEC(Ps3ImageCodec)
#endif #endif
#if defined USE_SDL_IMAGE #if defined USE_SDL_IMAGE
REGISTER_IMAGE_LOADER(SdlImageData)
REGISTER_IMAGE_CODEC(SdlImageCodec)
#endif #endif
REGISTER_IMAGE_LOADER(ZedImageData)
REGISTER_IMAGE_LOADER(ZedPaletteImageData)
REGISTER_IMAGE_CODEC(ZedImageCodec)
REGISTER_IMAGE_CODEC(ZedPaletteImageCodec)


return true; return true;
} }
@@ -63,8 +63,6 @@ static class ImageBank
public: public:
void Init(); void Init();
Image *Create(char const *path); Image *Create(char const *path);
bool Store(Image *img);
Image *Load(char const *path);


void Unref(Image *img); void Unref(Image *img);


@@ -75,51 +73,27 @@ g_image_bank;


void ImageBank::Init() void ImageBank::Init()
{ {
/* Initialise loaders (see above) */
static bool init = RegisterAllLoaders();
/* Initialise codecs (see above) */
static bool init = RegisterAllCodecs();
UNUSED(init); UNUSED(init);
} }


Image *ImageBank::Create(char const *path) 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; ++img->m_data->m_refcount;
return img; 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) void ImageBank::Unref(Image *img)
{ {
ASSERT(img->m_data->m_refcount > 0); ASSERT(img->m_data->m_refcount > 0);
@@ -145,24 +119,19 @@ Image *Image::Create(char const *path)
return g_image_bank.Create(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 * 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() void Image::Destroy()
@@ -172,7 +141,10 @@ void Image::Destroy()


bool Image::Save(char const *path) 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 ivec2 Image::GetSize() const
@@ -180,30 +152,73 @@ ivec2 Image::GetSize() const
return m_data->m_size; 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 PixelFormat Image::GetFormat() const
{ {
return m_data->m_format; 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 */ } /* namespace lol */


+ 20
- 28
src/lol/gpu/texture.h View File

@@ -19,38 +19,30 @@
namespace lol namespace lol
{ {


struct PixelFormat
enum class PixelFormat
{ {
/* XXX: make sure to update texture.cpp when this changes */ /* 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;
} }
}; };




+ 18
- 12
src/lol/image/image.h View File

@@ -21,31 +21,37 @@
namespace lol 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 class Image
{ {
friend class ImageBank; friend class ImageBank;
friend class ImageCodec;


public: 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); bool Save(char const *path);
void Destroy(); void Destroy();


ivec2 GetSize() const;
PixelFormat GetFormat() 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; bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const;


private: private:
Image(char const* path);
~Image();

class ImageData *m_data; class ImageData *m_data;
String m_path; String m_path;
}; };


+ 5
- 3
src/tileset.cpp View File

@@ -202,12 +202,13 @@ void TileSet::TickDraw(float seconds)
else if (m_data->m_image) else if (m_data->m_image)
{ {
PixelFormat format = m_data->m_image->GetFormat(); PixelFormat format = m_data->m_image->GetFormat();
int planes = format.BytesPerPixel();
int planes = BytesPerPixel(format);


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 = 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) if (w != m_data->m_image_size.x || h != m_data->m_image_size.y)
{ {
uint8_t *tmp = new uint8_t[planes * w * h]; 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, pixels + planes * m_data->m_image_size.x * line,
planes * m_data->m_image_size.x); planes * m_data->m_image_size.x);
pixels = tmp; pixels = tmp;
resized = false;
} }


m_data->m_texture = new Texture(ivec2(w, h), format); m_data->m_texture = new Texture(ivec2(w, h), format);
m_data->m_texture->SetData(pixels); m_data->m_texture->SetData(pixels);


if (pixels != m_data->m_image->GetData())
if (resized)
delete[] pixels; delete[] pixels;
m_data->m_image->Destroy(); m_data->m_image->Destroy();
m_data->m_image = nullptr; m_data->m_image = nullptr;


+ 8
- 7
test/unit/image.cpp View File

@@ -30,17 +30,18 @@ LOLUNIT_FIXTURE(ImageTest)
LOLUNIT_ASSERT_EQUAL(size.x, 256); LOLUNIT_ASSERT_EQUAL(size.x, 256);
LOLUNIT_ASSERT_EQUAL(size.y, 16); LOLUNIT_ASSERT_EQUAL(size.y, 16);


uint8_t *data = image->GetData();
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>();
LOLUNIT_ASSERT(data); 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(); image->Destroy();
} }
}; };


Loading…
Cancel
Save