IMG_png.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:50:51 -0800
changeset 496 6332f9425dcc
parent 490 fd7214657669
child 506 4076ac7cf0a1
permissions -rw-r--r--
Updated copyright for 2017
slouken@0
     1
/*
slouken@280
     2
  SDL_image:  An example image loading library for use with SDL
slouken@496
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@280
     5
  This software is provided 'as-is', without any express or implied
slouken@280
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@280
     7
  arising from the use of this software.
slouken@0
     8
slouken@280
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@280
    10
  including commercial applications, and to alter it and redistribute it
slouken@280
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@280
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@280
    14
     claim that you wrote the original software. If you use this software
slouken@280
    15
     in a product, an acknowledgment in the product documentation would be
slouken@280
    16
     appreciated but is not required.
slouken@280
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@280
    18
     misrepresented as being the original software.
slouken@280
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@0
    21
slouken@0
    22
/* This is a PNG image file loading framework */
slouken@0
    23
slouken@391
    24
#include "SDL_image.h"
slouken@0
    25
slouken@391
    26
#if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
slouken@0
    27
slouken@0
    28
#ifdef LOAD_PNG
slouken@0
    29
slouken@0
    30
/*=============================================================================
slouken@0
    31
        File: SDL_png.c
slouken@368
    32
     Purpose: A PNG loader and saver for the SDL library
slouken@368
    33
    Revision:
slouken@0
    34
  Created by: Philippe Lavoie          (2 November 1998)
slouken@0
    35
              lavoie@zeus.genie.uottawa.ca
slouken@368
    36
 Modified by:
slouken@0
    37
slouken@0
    38
 Copyright notice:
slouken@0
    39
          Copyright (C) 1998 Philippe Lavoie
slouken@368
    40
slouken@0
    41
          This library is free software; you can redistribute it and/or
slouken@0
    42
          modify it under the terms of the GNU Library General Public
slouken@0
    43
          License as published by the Free Software Foundation; either
slouken@0
    44
          version 2 of the License, or (at your option) any later version.
slouken@368
    45
slouken@0
    46
          This library is distributed in the hope that it will be useful,
slouken@0
    47
          but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    48
          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@0
    49
          Library General Public License for more details.
slouken@368
    50
slouken@0
    51
          You should have received a copy of the GNU Library General Public
slouken@0
    52
          License along with this library; if not, write to the Free
slouken@0
    53
          Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
slouken@0
    54
slouken@0
    55
    Comments: The load and save routine are basically the ones you can find
slouken@0
    56
             in the example.c file from the libpng distribution.
slouken@0
    57
slouken@0
    58
  Changes:
slouken@0
    59
    5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
slouken@0
    60
slouken@0
    61
=============================================================================*/
slouken@0
    62
slouken@0
    63
#include "SDL_endian.h"
slouken@0
    64
slouken@9
    65
#ifdef macintosh
slouken@9
    66
#define MACOS
slouken@9
    67
#endif
slouken@0
    68
#include <png.h>
slouken@0
    69
slouken@265
    70
/* Check for the older version of libpng */
slouken@383
    71
#if (PNG_LIBPNG_VER_MAJOR == 1) 
slouken@430
    72
#if (PNG_LIBPNG_VER_MINOR < 5)
slouken@265
    73
#define LIBPNG_VERSION_12
slouken@463
    74
typedef png_bytep png_const_bytep;
slouken@383
    75
#endif
slouken@383
    76
#if (PNG_LIBPNG_VER_MINOR < 6)
slouken@380
    77
typedef png_structp png_const_structrp;
slouken@380
    78
typedef png_infop png_const_inforp;
slouken@383
    79
#endif
slouken@265
    80
#endif
slouken@0
    81
slouken@143
    82
static struct {
slouken@368
    83
    int loaded;
slouken@368
    84
    void *handle;
slouken@378
    85
    png_infop (*png_create_info_struct) (png_const_structrp png_ptr);
slouken@368
    86
    png_structp (*png_create_read_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn);
slouken@368
    87
    void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
slouken@378
    88
    png_uint_32 (*png_get_IHDR) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method);
slouken@378
    89
    png_voidp (*png_get_io_ptr) (png_const_structrp png_ptr);
slouken@378
    90
    png_byte (*png_get_channels) (png_const_structrp png_ptr, png_const_inforp info_ptr);
