yading@10: /* yading@10: * PNM image format yading@10: * Copyright (c) 2002, 2003 Fabrice Bellard yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include yading@10: #include yading@10: yading@10: #include "libavutil/imgutils.h" yading@10: #include "avcodec.h" yading@10: #include "pnm.h" yading@10: yading@10: static inline int pnm_space(int c) yading@10: { yading@10: return c == ' ' || c == '\n' || c == '\r' || c == '\t'; yading@10: } yading@10: yading@10: static void pnm_get(PNMContext *sc, char *str, int buf_size) yading@10: { yading@10: char *s; yading@10: int c; yading@10: yading@10: /* skip spaces and comments */ yading@10: while (sc->bytestream < sc->bytestream_end) { yading@10: c = *sc->bytestream++; yading@10: if (c == '#') { yading@10: while (c != '\n' && sc->bytestream < sc->bytestream_end) { yading@10: c = *sc->bytestream++; yading@10: } yading@10: } else if (!pnm_space(c)) { yading@10: break; yading@10: } yading@10: } yading@10: yading@10: s = str; yading@10: while (sc->bytestream < sc->bytestream_end && !pnm_space(c)) { yading@10: if ((s - str) < buf_size - 1) yading@10: *s++ = c; yading@10: c = *sc->bytestream++; yading@10: } yading@10: *s = '\0'; yading@10: } yading@10: yading@10: int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) yading@10: { yading@10: char buf1[32], tuple_type[32]; yading@10: int h, w, depth, maxval; yading@10: yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: s->type= buf1[1]-'0'; yading@10: if(buf1[0] != 'P') yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: if (s->type==1 || s->type==4) { yading@10: avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; yading@10: } else if (s->type==2 || s->type==5) { yading@10: if (avctx->codec_id == AV_CODEC_ID_PGMYUV) yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV420P; yading@10: else yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY8; yading@10: } else if (s->type==3 || s->type==6) { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB24; yading@10: } else if (s->type==7) { yading@10: w = -1; yading@10: h = -1; yading@10: maxval = -1; yading@10: depth = -1; yading@10: tuple_type[0] = '\0'; yading@10: for (;;) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: if (!strcmp(buf1, "WIDTH")) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: w = strtol(buf1, NULL, 10); yading@10: } else if (!strcmp(buf1, "HEIGHT")) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: h = strtol(buf1, NULL, 10); yading@10: } else if (!strcmp(buf1, "DEPTH")) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: depth = strtol(buf1, NULL, 10); yading@10: } else if (!strcmp(buf1, "MAXVAL")) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: maxval = strtol(buf1, NULL, 10); yading@10: } else if (!strcmp(buf1, "TUPLTYPE") || yading@10: /* libavcodec used to write invalid files */ yading@10: !strcmp(buf1, "TUPLETYPE")) { yading@10: pnm_get(s, tuple_type, sizeof(tuple_type)); yading@10: } else if (!strcmp(buf1, "ENDHDR")) { yading@10: break; yading@10: } else { yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: } yading@10: /* check that all tags are present */ yading@10: if (w <= 0 || h <= 0 || maxval <= 0 || depth <= 0 || tuple_type[0] == '\0' || av_image_check_size(w, h, 0, avctx) || s->bytestream >= s->bytestream_end) yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: avctx->width = w; yading@10: avctx->height = h; yading@10: s->maxval = maxval; yading@10: if (depth == 1) { yading@10: if (maxval == 1) { yading@10: avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; yading@10: } else if (maxval == 255) { yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY8; yading@10: } else { yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY16BE; yading@10: } yading@10: } else if (depth == 2) { yading@10: if (maxval == 255) yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY8A; yading@10: } else if (depth == 3) { yading@10: if (maxval < 256) { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB24; yading@10: } else { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB48BE; yading@10: } yading@10: } else if (depth == 4) { yading@10: if (maxval < 256) { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGBA; yading@10: } else { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGBA64BE; yading@10: } yading@10: } else { yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: return 0; yading@10: } else { yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: w = atoi(buf1); yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: h = atoi(buf1); yading@10: if(w <= 0 || h <= 0 || av_image_check_size(w, h, 0, avctx) || s->bytestream >= s->bytestream_end) yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: avctx->width = w; yading@10: avctx->height = h; yading@10: yading@10: if (avctx->pix_fmt != AV_PIX_FMT_MONOWHITE && avctx->pix_fmt != AV_PIX_FMT_MONOBLACK) { yading@10: pnm_get(s, buf1, sizeof(buf1)); yading@10: s->maxval = atoi(buf1); yading@10: if (s->maxval <= 0) { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid maxval: %d\n", s->maxval); yading@10: s->maxval = 255; yading@10: } yading@10: if (s->maxval >= 256) { yading@10: if (avctx->pix_fmt == AV_PIX_FMT_GRAY8) { yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY16BE; yading@10: } else if (avctx->pix_fmt == AV_PIX_FMT_RGB24) { yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB48BE; yading@10: } else if (avctx->pix_fmt == AV_PIX_FMT_YUV420P && s->maxval < 65536) { yading@10: if (s->maxval < 512) yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV420P9BE; yading@10: else if (s->maxval < 1024) yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV420P10BE; yading@10: else yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV420P16; yading@10: } else { yading@10: av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n"); yading@10: avctx->pix_fmt = AV_PIX_FMT_NONE; yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: } yading@10: }else yading@10: s->maxval=1; yading@10: /* more check if YUV420 */ yading@10: if (av_pix_fmt_desc_get(avctx->pix_fmt)->flags & PIX_FMT_PLANAR) { yading@10: if ((avctx->width & 1) != 0) yading@10: return AVERROR_INVALIDDATA; yading@10: h = (avctx->height * 2); yading@10: if ((h % 3) != 0) yading@10: return AVERROR_INVALIDDATA; yading@10: h /= 3; yading@10: avctx->height = h; yading@10: } yading@10: return 0; yading@10: } yading@10: yading@10: av_cold int ff_pnm_init(AVCodecContext *avctx) yading@10: { yading@10: PNMContext *s = avctx->priv_data; yading@10: yading@10: avcodec_get_frame_defaults(&s->picture); yading@10: avctx->coded_frame = &s->picture; yading@10: yading@10: return 0; yading@10: }