Lock/Unlock mechanism safer, and implement the rule of three.undefined
@@ -1,42 +1,68 @@ | |||||
Important stuff | |||||
--------------- | |||||
[ ] Add scripting support (lua?). | |||||
[ ] Open several maps at the same time. | |||||
[ ] See whether we should use floats or at least subpixel coordinates for | |||||
on-screen objects, in order to handle velocity and other physics stuff | |||||
(with rounding when displaying, of course) | |||||
Large projects | |||||
---------------- | |||||
Needed for game | |||||
--------------- | |||||
[ ] Something to move a character around. | |||||
- [ ] Joystick input. | |||||
- [ ] Event listeners for keyboard or joystick. | |||||
[ ] Map collisions! | |||||
- [ ] Find where to store world collisions | |||||
- [ ] Handle entity/world collisions | |||||
- [ ] Handle entity/entity collisions | |||||
[ ] Pathfinding | |||||
- Add scripting support (lua?). | |||||
- Tiler and Forge are almost the same, try to refactor them. | |||||
Performance | |||||
----------- | |||||
[ ] Switch rendering to vertex/index buffer objects | |||||
Editor | |||||
------ | |||||
[ ] File requester for maps. | |||||
[X] Scroller/panner for maps. | |||||
[ ] Allow to modify maps. | |||||
[X] Do GTK stuff instead of waiting for the framerate | |||||
[X] "map viewer" object/asset, as opposed to more complex in-game manager | |||||
Specs | |||||
----- | |||||
[ ] Write a map file format. | |||||
[ ] Add special zones to the map. | |||||
Architecture | |||||
------------ | |||||
[ ] Tiler and Forge are almost the same, try to refactor them. | |||||
Engine classes | |||||
---------------- | |||||
Image: | |||||
- Handle pitch in SDL codec (and all others, actually) | |||||
- port libpipi files: | |||||
· accessors.cpp | |||||
· pipi-stubs.h | |||||
· render/noise.cpp | |||||
· render/screen.cpp | |||||
· tiles.cpp | |||||
· pipi-internals.h | |||||
· pipi-template.h | |||||
· paint/rectangle.cpp | |||||
· paint/line.cpp | |||||
· paint/floodfill.cpp | |||||
· paint/bezier.cpp | |||||
· paint/tile.cpp | |||||
· pipi-types.h | |||||
· stock.cpp | |||||
· context.cpp | |||||
· pipi.h | |||||
· dither.cpp | |||||
· colorstring.cpp | |||||
· codec/coreimage.cpp | |||||
· codec/coreimage.h | |||||
· codec/oric.cpp | |||||
· codec/jpeg.cpp | |||||
· resample/bresenham.cpp | |||||
· resample/bicubic.cpp | |||||
· quantize/reduce.cpp | |||||
· analysis/histogram.cpp | |||||
· analysis/measure.cpp | |||||
· crop.cpp | |||||
· pipi.cpp | |||||
· codec.cpp | |||||
· dither/ediff.cpp | |||||
· dither/ostromoukhov.cpp | |||||
· dither/random.cpp | |||||
· dither/ordered.cpp | |||||
· dither/dbs.cpp | |||||
· sequence.cpp | |||||
· filter/convolution.cpp | |||||
· filter/wave.cpp | |||||
· filter/dilate.cpp | |||||
· filter/color.cpp | |||||
· filter/blur.cpp | |||||
· filter/yuv.cpp | |||||
· filter/autocontrast.cpp | |||||
· filter/median.cpp | |||||
· filter/rotate.cpp | |||||
· filter/transform.cpp | |||||
· filter/sharpen.cpp | |||||
· combine/rgb.cpp | |||||
· combine/blit.cpp | |||||
· combine/minmax.cpp | |||||
· combine/subadd.cpp | |||||
· combine/merge.cpp | |||||
· combine/mulscreen.cpp | |||||
· pixels.cpp | |||||
@@ -429,15 +429,15 @@ fi | |||||
AM_CONDITIONAL(USE_CACA, test "${ac_cv_my_have_caca}" != "no") | AM_CONDITIONAL(USE_CACA, test "${ac_cv_my_have_caca}" != "no") | ||||
dnl Use libpipi? (required for video recording) | |||||
ac_cv_my_have_pipi="no" | |||||
PKG_CHECK_MODULES(PIPI, pipi, [ac_cv_my_have_pipi="yes"], [:]) | |||||
if test "${ac_cv_my_have_pipi}" != "no"; then | |||||
AC_DEFINE(USE_PIPI, 1, Define to 1 to use libpipi) | |||||
LOL_CFLAGS="${LOL_CFLAGS} ${PIPI_CFLAGS}" | |||||
LOL_LIBS="${LOL_LIBS} ${PIPI_LIBS}" | |||||
# Use Imlib2? | |||||
ac_cv_my_have_imlib2="no" | |||||
PKG_CHECK_MODULES(IMLIB2, imlib2, [ac_cv_my_have_imlib2="yes"], [:]) | |||||
if test "${ac_cv_my_have_imlib2}" != "no"; then | |||||
AC_DEFINE(USE_IMLIB2, 1, Define to 1 to use Imlib2) | |||||
LOL_CFLAGS="${LOL_CFLAGS} ${IMLIB2_CFLAGS}" | |||||
LOL_LIBS="${LOL_LIBS} ${IMLIB2_LIBS}" | |||||
fi | fi | ||||
AM_CONDITIONAL(USE_PIPI, test "${ac_cv_my_have_pipi}" != "no") | |||||
AM_CONDITIONAL(USE_IMLIB2, test "${ac_cv_my_have_imlib2}" = "yes") | |||||
dnl Use GTK+? (required for the deushax editor) | dnl Use GTK+? (required for the deushax editor) | ||||
@@ -111,8 +111,8 @@ liblolcore_sources = \ | |||||
sys/thread.cpp sys/threadbase.h \ | sys/thread.cpp sys/threadbase.h \ | ||||
\ | \ | ||||
image/image.cpp image/image-private.h \ | image/image.cpp image/image-private.h \ | ||||
image/codec/gdiplus-image.cpp \ | |||||
image/codec/ios-image.cpp \ | |||||
image/codec/gdiplus-image.cpp image/codec/imlib2-image.cpp \ | |||||
image/codec/sdl-image.cpp image/codec/ios-image.cpp \ | |||||
image/codec/zed-image.cpp image/codec/zed-palette-image.cpp \ | image/codec/zed-image.cpp image/codec/zed-palette-image.cpp \ | ||||
image/codec/dummy-image.cpp \ | image/codec/dummy-image.cpp \ | ||||
image/color/cie1931.cpp \ | image/color/cie1931.cpp \ | ||||
@@ -123,7 +123,6 @@ liblolcore_sources = \ | |||||
debug/record.cpp debug/record.h debug/stats.cpp debug/stats.h | debug/record.cpp debug/record.h debug/stats.cpp debug/stats.h | ||||
sdl_sources = \ | sdl_sources = \ | ||||
image/codec/sdl-image.cpp \ | |||||
platform/sdl/sdlapp.cpp platform/sdl/sdlapp.h \ | platform/sdl/sdlapp.cpp platform/sdl/sdlapp.h \ | ||||
platform/sdl/sdlinput.cpp platform/sdl/sdlinput.h | platform/sdl/sdlinput.cpp platform/sdl/sdlinput.h | ||||
@@ -92,18 +92,24 @@ Texture::Texture(ivec2 size, PixelFormat format) | |||||
/* FIXME: this is all mixed up for the RGBA/ARGB combinations */ | /* FIXME: this is all mixed up for the RGBA/ARGB combinations */ | ||||
# if defined USE_D3D9 | # if defined USE_D3D9 | ||||
{ D3DFMT_L8, 1 }, /* Y8 */ | |||||
{ D3DFMT_R8G8B8, 3 }, /* RGB_8 */ | { D3DFMT_R8G8B8, 3 }, /* RGB_8 */ | ||||
{ D3DFMT_A8R8G8B8, 4 }, /* RGBA_8 */ | { D3DFMT_A8R8G8B8, 4 }, /* RGBA_8 */ | ||||
{ D3DFMT_A8R8G8B8, 4 }, /* ARGB_8 */ | { D3DFMT_A8R8G8B8, 4 }, /* ARGB_8 */ | ||||
{ D3DFMT_UNKNOWN, 0 }, /* ABGR_8 */ | { D3DFMT_UNKNOWN, 0 }, /* ABGR_8 */ | ||||
{ D3DFMT_L8, 1 }, /* Y8 */ | |||||
{ D3DFMT_UNKNOWN, 0 }, /* Y_F32 */ | |||||
{ D3DFMT_UNKNOWN, 0 }, /* RGB_F32 */ | |||||
{ D3DFMT_UNKNOWN, 0 }, /* RGBA_F32 */ | |||||
# else | # else | ||||
{ D3DFMT_LIN_L8, 1 }, | |||||
{ D3DFMT_UNKNOWN, 0 }, | { D3DFMT_UNKNOWN, 0 }, | ||||
{ D3DFMT_UNKNOWN, 0 }, | { D3DFMT_UNKNOWN, 0 }, | ||||
/* By default the X360 will swizzle the texture. Ask for linear. */ | /* By default the X360 will swizzle the texture. Ask for linear. */ | ||||
{ D3DFMT_LIN_A8R8G8B8, 4 }, | { D3DFMT_LIN_A8R8G8B8, 4 }, | ||||
{ D3DFMT_UNKNOWN, 0 }, | { D3DFMT_UNKNOWN, 0 }, | ||||
{ D3DFMT_LIN_L8, 1 }, | |||||
{ D3DFMT_UNKNOWN, 0 }, /* Y_F32 */ | |||||
{ D3DFMT_UNKNOWN, 0 }, /* RGB_F32 */ | |||||
{ D3DFMT_UNKNOWN, 0 }, /* RGBA_F32 */ | |||||
# endif | # endif | ||||
}; | }; | ||||
@@ -133,27 +139,30 @@ Texture::Texture(ivec2 size, PixelFormat format) | |||||
/* FIXME: this is all mixed up for the RGBA/ARGB combinations */ | /* FIXME: this is all mixed up for the RGBA/ARGB combinations */ | ||||
#if __CELLOS_LV2__ | #if __CELLOS_LV2__ | ||||
{ GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||||
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, | { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, | ||||
{ GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | { GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | ||||
{ GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | { GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | ||||
{ GL_ARGB_SCE, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | { GL_ARGB_SCE, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 4 }, | ||||
{ GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||||
#elif defined __native_client__ || defined HAVE_GLES_2X | #elif defined __native_client__ || defined HAVE_GLES_2X | ||||
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||||
{ GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 3 }, | { GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 3 }, | ||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | ||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | ||||
/* FIXME: if GL_RGBA is not available, we should advertise | /* FIXME: if GL_RGBA is not available, we should advertise | ||||
* this format as "not available" on this platform. */ | * this format as "not available" on this platform. */ | ||||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | ||||
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||||
#else | #else | ||||
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, /* A8 */ | |||||
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, /* RGB_8 */ | { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, /* RGB_8 */ | ||||
/* Seems efficient for little endian textures */ | /* Seems efficient for little endian textures */ | ||||
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */ | { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */ | ||||
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */ | { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */ | ||||
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ABGR_8 */ | { GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ABGR_8 */ | ||||
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, /* A8 */ | |||||
#endif | #endif | ||||
{ 0, 0, 0, 0 }, /* Y_F32 */ | |||||
{ 0, 0, 0, 0 }, /* RGB_F32 */ | |||||
{ 0, 0, 0, 0 }, /* RGBA_F32 */ | |||||
}; | }; | ||||
m_data->m_internal_format = GET_CLAMPED(gl_formats, format).internal_format; | m_data->m_internal_format = GET_CLAMPED(gl_formats, format).internal_format; | ||||
@@ -52,7 +52,7 @@ bool DummyImageCodec::Load(Image *image, char const *path) | |||||
pixels->a = (((i >> 4) ^ (j >> 4)) & 1) * 0xff; | pixels->a = (((i >> 4) ^ (j >> 4)) & 1) * 0xff; | ||||
++pixels; | ++pixels; | ||||
} | } | ||||
image->Unlock(); | |||||
image->Unlock(pixels); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -1,148 +0,0 @@ | |||||
/* | |||||
* libpipi Pathetic image processing interface library | |||||
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org> | |||||
* All Rights Reserved | |||||
* | |||||
* $Id$ | |||||
* | |||||
* This library is free software. It comes without any warranty, to | |||||
* the extent permitted by applicable law. You can redistribute it | |||||
* and/or modify it under the terms of the Do What The Fuck You Want | |||||
* To Public License, Version 2, as published by Sam Hocevar. See | |||||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||||
*/ | |||||
/* | |||||
* gdi.c: Windows GDI I/O functions (BMP only) | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <windows.h> | |||||
#include "pipi.h" | |||||
#include "pipi-internals.h" | |||||
pipi_image_t *pipi_load_gdi(const char *name) | |||||
{ | |||||
BITMAPINFO binfo; | |||||
pipi_image_t *img; | |||||
pipi_pixels_t *p; | |||||
uint8_t *data; | |||||
HBITMAP hbmap; | |||||
HDC hdc; | |||||
hbmap = (HBITMAP)LoadImage(NULL, name, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); | |||||
if(!hbmap) | |||||
return NULL; | |||||
hdc = CreateCompatibleDC(NULL); | |||||
SelectObject(hdc, hbmap); | |||||
memset(&binfo, 0, sizeof(binfo)); | |||||
binfo.bmiHeader.biSize = sizeof(binfo.bmiHeader); | |||||
if(!GetDIBits(hdc, hbmap, 0, 0, 0, &binfo, DIB_RGB_COLORS)) | |||||
{ | |||||
ReleaseDC(0, hdc); | |||||
DeleteObject(hbmap); | |||||
return NULL; | |||||
} | |||||
img = pipi_new(binfo.bmiHeader.biWidth, binfo.bmiHeader.biHeight); | |||||
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8); | |||||
data = p->pixels; | |||||
binfo.bmiHeader.biBitCount = 32; | |||||
binfo.bmiHeader.biCompression = BI_RGB; | |||||
binfo.bmiHeader.biHeight = - abs(binfo.bmiHeader.biHeight); | |||||
if(!GetDIBits(hdc, hbmap, 0, abs(binfo.bmiHeader.biHeight), data, | |||||
&binfo, DIB_RGB_COLORS)) | |||||
{ | |||||
ReleaseDC(0, hdc); | |||||
DeleteObject(hbmap); | |||||
return NULL; | |||||
} | |||||
/* FIXME: do we need to swap bytes? Apparently Vista doesn't need it, | |||||
* but we'd need a more thorough test. */ | |||||
pipi_release_pixels(img, p); | |||||
img->codec_priv = NULL; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
ReleaseDC(0, hdc); | |||||
DeleteObject(hbmap); | |||||
return img; | |||||
} | |||||
int pipi_save_gdi(pipi_image_t *img, const char *name) | |||||
{ | |||||
BITMAPINFOHEADER binfo; | |||||
BITMAPFILEHEADER bfheader; | |||||
uint8_t dummy[4] = { 0 }; | |||||
HANDLE hfile; | |||||
pipi_pixels_t *p; | |||||
DWORD ret = 0; | |||||
uint8_t *data; | |||||
int x, y, padding; | |||||
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8); | |||||
data = p->pixels; | |||||
padding = ((img->w * 3) + 3) / 4 * 4 - img->w * 3; | |||||
memset(&binfo, 0, sizeof(binfo)); | |||||
binfo.biSize = sizeof(binfo); | |||||
binfo.biWidth = img->w; | |||||
binfo.biHeight = img->h; | |||||
binfo.biPlanes = 1; | |||||
binfo.biBitCount = 24; | |||||
binfo.biCompression = BI_RGB; | |||||
binfo.biSizeImage = (img->w * 3 + padding) * img->h; | |||||
memset(&bfheader, 0, sizeof(bfheader)); | |||||
bfheader.bfType = 0x4D42; | |||||
bfheader.bfOffBits = sizeof(bfheader) + sizeof(binfo); | |||||
bfheader.bfSize = bfheader.bfOffBits + binfo.biSizeImage; | |||||
/* We don’t even create the bitmap object, since we know exactly | |||||
* what kind of file we are saving. But later, when we support | |||||
* different depths and BMP options, we'll need to care about it. */ | |||||
hfile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, | |||||
FILE_ATTRIBUTE_ARCHIVE, NULL); | |||||
if(!hfile) | |||||
return -1; | |||||
WriteFile(hfile, &bfheader, sizeof(bfheader), &ret, NULL); | |||||
WriteFile(hfile, &binfo, sizeof(binfo), &ret, NULL); | |||||
for(y = 0; y < img->h; y++) | |||||
{ | |||||
for(x = 0; x < img->w; x++) | |||||
{ | |||||
uint8_t tmp[3]; | |||||
tmp[0] = data[4 * (img->w * (img->h - 1 - y) + x) + 0]; | |||||
tmp[1] = data[4 * (img->w * (img->h - 1 - y) + x) + 1]; | |||||
tmp[2] = data[4 * (img->w * (img->h - 1 - y) + x) + 2]; | |||||
WriteFile(hfile, tmp, 3, &ret, NULL); | |||||
} | |||||
if(padding) | |||||
WriteFile(hfile, dummy, padding, &ret, NULL); | |||||
} | |||||
CloseHandle(hfile); | |||||
pipi_release_pixels(img, p); | |||||
return 0; | |||||
} | |||||
/* | |||||
* XXX: The following functions are local. | |||||
*/ | |||||
@@ -122,12 +122,12 @@ bool GdiPlusImageCodec::Load(Image *image, 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. */ | ||||
image->SetSize(size); | image->SetSize(size); | ||||
u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>(); | |||||
u8vec4 *source = static_cast<u8vec4 *>(bdata.Scan0); | |||||
u8vec4 *pdst = image->Lock<PixelFormat::RGBA_8>(); | |||||
u8vec4 *psrc = static_cast<u8vec4 *>(bdata.Scan0); | |||||
for (int y = 0; y < size.y; y++) | for (int y = 0; y < size.y; y++) | ||||
for (int x = 0; x < size.x; x++) | for (int x = 0; x < size.x; x++) | ||||
*pixels++ = (*source++).bgra; | |||||
image->Unlock(); | |||||
*pdst++ = (*psrc++).bgra; | |||||
image->Unlock(pdst); | |||||
bitmap->UnlockBits(&bdata); | bitmap->UnlockBits(&bdata); | ||||
delete bitmap; | delete bitmap; | ||||
@@ -210,7 +210,7 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||||
for (int y = 0; y < size.y; y++) | for (int y = 0; y < size.y; y++) | ||||
for (int x = 0; x < size.x; x++) | for (int x = 0; x < size.x; x++) | ||||
*pdst++ = (*psrc++).bgra; | *pdst++ = (*psrc++).bgra; | ||||
image->Unlock(); | |||||
image->Unlock(pdst); | |||||
b->UnlockBits(&bdata); | b->UnlockBits(&bdata); | ||||
if (b->Save(wpath, &clsid, NULL) != Gdiplus::Ok) | if (b->Save(wpath, &clsid, NULL) != Gdiplus::Ok) | ||||
@@ -1,120 +0,0 @@ | |||||
/* | |||||
* libpipi Pathetic image processing interface library | |||||
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org> | |||||
* All Rights Reserved | |||||
* | |||||
* $Id$ | |||||
* | |||||
* This library is free software. It comes without any warranty, to | |||||
* the extent permitted by applicable law. You can redistribute it | |||||
* and/or modify it under the terms of the Do What The Fuck You Want | |||||
* To Public License, Version 2, as published by Sam Hocevar. See | |||||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||||
*/ | |||||
/* | |||||
* imlib.c: ImLib I/O functions | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <Imlib2.h> | |||||
#include "pipi.h" | |||||
#include "pipi-internals.h" | |||||
static int pipi_free_imlib2(pipi_image_t *); | |||||
pipi_image_t *pipi_load_imlib2(const char *name) | |||||
{ | |||||
pipi_image_t *img; | |||||
Imlib_Image priv = imlib_load_image(name); | |||||
if(!priv) | |||||
return NULL; | |||||
imlib_context_set_image(priv); | |||||
if(!imlib_image_get_data()) | |||||
{ | |||||
imlib_free_image(); | |||||
return NULL; | |||||
} | |||||
img = pipi_new(imlib_image_get_width(), imlib_image_get_height()); | |||||
img->p[PIPI_PIXELS_RGBA_U8].pixels = imlib_image_get_data(); | |||||
img->p[PIPI_PIXELS_RGBA_U8].w = img->w; | |||||
img->p[PIPI_PIXELS_RGBA_U8].h = img->h; | |||||
img->p[PIPI_PIXELS_RGBA_U8].pitch = 4 * img->w; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h; | |||||
img->last_modified = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_free = pipi_free_imlib2; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
return img; | |||||
} | |||||
int pipi_save_imlib2(pipi_image_t *img, const char *name) | |||||
{ | |||||
if(!img->codec_priv) | |||||
{ | |||||
Imlib_Image priv = imlib_create_image(img->w, img->h); | |||||
void *data; | |||||
imlib_context_set_image(priv); | |||||
imlib_image_set_has_alpha(1); | |||||
data = imlib_image_get_data(); | |||||
/* FIXME: check pitch differences here */ | |||||
if(img->last_modified == PIPI_PIXELS_RGBA_U8) | |||||
{ | |||||
memcpy(data, img->p[PIPI_PIXELS_RGBA_U8].pixels, | |||||
4 * img->w * img->h); | |||||
free(img->p[PIPI_PIXELS_RGBA_U8].pixels); | |||||
} | |||||
img->p[PIPI_PIXELS_RGBA_U8].pixels = data; | |||||
img->p[PIPI_PIXELS_RGBA_U8].w = imlib_image_get_width(); | |||||
img->p[PIPI_PIXELS_RGBA_U8].h = imlib_image_get_height(); | |||||
img->p[PIPI_PIXELS_RGBA_U8].pitch = 4 * imlib_image_get_width(); | |||||
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_free = pipi_free_imlib2; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
} | |||||
pipi_set_colorspace(img, img->codec_format); | |||||
imlib_context_set_image(img->codec_priv); | |||||
imlib_save_image(name); | |||||
return 0; | |||||
} | |||||
/* | |||||
* XXX: The following functions are local. | |||||
*/ | |||||
static int pipi_free_imlib2(pipi_image_t *img) | |||||
{ | |||||
imlib_context_set_image(img->codec_priv); | |||||
imlib_free_image(); | |||||
return 0; | |||||
} | |||||
@@ -0,0 +1,105 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net> | |||||
// This program is free software; you can redistribute it and/or | |||||
// modify it under the terms of the Do What The Fuck You Want To | |||||
// Public License, Version 2, as published by Sam Hocevar. See | |||||
// http://www.wtfpl.net/ for more details. | |||||
// | |||||
#if defined HAVE_CONFIG_H | |||||
# include "config.h" | |||||
#endif | |||||
#if defined USE_IMLIB2 | |||||
#include <Imlib2.h> | |||||
#include "core.h" | |||||
#include "../../image/image-private.h" | |||||
using namespace std; | |||||
namespace lol | |||||
{ | |||||
/* | |||||
* Imlib2 image codec | |||||
*/ | |||||
class Imlib2ImageCodec : public ImageCodec | |||||
{ | |||||
public: | |||||
virtual bool Load(Image *image, char const *path); | |||||
virtual bool Save(Image *image, char const *path); | |||||
}; | |||||
/* Set priority higher than SDL because we can save in many formats. */ | |||||
DECLARE_IMAGE_CODEC(Imlib2ImageCodec, 70) | |||||
bool Imlib2ImageCodec::Load(Image *image, char const *path) | |||||
{ | |||||
Imlib_Image priv = nullptr; | |||||
Array<String> pathlist = System::GetPathList(path); | |||||
for (int i = 0; i < pathlist.Count(); i++) | |||||
{ | |||||
priv = imlib_load_image(pathlist[i].C()); | |||||
if (priv) | |||||
break; | |||||
} | |||||
if (!priv) | |||||
{ | |||||
#if !LOL_BUILD_RELEASE | |||||
Log::Error("could not load image %s\n", path); | |||||
#endif | |||||
return false; | |||||
} | |||||
imlib_context_set_image(priv); | |||||
if (!imlib_image_get_data()) | |||||
{ | |||||
imlib_free_image(); | |||||
#if !LOL_BUILD_RELEASE | |||||
Log::Error("could not get image data for %s\n", path); | |||||
#endif | |||||
return false; | |||||
} | |||||
ivec2 size(imlib_image_get_width(), imlib_image_get_height()); | |||||
image->SetSize(size); | |||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||||
memcpy(data, imlib_image_get_data(), 4 * size.x * size.y); | |||||
image->Unlock(data); | |||||
imlib_free_image(); | |||||
return true; | |||||
} | |||||
bool Imlib2ImageCodec::Save(Image *image, char const *path) | |||||
{ | |||||
ivec2 size = image->GetSize(); | |||||
Imlib_Image priv = imlib_create_image(size.x, size.y); | |||||
imlib_context_set_image(priv); | |||||
imlib_image_set_has_alpha(1); | |||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||||
memcpy(imlib_image_get_data(), data, 4 * size.x * size.y); | |||||
image->Unlock(data); | |||||
imlib_save_image(path); | |||||
imlib_free_image(); | |||||
return true; | |||||
} | |||||
} /* namespace lol */ | |||||
#endif /* defined USE_IMLIB2 */ | |||||
@@ -1,112 +0,0 @@ | |||||
/* | |||||
* libpipi Pathetic image processing interface library | |||||
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org> | |||||
* All Rights Reserved | |||||
* | |||||
* $Id$ | |||||
* | |||||
* This library is free software. It comes without any warranty, to | |||||
* the extent permitted by applicable law. You can redistribute it | |||||
* and/or modify it under the terms of the Do What The Fuck You Want | |||||
* To Public License, Version 2, as published by Sam Hocevar. See | |||||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||||
*/ | |||||
/* | |||||
* image.c: image I/O functions | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <cv.h> | |||||
#include <highgui.h> | |||||
#include "pipi.h" | |||||
#include "pipi-internals.h" | |||||
/* FIXME: this whole file is broken until we support BGR24 images */ | |||||
static int pipi_free_opencv(pipi_image_t *); | |||||
pipi_image_t *pipi_load_opencv(const char *name) | |||||
{ | |||||
pipi_image_t *img; | |||||
IplImage *priv = cvLoadImage(name, 1); | |||||
if(!priv) | |||||
return NULL; | |||||
img = pipi_new(priv->width, priv->height); | |||||
img->p[PIPI_PIXELS_BGR_U8].pixels = priv->imageData; | |||||
img->p[PIPI_PIXELS_BGR_U8].w = priv->width; | |||||
img->p[PIPI_PIXELS_BGR_U8].h = priv->height; | |||||
img->p[PIPI_PIXELS_BGR_U8].pitch = priv->widthStep; | |||||
img->p[PIPI_PIXELS_BGR_U8].bpp = 24; | |||||
img->p[PIPI_PIXELS_BGR_U8].bytes = 3 * img->w * img->h; | |||||
img->last_modified = PIPI_PIXELS_BGR_U8; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_BGR_U8; | |||||
img->codec_free = pipi_free_opencv; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
return img; | |||||
} | |||||
int pipi_save_opencv(pipi_image_t *img, const char *name) | |||||
{ | |||||
if(!img->codec_priv) | |||||
{ | |||||
IplImage *priv = cvCreateImage(cvSize(img->w, img->h), | |||||
IPL_DEPTH_8U, 3); | |||||
/* FIXME: check pitch differences here */ | |||||
if(img->last_modified == PIPI_PIXELS_BGR_U8) | |||||
{ | |||||
memcpy(priv->imageData, img->p[PIPI_PIXELS_BGR_U8].pixels, | |||||
3 * img->w * img->h); | |||||
free(img->p[PIPI_PIXELS_BGR_U8].pixels); | |||||
} | |||||
img->p[PIPI_PIXELS_BGR_U8].pixels = priv->imageData; | |||||
img->p[PIPI_PIXELS_BGR_U8].w = priv->width; | |||||
img->p[PIPI_PIXELS_BGR_U8].h = priv->height; | |||||
img->p[PIPI_PIXELS_BGR_U8].pitch = priv->widthStep; | |||||
img->p[PIPI_PIXELS_BGR_U8].bpp = 24; | |||||
img->p[PIPI_PIXELS_BGR_U8].bytes = 3 * img->w * img->h; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_BGR_U8; | |||||
img->codec_free = pipi_free_opencv; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
} | |||||
pipi_set_colorspace(img, img->codec_format); | |||||
cvSaveImage(name, img->codec_priv, NULL); | |||||
return 0; | |||||
} | |||||
/* | |||||
* XXX: The following functions are local. | |||||
*/ | |||||
static int pipi_free_opencv(pipi_image_t *img) | |||||
{ | |||||
IplImage *iplimg; | |||||
iplimg = (IplImage *)img->codec_priv; | |||||
cvReleaseImage(&iplimg); | |||||
return 0; | |||||
} | |||||
@@ -48,10 +48,6 @@ public: | |||||
DECLARE_IMAGE_CODEC(SdlImageCodec, 50) | DECLARE_IMAGE_CODEC(SdlImageCodec, 50) | ||||
/* | |||||
* Public Image class | |||||
*/ | |||||
bool SdlImageCodec::Load(Image *image, char const *path) | bool SdlImageCodec::Load(Image *image, char const *path) | ||||
{ | { | ||||
SDL_Surface *surface = nullptr; | SDL_Surface *surface = nullptr; | ||||
@@ -85,7 +81,7 @@ bool SdlImageCodec::Load(Image *image, char const *path) | |||||
image->SetSize(size); | image->SetSize(size); | ||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | ||||
memcpy(data, surface->pixels, 4 * size.x * size.y); | memcpy(data, surface->pixels, 4 * size.x * size.y); | ||||
image->Unlock(); | |||||
image->Unlock(data); | |||||
SDL_FreeSurface(surface); | SDL_FreeSurface(surface); | ||||
@@ -99,7 +95,7 @@ bool SdlImageCodec::Save(Image *image, char const *path) | |||||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | ||||
memcpy(surface->pixels, data, 4 * size.x * size.y); | memcpy(surface->pixels, data, 4 * size.x * size.y); | ||||
image->Unlock(); | |||||
image->Unlock(data); | |||||
int ret = SDL_SaveBMP(surface, path); | int ret = SDL_SaveBMP(surface, path); | ||||
SDL_FreeSurface(surface); | SDL_FreeSurface(surface); | ||||
@@ -1,133 +0,0 @@ | |||||
/* | |||||
* libpipi Pathetic image processing interface library | |||||
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org> | |||||
* All Rights Reserved | |||||
* | |||||
* $Id$ | |||||
* | |||||
* This library is free software. It comes without any warranty, to | |||||
* the extent permitted by applicable law. You can redistribute it | |||||
* and/or modify it under the terms of the Do What The Fuck You Want | |||||
* To Public License, Version 2, as published by Sam Hocevar. See | |||||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||||
*/ | |||||
/* | |||||
* sdl.c: SDL_image I/O functions | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <SDL_image.h> | |||||
#include "pipi.h" | |||||
#include "pipi-internals.h" | |||||
static int pipi_free_sdl(pipi_image_t *); | |||||
static SDL_Surface *create_32bpp_surface(int w, int h); | |||||
pipi_image_t *pipi_load_sdl(const char *name) | |||||
{ | |||||
pipi_image_t *img; | |||||
SDL_Surface *priv = IMG_Load(name); | |||||
if(!priv) | |||||
return NULL; | |||||
if(priv->format->BytesPerPixel != 4) | |||||
{ | |||||
SDL_Surface *tmp = create_32bpp_surface(priv->w, priv->h); | |||||
SDL_BlitSurface(priv, NULL, tmp, NULL); | |||||
SDL_FreeSurface(priv); | |||||
priv = tmp; | |||||
} | |||||
img = pipi_new(priv->w, priv->h); | |||||
img->p[PIPI_PIXELS_RGBA_U8].pixels = priv->pixels; | |||||
img->p[PIPI_PIXELS_RGBA_U8].w = priv->w; | |||||
img->p[PIPI_PIXELS_RGBA_U8].h = priv->h; | |||||
img->p[PIPI_PIXELS_RGBA_U8].pitch = priv->pitch; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h; | |||||
img->last_modified = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_free = pipi_free_sdl; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
return img; | |||||
} | |||||
int pipi_save_sdl(pipi_image_t *img, const char *name) | |||||
{ | |||||
if(!img->codec_priv) | |||||
{ | |||||
SDL_Surface *priv = create_32bpp_surface(img->w, img->h); | |||||
/* FIXME: check pitch differences here */ | |||||
if(img->last_modified == PIPI_PIXELS_RGBA_U8) | |||||
{ | |||||
memcpy(priv->pixels, img->p[PIPI_PIXELS_RGBA_U8].pixels, | |||||
priv->pitch * priv->h); | |||||
free(img->p[PIPI_PIXELS_RGBA_U8].pixels); | |||||
} | |||||
img->p[PIPI_PIXELS_RGBA_U8].pixels = priv->pixels; | |||||
img->p[PIPI_PIXELS_RGBA_U8].w = priv->w; | |||||
img->p[PIPI_PIXELS_RGBA_U8].h = priv->h; | |||||
img->p[PIPI_PIXELS_RGBA_U8].pitch = priv->pitch; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32; | |||||
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h; | |||||
img->codec_priv = (void *)priv; | |||||
img->codec_format = PIPI_PIXELS_RGBA_U8; | |||||
img->codec_free = pipi_free_sdl; | |||||
img->wrap = 0; | |||||
img->u8 = 1; | |||||
} | |||||
pipi_set_colorspace(img, img->codec_format); | |||||
SDL_SaveBMP(img->codec_priv, name); | |||||
return 0; | |||||
} | |||||
/* | |||||
* XXX: The following functions are local. | |||||
*/ | |||||
static int pipi_free_sdl(pipi_image_t *img) | |||||
{ | |||||
SDL_FreeSurface(img->codec_priv); | |||||
return 0; | |||||
} | |||||
static SDL_Surface *create_32bpp_surface(int w, int h) | |||||
{ | |||||
Uint32 rmask, gmask, bmask, amask; | |||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN | |||||
rmask = 0xff000000; | |||||
gmask = 0x00ff0000; | |||||
bmask = 0x0000ff00; | |||||
amask = 0x00000000; | |||||
#else | |||||
rmask = 0x000000ff; | |||||
gmask = 0x0000ff00; | |||||
bmask = 0x00ff0000; | |||||
amask = 0x00000000; | |||||
#endif | |||||
return SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, | |||||
rmask, gmask, bmask, amask); | |||||
} | |||||
@@ -40,7 +40,6 @@ public: | |||||
} | } | ||||
private: | private: | ||||
uint8_t *m_pixels; | |||||
//<Pos, Size> | //<Pos, Size> | ||||
Array<ivec2, ivec2> m_tiles; | Array<ivec2, ivec2> m_tiles; | ||||
}; | }; | ||||
@@ -295,7 +294,7 @@ bool ZedImageCodec::Load(Image *image, char const *path) | |||||
j++; | j++; | ||||
} | } | ||||
} | } | ||||
image->Unlock(); | |||||
image->Unlock(pixels); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -75,7 +75,7 @@ bool ZedPaletteImageCodec::Load(Image *image, char const *path) | |||||
pixels->a = (i == 0) ? 0 : 255; | pixels->a = (i == 0) ? 0 : 255; | ||||
++pixels; | ++pixels; | ||||
} | } | ||||
image->Unlock(); | |||||
image->Unlock(pixels); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -31,7 +31,9 @@ public: | |||||
ivec2 m_size; | ivec2 m_size; | ||||
/* A map of the various available bitplanes */ | |||||
Map<int, void *> m_pixels; | Map<int, void *> m_pixels; | ||||
/* The last bitplane being accessed for writing */ | |||||
PixelFormat m_format; | PixelFormat m_format; | ||||
}; | }; | ||||
@@ -20,7 +20,7 @@ using namespace std; | |||||
namespace lol | namespace lol | ||||
{ | { | ||||
/* HACK: We cannot make this an ImageCodec member function, because the | |||||
/* HACK: We cannot make this an ImageLoader member function, because the | |||||
* REGISTER_IMAGE_CODEC 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 | ||||
@@ -50,6 +50,9 @@ static bool RegisterAllCodecs(Array<ImageCodec *> &codeclist) | |||||
#endif | #endif | ||||
REGISTER_IMAGE_CODEC(ZedImageCodec) | REGISTER_IMAGE_CODEC(ZedImageCodec) | ||||
REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) | REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) | ||||
#if defined USE_IMLIB2 | |||||
REGISTER_IMAGE_CODEC(Imlib2ImageCodec) | |||||
#endif | |||||
return true; | return true; | ||||
} | } | ||||
@@ -58,42 +61,21 @@ static bool RegisterAllCodecs(Array<ImageCodec *> &codeclist) | |||||
* Our static image loader | * Our static image loader | ||||
*/ | */ | ||||
static class ImageBank | |||||
static class ImageLoader | |||||
{ | { | ||||
public: | |||||
ImageBank(); | |||||
friend class Image; | |||||
bool Load(Image *image, char const *path); | |||||
bool Save(Image *image, char const *path); | |||||
public: | |||||
inline ImageLoader() | |||||
{ | |||||
RegisterAllCodecs(m_codecs); | |||||
} | |||||
private: | private: | ||||
Array<ImageCodec *> m_codecs; | Array<ImageCodec *> m_codecs; | ||||
Map<String, Image *> m_images; | Map<String, Image *> m_images; | ||||
} | } | ||||
g_image_bank; | |||||
ImageBank::ImageBank() | |||||
{ | |||||
RegisterAllCodecs(m_codecs); | |||||
} | |||||
bool ImageBank::Load(Image *image, char const *path) | |||||
{ | |||||
/* FIXME: priority is ignored */ | |||||
for (auto codec : m_codecs) | |||||
if (codec->Load(image, path)) | |||||
return true; | |||||
return false; | |||||
} | |||||
bool ImageBank::Save(Image *image, char const *path) | |||||
{ | |||||
/* FIXME: priority is ignored */ | |||||
for (auto codec : m_codecs) | |||||
if (codec->Save(image, path)) | |||||
return true; | |||||
return false; | |||||
} | |||||
g_image_loader; | |||||
/* | /* | ||||
* Public Image class | * Public Image class | ||||
@@ -116,6 +98,29 @@ Image::Image(ivec2 size) | |||||
SetSize(size); | SetSize(size); | ||||
} | } | ||||
Image::Image (Image const &other) | |||||
: m_data(new ImageData()) | |||||
{ | |||||
ivec2 size = other.GetSize(); | |||||
PixelFormat fmt = other.GetFormat(); | |||||
SetSize(size); | |||||
if (fmt != PixelFormat::Unknown) | |||||
{ | |||||
SetFormat(fmt); | |||||
void *psrc = other.m_data->m_pixels[(int)fmt]; | |||||
void *pdst = m_data->m_pixels[(int)fmt]; | |||||
memcpy(pdst, psrc, size.x * size.y * BytesPerPixel(fmt)); | |||||
} | |||||
} | |||||
Image & Image::operator =(Image other) | |||||
{ | |||||
/* Since the argument is passed by value, we’re assured it’s a new | |||||
* object and we can safely swap our m_data pointers. */ | |||||
std::swap(m_data, other.m_data); | |||||
} | |||||
Image::~Image() | Image::~Image() | ||||
{ | { | ||||
delete m_data; | delete m_data; | ||||
@@ -123,12 +128,18 @@ Image::~Image() | |||||
bool Image::Load(char const *path) | bool Image::Load(char const *path) | ||||
{ | { | ||||
return g_image_bank.Load(this, path); | |||||
for (auto codec : g_image_loader.m_codecs) | |||||
if (codec->Load(this, path)) | |||||
return true; | |||||
return false; | |||||
} | } | ||||
bool Image::Save(char const *path) | bool Image::Save(char const *path) | ||||
{ | { | ||||
return g_image_bank.Save(this, path); | |||||
for (auto codec : g_image_loader.m_codecs) | |||||
if (codec->Save(this, path)) | |||||
return true; | |||||
return false; | |||||
} | } | ||||
ivec2 Image::GetSize() const | ivec2 Image::GetSize() const | ||||
@@ -148,11 +159,10 @@ void Image::SetSize(ivec2 size) | |||||
} | } | ||||
m_data->m_size = size; | m_data->m_size = size; | ||||
/* FIXME: don’t do this! */ | |||||
/* FIXME: don’t do this! It’s useless. */ | |||||
if (m_data->m_format != PixelFormat::Unknown) | if (m_data->m_format != PixelFormat::Unknown) | ||||
{ | { | ||||
Lock<PixelFormat::RGBA_8>(); | |||||
Unlock(); | |||||
Unlock(Lock<PixelFormat::RGBA_8>()); | |||||
} | } | ||||
} | } | ||||
@@ -161,31 +171,45 @@ PixelFormat Image::GetFormat() const | |||||
return m_data->m_format; | return m_data->m_format; | ||||
} | } | ||||
/* The Lock() method and its explicit specialisations */ | |||||
template<PixelFormat T> | |||||
typename PixelType<T>::type *Image::Lock() | |||||
void Image::SetFormat(PixelFormat fmt) | |||||
{ | { | ||||
/* 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 we never used this format, allocate a new buffer: we will | |||||
* obviously need it. */ | |||||
if (m_data->m_pixels[(int)fmt] == nullptr) | |||||
{ | |||||
int bytes = m_data->m_size.x * m_data->m_size.y * BytesPerPixel(fmt); | |||||
m_data->m_pixels[(int)fmt] = new uint8_t[bytes]; | |||||
} | |||||
if (!m_data->m_pixels.HasKey((int)T)) | |||||
/* If the requested format is different from the current format, and if | |||||
* the current format is a valid format, convert pixels. */ | |||||
if (fmt != m_data->m_format && m_data->m_format != PixelFormat::Unknown) | |||||
{ | { | ||||
m_data->m_pixels[(int)T] = | |||||
new typename PixelType<T>::type[m_data->m_size.x * m_data->m_size.y]; | |||||
/* TODO: convert data */ | |||||
} | } | ||||
m_data->m_format = fmt; | |||||
} | |||||
/* The Lock() method */ | |||||
template<PixelFormat T> | |||||
typename PixelType<T>::type *Image::Lock() | |||||
{ | |||||
SetFormat(T); | |||||
return (typename PixelType<T>::type *)m_data->m_pixels[(int)T]; | return (typename PixelType<T>::type *)m_data->m_pixels[(int)T]; | ||||
} | } | ||||
template PixelType<PixelFormat::Y_8>::type * | |||||
Image::Lock<PixelFormat::Y_8>(); | |||||
template PixelType<PixelFormat::RGB_8>::type * | |||||
Image::Lock<PixelFormat::RGB_8>(); | |||||
template PixelType<PixelFormat::RGBA_8>::type * | |||||
Image::Lock<PixelFormat::RGBA_8>(); | |||||
/* Explicit specialisations for the above template */ | |||||
#define _T(T) template PixelType<T>::type *Image::Lock<T>() | |||||
_T(PixelFormat::Y_8); | |||||
_T(PixelFormat::RGB_8); | |||||
_T(PixelFormat::RGBA_8); | |||||
_T(PixelFormat::Y_F32); | |||||
_T(PixelFormat::RGB_F32); | |||||
_T(PixelFormat::RGBA_F32); | |||||
#undef _T | |||||
/* Special case for the "any" format */ | |||||
/* Special case for the "any" format: return the last active buffer */ | |||||
template<> | template<> | ||||
void *Image::Lock<PixelFormat::Unknown>() | void *Image::Lock<PixelFormat::Unknown>() | ||||
{ | { | ||||
@@ -194,10 +218,17 @@ void *Image::Lock<PixelFormat::Unknown>() | |||||
return m_data->m_pixels[(int)m_data->m_format]; | return m_data->m_pixels[(int)m_data->m_format]; | ||||
} | } | ||||
void Image::Unlock() | |||||
void Image::Unlock(void const *pixels) | |||||
{ | { | ||||
/* TODO: ensure we are actually unlocking something we locked */ | |||||
ASSERT(m_data->m_pixels.HasKey((int)m_data->m_format)); | ASSERT(m_data->m_pixels.HasKey((int)m_data->m_format)); | ||||
/* Ensure that the unlocked data is inside the buffer */ | |||||
uintptr_t start = (uintptr_t)m_data->m_pixels[(int)m_data->m_format]; | |||||
uintptr_t end = start + m_data->m_size.x * m_data->m_size.y | |||||
* BytesPerPixel(m_data->m_format); | |||||
ASSERT(start <= (uintptr_t)pixels); | |||||
ASSERT((uintptr_t)pixels <= end); | |||||
} | } | ||||
bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const | ||||
@@ -23,11 +23,14 @@ enum class PixelFormat | |||||
{ | { | ||||
/* XXX: make sure to update texture.cpp when this changes */ | /* XXX: make sure to update texture.cpp when this changes */ | ||||
Unknown = 0, | Unknown = 0, | ||||
Y_8, | |||||
RGB_8, | RGB_8, | ||||
RGBA_8, | RGBA_8, | ||||
ARGB_8, | ARGB_8, | ||||
ABGR_8, | ABGR_8, | ||||
Y_8, | |||||
Y_F32, | |||||
RGB_F32, | |||||
RGBA_F32, | |||||
}; | }; | ||||
static inline uint8_t BytesPerPixel(PixelFormat format) | static inline uint8_t BytesPerPixel(PixelFormat format) | ||||
@@ -41,8 +44,15 @@ static inline uint8_t BytesPerPixel(PixelFormat format) | |||||
case PixelFormat::RGBA_8: | case PixelFormat::RGBA_8: | ||||
case PixelFormat::ARGB_8: | case PixelFormat::ARGB_8: | ||||
case PixelFormat::ABGR_8: | case PixelFormat::ABGR_8: | ||||
default: | |||||
return 4; | return 4; | ||||
case PixelFormat::Y_F32: | |||||
return 4; | |||||
case PixelFormat::RGB_F32: | |||||
return 12; | |||||
case PixelFormat::RGBA_F32: | |||||
return 16; | |||||
default: | |||||
return 1; | |||||
} | } | ||||
}; | }; | ||||
@@ -25,6 +25,9 @@ template <PixelFormat T> struct PixelType { typedef void type; }; | |||||
template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; }; | template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; }; | ||||
template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; }; | template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; }; | ||||
template<> struct PixelType<PixelFormat::RGBA_8> { typedef u8vec4 type; }; | template<> struct PixelType<PixelFormat::RGBA_8> { typedef u8vec4 type; }; | ||||
template<> struct PixelType<PixelFormat::Y_F32> { typedef float type; }; | |||||
template<> struct PixelType<PixelFormat::RGB_F32> { typedef vec3 type; }; | |||||
template<> struct PixelType<PixelFormat::RGBA_F32> { typedef vec4 type; }; | |||||
class Image | class Image | ||||
{ | { | ||||
@@ -34,6 +37,10 @@ public: | |||||
/* XXX: use of this ctor should be discouraged, as it will not | /* XXX: use of this ctor should be discouraged, as it will not | ||||
* return information about a possible error. */ | * return information about a possible error. */ | ||||
Image(char const *path); | Image(char const *path); | ||||
/* Rule of three */ | |||||
Image (Image const &other); | |||||
Image & operator =(Image other); | |||||
~Image(); | ~Image(); | ||||
bool Load(char const *path); | bool Load(char const *path); | ||||
@@ -43,8 +50,11 @@ public: | |||||
void SetSize(ivec2); | void SetSize(ivec2); | ||||
PixelFormat GetFormat() const; | PixelFormat GetFormat() const; | ||||
template<PixelFormat T> typename PixelType<T>::type *Lock(); | |||||
void Unlock(); | |||||
void SetFormat(PixelFormat fmt); | |||||
template<PixelFormat T = PixelFormat::Unknown> | |||||
typename PixelType<T>::type *Lock(); | |||||
void Unlock(void const *pixels); | |||||
bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const; | bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const; | ||||
@@ -41,7 +41,7 @@ LOLUNIT_FIXTURE(ImageTest) | |||||
LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | ||||
LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | ||||
image.Unlock(); | |||||
image.Unlock(data); | |||||
} | } | ||||
}; | }; | ||||