slouken@378
    91
    png_uint_32 (*png_get_PLTE) (png_const_structrp png_ptr, png_infop info_ptr, png_colorp *palette, int *num_palette);
slouken@378
    92
    png_uint_32 (*png_get_tRNS) (png_const_structrp png_ptr, png_infop info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values);
slouken@378
    93
    png_uint_32 (*png_get_valid) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag);
slouken@368
    94
    void (*png_read_image) (png_structp png_ptr, png_bytepp image);
slouken@368
    95
    void (*png_read_info) (png_structp png_ptr, png_infop info_ptr);
slouken@368
    96
    void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr);
slouken@368
    97
    void (*png_set_expand) (png_structp png_ptr);
slouken@368
    98
    void (*png_set_gray_to_rgb) (png_structp png_ptr);
slouken@368
    99
    void (*png_set_packing) (png_structp png_ptr);
slouken@368
   100
    void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn);
slouken@368
   101
    void (*png_set_strip_16) (png_structp png_ptr);
slouken@378
   102
    int (*png_sig_cmp) (png_const_bytep sig, png_size_t start, png_size_t num_to_check);
slouken@265
   103
#ifndef LIBPNG_VERSION_12
slouken@368
   104
    jmp_buf* (*png_set_longjmp_fn) (png_structp, png_longjmp_ptr, size_t);
slouken@265
   105
#endif
slouken@143
   106
} lib;
slouken@143
   107
slouken@143
   108
#ifdef LOAD_PNG_DYNAMIC
slouken@205
   109
