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") | |||
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 | |||
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) | |||
@@ -111,8 +111,8 @@ liblolcore_sources = \ | |||
sys/thread.cpp sys/threadbase.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/dummy-image.cpp \ | |||
image/color/cie1931.cpp \ | |||
@@ -123,7 +123,6 @@ liblolcore_sources = \ | |||
debug/record.cpp debug/record.h debug/stats.cpp debug/stats.h | |||
sdl_sources = \ | |||
image/codec/sdl-image.cpp \ | |||
platform/sdl/sdlapp.cpp platform/sdl/sdlapp.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 */ | |||
# if defined USE_D3D9 | |||
{ D3DFMT_L8, 1 }, /* Y8 */ | |||
{ D3DFMT_R8G8B8, 3 }, /* RGB_8 */ | |||
{ D3DFMT_A8R8G8B8, 4 }, /* RGBA_8 */ | |||
{ D3DFMT_A8R8G8B8, 4 }, /* ARGB_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 | |||
{ D3DFMT_LIN_L8, 1 }, | |||
{ D3DFMT_UNKNOWN, 0 }, | |||
{ D3DFMT_UNKNOWN, 0 }, | |||
/* By default the X360 will swizzle the texture. Ask for linear. */ | |||
{ D3DFMT_LIN_A8R8G8B8, 4 }, | |||
{ D3DFMT_UNKNOWN, 0 }, | |||
{ D3DFMT_LIN_L8, 1 }, | |||
{ D3DFMT_UNKNOWN, 0 }, /* Y_F32 */ | |||
{ D3DFMT_UNKNOWN, 0 }, /* RGB_F32 */ | |||
{ D3DFMT_UNKNOWN, 0 }, /* RGBA_F32 */ | |||
# endif | |||
}; | |||
@@ -133,27 +139,30 @@ Texture::Texture(ivec2 size, PixelFormat format) | |||
/* FIXME: this is all mixed up for the RGBA/ARGB combinations */ | |||
#if __CELLOS_LV2__ | |||
{ GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||
{ 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_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 | |||
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||
{ GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 3 }, | |||
{ 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 | |||
* this format as "not available" on this platform. */ | |||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | |||
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, | |||
#else | |||
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, /* A8 */ | |||
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, /* RGB_8 */ | |||
/* 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_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ABGR_8 */ | |||
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, /* A8 */ | |||
#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; | |||
@@ -52,7 +52,7 @@ bool DummyImageCodec::Load(Image *image, char const *path) | |||
pixels->a = (((i >> 4) ^ (j >> 4)) & 1) * 0xff; | |||
++pixels; | |||
} | |||
image->Unlock(); | |||
image->Unlock(pixels); | |||
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 | |||
* this in the shader. */ | |||
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 x = 0; x < size.x; x++) | |||
*pixels++ = (*source++).bgra; | |||
image->Unlock(); | |||
*pdst++ = (*psrc++).bgra; | |||
image->Unlock(pdst); | |||
bitmap->UnlockBits(&bdata); | |||
delete bitmap; | |||
@@ -210,7 +210,7 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) | |||
for (int y = 0; y < size.y; y++) | |||
for (int x = 0; x < size.x; x++) | |||
*pdst++ = (*psrc++).bgra; | |||
image->Unlock(); | |||
image->Unlock(pdst); | |||
b->UnlockBits(&bdata); | |||
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) | |||
/* | |||
* Public Image class | |||
*/ | |||
bool SdlImageCodec::Load(Image *image, char const *path) | |||
{ | |||
SDL_Surface *surface = nullptr; | |||
@@ -85,7 +81,7 @@ bool SdlImageCodec::Load(Image *image, char const *path) | |||
image->SetSize(size); | |||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||
memcpy(data, surface->pixels, 4 * size.x * size.y); | |||
image->Unlock(); | |||
image->Unlock(data); | |||
SDL_FreeSurface(surface); | |||
@@ -99,7 +95,7 @@ bool SdlImageCodec::Save(Image *image, char const *path) | |||
u8vec4 *data = image->Lock<PixelFormat::RGBA_8>(); | |||
memcpy(surface->pixels, data, 4 * size.x * size.y); | |||
image->Unlock(); | |||
image->Unlock(data); | |||
int ret = SDL_SaveBMP(surface, path); | |||
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: | |||
uint8_t *m_pixels; | |||
//<Pos, Size> | |||
Array<ivec2, ivec2> m_tiles; | |||
}; | |||
@@ -295,7 +294,7 @@ bool ZedImageCodec::Load(Image *image, char const *path) | |||
j++; | |||
} | |||
} | |||
image->Unlock(); | |||
image->Unlock(pixels); | |||
return true; | |||
} | |||
@@ -75,7 +75,7 @@ bool ZedPaletteImageCodec::Load(Image *image, char const *path) | |||
pixels->a = (i == 0) ? 0 : 255; | |||
++pixels; | |||
} | |||
image->Unlock(); | |||
image->Unlock(pixels); | |||
return true; | |||
} | |||
@@ -31,7 +31,9 @@ public: | |||
ivec2 m_size; | |||
/* A map of the various available bitplanes */ | |||
Map<int, void *> m_pixels; | |||
/* The last bitplane being accessed for writing */ | |||
PixelFormat m_format; | |||
}; | |||
@@ -20,7 +20,7 @@ using namespace std; | |||
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 | |||
* the "lol" namespace. An apparent bug in Visual Studio's compiler | |||
* makes it think these functions are actually in the top-level | |||
@@ -50,6 +50,9 @@ static bool RegisterAllCodecs(Array<ImageCodec *> &codeclist) | |||
#endif | |||
REGISTER_IMAGE_CODEC(ZedImageCodec) | |||
REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) | |||
#if defined USE_IMLIB2 | |||
REGISTER_IMAGE_CODEC(Imlib2ImageCodec) | |||
#endif | |||
return true; | |||
} | |||
@@ -58,42 +61,21 @@ static bool RegisterAllCodecs(Array<ImageCodec *> &codeclist) | |||
* 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: | |||
Array<ImageCodec *> m_codecs; | |||
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 | |||
@@ -116,6 +98,29 @@ Image::Image(ivec2 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() | |||
{ | |||
delete m_data; | |||
@@ -123,12 +128,18 @@ Image::~Image() | |||
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) | |||
{ | |||
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 | |||
@@ -148,11 +159,10 @@ void Image::SetSize(ivec2 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) | |||
{ | |||
Lock<PixelFormat::RGBA_8>(); | |||
Unlock(); | |||
Unlock(Lock<PixelFormat::RGBA_8>()); | |||
} | |||
} | |||
@@ -161,31 +171,45 @@ PixelFormat Image::GetFormat() const | |||
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]; | |||
} | |||
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<> | |||
void *Image::Lock<PixelFormat::Unknown>() | |||
{ | |||
@@ -194,10 +218,17 @@ void *Image::Lock<PixelFormat::Unknown>() | |||
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)); | |||
/* 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 | |||
@@ -23,11 +23,14 @@ enum class PixelFormat | |||
{ | |||
/* XXX: make sure to update texture.cpp when this changes */ | |||
Unknown = 0, | |||
Y_8, | |||
RGB_8, | |||
RGBA_8, | |||
ARGB_8, | |||
ABGR_8, | |||
Y_8, | |||
Y_F32, | |||
RGB_F32, | |||
RGBA_F32, | |||
}; | |||
static inline uint8_t BytesPerPixel(PixelFormat format) | |||
@@ -41,8 +44,15 @@ static inline uint8_t BytesPerPixel(PixelFormat format) | |||
case PixelFormat::RGBA_8: | |||
case PixelFormat::ARGB_8: | |||
case PixelFormat::ABGR_8: | |||
default: | |||
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::RGB_8> { typedef u8vec3 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 | |||
{ | |||
@@ -34,6 +37,10 @@ public: | |||
/* XXX: use of this ctor should be discouraged, as it will not | |||
* return information about a possible error. */ | |||
Image(char const *path); | |||
/* Rule of three */ | |||
Image (Image const &other); | |||
Image & operator =(Image other); | |||
~Image(); | |||
bool Load(char const *path); | |||
@@ -43,8 +50,11 @@ public: | |||
void SetSize(ivec2); | |||
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; | |||
@@ -41,7 +41,7 @@ LOLUNIT_FIXTURE(ImageTest) | |||
LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff); | |||
LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff); | |||
image.Unlock(); | |||
image.Unlock(data); | |||
} | |||
}; | |||