How to use LodePNG to load into images into SDL
So Tony suggested that I posted the code from Gulpman we just added to load PNG images using LodePNG. The reason we ended up with LodePNG rather than SDL_image 2.0 is that there was some colour alternation on image load on Mac OS X platform (but not Linux or Windows).
Some of this code was based on examples from the LodePNG site. It theoretically allows you to switch back to SDL2_image - but the IMG_Init code isn't ideal here, and was only included for testing.
We don't have any speed issues in our case for loading or saving images, so I haven't worried about anything about that here. It's under the zlib license, so should be usable in your project.
The only specific header is "Utilities.h" which provides a fatalError in the Utilities namespace. You can probably replace that with a print and exit() - because that's what fatalError contains in our case.
The only thing which we plan to tidy up is the ugly mix of std::cout, fprint(stderr) and printf for error reporting.
Bugs/patches/comments welcome to mailto:team@gulpman.net.
/*
* image_loader.h
* Gulpman
*
* Created by Rob Probin on 18/02/2014.
*
* ------------------------------------------------------------------------------
* Copyright (c) 2014 Rob Probin
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
* -----------------------------------------------------------------------------
* (This is the zlib License)
*
*/
#include "SDL.h"
SDL_Surface* load_image(const char* filename); // loads BMP or PNG files
// PNG specific
SDL_Surface* load_PNG(const char* filename);
int save_PNG(SDL_Surface* surface, const char* filename);
/*
* image_loader.cpp
* Gulpman
*
* Created by Rob Probin on 18/02/2014.
*
* ------------------------------------------------------------------------------
* Copyright (c) 2014 Rob Probin
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
* -----------------------------------------------------------------------------
* (This is the zlib License)
*
*/
#include "image_loader.h"
#include "lodepng.h"
#include "SDL.h"
#include
#include
#include
#include "Utilities.h"
#define SDL2_image_load 0
#if SDL2_image_load
#include "SDL_image.h"
#endif
SDL_Surface* load_image(const char* filename)
{
SDL_Surface* img = 0;
size_t namesize = strlen(filename);
size_t end_index = namesize <= 4 ? 0 : namesize - 4;
const char* extension = filename + end_index;
if(strcmp(extension, ".PNG")==0 or strcmp(extension, ".png")==0)
{
#if SDL2_image_load
static int SDL2_image_init = 0;
if(SDL2_image_init == 0) {
if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG) { Utilities::fatalError("Couldn't init SDL2_image", 42077); }
SDL2_image_init = 1;
}
img = IMG_Load(filename);
#else
img = load_PNG(filename);
#endif
}
else if(strcmp(extension, ".BMP")==0 or strcmp(extension, ".bmp")==0)
{
img = SDL_LoadBMP(filename);
if(img==NULL)
{
printf("SDL_LoadBMP failed: %s\n", SDL_GetError());
}
}
else
{
printf("Unknown image filename extension in load_image\n");
}
return img;
}
SDL_Surface* load_PNG(const char* filename)
{
std::vector buffer, image;
lodepng::load_file(buffer, filename); //load the image file with given filename
unsigned w, h;
unsigned error = lodepng::decode(image, w, h, buffer); //decode the png
if (error)
{
std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
return 0;
}
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
//avoid too large window size by downscaling large image
unsigned jump = 1;
//if(w / 1024 >= jump) jump = w / 1024 + 1;
//if(h / 1024 >= jump) jump = h / 1024 + 1;
SDL_Surface *dest = SDL_CreateRGBSurface(0, w, h, 32,
rmask, gmask, bmask, amask);
if(dest == NULL) {
fprintf(stderr, "CreateRGBSurface failed: %s\n", SDL_GetError());
return 0;
}
if(SDL_MUSTLOCK(dest)) { SDL_LockSurface(dest); }
//plot the pixels of the PNG file
for(unsigned y = 0; y + jump - 1 < h; y += jump)
for(unsigned x = 0; x + jump - 1 < w; x += jump)
{
//get RGBA components
Uint32 r = image[4 * y * w + 4 * x + 0]; //red
Uint32 g = image[4 * y * w + 4 * x + 1]; //green
Uint32 b = image[4 * y * w + 4 * x + 2]; //blue
Uint32 a = image[4 * y * w + 4 * x + 3]; //alpha
//make translucency visible by placing checkerboard pattern behind image
//int checkerColor = 191 + 64 * (((x / 16) % 2) == ((y / 16) % 2));
//r = (a * r + (255 - a) * checkerColor) / 255;
//g = (a * g + (255 - a) * checkerColor) / 255;
//b = (a * b + (255 - a) * checkerColor) / 255;
//give the color value to the pixel of the screenbuffer
Uint32* bufp = (Uint32 *)dest->pixels + (y * dest->pitch / 4) / jump + (x / jump);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
*bufp = 0x01000000* r + 65536 * g + 256 * b + a;
#else
*bufp = 0x01000000* a + 65536 * b + 256 * g + r;
#endif
}
if(SDL_MUSTLOCK(dest)) { SDL_UnlockSurface(dest); }
return dest;
}
//#define save_png_with_SDL
#ifdef save_png_with_SDL
#include "SDL_image.h"
#endif
int save_PNG(SDL_Surface* surface, const char* filename)
{
#ifdef save_png_with_SDL
return IMG_SavePNG(surface, filename);
#else
// double check parameters
if(!surface) return -2000;
if(!filename) return -2001;
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
std::vector image;
unsigned w = surface->w;
unsigned h = surface->h;
image.resize(w*h*4); // make sure image vector is big enough
if(SDL_MUSTLOCK(surface)) { SDL_LockSurface(surface); }
//decode the pixels for the PNG file
for(unsigned y = 0; y < h; y ++)
for(unsigned x = 0; x < w; x ++)
{
//get RGBA components from the pixel of the screenbuffer
Uint32* bufp = (Uint32 *)surface->pixels + (y * surface->pitch / 4) + (x);
Uint32 data = *bufp;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint32 r = data >> 24;
Uint32 g = (data >> 16) & 0xff;
Uint32 b = (data >> 8) & 0xff;
Uint32 a = data & 0xff;
#else
Uint32 a = data >> 24;
Uint32 b = (data >> 16) & 0xff;
Uint32 g = (data >> 8) & 0xff;
Uint32 r = data & 0xff;
#endif
// write the r g b a components into the image
image[4 * y * w + 4 * x + 0] = r; //red
image[4 * y * w + 4 * x + 1] = g; //green
image[4 * y * w + 4 * x + 2] = b; //blue
image[4 * y * w + 4 * x + 3] = a; //alpha
}
if(SDL_MUSTLOCK(surface)) { SDL_UnlockSurface(surface); }
std::vector png;
unsigned error = lodepng::encode(png, image, w, h);
if(error)
{
std::cout << "PNG encoding error " << error << ": " << lodepng_error_text(error) << std::endl;
return -2003;
}
lodepng::save_file(png, filename);
return 0;
#endif
}
Some of this code was based on examples from the LodePNG site. It theoretically allows you to switch back to SDL2_image - but the IMG_Init code isn't ideal here, and was only included for testing.
We don't have any speed issues in our case for loading or saving images, so I haven't worried about anything about that here. It's under the zlib license, so should be usable in your project.
The only specific header is "Utilities.h" which provides a fatalError in the Utilities namespace. You can probably replace that with a print and exit() - because that's what fatalError contains in our case.
The only thing which we plan to tidy up is the ugly mix of std::cout, fprint(stderr) and printf for error reporting.
Bugs/patches/comments welcome to mailto:team@gulpman.net.
/*
* image_loader.h
* Gulpman
*
* Created by Rob Probin on 18/02/2014.
*
* ------------------------------------------------------------------------------
* Copyright (c) 2014 Rob Probin
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
* -----------------------------------------------------------------------------
* (This is the zlib License)
*
*/
#include "SDL.h"
SDL_Surface* load_image(const char* filename); // loads BMP or PNG files
// PNG specific
SDL_Surface* load_PNG(const char* filename);
int save_PNG(SDL_Surface* surface, const char* filename);
/*
* image_loader.cpp
* Gulpman
*
* Created by Rob Probin on 18/02/2014.
*
* ------------------------------------------------------------------------------
* Copyright (c) 2014 Rob Probin
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
* -----------------------------------------------------------------------------
* (This is the zlib License)
*
*/
#include "image_loader.h"
#include "lodepng.h"
#include "SDL.h"
#include
#include
#include
#include "Utilities.h"
#define SDL2_image_load 0
#if SDL2_image_load
#include "SDL_image.h"
#endif
SDL_Surface* load_image(const char* filename)
{
SDL_Surface* img = 0;
size_t namesize = strlen(filename);
size_t end_index = namesize <= 4 ? 0 : namesize - 4;
const char* extension = filename + end_index;
if(strcmp(extension, ".PNG")==0 or strcmp(extension, ".png")==0)
{
#if SDL2_image_load
static int SDL2_image_init = 0;
if(SDL2_image_init == 0) {
if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG) { Utilities::fatalError("Couldn't init SDL2_image", 42077); }
SDL2_image_init = 1;
}
img = IMG_Load(filename);
#else
img = load_PNG(filename);
#endif
}
else if(strcmp(extension, ".BMP")==0 or strcmp(extension, ".bmp")==0)
{
img = SDL_LoadBMP(filename);
if(img==NULL)
{
printf("SDL_LoadBMP failed: %s\n", SDL_GetError());
}
}
else
{
printf("Unknown image filename extension in load_image\n");
}
return img;
}
SDL_Surface* load_PNG(const char* filename)
{
std::vector
lodepng::load_file(buffer, filename); //load the image file with given filename
unsigned w, h;
unsigned error = lodepng::decode(image, w, h, buffer); //decode the png
if (error)
{
std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
return 0;
}
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
//avoid too large window size by downscaling large image
unsigned jump = 1;
//if(w / 1024 >= jump) jump = w / 1024 + 1;
//if(h / 1024 >= jump) jump = h / 1024 + 1;
SDL_Surface *dest = SDL_CreateRGBSurface(0, w, h, 32,
rmask, gmask, bmask, amask);
if(dest == NULL) {
fprintf(stderr, "CreateRGBSurface failed: %s\n", SDL_GetError());
return 0;
}
if(SDL_MUSTLOCK(dest)) { SDL_LockSurface(dest); }
//plot the pixels of the PNG file
for(unsigned y = 0; y + jump - 1 < h; y += jump)
for(unsigned x = 0; x + jump - 1 < w; x += jump)
{
//get RGBA components
Uint32 r = image[4 * y * w + 4 * x + 0]; //red
Uint32 g = image[4 * y * w + 4 * x + 1]; //green
Uint32 b = image[4 * y * w + 4 * x + 2]; //blue
Uint32 a = image[4 * y * w + 4 * x + 3]; //alpha
//make translucency visible by placing checkerboard pattern behind image
//int checkerColor = 191 + 64 * (((x / 16) % 2) == ((y / 16) % 2));
//r = (a * r + (255 - a) * checkerColor) / 255;
//g = (a * g + (255 - a) * checkerColor) / 255;
//b = (a * b + (255 - a) * checkerColor) / 255;
//give the color value to the pixel of the screenbuffer
Uint32* bufp = (Uint32 *)dest->pixels + (y * dest->pitch / 4) / jump + (x / jump);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
*bufp = 0x01000000* r + 65536 * g + 256 * b + a;
#else
*bufp = 0x01000000* a + 65536 * b + 256 * g + r;
#endif
}
if(SDL_MUSTLOCK(dest)) { SDL_UnlockSurface(dest); }
return dest;
}
//#define save_png_with_SDL
#ifdef save_png_with_SDL
#include "SDL_image.h"
#endif
int save_PNG(SDL_Surface* surface, const char* filename)
{
#ifdef save_png_with_SDL
return IMG_SavePNG(surface, filename);
#else
// double check parameters
if(!surface) return -2000;
if(!filename) return -2001;
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
std::vector
unsigned w = surface->w;
unsigned h = surface->h;
image.resize(w*h*4); // make sure image vector is big enough
if(SDL_MUSTLOCK(surface)) { SDL_LockSurface(surface); }
//decode the pixels for the PNG file
for(unsigned y = 0; y < h; y ++)
for(unsigned x = 0; x < w; x ++)
{
//get RGBA components from the pixel of the screenbuffer
Uint32* bufp = (Uint32 *)surface->pixels + (y * surface->pitch / 4) + (x);
Uint32 data = *bufp;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint32 r = data >> 24;
Uint32 g = (data >> 16) & 0xff;
Uint32 b = (data >> 8) & 0xff;
Uint32 a = data & 0xff;
#else
Uint32 a = data >> 24;
Uint32 b = (data >> 16) & 0xff;
Uint32 g = (data >> 8) & 0xff;
Uint32 r = data & 0xff;
#endif
// write the r g b a components into the image
image[4 * y * w + 4 * x + 0] = r; //red
image[4 * y * w + 4 * x + 1] = g; //green
image[4 * y * w + 4 * x + 2] = b; //blue
image[4 * y * w + 4 * x + 3] = a; //alpha
}
if(SDL_MUSTLOCK(surface)) { SDL_UnlockSurface(surface); }
std::vector
unsigned error = lodepng::encode(png, image, w, h);
if(error)
{
std::cout << "PNG encoding error " << error << ": " << lodepng_error_text(error) << std::endl;
return -2003;
}
lodepng::save_file(png, filename);
return 0;
#endif
}
0 Comments:
Post a Comment
<< Home