int IMG_InitPNG()
slouken@143
   110
{
slouken@368
   111
    if ( lib.loaded == 0 ) {
slouken@368
   112
        lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
slouken@368
   113
        if ( lib.handle == NULL ) {
slouken@368
   114
            return -1;
slouken@368
   115
        }
slouken@368
   116
        lib.png_create_info_struct =
slouken@378
   117
            (png_infop (*) (png_const_structrp))
slouken@368
   118
            SDL_LoadFunction(lib.handle, "png_create_info_struct");
slouken@368
   119
        if ( lib.png_create_info_struct == NULL ) {
slouken@368
   120
            SDL_UnloadObject(lib.handle);
slouken@368
   121
            return -1;
slouken@368
   122
        }
slouken@368
   123
        lib.png_create_read_struct =
slouken@368
   124
            (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr))
slouken@368
   125
            SDL_LoadFunction(lib.handle, "png_create_read_struct");
slouken@368
   126
        if ( lib.png_create_read_struct == NULL ) {
slouken@368
   127
            SDL_UnloadObject(lib.handle);
slouken@368
   128
            return -1;
slouken@368
   129
        }
slouken@368
   130
        lib.png_destroy_read_struct =
slouken@368
   131
            (void (*) (png_structpp, png_infopp, png_infopp))
slouken@368
   132
            SDL_LoadFunction(lib.handle, "png_destroy_read_struct");
slouken@368
   133
        if ( lib.png_destroy_read_struct == NULL ) {
slouken@368
   134
            SDL_UnloadObject(lib.handle);
slouken@368
   135
            return -1;
slouken@368
   136
        }
slouken@368
   137
        lib.png_get_IHDR =
slouken@378
   138
            (png_uint_32 (*) (png_const_structrp, png_const_inforp, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *))
slouken@368
   139
            SDL_LoadFunction(lib.handle, "png_get_IHDR");
slouken@368
   140
        if ( lib.png_get_IHDR == NULL ) {
slouken@368
   141
            SDL_UnloadObject(lib.handle);
slouken@368
   142
            return -1;
slouken@368
   143
        }
slouken@368
   144
        lib.png_get_channels =
slouken@378
   145
            (png_byte (*) (png_const_structrp, png_const_inforp))
slouken@368
   146
            SDL_LoadFunction(lib.handle, "png_get_channels");
slouken@368
   147
        if ( lib.png_get_channels == NULL ) {
slouken@368
   148
            SDL_UnloadObject(lib.handle);
slouken@368
   149
            return -1;
slouken@368
   150
        }
slouken@368
   151
        lib.png_get_io_ptr =
slouken@378
   152
            (png_voidp (*) (png_const_structrp))
slouken@368
   153
            SDL_LoadFunction(lib.handle, "png_get_io_ptr");
slouken@368
   154
        if ( lib.png_get_io_ptr == NULL ) {
slouken@368
   155
            SDL_UnloadObject(lib.handle);
slouken@368
   156
            return -1;
slouken@368
   157
        }
slouken@368
   158
        lib.png_get_PLTE =
slouken@378
   159
            (png_uint_32 (*) (png_const_structrp, png_infop, png_colorp *, int *))
slouken@368
   160
            SDL_LoadFunction(lib.handle, "png_get_PLTE");
slouken@368
   161
        if ( lib.png_get_PLTE == NULL ) {
slouken@368
   162
            SDL_UnloadObject(lib.handle);
slouken@368
   163
            return -1;
slouken@368
   164
        }
slouken@368
   165
        lib.png_get_tRNS =
slouken@378
   166
            (png_uint_32 (*) (png_const_structrp, png_infop, png_bytep *, int *, png_color_16p *))
slouken@368
   167
            SDL_LoadFunction(lib.handle, "png_get_tRNS");
slouken@368
   168
        if ( lib.png_get_tRNS == NULL ) {
slouken@368
   169
            SDL_UnloadObject(lib.handle);
slouken@368
   170
            return -1;
slouken@368
   171
        }
slouken@368
   172
        lib.png_get_valid =
slouken@378
   173
            (png_uint_32 (*) (png_const_structrp, png_const_inforp, png_uint_32))
slouken@368
   174
            SDL_LoadFunction(lib.handle, "png_get_valid");
slouken@368
   175
        if ( lib.png_get_valid == NULL ) {
slouken@368
   176
            SDL_UnloadObject(lib.handle);
slouken@368
   177
            return -1;
slouken@368
   178
        }
slouken@368
   179
        lib.png_read_image =
slouken@368
   180
            (void (*) (png_structp, png_bytepp))
slouken@368
   181
            SDL_LoadFunction(lib.handle, "png_read_image");
slouken@368
   182
        if ( lib.png_read_image == NULL ) {
slouken@368
   183
            SDL_UnloadObject(lib.handle);
slouken@368
   184
            return -1;
slouken@368
   185
        }
slouken@368
   186
        lib.png_read_info =
slouken@368
   187
            (void (*) (png_structp, png_infop))
slouken@368
   188
            SDL_LoadFunction(lib.handle, "png_read_info");
slouken@368
   189
        if ( lib.png_read_info == NULL ) {
slouken@368
   190
            SDL_UnloadObject(lib.handle);
slouken@368
   191
            return -1;
slouken@368
   192
        }
slouken@368
   193
        lib.png_read_update_info =
slouken@368
   194
            (void (*) (png_structp, png_infop))
slouken@368
   195
            SDL_LoadFunction(lib.handle, "png_read_update_info");
slouken@368
   196
        if ( lib.png_read_update_info == NULL ) {
slouken@368
   197
            SDL_UnloadObject(lib.handle);
slouken@368
   198
            return -1;
slouken@368
   199
        }
slouken@368
   200
        lib.png_set_expand =
slouken@368
   201
            (void (*) (png_structp))
slouken@368
   202
            SDL_LoadFunction(lib.handle, "png_set_expand");
slouken@368
   203
        if ( lib.png_set_expand == NULL ) {
slouken@368
   204
            SDL_UnloadObject(lib.handle);
slouken@368
   205
            return -1;
slouken@368
   206
        }
slouken@368
   207
        lib.png_set_gray_to_rgb =
slouken@368
   208
            (void (*) (png_structp))
slouken@368
   209
            SDL_LoadFunction(lib.handle, "png_set_gray_to_rgb");
slouken@368
   210
        if ( lib.png_set_gray_to_rgb == NULL ) {
slouken@368
   211
            SDL_UnloadObject(lib.handle);
slouken@368
   212
            return -1;
slouken@368
   213
        }
slouken@368
   214
        lib.png_set_packing =
slouken@368
   215
            (void (*) (png_structp))
slouken@368
   216
            SDL_LoadFunction(lib.handle, "png_set_packing");
slouken@368
   217
        if ( lib.png_set_packing == NULL ) {
slouken@368
   218
            SDL_UnloadObject(lib.handle);
slouken@368
   219
            return -1;
slouken@368
   220
        }
slouken@368
   221
        lib.png_set_read_fn =
slouken@368
   222
            (void (*) (png_structp, png_voidp, png_rw_ptr))
slouken@368
   223
            SDL_LoadFunction(lib.handle, "png_set_read_fn");
slouken@368
   224
        if ( lib.png_set_read_fn == NULL ) {
slouken@368
   225
            SDL_UnloadObject(lib.handle);
slouken@368
   226
            return -1;
slouken@368
   227
        }
slouken@368
   228
        lib.png_set_strip_16 =
slouken@368
   229
            (void (*) (png_structp))
slouken@368
   230
            SDL_LoadFunction(lib.handle, "png_set_strip_16");
slouken@368
   231
        if ( lib.png_set_strip_16 == NULL ) {
slouken@368
   232
            SDL_UnloadObject(lib.handle);
slouken@368
   233
            return -1;
slouken@368
   234
        }
slouken@368
   235
        lib.png_sig_cmp =
slouken@378
   236
            (int (*) (png_const_bytep, png_size_t, png_size_t))
slouken@368
   237
            SDL_LoadFunction(lib.handle, "png_sig_cmp");
slouken@368
   238
        if ( lib.png_sig_cmp == NULL ) {
slouken@368
   239
            SDL_UnloadObject(lib.handle);
slouken@368
   240
            return -1;
slouken@368
   241
        }
slouken@265
   242
#ifndef LIBPNG_VERSION_12
slouken@368
   243
        lib.png_set_longjmp_fn =
slouken@368
   244
            (jmp_buf * (*) (png_structp, png_longjmp_ptr, size_t))
slouken@368
   245
            SDL_LoadFunction(lib.handle, "png_set_longjmp_fn");
slouken@368
   246
        if ( lib.png_set_longjmp_fn == NULL ) {
slouken@368
   247
            SDL_UnloadObject(lib.handle);
slouken@368
   248
            return -1;
slouken@368
   249
        }
slouken@265
   250
#endif
slouken@368
   251
    }
