diff --git a/TODO b/TODO index 3a56919c..0fce7277 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/configure.ac b/configure.ac index 73590161..7fffa324 100644 --- a/configure.ac +++ b/configure.ac @@ -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) diff --git a/src/Makefile.am b/src/Makefile.am index bf865c48..75d97e35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/gpu/texture.cpp b/src/gpu/texture.cpp index 0c9628d8..1498e5a3 100644 --- a/src/gpu/texture.cpp +++ b/src/gpu/texture.cpp @@ -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; diff --git a/src/image/codec/dummy-image.cpp b/src/image/codec/dummy-image.cpp index 5ab99f8b..fc971dfd 100644 --- a/src/image/codec/dummy-image.cpp +++ b/src/image/codec/dummy-image.cpp @@ -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; } diff --git a/src/image/codec/gdi.cpp b/src/image/codec/gdi.cpp deleted file mode 100644 index 08d0690b..00000000 --- a/src/image/codec/gdi.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 -#include -#include - -#include - -#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. - */ - diff --git a/src/image/codec/gdiplus-image.cpp b/src/image/codec/gdiplus-image.cpp index 724e3f93..ecb86168 100644 --- a/src/image/codec/gdiplus-image.cpp +++ b/src/image/codec/gdiplus-image.cpp @@ -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(); - u8vec4 *source = static_cast(bdata.Scan0); + u8vec4 *pdst = image->Lock(); + u8vec4 *psrc = static_cast(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) diff --git a/src/image/codec/imlib.cpp b/src/image/codec/imlib.cpp deleted file mode 100644 index 0ed3bf65..00000000 --- a/src/image/codec/imlib.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 -#include -#include - -#include - -#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; -} - diff --git a/src/image/codec/imlib2-image.cpp b/src/image/codec/imlib2-image.cpp new file mode 100644 index 00000000..939813e1 --- /dev/null +++ b/src/image/codec/imlib2-image.cpp @@ -0,0 +1,105 @@ +// +// Lol Engine +// +// Copyright: (c) 2010-2013 Sam Hocevar +// 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 + +#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 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(); + 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(); + 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 */ + diff --git a/src/image/codec/opencv.cpp b/src/image/codec/opencv.cpp deleted file mode 100644 index 04d60f4e..00000000 --- a/src/image/codec/opencv.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 -#include -#include - -#include -#include - -#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; -} - diff --git a/src/image/codec/sdl-image.cpp b/src/image/codec/sdl-image.cpp index d7e0197d..61ec6467 100644 --- a/src/image/codec/sdl-image.cpp +++ b/src/image/codec/sdl-image.cpp @@ -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(); 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(); memcpy(surface->pixels, data, 4 * size.x * size.y); - image->Unlock(); + image->Unlock(data); int ret = SDL_SaveBMP(surface, path); SDL_FreeSurface(surface); diff --git a/src/image/codec/sdl.cpp b/src/image/codec/sdl.cpp deleted file mode 100644 index 26d63ccc..00000000 --- a/src/image/codec/sdl.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 -#include -#include - -#include - -#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); -} - diff --git a/src/image/codec/zed-image.cpp b/src/image/codec/zed-image.cpp index af698897..ef4713ca 100644 --- a/src/image/codec/zed-image.cpp +++ b/src/image/codec/zed-image.cpp @@ -40,7 +40,6 @@ public: } private: - uint8_t *m_pixels; // Array m_tiles; }; @@ -295,7 +294,7 @@ bool ZedImageCodec::Load(Image *image, char const *path) j++; } } - image->Unlock(); + image->Unlock(pixels); return true; } diff --git a/src/image/codec/zed-palette-image.cpp b/src/image/codec/zed-palette-image.cpp index b2b889b5..2acd1b33 100644 --- a/src/image/codec/zed-palette-image.cpp +++ b/src/image/codec/zed-palette-image.cpp @@ -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; } diff --git a/src/image/image-private.h b/src/image/image-private.h index 7ba0d3f6..165a4f72 100644 --- a/src/image/image-private.h +++ b/src/image/image-private.h @@ -31,7 +31,9 @@ public: ivec2 m_size; + /* A map of the various available bitplanes */ Map m_pixels; + /* The last bitplane being accessed for writing */ PixelFormat m_format; }; diff --git a/src/image/image.cpp b/src/image/image.cpp index e2359a7f..b6d5e32c 100644 --- a/src/image/image.cpp +++ b/src/image/image.cpp @@ -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 &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 &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 m_codecs; Map 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(); - Unlock(); + Unlock(Lock()); } } @@ -161,31 +171,45 @@ PixelFormat Image::GetFormat() const return m_data->m_format; } -/* The Lock() method and its explicit specialisations */ -template -typename PixelType::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::type[m_data->m_size.x * m_data->m_size.y]; + /* TODO: convert data */ } + m_data->m_format = fmt; +} + +/* The Lock() method */ +template +typename PixelType::type *Image::Lock() +{ + SetFormat(T); return (typename PixelType::type *)m_data->m_pixels[(int)T]; } -template PixelType::type * -Image::Lock(); -template PixelType::type * -Image::Lock(); -template PixelType::type * -Image::Lock(); +/* Explicit specialisations for the above template */ +#define _T(T) template PixelType::type *Image::Lock() +_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() { @@ -194,10 +218,17 @@ void *Image::Lock() 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& tiles) const diff --git a/src/lol/gpu/texture.h b/src/lol/gpu/texture.h index ec2a3fa0..5d04d194 100644 --- a/src/lol/gpu/texture.h +++ b/src/lol/gpu/texture.h @@ -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; } }; diff --git a/src/lol/image/image.h b/src/lol/image/image.h index c5e264ce..1abea19e 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -25,6 +25,9 @@ template struct PixelType { typedef void type; }; template<> struct PixelType { typedef uint8_t type; }; template<> struct PixelType { typedef u8vec3 type; }; template<> struct PixelType { typedef u8vec4 type; }; +template<> struct PixelType { typedef float type; }; +template<> struct PixelType { typedef vec3 type; }; +template<> struct PixelType { 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 typename PixelType::type *Lock(); - void Unlock(); + void SetFormat(PixelFormat fmt); + + template + typename PixelType::type *Lock(); + void Unlock(void const *pixels); bool RetrieveTiles(Array& tiles) const; diff --git a/test/unit/image.cpp b/test/unit/image.cpp index 02ed88ea..1bb8134f 100644 --- a/test/unit/image.cpp +++ b/test/unit/image.cpp @@ -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); } };