| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * GEM Raster image decoder | ||
| 3 | * Copyright (c) 2021 Peter Ross (pross@xvid.org) | ||
| 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 | /** | ||
| 23 | * @file | ||
| 24 | * GEM Raster image decoder | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include "libavutil/mem.h" | ||
| 28 | #include "avcodec.h" | ||
| 29 | #include "bytestream.h" | ||
| 30 | #include "codec_internal.h" | ||
| 31 | #include "decode.h" | ||
| 32 | |||
| 33 | static const uint32_t gem_color_palette[16]={ | ||
| 34 | 0xFFFFFFFF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00, | ||
| 35 | 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFAEAEAE, | ||
| 36 | 0xFF555555, 0xFFAE0000, 0xFF00AE00, 0xFFAEAE00, | ||
| 37 | 0xFF0000AE, 0xFFAE00AE, 0xFF00AEAE, 0xFF000000, | ||
| 38 | }; | ||
| 39 | |||
| 40 | static const uint8_t gem_gray[256]={ | ||
| 41 | 0xFF, 0x7F, 0xBF, 0x3F, 0xDF, 0x5F, 0x9F, 0x1F, 0xEF, 0x6F, 0xAF, 0x2F, 0xCF, 0x4F, 0x8F, 0x0F, | ||
| 42 | 0xF7, 0x77, 0xB7, 0x37, 0xD7, 0x57, 0x97, 0x17, 0xE7, 0x67, 0xA7, 0x27, 0xC7, 0x47, 0x87, 0x07, | ||
| 43 | 0xFB, 0x7B, 0xBB, 0x3B, 0xDB, 0x5B, 0x9B, 0x1B, 0xEB, 0x6B, 0xAB, 0x2B, 0xCB, 0x4B, 0x8B, 0x0B, | ||
| 44 | 0xF3, 0x73, 0xB3, 0x33, 0xD3, 0x53, 0x93, 0x13, 0xE3, 0x63, 0xA3, 0x23, 0xC3, 0x43, 0x83, 0x03, | ||
| 45 | 0xFD, 0x7D, 0xBD, 0x3D, 0xDD, 0x5D, 0x9D, 0x1D, 0xED, 0x6D, 0xAD, 0x2D, 0xCD, 0x4D, 0x8D, 0x0D, | ||
| 46 | 0xF5, 0x75, 0xB5, 0x35, 0xD5, 0x55, 0x95, 0x15, 0xE5, 0x65, 0xA5, 0x25, 0xC5, 0x45, 0x85, 0x05, | ||
| 47 | 0xF9, 0x79, 0xB9, 0x39, 0xD9, 0x59, 0x99, 0x19, 0xE9, 0x69, 0xA9, 0x29, 0xC9, 0x49, 0x89, 0x09, | ||
| 48 | 0xF1, 0x71, 0xB1, 0x31, 0xD1, 0x51, 0x91, 0x11, 0xE1, 0x61, 0xA1, 0x21, 0xC1, 0x41, 0x81, 0x01, | ||
| 49 | 0xFE, 0x7E, 0xBE, 0x3E, 0xDE, 0x5E, 0x9E, 0x1E, 0xEE, 0x6E, 0xAE, 0x2E, 0xCE, 0x4E, 0x8E, 0x0E, | ||
| 50 | 0xF6, 0x76, 0xB6, 0x36, 0xD6, 0x56, 0x96, 0x16, 0xE6, 0x66, 0xA6, 0x26, 0xC6, 0x46, 0x86, 0x06, | ||
| 51 | 0xFA, 0x7A, 0xBA, 0x3A, 0xDA, 0x5A, 0x9A, 0x1A, 0xEA, 0x6A, 0xAA, 0x2A, 0xCA, 0x4A, 0x8A, 0x0A, | ||
| 52 | 0xF2, 0x72, 0xB2, 0x32, 0xD2, 0x52, 0x92, 0x12, 0xE2, 0x62, 0xA2, 0x22, 0xC2, 0x42, 0x82, 0x02, | ||
| 53 | 0xFC, 0x7C, 0xBC, 0x3C, 0xDC, 0x5C, 0x9C, 0x1C, 0xEC, 0x6C, 0xAC, 0x2C, 0xCC, 0x4C, 0x8C, 0x0C, | ||
| 54 | 0xF4, 0x74, 0xB4, 0x34, 0xD4, 0x54, 0x94, 0x14, 0xE4, 0x64, 0xA4, 0x24, 0xC4, 0x44, 0x84, 0x04, | ||
| 55 | 0xF8, 0x78, 0xB8, 0x38, 0xD8, 0x58, 0x98, 0x18, 0xE8, 0x68, 0xA8, 0x28, 0xC8, 0x48, 0x88, 0x08, | ||
| 56 | 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00, | ||
| 57 | }; | ||
| 58 | |||
| 59 | typedef struct { | ||
| 60 | int y, pl, x, vdup; | ||
| 61 | } State; | ||
| 62 | |||
| 63 | ✗ | static void put_lines_bits(AVCodecContext *avctx, int planes, int row_width, int pixel_size, State * state, uint8_t * row, AVFrame *p) | |
| 64 | { | ||
| 65 | ✗ | int pl_byte = state->pl / 8; | |
| 66 | ✗ | int pl_bit = state->pl & 7; | |
| 67 | ✗ | for (int y = 0; y < state->vdup && (state->y + y) < avctx->height; y++) | |
| 68 | ✗ | for (int x = 0; x < row_width; x++) | |
| 69 | ✗ | for (int i = 7; i >= 0 && x * 8 + 7 - i < avctx->width; i--) | |
| 70 | ✗ | p->data[0][ (state->y + y) * p->linesize[0] + (x * 8 + 7 - i) * pixel_size + pl_byte] |= !!(row[x] & (1 << i)) << pl_bit; | |
| 71 | |||
| 72 | ✗ | state->pl++; | |
| 73 | ✗ | if (state->pl >= planes) { | |
| 74 | ✗ | state->pl = 0; | |
| 75 | ✗ | state->y += state->vdup; | |
| 76 | ✗ | state->vdup = 1; | |
| 77 | } | ||
| 78 | ✗ | } | |
| 79 | |||
| 80 | ✗ | static void put_lines_bytes(AVCodecContext *avctx, int planes, int row_width, int pixel_size, State * state, uint8_t * row, AVFrame *p) | |
| 81 | { | ||
| 82 | ✗ | for (int y = 0; y < state->vdup && (state->y + y) < avctx->height; y++) | |
| 83 | ✗ | memcpy(p->data[0] + (state->y + y) * p->linesize[0], row, avctx->width * pixel_size); | |
| 84 | |||
| 85 | ✗ | state->y += state->vdup; | |
| 86 | ✗ | state->vdup = 1; | |
| 87 | ✗ | } | |
| 88 | |||
| 89 | ✗ | static int gem_decode_frame(AVCodecContext *avctx, AVFrame *p, | |
| 90 | int *got_frame, AVPacket *avpkt) | ||
| 91 | { | ||
| 92 | ✗ | const uint8_t *buf = avpkt->data; | |
| 93 | ✗ | int buf_size = avpkt->size; | |
| 94 | ✗ | const uint8_t *buf_end = buf + buf_size; | |
| 95 | ✗ | int header_size, planes, pattern_size, tag = 0, count_scalar = 1, ret; | |
| 96 | unsigned int x, count, v; | ||
| 97 | GetByteContext gb; | ||
| 98 | uint32_t *palette; | ||
| 99 | const uint8_t * b; | ||
| 100 | uint8_t * row; | ||
| 101 | int row_width, pixel_size; | ||
| 102 | ✗ | State state = {.y = 0, .pl = 0, .x = 0, .vdup = 1}; | |
| 103 | void (*put_lines)(AVCodecContext *avctx, int planes, int row_width, int pixel_size, State * state, uint8_t * row, AVFrame *p); | ||
| 104 | int width, height; | ||
| 105 | |||
| 106 | ✗ | if (buf_size <= 16) | |
| 107 | ✗ | return AVERROR_INVALIDDATA; | |
| 108 | |||
| 109 | ✗ | bytestream2_init(&gb, buf + 2, buf_size - 2); | |
| 110 | ✗ | header_size = bytestream2_get_be16(&gb); | |
| 111 | ✗ | if (header_size < 8 || buf_size <= header_size * 2) | |
| 112 | ✗ | return AVERROR_INVALIDDATA; | |
| 113 | |||
| 114 | ✗ | planes = bytestream2_get_be16(&gb); | |
| 115 | ✗ | pattern_size = bytestream2_get_be16(&gb); | |
| 116 | ✗ | avctx->sample_aspect_ratio.num = bytestream2_get_be16(&gb); | |
| 117 | ✗ | avctx->sample_aspect_ratio.den = bytestream2_get_be16(&gb); | |
| 118 | ✗ | width = bytestream2_get_be16(&gb); | |
| 119 | ✗ | height = bytestream2_get_be16(&gb); | |
| 120 | ✗ | ret = ff_set_dimensions(avctx, width, height); | |
| 121 | ✗ | if (ret < 0) | |
| 122 | ✗ | return ret; | |
| 123 | |||
| 124 | ✗ | row_width = (avctx->width + 7) / 8; | |
| 125 | ✗ | put_lines = put_lines_bits; | |
| 126 | |||
| 127 | ✗ | if (header_size == 9) { | |
| 128 | ✗ | count_scalar = bytestream2_get_be16(&gb); | |
| 129 | ✗ | if (count_scalar != 3) { | |
| 130 | ✗ | avpriv_request_sample(avctx, "count_scalar=%d", count_scalar); | |
| 131 | ✗ | return AVERROR_PATCHWELCOME; | |
| 132 | } | ||
| 133 | ✗ | planes = 24; | |
| 134 | ✗ | avctx->pix_fmt = AV_PIX_FMT_BGR24; | |
| 135 | ✗ | pixel_size = 3; | |
| 136 | ✗ | } else if (planes == 15) { | |
| 137 | #if HAVE_BIGENDIAN | ||
| 138 | avctx->pix_fmt = AV_PIX_FMT_BGR555BE; | ||
| 139 | #else | ||
| 140 | ✗ | avctx->pix_fmt = AV_PIX_FMT_BGR555LE; | |
| 141 | #endif | ||
| 142 | ✗ | pixel_size = 2; | |
| 143 | ✗ | } else if (planes == 16) { | |
| 144 | ✗ | avctx->pix_fmt = AV_PIX_FMT_RGB565BE; | |
| 145 | ✗ | pixel_size = 2; | |
| 146 | ✗ | } else if (planes == 24) { | |
| 147 | ✗ | avctx->pix_fmt = AV_PIX_FMT_RGB24; | |
| 148 | ✗ | pixel_size = 3; | |
| 149 | ✗ | } else if (planes == 32) { | |
| 150 | ✗ | avctx->pix_fmt = AV_PIX_FMT_0RGB; | |
| 151 | ✗ | pixel_size = 4; | |
| 152 | } else { | ||
| 153 | ✗ | avctx->pix_fmt = AV_PIX_FMT_PAL8; | |
| 154 | ✗ | pixel_size = 1; | |
| 155 | } | ||
| 156 | |||
| 157 | ✗ | if (header_size >= 11) | |
| 158 | ✗ | tag = bytestream2_peek_be32(&gb); | |
| 159 | |||
| 160 | ✗ | if (tag == AV_RB32("STTT")) { | |
| 161 | ✗ | if (planes != 4) { | |
| 162 | ✗ | avpriv_request_sample(avctx, "STTT planes=%d", planes); | |
| 163 | ✗ | return AVERROR_PATCHWELCOME; | |
| 164 | } | ||
| 165 | ✗ | } else if (tag == AV_RB32("TIMG")) { | |
| 166 | ✗ | if (planes != 15) { | |
| 167 | ✗ | avpriv_request_sample(avctx, "TIMG planes=%d", planes); | |
| 168 | ✗ | return AVERROR_PATCHWELCOME; | |
| 169 | } | ||
| 170 | ✗ | } else if (tag == AV_RB32("XIMG")) { | |
| 171 | ✗ | if (planes != 1 && planes != 2 && planes != 4 && planes != 8 && planes != 16 && planes != 24 && planes != 32) { | |
| 172 | ✗ | avpriv_request_sample(avctx, "XIMG planes=%d", planes); | |
| 173 | ✗ | return AVERROR_PATCHWELCOME; | |
| 174 | } | ||
| 175 | ✗ | } else if (planes != 1 && planes != 2 && planes != 3 && planes != 4 && planes != 8 && planes != 16 && planes != 24) { | |
| 176 | ✗ | avpriv_request_sample(avctx, "planes=%d", planes); | |
| 177 | ✗ | return AVERROR_PATCHWELCOME; | |
| 178 | } | ||
| 179 | |||
| 180 | ✗ | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) | |
| 181 | ✗ | return ret; | |
| 182 | |||
| 183 | ✗ | p->pict_type = AV_PICTURE_TYPE_I; | |
| 184 | ✗ | p->flags |= AV_FRAME_FLAG_KEY; | |
| 185 | ✗ | palette = (uint32_t *)p->data[1]; | |
| 186 | |||
| 187 | ✗ | if (tag == AV_RB32("STTT")) { | |
| 188 | ✗ | bytestream2_skip(&gb, 6); | |
| 189 | ✗ | if (planes == 4) { | |
| 190 | ✗ | for (int i = 0; i < (1 << planes); i++) { | |
| 191 | ✗ | int v = bytestream2_get_be16(&gb); | |
| 192 | ✗ | int r = ((v >> 8) & 0x7) << 5; | |
| 193 | ✗ | int g = ((v >> 4) & 0x7) << 5; | |
| 194 | ✗ | int b = ((v ) & 0x7) << 5; | |
| 195 | ✗ | palette[i] = 0xFF000000 | r << 16 | g << 8 | b; | |
| 196 | } | ||
| 197 | } else { | ||
| 198 | ✗ | av_assert0(0); | |
| 199 | } | ||
| 200 | ✗ | } else if (tag == AV_RB32("TIMG")) { | |
| 201 | ✗ | bytestream2_skip(&gb, 4); | |
| 202 | ✗ | if (planes != 15) { | |
| 203 | ✗ | av_assert0(0); | |
| 204 | } | ||
| 205 | ✗ | } else if (tag == AV_RB32("XIMG")) { | |
| 206 | ✗ | bytestream2_skip(&gb, 6); | |
| 207 | ✗ | if (planes == 1 || planes == 2 || planes == 4 || planes == 8) { | |
| 208 | ✗ | for (int i = 0; i < (1 << planes); i++) { | |
| 209 | ✗ | int r = (bytestream2_get_be16(&gb) * 51 + 100) / 200; | |
| 210 | ✗ | int g = (bytestream2_get_be16(&gb) * 51 + 100) / 200; | |
| 211 | ✗ | int b = (bytestream2_get_be16(&gb) * 51 + 100) / 200; | |
| 212 | ✗ | palette[i] = 0xFF000000 | r << 16 | g << 8 | b; | |
| 213 | } | ||
| 214 | ✗ | } else if (planes == 16) { | |
| 215 | ✗ | planes = 1; | |
| 216 | ✗ | row_width = ((avctx->width + 7)/8)*8 * pixel_size; | |
| 217 | ✗ | put_lines = put_lines_bytes; | |
| 218 | ✗ | } else if (planes == 24) { | |
| 219 | ✗ | planes = 1; | |
| 220 | ✗ | row_width = ((avctx->width + 15)/16)*16 * pixel_size; | |
| 221 | ✗ | put_lines = put_lines_bytes; | |
| 222 | ✗ | } else if (planes == 32) { | |
| 223 | ✗ | planes = 1; | |
| 224 | ✗ | row_width = avctx->width * pixel_size; | |
| 225 | ✗ | put_lines = put_lines_bytes; | |
| 226 | } else { | ||
| 227 | ✗ | av_assert0(0); | |
| 228 | } | ||
| 229 | ✗ | } else if (planes == 1) { | |
| 230 | ✗ | palette[0] = 0xFFFFFFFF; | |
| 231 | ✗ | palette[1] = 0xFF000000; | |
| 232 | ✗ | } else if (planes == 2 || planes == 3 || planes == 4) { | |
| 233 | ✗ | if (header_size == 9 + (1 << planes)) { | |
| 234 | ✗ | bytestream2_skip(&gb, 2); | |
| 235 | ✗ | for (int i = 0; i < (1 << planes); i++) { | |
| 236 | ✗ | int v = bytestream2_get_be16(&gb); | |
| 237 | ✗ | int r = ((v >> 8) & 0x7) << 5; | |
| 238 | ✗ | int g = ((v >> 4) & 0x7) << 5; | |
| 239 | ✗ | int b = ((v ) & 0x7) << 5; | |
| 240 | ✗ | palette[i] = 0xFF000000 | r << 16 | g << 8 | b; | |
| 241 | } | ||
| 242 | } else | ||
| 243 | ✗ | memcpy(palette, gem_color_palette, sizeof(gem_color_palette)); | |
| 244 | ✗ | } else if (planes == 8) { | |
| 245 | ✗ | for (int i = 0; i < 256; i++) | |
| 246 | ✗ | palette[i] = 0xFF000000 | (gem_gray[i]<<16) | (gem_gray[i]<<8) | gem_gray[i]; | |
| 247 | ✗ | } else if (planes == 16) { | |
| 248 | ✗ | planes = 1; | |
| 249 | ✗ | row_width = avctx->width * pixel_size; | |
| 250 | ✗ | put_lines = put_lines_bytes; | |
| 251 | ✗ | } else if (planes == 24) { | |
| 252 | ✗ | planes = 1; | |
| 253 | ✗ | row_width = avctx->width * pixel_size; | |
| 254 | ✗ | put_lines = put_lines_bytes; | |
| 255 | } else | ||
| 256 | ✗ | av_assert0(0); | |
| 257 | |||
| 258 | ✗ | ret = av_reallocp_array(&avctx->priv_data, planes, row_width); | |
| 259 | ✗ | if (ret < 0) | |
| 260 | ✗ | return ret; | |
| 261 | ✗ | row = avctx->priv_data; | |
| 262 | |||
| 263 | ✗ | memset(p->data[0], 0, avctx->height * p->linesize[0]); | |
| 264 | ✗ | b = buf + header_size * 2; | |
| 265 | ✗ | x = 0; | |
| 266 | |||
| 267 | #define SKIP \ | ||
| 268 | do { \ | ||
| 269 | x++; \ | ||
| 270 | if (x >= row_width) { \ | ||
| 271 | put_lines(avctx, planes, row_width, pixel_size, &state, row + state.pl * row_width, p); \ | ||
| 272 | if (state.y >= avctx->height) goto abort; \ | ||
| 273 | x = 0; \ | ||
| 274 | } \ | ||
| 275 | } while(0) | ||
| 276 | |||
| 277 | #define PUT(v) \ | ||
| 278 | do { \ | ||
| 279 | row[state.pl * row_width + x++] = v; \ | ||
| 280 | if (x >= row_width) { \ | ||
| 281 | put_lines(avctx, planes, row_width, pixel_size, &state, row + state.pl * row_width, p); \ | ||
| 282 | if (state.y >= avctx->height) goto abort; \ | ||
| 283 | x = 0; \ | ||
| 284 | } \ | ||
| 285 | } while(0) | ||
| 286 | |||
| 287 | ✗ | while(b < buf_end) { | |
| 288 | ✗ | int opcode = *b++; | |
| 289 | ✗ | if (opcode == 0x80) { /* copy */ | |
| 290 | ✗ | if (b >= buf_end) | |
| 291 | ✗ | goto abort; | |
| 292 | ✗ | count = *b++; | |
| 293 | ✗ | if (!count) | |
| 294 | ✗ | count = 256; | |
| 295 | ✗ | count *= count_scalar; | |
| 296 | ✗ | for (int j = 0; j < count; j++) { | |
| 297 | ✗ | if (b >= buf_end) | |
| 298 | ✗ | goto abort; | |
| 299 | ✗ | PUT(*b++); | |
| 300 | } | ||
| 301 | ✗ | } else if (opcode) { /* run */ | |
| 302 | ✗ | count = opcode & 0x7f; | |
| 303 | ✗ | if (!count) | |
| 304 | ✗ | count = 256; | |
| 305 | ✗ | count *= count_scalar; | |
| 306 | ✗ | v = opcode & 0x80 ? 0xFF : 0x00; | |
| 307 | ✗ | for (int i = 0; i < count; i++) | |
| 308 | ✗ | PUT(v); | |
| 309 | } else { | ||
| 310 | ✗ | if (b >= buf_end) | |
| 311 | ✗ | goto abort; | |
| 312 | ✗ | count = *b++; | |
| 313 | ✗ | if (count) { /* pattern */ | |
| 314 | ✗ | if (b > buf_end - pattern_size) | |
| 315 | ✗ | goto abort; | |
| 316 | |||
| 317 | ✗ | count *= count_scalar; | |
| 318 | ✗ | for (int j = 0; j < count; j++) | |
| 319 | ✗ | for (int k = 0; k < pattern_size; k++) | |
| 320 | ✗ | PUT(b[k]); | |
| 321 | |||
| 322 | ✗ | b += pattern_size; | |
| 323 | } else { | ||
| 324 | ✗ | if (b >= buf_end) | |
| 325 | ✗ | goto abort; | |
| 326 | ✗ | count = *b++; | |
| 327 | ✗ | if (count == 0xFF) { /* vertical duplication */ | |
| 328 | ✗ | if (b >= buf_end) | |
| 329 | ✗ | goto abort; | |
| 330 | ✗ | state.vdup = *b++; | |
| 331 | ✗ | if (!state.vdup) | |
| 332 | ✗ | state.vdup = 256; | |
| 333 | } else { /* horizontal duplication */ | ||
| 334 | ✗ | for (int i = 0; i < count + 1; i++) | |
| 335 | ✗ | SKIP; | |
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | ✗ | abort: | |
| 342 | |||
| 343 | ✗ | *got_frame = 1; | |
| 344 | ✗ | return buf_size; | |
| 345 | } | ||
| 346 | |||
| 347 | ✗ | static av_cold int gem_close(AVCodecContext *avctx) | |
| 348 | { | ||
| 349 | ✗ | av_freep(&avctx->priv_data); | |
| 350 | ✗ | return 0; | |
| 351 | } | ||
| 352 | |||
| 353 | const FFCodec ff_gem_decoder = { | ||
| 354 | .p.name = "gem", | ||
| 355 | CODEC_LONG_NAME("GEM Raster image"), | ||
| 356 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
| 357 | .p.id = AV_CODEC_ID_GEM, | ||
| 358 | .p.capabilities = AV_CODEC_CAP_DR1, | ||
| 359 | FF_CODEC_DECODE_CB(gem_decode_frame), | ||
| 360 | .close = gem_close, | ||
| 361 | }; | ||
| 362 |