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