• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * PGX image format
3  * Copyright (c) 2020 Gautam Ramakrishnan
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 "avcodec.h"
23 #include "internal.h"
24 #include "bytestream.h"
25 #include "libavutil/imgutils.h"
26 
pgx_get_number(AVCodecContext * avctx,GetByteContext * g,int * number)27 static int pgx_get_number(AVCodecContext *avctx, GetByteContext *g, int *number) {
28     int ret = AVERROR_INVALIDDATA;
29     char digit;
30 
31     *number = 0;
32     while (1) {
33         uint64_t temp;
34         if (!bytestream2_get_bytes_left(g))
35             return AVERROR_INVALIDDATA;
36         digit = bytestream2_get_byte(g);
37         if (digit == ' ' || digit == 0xA || digit == 0xD)
38             break;
39         else if (digit < '0' || digit > '9')
40             return AVERROR_INVALIDDATA;
41 
42         temp = (uint64_t)10 * (*number) + (digit - '0');
43         if (temp > INT_MAX)
44             return AVERROR_INVALIDDATA;
45         *number = temp;
46         ret = 0;
47     }
48 
49     return ret;
50 }
51 
pgx_decode_header(AVCodecContext * avctx,GetByteContext * g,int * depth,int * width,int * height,int * sign)52 static int pgx_decode_header(AVCodecContext *avctx, GetByteContext *g,
53                              int *depth, int *width, int *height,
54                              int *sign)
55 {
56     int byte;
57 
58     if (bytestream2_get_bytes_left(g) < 6) {
59         return AVERROR_INVALIDDATA;
60     }
61 
62     bytestream2_skip(g, 6);
63 
64     // Is the component signed?
65     byte = bytestream2_peek_byte(g);
66     if (byte == '+') {
67         *sign = 0;
68         bytestream2_skip(g, 1);
69     } else if (byte == '-') {
70         *sign = 1;
71         bytestream2_skip(g, 1);
72     } else if (byte == 0)
73         goto error;
74 
75     byte = bytestream2_peek_byte(g);
76     if (byte == ' ')
77         bytestream2_skip(g, 1);
78     else if (byte == 0)
79         goto error;
80 
81     if (pgx_get_number(avctx, g, depth))
82         goto error;
83     if (pgx_get_number(avctx, g, width))
84         goto error;
85     if (pgx_get_number(avctx, g, height))
86         goto error;
87 
88     if (bytestream2_peek_byte(g) == 0xA)
89         bytestream2_skip(g, 1);
90     return 0;
91 
92 error:
93     av_log(avctx, AV_LOG_ERROR, "Error in decoding header.\n");
94     return AVERROR_INVALIDDATA;
95 }
96 
97 #define WRITE_FRAME(D, PIXEL, suffix)                                                       \
98     static inline void write_frame_ ##D(AVFrame *frame, GetByteContext *g, \
99                                         int width, int height, int sign, int depth)         \
100     {                                                                                       \
101         int i, j;                                                                           \
102         for (i = 0; i < height; i++) {                                                      \
103             PIXEL *line = (PIXEL*)frame->data[0] + i*frame->linesize[0]/sizeof(PIXEL);      \
104             for (j = 0; j < width; j++) {                                                   \
105                 unsigned val;                                                               \
106                 if (sign)                                                                   \
107                     val = (PIXEL)bytestream2_get_ ##suffix(g) + (1 << (depth - 1));         \
108                 else                                                                        \
109                     val = bytestream2_get_ ##suffix(g);                                     \
110                 val <<= (D - depth);                                                        \
111                 *(line + j) = val;                                                          \
112             }                                                                               \
113         }                                                                                   \
114     }                                                                                       \
115 
116 WRITE_FRAME(8, int8_t, byte)
117 WRITE_FRAME(16, int16_t, be16)
118 
pgx_decode_frame(AVCodecContext * avctx,void * data,int * got_frame,AVPacket * avpkt)119 static int pgx_decode_frame(AVCodecContext *avctx, void *data,
120                             int *got_frame, AVPacket *avpkt)
121 {
122     AVFrame *p = data;
123     int ret;
124     int bpp;
125     int width, height, depth;
126     int sign = 0;
127     GetByteContext g;
128     bytestream2_init(&g, avpkt->data, avpkt->size);
129 
130     if ((ret = pgx_decode_header(avctx, &g, &depth, &width, &height, &sign)) < 0)
131         return ret;
132 
133     if ((ret = ff_set_dimensions(avctx, width, height)) < 0)
134         return ret;
135 
136     if (depth > 0 && depth <= 8) {
137         avctx->pix_fmt = AV_PIX_FMT_GRAY8;
138         bpp = 8;
139     } else if (depth > 0 && depth <= 16) {
140         avctx->pix_fmt = AV_PIX_FMT_GRAY16;
141         bpp = 16;
142     } else {
143         av_log(avctx, AV_LOG_ERROR, "depth %d is invalid or unsupported.\n", depth);
144         return AVERROR_PATCHWELCOME;
145     }
146     if (bytestream2_get_bytes_left(&g) < width * height * (bpp >> 3))
147         return AVERROR_INVALIDDATA;
148     if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
149         return ret;
150     p->pict_type = AV_PICTURE_TYPE_I;
151     p->key_frame = 1;
152     avctx->bits_per_raw_sample = depth;
153     if (bpp == 8)
154         write_frame_8(p, &g, width, height, sign, depth);
155     else if (bpp == 16)
156         write_frame_16(p, &g, width, height, sign, depth);
157     *got_frame = 1;
158     return 0;
159 }
160 
161 AVCodec ff_pgx_decoder = {
162     .name           = "pgx",
163     .long_name      = NULL_IF_CONFIG_SMALL("PGX (JPEG2000 Test Format)"),
164     .type           = AVMEDIA_TYPE_VIDEO,
165     .id             = AV_CODEC_ID_PGX,
166     .decode         = pgx_decode_frame,
167     .capabilities   = AV_CODEC_CAP_DR1,
168 };
169