IMG_pnm.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 22 Oct 2017 01:23:38 -0700
changeset 528 7f9e88f4b45e
parent 496 6332f9425dcc
child 575 36e9e2255178
permissions -rwxr-xr-x
Removed some dependencies on C runtime for building on Windows
slouken@25
     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@25
     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@25
     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@25
    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@25
    20
*/
slouken@25
    21
slouken@25
    22
/*
slouken@25
    23
 * PNM (portable anymap) image loader:
slouken@25
    24
 *
slouken@25
    25
 * Supports: PBM, PGM and PPM, ASCII and binary formats
slouken@25
    26
 * (PBM and PGM are loaded as 8bpp surfaces)
slouken@25
    27
 * Does not support: maximum component value > 255
slouken@25
    28
 */
slouken@25
    29
slouken@25
    30
#include "SDL_image.h"
slouken@25
    31
slouken@25
    32
#ifdef LOAD_PNM
slouken@25
    33
slouken@25
    34
/* See if an image is contained in a data source */
slouken@25
    35
int IMG_isPNM(SDL_RWops *src)
slouken@25
    36
{
slouken@368
    37
    Sint64 start;
slouken@368
    38
    int is_PNM;
slouken@368
    39
    char magic[2];
slouken@25
    40
slouken@368
    41
    if ( !src )
slouken@368
    42
        return 0;
slouken@368
    43
    start = SDL_RWtell(src);
slouken@368
    44
    is_PNM = 0;
slouken@368
    45
    if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
slouken@368
    46
        /*
slouken@368
    47
         * PNM magic signatures:
slouken@368
    48
         * P1   PBM, ascii format
slouken@368
    49
         * P2   PGM, ascii format
slouken@368
    50
         * P3   PPM, ascii format
slouken@368
    51
         * P4   PBM, binary format
slouken@368
    52
         * P5   PGM, binary format
slouken@368
    53
         * P6   PPM, binary format
slouken@368
    54
         * P7   PAM, a general wrapper for PNM data
slouken@368
    55
         */
slouken@368
    56
        if ( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) {
slouken@368
    57
            is_PNM = 1;
slouken@368
    58
        }
slouken@368
    59
    }
slouken@368
    60
    SDL_RWseek(src, start, RW_SEEK_SET);
slouken@368
    61
    return(is_PNM);
slouken@25
    62
}
slouken@25
    63
slouken@25
    64
/* read a non-negative integer from the source. return -1 upon error */
slouken@25
    65
static int ReadNumber(SDL_RWops *src)
slouken@25
    66
{
slouken@368
    67
    int number;
slouken@368
    68
    unsigned char ch;
slouken@25
    69
slouken@368
    70
    /* Initialize return value */
slouken@368
    71
    number = 0;
slouken@25
    72
slouken@368
    73
    /* Skip leading whitespace */
slouken@368
    74
    do {
slouken@368
    75
        if ( ! SDL_RWread(src, &ch, 1, 1) ) {
slouken@368
    76
            return(0);
slouken@368
    77
        }
slouken@368
    78
        /* Eat comments as whitespace */
slouken@368
    79
        if ( ch == '#' ) {  /* Comment is '#' to end of line */
slouken@368
    80
            do {
slouken@368
    81
                if ( ! SDL_RWread(src, &ch, 1, 1) ) {
slouken@368
    82
                    return -1;
slouken@368
    83
                }
slouken@368
    84
            } while ( (ch != '\r') && (ch != '\n') );
slouken@368
    85
        }
slouken@481
    86
    } while ( SDL_isspace(ch) );
slouken@25
    87
slouken@368
    88
    /* Add up the number */
slouken@368
    89
    do {
slouken@368
    90
        number *= 10;
slouken@368
    91
        number += ch-'0';
slouken@25
    92
slouken@368
    93
        if ( !SDL_RWread(src, &ch, 1, 1) ) {
slouken@368
    94
            return -1;
slouken@368
    95
        }
slouken@481
    96
    } while ( SDL_isdigit(ch) );
slouken@25
    97
slouken@368
    98
    return(number);
slouken@25
    99
}
slouken@25
   100