slouken@368
   252
    ++lib.loaded;
slouken@143
   253
slouken@368
   254
    return 0;
slouken@143
   255
}
slouken@205
   256
void IMG_QuitPNG()
slouken@143
   257
{
slouken@368
   258
    if ( lib.loaded == 0 ) {
slouken@368
   259
        return;
slouken@368
   260
    }
slouken@368
   261
    if ( lib.loaded == 1 ) {
slouken@368
   262
        SDL_UnloadObject(lib.handle);
slouken@368
   263
    }
slouken@368
   264
    --lib.loaded;
slouken@143
   265
}
slouken@143
   266
#else
slouken@205
   267
int IMG_InitPNG()
slouken@143
   268
{
slouken@368
   269
    if ( lib.loaded == 0 ) {
slouken@368
   270
        lib.png_create_info_struct = png_create_info_struct;
slouken@368
   271
        lib.png_create_read_struct = png_create_read_struct;
slouken@368
   272
        lib.png_destroy_read_struct = png_destroy_read_struct;
slouken@368
   273
        lib.png_get_IHDR = png_get_IHDR;
slouken@368
   274
        lib.png_get_channels = png_get_channels;
slouken@368
   275
        lib.png_get_io_ptr = png_get_io_ptr;
slouken@368
   276
        lib.png_get_PLTE = png_get_PLTE;
slouken@368
   277
        lib.png_get_tRNS = png_get_tRNS;
slouken@368
   278
        lib.png_get_valid = png_get_valid;
slouken@368
   279
        lib.png_read_image = png_read_image;
slouken@368
   280
        lib.png_read_info = png_read_info;
slouken@368
   281
        lib.png_read_update_info = png_read_update_info;
slouken@368
   282
        lib.png_set_expand = png_set_expand;
slouken@368
   283
        lib.png_set_gray_to_rgb = png_set_gray_to_rgb;
slouken@368
   284
        lib.png_set_packing = png_set_packing;
slouken@368
   285
        lib.png_set_read_fn = png_set_read_fn;
slouken@368
   286
        lib.png_set_strip_16 = png_set_strip_16;
slouken@368
   287
        lib.png_sig_cmp = png_sig_cmp;
slouken@267
   288
#ifndef LIBPNG_VERSION_12
slouken@368
   289
        lib.png_set_longjmp_fn = png_set_longjmp_fn;
slouken@267
   290
#endif
slouken@368
   291
    }
slouken@368
   292
    ++lib.loaded;
slouken@146
   293
slouken@368
   294
    return 0;
slouken@143
   295
}
slouken@205
   296
