Browse Source

image: add an optional Imlib2 codec, prepare for float32 images, make the

Lock/Unlock mechanism safer, and implement the rule of three.
undefined
Sam Hocevar 10 years ago
parent
commit
5fc5d62849
19 changed files with 313 additions and 639 deletions
  1. +63
    -37
      TODO
  2. +8
    -8
      configure.ac
  3. +2
    -3
      src/Makefile.am
  4. +14
    -5
      src/gpu/texture.cpp
  5. +1
    -1
      src/image/codec/dummy-image.cpp
  6. +0
    -148
      src/image/codec/gdi.cpp
  7. +5
    -5
      src/image/codec/gdiplus-image.cpp
  8. +0
    -120
      src/image/codec/imlib.cpp
  9. +105
    -0
      src/image/codec/imlib2-image.cpp
  10. +0
    -112
      src/image/codec/opencv.cpp
  11. +2
    -6
      src/image/codec/sdl-image.cpp
  12. +0
    -133
      src/image/codec/sdl.cpp
  13. +1
    -2
      src/image/codec/zed-image.cpp
  14. +1
    -1
      src/image/codec/zed-palette-image.cpp
  15. +2
    -0
      src/image/image-private.h
  16. +84
    -53
      src/image/image.cpp
  17. +12
    -2
      src/lol/gpu/texture.h
  18. +12
    -2
      src/lol/image/image.h
  19. +1
    -1
      test/unit/image.cpp

+ 63
- 37
TODO View File

@@ -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


+ 8
- 8
configure.ac View File

@@ -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)


+ 2
- 3
src/Makefile.am View File

@@ -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



+ 14
- 5
src/gpu/texture.cpp View File

@@ -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;


+ 1
- 1
src/image/codec/dummy-image.cpp View File

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


+ 0
- 148
src/image/codec/gdi.cpp View File

@@ -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.
*/


+ 5
- 5
src/image/codec/gdiplus-image.cpp View File

@@ -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)


+ 0
- 120
src/image/codec/imlib.cpp View File

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


+ 105
- 0
src/image/codec/imlib2-image.cpp View File

@@ -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 */


+ 0
- 112
src/image/codec/opencv.cpp View File

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


+ 2
- 6
src/image/codec/sdl-image.cpp View File

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


+ 0
- 133
src/image/codec/sdl.cpp View File

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


+ 1
- 2
src/image/codec/zed-image.cpp View File

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


+ 1
- 1
src/image/codec/zed-palette-image.cpp View File

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


+ 2
- 0
src/image/image-private.h View File

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



+ 84
- 53
src/image/image.cpp View File

@@ -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


+ 12
- 2
src/lol/gpu/texture.h View File

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



+ 12
- 2
src/lol/image/image.h View File

@@ -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;



+ 1
- 1
test/unit/image.cpp View File

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



Loading…
Cancel
Save