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); | |||
| } | |||
| }; | |||