void IMG_QuitPNG()
slouken@143
   297
{
slouken@368
   298
    if ( lib.loaded == 0 ) {
slouken@368
   299
        return;
slouken@368
   300
    }
slouken@368
   301
    if ( lib.loaded == 1 ) {
slouken@368
   302
    }
slouken@368
   303
    --lib.loaded;
slouken@143
   304
}
slouken@143
   305
#endif /* LOAD_PNG_DYNAMIC */
slouken@143
   306
slouken@0
   307
/* See if an image is contained in a data source */
slouken@0
   308
int IMG_isPNG(SDL_RWops *src)
slouken@0
   309
{
slouken@368
   310
    Sint64 start;
slouken@368
   311
    int is_PNG;
slouken@368
   312
    Uint8 magic[4];
slouken@0
   313
slouken@390
   314
    if ( !src ) {
slouken@368
   315
        return 0;
slouken@390
   316
    }
slouken@390
   317
slouken@368
   318
    start = SDL_RWtell(src);
slouken@368
   319
    is_PNG = 0;
slouken@368
   320
    if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
slouken@390
   321
        if ( magic[0] == 0x89 &&
slouken@390
   322
             magic[1] == 'P' &&
slouken@390
   323
             magic[2] == 'N' &&
slouken@390
   324
             magic[3] == 'G' ) {
slouken@368
   325
            is_PNG = 1;
slouken@368
   326
        }
slouken@368
   327
    }
slouken@368
   328
    SDL_RWseek(src, start, RW_SEEK_SET);
slouken@368
   329
    return(is_PNG);
slouken@0
   330
}
slouken@0
   331
slouken@0
   332
/* Load a PNG type image from an SDL datasource */
slouken@0
   333
static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
slouken@0
   334
{
slouken@368
   335
    SDL_RWops *src;
slouken@0
   336
slouken@368
   337
    src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
slouken@368
   338
    SDL_RWread(src, area, size, 1);
slouken@0
   339
}
slouken@0
   340
SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
slouken@0
   341
{
slouken@368
   342
    Sint64 start;
slouken@368
   343
    const char *error;
slouken@368
   344
    SDL_Surface *volatile surface;
slouken@368
   345
    png_structp png_ptr;
slouken@368
   346
    png_infop info_ptr;
slouken@368
   347
    png_uint_32 width, height;
slouken@368
   348
    int bit_depth, color_type, interlace_type, num_channels;
slouken@368
   349
    Uint32 Rmask;
slouken@368
   350
    Uint32 Gmask;
slouken@368
   351
    Uint32 Bmask;
slouken@368
   352
    Uint32 Amask;
slouken@368
   353
    SDL_Palette *palette;
slouken@368
   354
    png_bytep *volatile row_pointers;
slouken@368
   355
    int row, i;
slouken@368
   356
    int ckey = -1;
slouken@368
   357
    png_color_16 *transv;
slouken@0
   358
slouken@368
   359
    if ( !src ) {
slouken@368
   360
        /* The error message has been set in SDL_RWFromFile */
slouken@368
   361
        return NULL;
slouken@368
   362
    }
slouken@368
   363
    start = SDL_RWtell(src);
slouken@98
   364
slouken@490
   365
    if ( (IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) == 0 ) {
slouken@368
   366
        return NULL;
slouken@368
   367
    }
slouken@143
   368
slouken@368
   369
    /* Initialize the data we will clean up when we're done */
slouken@368
   370
    error = NULL;
slouken@368
   371
    png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
slouken@0
   372
slouken@368
   373
    /* Create the PNG loading context structure */
slouken@368
   374
    png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
slouken@368
   375
                      NULL,NULL,NULL);
slouken@368
   376
    if (png_ptr == NULL){
slouken@368
   377
        error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
slouken@368
   378
        goto done;
slouken@368
   379
    }
slouken@0
   380
slouken@368
   381
     /* Allocate/initialize the memory for image information.  REQUIRED. */
slouken@368
   382
    info_ptr = lib.png_create_info_struct(png_ptr);
slouken@368
   383
    if (info_ptr == NULL) {
slouken@368
   384
        error = "Couldn't create image information for PNG file";
slouken@368
   385
        goto done;
slouken@368
   386
    }
slouken@0
   387
slouken@368
   388
    /* Set error handling if you are using setjmp/longjmp method (this is
slouken@368
   389
     * the normal method of doing things with libpng).  REQUIRED unless you
slouken@368
   390
     * set up your own error handlers in png_create_read_struct() earlier.
slouken@368
   391
     */
slouken@265
   392
