IMG_pnm.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 26 Sep 2018 15:19:34 -0400
changeset 587 32a18ca05935
parent 575 36e9e2255178
child 591 90a531f221f2
permissions -rw-r--r--
pnm: Don't get into infinite loops on truncated files.
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 /*
    23  * PNM (portable anymap) image loader:
    24  *
    25  * Supports: PBM, PGM and PPM, ASCII and binary formats
    26  * (PBM and PGM are loaded as 8bpp surfaces)
    27  * Does not support: maximum component value > 255
    28  */
    29 
    30 #include "SDL_image.h"
    31 
    32 #ifdef LOAD_PNM
    33 
    34 /* See if an image is contained in a data source */
    35 int IMG_isPNM(SDL_RWops *src)
    36 {
    37     Sint64 start;
    38     int is_PNM;
    39     char magic[2];
    40 
    41     if ( !src )
    42         return 0;
    43     start = SDL_RWtell(src);
    44     is_PNM = 0;
    45     if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
    46         /*
    47          * PNM magic signatures:
    48          * P1   PBM, ascii format
    49          * P2   PGM, ascii format
    50          * P3   PPM, ascii format
    51          * P4   PBM, binary format
    52          * P5   PGM, binary format
    53          * P6   PPM, binary format
    54          * P7   PAM, a general wrapper for PNM data
    55          */
    56         if ( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) {
    57             is_PNM = 1;
    58         }
    59     }
    60     SDL_RWseek(src, start, RW_SEEK_SET);
    61     return(is_PNM);
    62 }
    63 
    64 /* read a non-negative integer from the source. return -1 upon error */
    65 static int ReadNumber(SDL_RWops *src)
    66 {
    67     int number;
    68     unsigned char ch;
    69 
    70     /* Initialize return value */
    71     number = 0;
    72 
    73     /* Skip leading whitespace */
    74     do {
    75         if ( ! SDL_RWread(src, &ch, 1, 1) ) {
    76             return(-1);
    77         }
    78         /* Eat comments as whitespace */
    79         if ( ch == '#' ) {  /* Comment is '#' to end of line */
    80             do {
    81                 if ( ! SDL_RWread(src, &ch, 1, 1) ) {
    82                     return -1;
    83                 }
    84             } while ( (ch != '\r') && (ch != '\n') );
    85         }
    86     } while ( SDL_isspace(ch) );
    87 
    88     /* Add up the number */
    89     do {
    90         number *= 10;
    91         number += ch-'0';
    92 
    93         if ( !SDL_RWread(src, &ch, 1, 1) ) {
    94             return -1;
    95         }
    96     } while ( SDL_isdigit(ch) );
    97 
    98     return(number);
    99 }
   100 
   101 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
   102 {
   103     Sint64 start;
   104     SDL_Surface *surface = NULL;
   105     int width, height;
   106     int maxval, y, bpl;
   107     Uint8 *row;
   108     Uint8 *buf = NULL;
   109     char *error = NULL;
   110     Uint8 magic[2];
   111     int ascii;
   112     enum { PBM, PGM, PPM, PAM } kind;
   113 
   114 #define ERROR(s) do { error = (s); goto done; } while(0)
   115 
   116     if ( !src ) {
   117         /* The error message has been set in SDL_RWFromFile */
   118         return NULL;
   119     }
   120     start = SDL_RWtell(src);
   121 
   122     SDL_RWread(src, magic, 2, 1);
   123     kind = magic[1] - '1';
   124     ascii = 1;
   125     if(kind >= 3) {
   126         ascii = 0;
   127         kind -= 3;
   128     }
   129 
   130     width = ReadNumber(src);
   131     height = ReadNumber(src);
   132     if(width <= 0 || height <= 0)
   133         ERROR("Unable to read image width and height");
   134 
   135     if(kind != PBM) {
   136         maxval = ReadNumber(src);
   137         if(maxval <= 0 || maxval > 255)
   138             ERROR("unsupported PNM format");
   139     } else
   140         maxval = 255;   /* never scale PBMs */
   141 
   142     /* binary PNM allows just a single character of whitespace after
   143        the last parameter, and we've already consumed it */
   144 
   145     if(kind == PPM) {
   146         /* 24-bit surface in R,G,B byte order */
   147         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24,
   148 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   149                        0x000000ff, 0x0000ff00, 0x00ff0000,
   150 #else
   151                        0x00ff0000, 0x0000ff00, 0x000000ff,
   152 #endif
   153                        0);
   154     } else {
   155         /* load PBM/PGM as 8-bit indexed images */
   156         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8,
   157                        0, 0, 0, 0);
   158     }
   159     if ( surface == NULL )
   160         ERROR("Out of memory");
   161     bpl = width * surface->format->BytesPerPixel;
   162     if(kind == PGM) {
   163         SDL_Color *c = surface->format->palette->colors;
   164         int i;
   165         for(i = 0; i < 256; i++)
   166             c[i].r = c[i].g = c[i].b = i;
   167         surface->format->palette->ncolors = 256;
   168     } else if(kind == PBM) {
   169         /* for some reason PBM has 1=black, 0=white */
   170         SDL_Color *c = surface->format->palette->colors;
   171         c[0].r = c[0].g = c[0].b = 255;
   172         c[1].r = c[1].g = c[1].b = 0;
   173         surface->format->palette->ncolors = 2;
   174         bpl = (width + 7) >> 3;
   175         buf = (Uint8 *)SDL_malloc(bpl);
   176         if(buf == NULL)
   177             ERROR("Out of memory");
   178     }
   179 
   180     /* Read the image into the surface */
   181     row = (Uint8 *)surface->pixels;
   182     for(y = 0; y < height; y++) {
   183         if(ascii) {
   184             int i;
   185             if(kind == PBM) {
   186                 for(i = 0; i < width; i++) {
   187                     Uint8 ch;
   188                     do {
   189                         if(!SDL_RWread(src, &ch,
   190                                    1, 1))
   191                                ERROR("file truncated");
   192                         ch -= '0';
   193                     } while(ch > 1);
   194                     row[i] = ch;
   195                 }
   196             } else {
   197                 for(i = 0; i < bpl; i++) {
   198                     int c;
   199                     c = ReadNumber(src);
   200                     if(c < 0)
   201                         ERROR("file truncated");
   202                     row[i] = c;
   203                 }
   204             }
   205         } else {
   206             Uint8 *dst = (kind == PBM) ? buf : row;
   207             if(!SDL_RWread(src, dst, bpl, 1))
   208                 ERROR("file truncated");
   209             if(kind == PBM) {
   210                 /* expand bitmap to 8bpp */
   211                 int i;
   212                 for(i = 0; i < width; i++) {
   213                     int bit = 7 - (i & 7);
   214                     row[i] = (buf[i >> 3] >> bit) & 1;
   215                 }
   216             }
   217         }
   218         if(maxval < 255) {
   219             /* scale up to full dynamic range (slow) */
   220             int i;
   221             for(i = 0; i < bpl; i++)
   222                 row[i] = row[i] * 255 / maxval;
   223         }
   224         row += surface->pitch;
   225     }
   226 done:
   227     SDL_free(buf);
   228     if(error) {
   229         SDL_RWseek(src, start, RW_SEEK_SET);
   230         if ( surface ) {
   231             SDL_FreeSurface(surface);
   232             surface = NULL;
   233         }
   234         IMG_SetError("%s", error);
   235     }
   236     return(surface);
   237 }
   238 
   239 #else
   240 
   241 /* See if an image is contained in a data source */
   242 int IMG_isPNM(SDL_RWops *src)
   243 {
   244     return(0);
   245 }
   246 
   247 /* Load a PNM type image from an SDL datasource */
   248 SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src)
   249 {
   250     return(NULL);
   251 }
   252 
   253 #endif /* LOAD_PNM */