1/* 2 * PNM image format 3 * Copyright (c) 2002, 2003 Fabrice Bellard 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <stdlib.h> 23#include <string.h> 24 25#include "libavutil/avassert.h" 26#include "libavutil/imgutils.h" 27#include "libavutil/avstring.h" 28#include "avcodec.h" 29#include "internal.h" 30#include "pnm.h" 31 32static inline int pnm_space(int c) 33{ 34 return c == ' ' || c == '\n' || c == '\r' || c == '\t'; 35} 36 37static void pnm_get(PNMContext *sc, char *str, int buf_size) 38{ 39 char *s; 40 int c; 41 uint8_t *bs = sc->bytestream; 42 const uint8_t *end = sc->bytestream_end; 43 44 /* skip spaces and comments */ 45 while (bs < end) { 46 c = *bs++; 47 if (c == '#') { 48 while (c != '\n' && bs < end) { 49 c = *bs++; 50 } 51 } else if (!pnm_space(c)) { 52 break; 53 } 54 } 55 56 s = str; 57 while (bs < end && !pnm_space(c) && (s - str) < buf_size - 1) { 58 *s++ = c; 59 c = *bs++; 60 } 61 *s = '\0'; 62 sc->bytestream = bs; 63} 64 65int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) 66{ 67 char buf1[32], tuple_type[32]; 68 int h, w, depth, maxval; 69 int ret; 70 71 if (s->bytestream_end - s->bytestream < 3 || 72 s->bytestream[0] != 'P' || 73 (s->bytestream[1] < '1' || 74 s->bytestream[1] > '7' && 75 s->bytestream[1] != 'f' && 76 s->bytestream[1] != 'F' && 77 s->bytestream[1] != 'H' && 78 s->bytestream[1] != 'h')) { 79 s->bytestream += s->bytestream_end > s->bytestream; 80 s->bytestream += s->bytestream_end > s->bytestream; 81 return AVERROR_INVALIDDATA; 82 } 83 pnm_get(s, buf1, sizeof(buf1)); 84 s->type= buf1[1]-'0'; 85 s->half = 0; 86 87 if (buf1[1] == 'F') { 88 avctx->pix_fmt = AV_PIX_FMT_GBRPF32; 89 } else if (buf1[1] == 'f') { 90 avctx->pix_fmt = AV_PIX_FMT_GRAYF32; 91 } else if (buf1[1] == 'H') { 92 avctx->pix_fmt = AV_PIX_FMT_GBRPF32; 93 s->half = 1; 94 } else if (buf1[1] == 'h') { 95 avctx->pix_fmt = AV_PIX_FMT_GRAYF32; 96 s->half = 1; 97 } else if (s->type==1 || s->type==4) { 98 avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; 99 } else if (s->type==2 || s->type==5) { 100 if (avctx->codec_id == AV_CODEC_ID_PGMYUV) 101 avctx->pix_fmt = AV_PIX_FMT_YUV420P; 102 else 103 avctx->pix_fmt = AV_PIX_FMT_GRAY8; 104 } else if (s->type==3 || s->type==6) { 105 avctx->pix_fmt = AV_PIX_FMT_RGB24; 106 } else if (s->type==7) { 107 w = -1; 108 h = -1; 109 maxval = -1; 110 depth = -1; 111 tuple_type[0] = '\0'; 112 for (;;) { 113 pnm_get(s, buf1, sizeof(buf1)); 114 if (!strcmp(buf1, "WIDTH")) { 115 pnm_get(s, buf1, sizeof(buf1)); 116 w = strtol(buf1, NULL, 10); 117 } else if (!strcmp(buf1, "HEIGHT")) { 118 pnm_get(s, buf1, sizeof(buf1)); 119 h = strtol(buf1, NULL, 10); 120 } else if (!strcmp(buf1, "DEPTH")) { 121 pnm_get(s, buf1, sizeof(buf1)); 122 depth = strtol(buf1, NULL, 10); 123 } else if (!strcmp(buf1, "MAXVAL")) { 124 pnm_get(s, buf1, sizeof(buf1)); 125 maxval = strtol(buf1, NULL, 10); 126 } else if (!strcmp(buf1, "TUPLTYPE") || 127 /* libavcodec used to write invalid files */ 128 !strcmp(buf1, "TUPLETYPE")) { 129 pnm_get(s, tuple_type, sizeof(tuple_type)); 130 } else if (!strcmp(buf1, "ENDHDR")) { 131 break; 132 } else { 133 return AVERROR_INVALIDDATA; 134 } 135 } 136 if (!pnm_space(s->bytestream[-1])) 137 return AVERROR_INVALIDDATA; 138 139 /* check that all tags are present */ 140 if (w <= 0 || h <= 0 || maxval <= 0 || maxval > UINT16_MAX || depth <= 0 || tuple_type[0] == '\0' || 141 av_image_check_size(w, h, 0, avctx) || s->bytestream >= s->bytestream_end) 142 return AVERROR_INVALIDDATA; 143 144 ret = ff_set_dimensions(avctx, w, h); 145 if (ret < 0) 146 return ret; 147 s->maxval = maxval; 148 if (depth == 1) { 149 if (maxval == 1) { 150 avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; 151 } else if (maxval < 256) { 152 avctx->pix_fmt = AV_PIX_FMT_GRAY8; 153 } else { 154 avctx->pix_fmt = AV_PIX_FMT_GRAY16; 155 } 156 } else if (depth == 2) { 157 if (maxval < 256) { 158 avctx->pix_fmt = AV_PIX_FMT_GRAY8A; 159 } else { 160 avctx->pix_fmt = AV_PIX_FMT_YA16; 161 } 162 } else if (depth == 3) { 163 if (maxval < 256) { 164 avctx->pix_fmt = AV_PIX_FMT_RGB24; 165 } else { 166 avctx->pix_fmt = AV_PIX_FMT_RGB48; 167 } 168 } else if (depth == 4) { 169 if (maxval < 256) { 170 avctx->pix_fmt = AV_PIX_FMT_RGBA; 171 } else { 172 avctx->pix_fmt = AV_PIX_FMT_RGBA64; 173 } 174 } else { 175 return AVERROR_INVALIDDATA; 176 } 177 return 0; 178 } else { 179 av_assert0(0); 180 } 181 pnm_get(s, buf1, sizeof(buf1)); 182 w = atoi(buf1); 183 pnm_get(s, buf1, sizeof(buf1)); 184 h = atoi(buf1); 185 if(w <= 0 || h <= 0 || av_image_check_size(w, h, 0, avctx) || s->bytestream >= s->bytestream_end) 186 return AVERROR_INVALIDDATA; 187 188 ret = ff_set_dimensions(avctx, w, h); 189 if (ret < 0) 190 return ret; 191 192 if (avctx->pix_fmt == AV_PIX_FMT_GBRPF32 || avctx->pix_fmt == AV_PIX_FMT_GRAYF32) { 193 pnm_get(s, buf1, sizeof(buf1)); 194 if (av_sscanf(buf1, "%f", &s->scale) != 1 || s->scale == 0.0 || !isfinite(s->scale)) { 195 av_log(avctx, AV_LOG_ERROR, "Invalid scale.\n"); 196 return AVERROR_INVALIDDATA; 197 } 198 s->endian = s->scale < 0.f; 199 s->scale = fabsf(s->scale); 200 s->maxval = (1ULL << 32) - 1; 201 } else if (avctx->pix_fmt != AV_PIX_FMT_MONOWHITE && avctx->pix_fmt != AV_PIX_FMT_MONOBLACK) { 202 pnm_get(s, buf1, sizeof(buf1)); 203 s->maxval = atoi(buf1); 204 if (s->maxval <= 0 || s->maxval > UINT16_MAX) { 205 av_log(avctx, AV_LOG_ERROR, "Invalid maxval: %d\n", s->maxval); 206 s->maxval = 255; 207 } 208 if (s->maxval >= 256) { 209 if (avctx->pix_fmt == AV_PIX_FMT_GRAY8) { 210 avctx->pix_fmt = AV_PIX_FMT_GRAY16; 211 } else if (avctx->pix_fmt == AV_PIX_FMT_RGB24) { 212 avctx->pix_fmt = AV_PIX_FMT_RGB48; 213 } else if (avctx->pix_fmt == AV_PIX_FMT_YUV420P && s->maxval < 65536) { 214 if (s->maxval < 512) 215 avctx->pix_fmt = AV_PIX_FMT_YUV420P9; 216 else if (s->maxval < 1024) 217 avctx->pix_fmt = AV_PIX_FMT_YUV420P10; 218 else 219 avctx->pix_fmt = AV_PIX_FMT_YUV420P16; 220 } else { 221 av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n"); 222 avctx->pix_fmt = AV_PIX_FMT_NONE; 223 return AVERROR_INVALIDDATA; 224 } 225 } 226 }else 227 s->maxval=1; 228 229 if (!pnm_space(s->bytestream[-1])) 230 return AVERROR_INVALIDDATA; 231 232 /* more check if YUV420 */ 233 if ((av_pix_fmt_desc_get(avctx->pix_fmt)->flags & AV_PIX_FMT_FLAG_PLANAR) && 234 avctx->pix_fmt != AV_PIX_FMT_GBRPF32) { 235 if ((avctx->width & 1) != 0) 236 return AVERROR_INVALIDDATA; 237 h = (avctx->height * 2); 238 if ((h % 3) != 0) 239 return AVERROR_INVALIDDATA; 240 h /= 3; 241 avctx->height = h; 242 } 243 return 0; 244} 245