#ifndef LIBPNG_VERSION_12
slouken@368
   393
    if ( setjmp(*lib.png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))) )
slouken@265
   394
#else
slouken@368
   395
    if ( setjmp(png_ptr->jmpbuf) )
slouken@265
   396
#endif
slouken@368
   397
    {
slouken@368
   398
        error = "Error reading the PNG file.";
slouken@368
   399
        goto done;
slouken@368
   400
    }
slouken@0
   401
slouken@368
   402
    /* Set up the input control */
slouken@368
   403
    lib.png_set_read_fn(png_ptr, src, png_read_data);
slouken@0
   404
slouken@368
   405
    /* Read PNG header info */
slouken@368
   406
    lib.png_read_info(png_ptr, info_ptr);
slouken@368
   407
    lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
slouken@368
   408
            &color_type, &interlace_type, NULL, NULL);
slouken@0
   409
slouken@368
   410
    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
slouken@368
   411
    lib.png_set_strip_16(png_ptr) ;
slouken@0
   412
slouken@368
   413
    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
slouken@368
   414
     * byte into separate bytes (useful for paletted and grayscale images).
slouken@368
   415
     */
slouken@368
   416
    lib.png_set_packing(png_ptr);
slouken@0
   417
slouken@368
   418
    /* scale greyscale values to the range 0..255 */
slouken@368
   419
    if (color_type == PNG_COLOR_TYPE_GRAY)
slouken@368
   420
        lib.png_set_expand(png_ptr);
slouken@0
   421
slouken@368
   422
    /* For images with a single "transparent colour", set colour key;
slouken@368
   423
       if more than one index has transparency, or if partially transparent
slouken@368
   424
       entries exist, use full alpha channel */
slouken@368
   425
    if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
slouken@390
   426
        int num_trans;
slouken@368
   427
        Uint8 *trans;
slouken@390
   428
        lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &transv);
slouken@368
   429
        if (color_type == PNG_COLOR_TYPE_PALETTE) {
slouken@368
   430
            /* Check if all tRNS entries are opaque except one */
slouken@368
   431
            int j, t = -1;
slouken@368
   432
            for (j = 0; j < num_trans; j++) {
slouken@390
   433
                if (trans[j] == 0) {
slouken@390
   434
                    if (t >= 0) {
slouken@390
   435
                        break;
slouken@361
   436
                    }
slouken@390
   437
                    t = j;
slouken@390
   438
                } else if (trans[j] != 255) {
slouken@390
   439
                    break;
slouken@390
   440
                }
slouken@390
   441
            }
slouken@368
   442
            if (j == num_trans) {
slouken@390
   443
                /* exactly one transparent index */
slouken@390
   444
                ckey = t;
slouken@368
   445
            } else {
slouken@390
   446
                /* more than one transparent index, or translucency */
slouken@390
   447
                lib.png_set_expand(png_ptr);
slouken@368
   448
            }
slouken@390
   449
        } else {
slouken@368
   450
            ckey = 0; /* actual value will be set later */
slouken@390
   451
        }
slouken@368
   452
    }
slouken@0
   453
slouken@368
   454
    if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
slouken@368
   455
        lib.png_set_gray_to_rgb(png_ptr);
slouken@0
   456
slouken@368
   457
    lib.png_read_update_info(png_ptr, info_ptr);
slouken@0
   458
slouken@368
   459
    lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
slouken@368
   460
            &color_type, &interlace_type, NULL, NULL);
slouken@0
   461
slouken@368
   462
    /* Allocate the SDL surface to hold the image */
slouken@368
   463
    Rmask = Gmask = Bmask = Amask = 0 ;
slouken@368
   464
    num_channels = lib.png_get_channels(png_ptr, info_ptr);
slouken@368
   465
    if ( num_channels >= 3 ) {
aschiffler@343
   466
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@390
   467
        Rmask = 0x000000FF;
slouken@390
   468
        Gmask = 0x0000FF00;
slouken@390
   469
        Bmask = 0x00FF0000;
slouken@390
   470
        Amask = (num_channels == 4) ? 0xFF000000 : 0;
aschiffler@343
   471
#else
slouken@390
   472
        int s = (num_channels == 4) ? 0 : 8;
slouken@390
   473
        Rmask = 0xFF000000 >> s;
slouken@390
   474
        Gmask = 0x00FF0000 >> s;
slouken@390
   475
        Bmask = 0x0000FF00 >> s;
slouken@390
   476
        Amask = 0x000000FF >> s;
aschiffler@343
   477
#endif
slouken@368
   478
    }