slouken@25
   101
SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
slouken@25
   102
{
slouken@368
   103
    Sint64 start;
slouken@368
   104
    SDL_Surface *surface = NULL;
slouken@368
   105
    int width, height;
slouken@368
   106
    int maxval, y, bpl;
slouken@368
   107
    Uint8 *row;
slouken@368
   108
    Uint8 *buf = NULL;
slouken@368
   109
    char *error = NULL;
slouken@368
   110
    Uint8 magic[2];
slouken@368
   111
    int ascii;
slouken@368
   112
    enum { PBM, PGM, PPM, PAM } kind;
slouken@25
   113
slouken@25
   114
#define ERROR(s) do { error = (s); goto done; } while(0)
slouken@25
   115
slouken@368
   116
    if ( !src ) {
slouken@368
   117
        /* The error message has been set in SDL_RWFromFile */
slouken@368
   118
        return NULL;
slouken@368
   119
    }
slouken@368
   120
    start = SDL_RWtell(src);
slouken@98
   121
slouken@368
   122
    SDL_RWread(src, magic, 2, 1);
slouken@368
   123
    kind = magic[1] - '1';
slouken@368
   124
    ascii = 1;
slouken@368
   125
    if(kind >= 3) {
slouken@368
   126
        ascii = 0;
slouken@368
   127
        kind -= 3;
slouken@368
   128
    }
slouken@25
   129
slouken@368
   130
    width = ReadNumber(src);
slouken@368
   131
    height = ReadNumber(src);
slouken@368
   132
    if(width <= 0 || height <= 0)
slouken@368
   133
        ERROR("Unable to read image width and height");
slouken@25
   134
slouken@368
   135
    if(kind != PBM) {
slouken@368
   136
        maxval = ReadNumber(src);
slouken@368
   137
        if(maxval <= 0 || maxval > 255)
slouken@368
   138
            ERROR("unsupported PNM format");
slouken@368
   139
    } else
slouken@368
   140
        maxval = 255;   /* never scale PBMs */
slouken@25
   141
slouken@368
   142
    /* binary PNM allows just a single character of whitespace after
slouken@368
   143
       the last parameter, and we've already consumed it */
slouken@25
   144
slouken@368
   145
    if(kind == PPM) {
slouken@368
   146
        /* 24-bit surface in R,G,B byte order */
slouken@368
   147
        surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24,
slouken@25
   148
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@368
   149
                       0x000000ff, 0x0000ff00, 0x00ff0000,
slouken@25
   150
#else
slouken@368
   151
                       0x00ff0000, 0x0000ff00, 0x000000ff,
slouken@25
   152
#endif
slouken@368
   153
                       0);
slouken@368
   154
    } else {
slouken@368
   155
        /* load PBM/PGM as 8-bit indexed images */
slouken@368
   156
        surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8,
slouken@368
   157
                       0, 0, 0, 0);
slouken@368
   158
    }
slouken@368
   159
    if ( surface == NULL )
slouken@368
   160
        ERROR("Out of memory");
slouken@368
   161
    bpl = width * surface->format->BytesPerPixel;
slouken@368
   162
    if(kind == PGM) {
slouken@368
   163
        SDL_Color *c = surface->format->palette->colors;
slouken@368
   164
        int i;
slouken@368
   165
        for(i = 0; i < 256; i++)
slouken@368
   166
            c[i].r = c[i].g = c[i].b = i;
slouken@368
   167
        surface->format->palette->ncolors = 256;
slouken@368
   168
    } else if(kind == PBM) {
slouken@368
   169
        /* for some reason PBM has 1=black, 0=white */
slouken@368
   170
        SDL_Color *c = surface->format->palette->colors;
slouken@368
   171
        c[0].r = c[0].g = c[0].b = 255;
slouken@368
   172
        c[1].r = c[1].g = c[1].b = 0;
slouken@368
   173
        surface->format->palette->ncolors = 2;
slouken@368
   174
        bpl = (width + 7) >> 3;
slouken@368
   175
        buf = (Uint8 *)SDL_malloc(bpl);
slouken@368
   176
        if(buf == NULL)
slouken@368
   177
            ERROR("Out of memory");
slouken@368
   178
    }
