/src/ffmpeg/libavcodec/pgxdec.c
Line | Count | Source |
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 "bytestream.h" |
24 | | #include "codec_internal.h" |
25 | | #include "decode.h" |
26 | | |
27 | 427k | static int pgx_get_number(AVCodecContext *avctx, GetByteContext *g, int *number) { |
28 | 427k | int ret = AVERROR_INVALIDDATA; |
29 | 427k | char digit; |
30 | | |
31 | 427k | *number = 0; |
32 | 877k | while (1) { |
33 | 877k | uint64_t temp; |
34 | 877k | if (bytestream2_get_bytes_left(g) <= 0) |
35 | 1.10k | return AVERROR_INVALIDDATA; |
36 | 876k | digit = bytestream2_get_byteu(g); |
37 | 876k | if (digit == ' ' || digit == 0xA || digit == 0xD) |
38 | 415k | break; |
39 | 461k | else if (digit < '0' || digit > '9') |
40 | 9.34k | return AVERROR_INVALIDDATA; |
41 | | |
42 | 452k | temp = (uint64_t)10 * (*number) + (digit - '0'); |
43 | 452k | if (temp > INT_MAX) |
44 | 1.25k | return AVERROR_INVALIDDATA; |
45 | 450k | *number = temp; |
46 | 450k | ret = 0; |
47 | 450k | } |
48 | | |
49 | 415k | return ret; |
50 | 427k | } |
51 | | |
52 | | static int pgx_decode_header(AVCodecContext *avctx, GetByteContext *g, |
53 | | int *depth, int *width, int *height, |
54 | | int *sign) |
55 | 270k | { |
56 | 270k | int byte; |
57 | | |
58 | 270k | if (bytestream2_get_bytes_left(g) < 12) |
59 | 121k | return AVERROR_INVALIDDATA; |
60 | | |
61 | 148k | bytestream2_skipu(g, 6); |
62 | | |
63 | | // Is the component signed? |
64 | 148k | byte = bytestream2_peek_byteu(g); |
65 | 148k | if (byte == '+') { |
66 | 531 | *sign = 0; |
67 | 531 | bytestream2_skipu(g, 1); |
68 | 147k | } else if (byte == '-') { |
69 | 912 | *sign = 1; |
70 | 912 | bytestream2_skipu(g, 1); |
71 | 912 | } |
72 | | |
73 | 148k | byte = bytestream2_peek_byteu(g); |
74 | 148k | if (byte == ' ') |
75 | 226 | bytestream2_skipu(g, 1); |
76 | | |
77 | 148k | if (pgx_get_number(avctx, g, depth)) |
78 | 7.94k | goto error; |
79 | 140k | if (pgx_get_number(avctx, g, width)) |
80 | 2.61k | goto error; |
81 | 137k | if (pgx_get_number(avctx, g, height)) |
82 | 2.57k | goto error; |
83 | | |
84 | 135k | if (bytestream2_peek_byte(g) == 0xA) |
85 | 771 | bytestream2_skip(g, 1); |
86 | 135k | return 0; |
87 | | |
88 | 13.1k | error: |
89 | 13.1k | av_log(avctx, AV_LOG_ERROR, "Error in decoding header.\n"); |
90 | 13.1k | return AVERROR_INVALIDDATA; |
91 | 137k | } |
92 | | |
93 | | #define WRITE_FRAME(D, PIXEL, suffix) \ |
94 | | static inline void write_frame_ ##D(AVFrame *frame, GetByteContext *g, \ |
95 | | int width, int height, int sign, int depth) \ |
96 | 128k | { \ |
97 | 128k | const unsigned offset = sign ? (1 << (D - 1)) : 0; \ |
98 | 128k | int i, j; \ |
99 | 1.86M | for (i = 0; i < height; i++) { \ |
100 | 1.73M | PIXEL *line = (PIXEL*)(frame->data[0] + i * frame->linesize[0]); \ |
101 | 13.9M | for (j = 0; j < width; j++) { \ |
102 | 12.2M | unsigned val = bytestream2_get_ ##suffix##u(g) << (D - depth); \ |
103 | 12.2M | val ^= offset; \ |
104 | 12.2M | *(line + j) = val; \ |
105 | 12.2M | } \ |
106 | 1.73M | } \ |
107 | 128k | } \ Line | Count | Source | 96 | 125k | { \ | 97 | 125k | const unsigned offset = sign ? (1 << (D - 1)) : 0; \ | 98 | 125k | int i, j; \ | 99 | 1.77M | for (i = 0; i < height; i++) { \ | 100 | 1.64M | PIXEL *line = (PIXEL*)(frame->data[0] + i * frame->linesize[0]); \ | 101 | 13.7M | for (j = 0; j < width; j++) { \ | 102 | 12.0M | unsigned val = bytestream2_get_ ##suffix##u(g) << (D - depth); \ | 103 | 12.0M | val ^= offset; \ | 104 | 12.0M | *(line + j) = val; \ | 105 | 12.0M | } \ | 106 | 1.64M | } \ | 107 | 125k | } \ |
Line | Count | Source | 96 | 2.07k | { \ | 97 | 2.07k | const unsigned offset = sign ? (1 << (D - 1)) : 0; \ | 98 | 2.07k | int i, j; \ | 99 | 85.8k | for (i = 0; i < height; i++) { \ | 100 | 83.7k | PIXEL *line = (PIXEL*)(frame->data[0] + i * frame->linesize[0]); \ | 101 | 241k | for (j = 0; j < width; j++) { \ | 102 | 157k | unsigned val = bytestream2_get_ ##suffix##u(g) << (D - depth); \ | 103 | 157k | val ^= offset; \ | 104 | 157k | *(line + j) = val; \ | 105 | 157k | } \ | 106 | 83.7k | } \ | 107 | 2.07k | } \ |
|
108 | | |
109 | | WRITE_FRAME(8, uint8_t, byte) |
110 | | WRITE_FRAME(16, uint16_t, be16) |
111 | | |
112 | | static int pgx_decode_frame(AVCodecContext *avctx, AVFrame *p, |
113 | | int *got_frame, AVPacket *avpkt) |
114 | 270k | { |
115 | 270k | int ret; |
116 | 270k | int bpp; |
117 | 270k | int width, height, depth; |
118 | 270k | int sign = 0; |
119 | 270k | GetByteContext g; |
120 | 270k | bytestream2_init(&g, avpkt->data, avpkt->size); |
121 | | |
122 | 270k | if ((ret = pgx_decode_header(avctx, &g, &depth, &width, &height, &sign)) < 0) |
123 | 134k | return ret; |
124 | | |
125 | 135k | if ((ret = ff_set_dimensions(avctx, width, height)) < 0) |
126 | 1.87k | return ret; |
127 | | |
128 | 133k | if (depth > 0 && depth <= 8) { |
129 | 128k | avctx->pix_fmt = AV_PIX_FMT_GRAY8; |
130 | 128k | bpp = 8; |
131 | 128k | } else if (depth > 0 && depth <= 16) { |
132 | 2.36k | avctx->pix_fmt = AV_PIX_FMT_GRAY16; |
133 | 2.36k | bpp = 16; |
134 | 2.51k | } else { |
135 | 2.51k | av_log(avctx, AV_LOG_ERROR, "depth %d is invalid or unsupported.\n", depth); |
136 | 2.51k | return AVERROR_PATCHWELCOME; |
137 | 2.51k | } |
138 | 131k | if (bytestream2_get_bytes_left(&g) < width * height * (bpp >> 3)) |
139 | 3.00k | return AVERROR_INVALIDDATA; |
140 | 128k | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) |
141 | 0 | return ret; |
142 | 128k | avctx->bits_per_raw_sample = depth; |
143 | 128k | if (bpp == 8) |
144 | 125k | write_frame_8(p, &g, width, height, sign, depth); |
145 | 2.07k | else if (bpp == 16) |
146 | 2.07k | write_frame_16(p, &g, width, height, sign, depth); |
147 | 128k | *got_frame = 1; |
148 | 128k | return 0; |
149 | 128k | } |
150 | | |
151 | | const FFCodec ff_pgx_decoder = { |
152 | | .p.name = "pgx", |
153 | | CODEC_LONG_NAME("PGX (JPEG2000 Test Format)"), |
154 | | .p.type = AVMEDIA_TYPE_VIDEO, |
155 | | .p.id = AV_CODEC_ID_PGX, |
156 | | .p.capabilities = AV_CODEC_CAP_DR1, |
157 | | FF_CODEC_DECODE_CB(pgx_decode_frame), |
158 | | }; |