slouken@368
   479
    surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
slouken@368
   480
            bit_depth*num_channels, Rmask,Gmask,Bmask,Amask);
slouken@368
   481
    if ( surface == NULL ) {
slouken@368
   482
        error = SDL_GetError();
slouken@368
   483
        goto done;
slouken@368
   484
    }
slouken@0
   485
slouken@368
   486
    if (ckey != -1) {
slouken@390
   487
        if (color_type != PNG_COLOR_TYPE_PALETTE) {
slouken@368
   488
            /* FIXME: Should these be truncated or shifted down? */
slouken@390
   489
            ckey = SDL_MapRGB(surface->format,
slouken@390
   490
                         (Uint8)transv->red,
slouken@390
   491
                         (Uint8)transv->green,
slouken@390
   492
                         (Uint8)transv->blue);
slouken@390
   493
        }
slouken@390
   494
        SDL_SetColorKey(surface, SDL_TRUE, ckey);
slouken@368
   495
    }
slouken@0
   496
slouken@368
   497
    /* Create the array of pointers to image data */
slouken@368
   498
    row_pointers = (png_bytep*) SDL_malloc(sizeof(png_bytep)*height);
slouken@424
   499
    if (!row_pointers) {
slouken@368
   500
        error = "Out of memory";
slouken@368
   501
        goto done;
slouken@368
   502
    }
slouken@368
   503
    for (row = 0; row < (int)height; row++) {
slouken@368
   504
        row_pointers[row] = (png_bytep)
slouken@368
   505
                (Uint8 *)surface->pixels + row*surface->pitch;
slouken@368
   506
    }
slouken@0
   507
slouken@368
   508
    /* Read the entire image in one go */
slouken@368
   509
    lib.png_read_image(png_ptr, row_pointers);
slouken@0
   510
slouken@368
   511
    /* and we're done!  (png_read_end() can be omitted if no processing of
slouken@368
   512
     * post-IDAT text/time/etc. is desired)
slouken@368
   513
     * In some cases it can't read PNG's created by some popular programs (ACDSEE),
slouken@368
   514
     * we do not want to process comments, so we omit png_read_end
slouken@100
   515
slouken@368
   516
    lib.png_read_end(png_ptr, info_ptr);
slouken@368
   517
    */
slouken@0
   518
slouken@368
   519
    /* Load the palette, if any */
slouken@368
   520
    palette = surface->format->palette;
slouken@368
   521
    if ( palette ) {
slouken@368
   522
        int png_num_palette;
slouken@368
   523
        png_colorp png_palette;
slouken@368
   524
        lib.png_get_PLTE(png_ptr, info_ptr, &png_palette, &png_num_palette);
slouken@368
   525
        if (color_type == PNG_COLOR_TYPE_GRAY) {
slouken@390
   526
            palette->ncolors = 256;
slouken@390
   527
            for (i = 0; i < 256; i++) {
slouken@390
   528
                palette->colors[i].r = i;
slouken@390
   529
                palette->colors[i].g = i;
slouken@390
   530
                palette->colors[i].b = i;
slouken@390
   531
            }
slouken@368
   532
        } else if (png_num_palette > 0 ) {
slouken@390
   533
            palette->ncolors = png_num_palette;
slouken@390
   534
            for ( i=0; i<png_num_palette; ++i ) {
slouken@390
   535
                palette->colors[i].b = png_palette[i].blue;
slouken@390
   536
                palette->colors[i].g = png_palette[i].green;
slouken@390
   537
                palette->colors[i].r = png_palette[i].red;
slouken@390
   538
            }
slouken@368
   539
        }
slouken@368
   540
    }
slouken@0
   541
slouken@368
   542
done:   /* Clean up and return */
slouken@368
   543
    if ( png_ptr ) {
slouken@368
   544
        lib.png_destroy_read_struct(&png_ptr,
slouken@368
   545
                                info_ptr ? &info_ptr : (png_infopp)0,
slouken@368
   546
                                (png_infopp)0);
slouken@368
   547
    }
slouken@368
   548
    if ( row_pointers ) {
slouken@368
   549
        SDL_free(row_pointers);
slouken@368
   550
    }
