Sunday, February 23, 2014

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
}



0 Comments:

Post a Comment

<< Home

Newer›  ‹Older