slouken@25
   179
slouken@368
   180
    /* Read the image into the surface */
slouken@368
   181
    row = (Uint8 *)surface->pixels;
slouken@368
   182
    for(y = 0; y < height; y++) {
slouken@368
   183
        if(ascii) {
slouken@368
   184
            int i;
slouken@368
   185
            if(kind == PBM) {
slouken@368
   186
                for(i = 0; i < width; i++) {
slouken@368
   187
                    Uint8 ch;
slouken@368
   188
                    do {
slouken@368
   189
                        if(!SDL_RWread(src, &ch,
slouken@368
   190
                                   1, 1))
slouken@368
   191
                               ERROR("file truncated");
slouken@368
   192
                        ch -= '0';
slouken@368
   193
                    } while(ch > 1);
slouken@368
   194
                    row[i] = ch;
slouken@368
   195
                }
slouken@368
   196
            } else {
slouken@368
   197
                for(i = 0; i < bpl; i++) {
slouken@368
   198
                    int c;
slouken@368
   199
                    c = ReadNumber(src);
slouken@368
   200
                    if(c < 0)
slouken@368
   201
                        ERROR("file truncated");
slouken@368
   202
                    row[i] = c;
slouken@368
   203
                }
slouken@368
   204
            }
slouken@368
   205
        } else {
slouken@368
   206
            Uint8 *dst = (kind == PBM) ? buf : row;
slouken@368
   207
            if(!SDL_RWread(src, dst, bpl, 1))
slouken@368
   208
                ERROR("file truncated");
slouken@368
   209
            if(kind == PBM) {
slouken@368
   210
                /* expand bitmap to 8bpp */
slouken@368
   211
                int i;
slouken@368
   212
                for(i = 0; i < width; i++) {
slouken@368
   213
                    int bit = 7 - (i & 7);
slouken@368
   214
                    row[i] = (buf[i >> 3] >> bit) & 1;
slouken@368
   215
                }
slouken@368
   216
            }
slouken@368
   217
        }
slouken@368
   218
        if(maxval < 255) {
slouken@368
   219
            /* scale up to full dynamic range (slow) */
slouken@368
   220
            int i;
slouken@368
   221
            for(i = 0; i < bpl; i++)
slouken@368
   222
                row[i] = row[i] * 255 / maxval;
slouken@368
   223
        }
slouken@368
   224
        row += surface->pitch;
slouken@368
   225
    }
slouken@25
   226
done:
slouken@368
   227
    SDL_free(buf);
slouken@368
   228
    if(error) {
slouken@368
   229
        SDL_RWseek(src, start, RW_SEEK_SET);
slouken@368
   230
        if ( surface ) {
slouken@368
   231
            SDL_FreeSurface(surface);
slouken@368
   232
            surface = NULL;
slouken@368
   233
        }
slouken@451
   234
        IMG_SetError("%s", error);
slouken@368
   235
    }
slouken@368
   236
    return(surface);
slouken@25
   237
}
slouken@25
   238
slouken@25
   239
#else
slouken@25
   240
slouken@25
   241
/* See if an image is contained in a data source */
slouken@25
   242
int IMG_isPNM(SDL_RWops *src)
slouken@25
   243
{
slouken@368
   244
    return(0);
slouken@25
   245
}
slouken@25
   246
slouken@25
   247
/* Load a PNM type image from an SDL datasource */
slouken@25
   248
SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
slouken@25
   249
{
slouken@368
   250
    return(NULL);
slouken@25
   251
}
slouken@25
   252
slouken@25
   253
#endif /* LOAD_PNM */