slouken@368
   551
    if ( error ) {
slouken@368
   552
        SDL_RWseek(src, start, RW_SEEK_SET);
slouken@368
   553
        if ( surface ) {
slouken@368
   554
            SDL_FreeSurface(surface);
slouken@368
   555
            surface = NULL;
slouken@368
   556
        }
slouken@451
   557
        IMG_SetError("%s", error);
slouken@368
   558
    }
slouken@368
   559
    return(surface);
slouken@0
   560
}
slouken@0
   561
slouken@0
   562
#else
slouken@0
   563
slouken@205
   564
int IMG_InitPNG()
slouken@205
   565
{
slouken@368
   566
    IMG_SetError("PNG images are not supported");
slouken@368
   567
    return(-1);
slouken@205
   568
}
slouken@205
   569
slouken@205
   570
void IMG_QuitPNG()
slouken@205
   571
{
slouken@205
   572
}
slouken@205
   573
slouken@0
   574
/* See if an image is contained in a data source */
slouken@0
   575
int IMG_isPNG(SDL_RWops *src)
slouken@0
   576
{
slouken@368
   577
    return(0);
slouken@0
   578
}
slouken@0
   579
slouken@0
   580
/* Load a PNG type image from an SDL datasource */
slouken@0
   581
SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
slouken@0
   582
{
slouken@368
   583
    return(NULL);
slouken@0
   584
}
slouken@0
   585
slouken@0
   586
#endif /* LOAD_PNG */
slouken@237
   587
slouken@237
   588
#endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
slouken@391
   589
slouken@391
   590
/* We'll always have PNG save support */
slouken@391
   591
#define SAVE_PNG
slouken@391
   592
slouken@391
   593
#ifdef SAVE_PNG
slouken@391
   594
slouken@397
   595
#include "miniz.h"
slouken@391
   596
slouken@391
   597
int IMG_SavePNG(SDL_Surface *surface, const char *file)
slouken@391
   598
{
slouken@391
   599
    SDL_RWops *dst = SDL_RWFromFile(file, "wb");
slouken@391
   600
    if (dst) {
slouken@391
   601
        return IMG_SavePNG_RW(surface, dst, 1);
slouken@391
   602
    } else {
slouken@391
   603
        return -1;
slouken@391
   604
    }
slouken@391
   605
}
slouken@391
   606
slouken@391
   607
int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst)
slouken@391
   608
{
slouken@391
   609
    int result = -1;
slouken@391
   610
slouken@391
   611
    if (dst) {
slouken@391
   612
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@391
   613
        static const Uint32 png_format = SDL_PIXELFORMAT_ABGR8888;
slouken@391
   614
#else
slouken@391
   615
        static const Uint32 png_format = SDL_PIXELFORMAT_RGBA8888;
slouken@391
   616
#endif
slouken@391
   617
        size_t size;
slouken@427
   618
        void *png = NULL;
slouken@391
   619
slouken@391
   620
        if (surface->format->format == png_format) {
slouken@431
   621
            png = tdefl_write_image_to_png_file_in_memory(surface->pixels, surface->w, surface->h, surface->format->BytesPerPixel, surface->pitch, &size);
slouken@391
   622
        } else {
slouken@391
   623
            SDL_Surface *cvt = SDL_ConvertSurfaceFormat(surface, png_format, 0);
slouken@391
   624
            if (cvt) {
slouken@431
   625
                png = tdefl_write_image_to_png_file_in_memory(cvt->pixels, cvt->w, cvt->h, cvt->format->BytesPerPixel, cvt->pitch, &size);
slouken@391
   626
                SDL_FreeSurface(cvt);
slouken@391
   627
            }
slouken@391
   628
        }
slouken@391
   629
        if (png) {
slouken@391
   630
            if (SDL_RWwrite(dst, png, size, 1)) {
slouken@391
   631
                result = 0;
slouken@391
   632
            }
slouken@391
   633
            SDL_free(png);
slouken@391
   634
        } else {
slouken@391
   635
            SDL_SetError("Failed to convert and save image");
slouken@391
   636
        }
slouken@391
   637
        if (freedst) {
slouken@391
   638
            SDL_RWclose(dst);
slouken@391
   639
        }
slouken@391
   640
    } else {
slouken@391
   641
        SDL_SetError("Passed NULL dst");
slouken@391
   642
    }
slouken@391
   643
    return result;
slouken@391
   644
}
slouken@391
   645
slouken@391
   646
#endif /* SAVE_PNG */