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