1/* 2 * XPM image format 3 * 4 * Copyright (c) 2012 Paul B Mahol 5 * Copyright (c) 2017 Paras Chadha 6 * 7 * This file is part of FFmpeg. 8 * 9 * FFmpeg is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * FFmpeg is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with FFmpeg; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#include "libavutil/parseutils.h" 25#include "libavutil/avstring.h" 26#include "avcodec.h" 27#include "codec_internal.h" 28#include "internal.h" 29 30#define MIN_ELEMENT ' ' 31#define MAX_ELEMENT 0xfe 32#define NB_ELEMENTS (MAX_ELEMENT - MIN_ELEMENT + 1) 33 34typedef struct XPMContext { 35 uint32_t *pixels; 36 int pixels_size; 37 uint8_t *buf; 38 int buf_size; 39} XPMDecContext; 40 41typedef struct ColorEntry { 42 const char *name; ///< a string representing the name of the color 43 uint32_t rgb_color; ///< RGB values for the color 44} ColorEntry; 45 46static int color_table_compare(const void *lhs, const void *rhs) 47{ 48 return av_strcasecmp(lhs, ((const ColorEntry *)rhs)->name); 49} 50 51static const ColorEntry color_table[] = { 52 { "AliceBlue", 0xFFF0F8FF }, 53 { "AntiqueWhite", 0xFFFAEBD7 }, 54 { "Aqua", 0xFF00FFFF }, 55 { "Aquamarine", 0xFF7FFFD4 }, 56 { "Azure", 0xFFF0FFFF }, 57 { "Beige", 0xFFF5F5DC }, 58 { "Bisque", 0xFFFFE4C4 }, 59 { "Black", 0xFF000000 }, 60 { "BlanchedAlmond", 0xFFFFEBCD }, 61 { "Blue", 0xFF0000FF }, 62 { "BlueViolet", 0xFF8A2BE2 }, 63 { "Brown", 0xFFA52A2A }, 64 { "BurlyWood", 0xFFDEB887 }, 65 { "CadetBlue", 0xFF5F9EA0 }, 66 { "Chartreuse", 0xFF7FFF00 }, 67 { "Chocolate", 0xFFD2691E }, 68 { "Coral", 0xFFFF7F50 }, 69 { "CornflowerBlue", 0xFF6495ED }, 70 { "Cornsilk", 0xFFFFF8DC }, 71 { "Crimson", 0xFFDC143C }, 72 { "Cyan", 0xFF00FFFF }, 73 { "DarkBlue", 0xFF00008B }, 74 { "DarkCyan", 0xFF008B8B }, 75 { "DarkGoldenRod", 0xFFB8860B }, 76 { "DarkGray", 0xFFA9A9A9 }, 77 { "DarkGreen", 0xFF006400 }, 78 { "DarkKhaki", 0xFFBDB76B }, 79 { "DarkMagenta", 0xFF8B008B }, 80 { "DarkOliveGreen", 0xFF556B2F }, 81 { "Darkorange", 0xFFFF8C00 }, 82 { "DarkOrchid", 0xFF9932CC }, 83 { "DarkRed", 0xFF8B0000 }, 84 { "DarkSalmon", 0xFFE9967A }, 85 { "DarkSeaGreen", 0xFF8FBC8F }, 86 { "DarkSlateBlue", 0xFF483D8B }, 87 { "DarkSlateGray", 0xFF2F4F4F }, 88 { "DarkTurquoise", 0xFF00CED1 }, 89 { "DarkViolet", 0xFF9400D3 }, 90 { "DeepPink", 0xFFFF1493 }, 91 { "DeepSkyBlue", 0xFF00BFFF }, 92 { "DimGray", 0xFF696969 }, 93 { "DodgerBlue", 0xFF1E90FF }, 94 { "FireBrick", 0xFFB22222 }, 95 { "FloralWhite", 0xFFFFFAF0 }, 96 { "ForestGreen", 0xFF228B22 }, 97 { "Fuchsia", 0xFFFF00FF }, 98 { "Gainsboro", 0xFFDCDCDC }, 99 { "GhostWhite", 0xFFF8F8FF }, 100 { "Gold", 0xFFFFD700 }, 101 { "GoldenRod", 0xFFDAA520 }, 102 { "Gray", 0xFFBEBEBE }, 103 { "Green", 0xFF00FF00 }, 104 { "GreenYellow", 0xFFADFF2F }, 105 { "HoneyDew", 0xFFF0FFF0 }, 106 { "HotPink", 0xFFFF69B4 }, 107 { "IndianRed", 0xFFCD5C5C }, 108 { "Indigo", 0xFF4B0082 }, 109 { "Ivory", 0xFFFFFFF0 }, 110 { "Khaki", 0xFFF0E68C }, 111 { "Lavender", 0xFFE6E6FA }, 112 { "LavenderBlush", 0xFFFFF0F5 }, 113 { "LawnGreen", 0xFF7CFC00 }, 114 { "LemonChiffon", 0xFFFFFACD }, 115 { "LightBlue", 0xFFADD8E6 }, 116 { "LightCoral", 0xFFF08080 }, 117 { "LightCyan", 0xFFE0FFFF }, 118 { "LightGoldenRodYellow", 0xFFFAFAD2 }, 119 { "LightGreen", 0xFF90EE90 }, 120 { "LightGrey", 0xFFD3D3D3 }, 121 { "LightPink", 0xFFFFB6C1 }, 122 { "LightSalmon", 0xFFFFA07A }, 123 { "LightSeaGreen", 0xFF20B2AA }, 124 { "LightSkyBlue", 0xFF87CEFA }, 125 { "LightSlateGray", 0xFF778899 }, 126 { "LightSteelBlue", 0xFFB0C4DE }, 127 { "LightYellow", 0xFFFFFFE0 }, 128 { "Lime", 0xFF00FF00 }, 129 { "LimeGreen", 0xFF32CD32 }, 130 { "Linen", 0xFFFAF0E6 }, 131 { "Magenta", 0xFFFF00FF }, 132 { "Maroon", 0xFFB03060 }, 133 { "MediumAquaMarine", 0xFF66CDAA }, 134 { "MediumBlue", 0xFF0000CD }, 135 { "MediumOrchid", 0xFFBA55D3 }, 136 { "MediumPurple", 0xFF9370D8 }, 137 { "MediumSeaGreen", 0xFF3CB371 }, 138 { "MediumSlateBlue", 0xFF7B68EE }, 139 { "MediumSpringGreen", 0xFF00FA9A }, 140 { "MediumTurquoise", 0xFF48D1CC }, 141 { "MediumVioletRed", 0xFFC71585 }, 142 { "MidnightBlue", 0xFF191970 }, 143 { "MintCream", 0xFFF5FFFA }, 144 { "MistyRose", 0xFFFFE4E1 }, 145 { "Moccasin", 0xFFFFE4B5 }, 146 { "NavajoWhite", 0xFFFFDEAD }, 147 { "Navy", 0xFF000080 }, 148 { "None", 0x00000000 }, 149 { "OldLace", 0xFFFDF5E6 }, 150 { "Olive", 0xFF808000 }, 151 { "OliveDrab", 0xFF6B8E23 }, 152 { "Orange", 0xFFFFA500 }, 153 { "OrangeRed", 0xFFFF4500 }, 154 { "Orchid", 0xFFDA70D6 }, 155 { "PaleGoldenRod", 0xFFEEE8AA }, 156 { "PaleGreen", 0xFF98FB98 }, 157 { "PaleTurquoise", 0xFFAFEEEE }, 158 { "PaleVioletRed", 0xFFD87093 }, 159 { "PapayaWhip", 0xFFFFEFD5 }, 160 { "PeachPuff", 0xFFFFDAB9 }, 161 { "Peru", 0xFFCD853F }, 162 { "Pink", 0xFFFFC0CB }, 163 { "Plum", 0xFFDDA0DD }, 164 { "PowderBlue", 0xFFB0E0E6 }, 165 { "Purple", 0xFFA020F0 }, 166 { "Red", 0xFFFF0000 }, 167 { "RosyBrown", 0xFFBC8F8F }, 168 { "RoyalBlue", 0xFF4169E1 }, 169 { "SaddleBrown", 0xFF8B4513 }, 170 { "Salmon", 0xFFFA8072 }, 171 { "SandyBrown", 0xFFF4A460 }, 172 { "SeaGreen", 0xFF2E8B57 }, 173 { "SeaShell", 0xFFFFF5EE }, 174 { "Sienna", 0xFFA0522D }, 175 { "Silver", 0xFFC0C0C0 }, 176 { "SkyBlue", 0xFF87CEEB }, 177 { "SlateBlue", 0xFF6A5ACD }, 178 { "SlateGray", 0xFF708090 }, 179 { "Snow", 0xFFFFFAFA }, 180 { "SpringGreen", 0xFF00FF7F }, 181 { "SteelBlue", 0xFF4682B4 }, 182 { "Tan", 0xFFD2B48C }, 183 { "Teal", 0xFF008080 }, 184 { "Thistle", 0xFFD8BFD8 }, 185 { "Tomato", 0xFFFF6347 }, 186 { "Turquoise", 0xFF40E0D0 }, 187 { "Violet", 0xFFEE82EE }, 188 { "Wheat", 0xFFF5DEB3 }, 189 { "White", 0xFFFFFFFF }, 190 { "WhiteSmoke", 0xFFF5F5F5 }, 191 { "Yellow", 0xFFFFFF00 }, 192 { "YellowGreen", 0xFF9ACD32 } 193}; 194 195static unsigned hex_char_to_number(uint8_t x) 196{ 197 if (x >= 'a' && x <= 'f') 198 x -= 'a' - 10; 199 else if (x >= 'A' && x <= 'F') 200 x -= 'A' - 10; 201 else if (x >= '0' && x <= '9') 202 x -= '0'; 203 else 204 x = 0; 205 return x; 206} 207 208/* 209 * Function same as strcspn but ignores characters if they are inside a C style comments 210 */ 211static size_t mod_strcspn(const char *string, const char *reject) 212{ 213 int i, j; 214 215 for (i = 0; string && string[i]; i++) { 216 if (string[i] == '/' && string[i+1] == '*') { 217 i += 2; 218 while ( string && string[i] && (string[i] != '*' || string[i+1] != '/') ) 219 i++; 220 i++; 221 } else if (string[i] == '/' && string[i+1] == '/') { 222 i += 2; 223 while ( string && string[i] && string[i] != '\n' ) 224 i++; 225 } else { 226 for (j = 0; reject && reject[j]; j++) { 227 if (string[i] == reject[j]) 228 break; 229 } 230 if (reject && reject[j]) 231 break; 232 } 233 } 234 return i; 235} 236 237static uint32_t color_string_to_rgba(const char *p, int len) 238{ 239 uint32_t ret = 0xFF000000; 240 const ColorEntry *entry; 241 char color_name[100]; 242 243 len = FFMIN(FFMAX(len, 0), sizeof(color_name) - 1); 244 245 if (*p == '#') { 246 p++; 247 len--; 248 if (len == 3) { 249 ret |= (hex_char_to_number(p[2]) << 4) | 250 (hex_char_to_number(p[1]) << 12) | 251 (hex_char_to_number(p[0]) << 20); 252 } else if (len == 4) { 253 ret = (hex_char_to_number(p[3]) << 4) | 254 (hex_char_to_number(p[2]) << 12) | 255 (hex_char_to_number(p[1]) << 20) | 256 (hex_char_to_number(p[0]) << 28); 257 } else if (len == 6) { 258 ret |= hex_char_to_number(p[5]) | 259 (hex_char_to_number(p[4]) << 4) | 260 (hex_char_to_number(p[3]) << 8) | 261 (hex_char_to_number(p[2]) << 12) | 262 (hex_char_to_number(p[1]) << 16) | 263 (hex_char_to_number(p[0]) << 20); 264 } else if (len == 8) { 265 ret = hex_char_to_number(p[7]) | 266 (hex_char_to_number(p[6]) << 4) | 267 (hex_char_to_number(p[5]) << 8) | 268 (hex_char_to_number(p[4]) << 12) | 269 (hex_char_to_number(p[3]) << 16) | 270 (hex_char_to_number(p[2]) << 20) | 271 (hex_char_to_number(p[1]) << 24) | 272 (hex_char_to_number(p[0]) << 28); 273 } 274 } else { 275 strncpy(color_name, p, len); 276 color_name[len] = '\0'; 277 278 entry = bsearch(color_name, 279 color_table, 280 FF_ARRAY_ELEMS(color_table), 281 sizeof(ColorEntry), 282 color_table_compare); 283 284 if (!entry) 285 return ret; 286 287 ret = entry->rgb_color; 288 } 289 return ret; 290} 291 292static int ascii2index(const uint8_t *cpixel, int cpp) 293{ 294 const uint8_t *p = cpixel; 295 int n = 0, m = 1, i; 296 297 for (i = 0; i < cpp; i++) { 298 if (*p < MIN_ELEMENT || *p > MAX_ELEMENT) 299 return AVERROR_INVALIDDATA; 300 n += (*p++ - MIN_ELEMENT) * m; 301 m *= NB_ELEMENTS; 302 } 303 return n; 304} 305 306static int xpm_decode_frame(AVCodecContext *avctx, AVFrame *p, 307 int *got_frame, AVPacket *avpkt) 308{ 309 XPMDecContext *x = avctx->priv_data; 310 const uint8_t *end, *ptr; 311 int ncolors, cpp, ret, i, j; 312 int64_t size; 313 uint32_t *dst; 314 int width, height; 315 316 avctx->pix_fmt = AV_PIX_FMT_BGRA; 317 318 av_fast_padded_malloc(&x->buf, &x->buf_size, avpkt->size); 319 if (!x->buf) 320 return AVERROR(ENOMEM); 321 memcpy(x->buf, avpkt->data, avpkt->size); 322 x->buf[avpkt->size] = 0; 323 324 ptr = x->buf; 325 end = x->buf + avpkt->size; 326 while (end - ptr > 9 && memcmp(ptr, "/* XPM */", 9)) 327 ptr++; 328 329 if (end - ptr <= 9) { 330 av_log(avctx, AV_LOG_ERROR, "missing signature\n"); 331 return AVERROR_INVALIDDATA; 332 } 333 334 ptr += mod_strcspn(ptr, "\""); 335 if (sscanf(ptr, "\"%u %u %u %u\",", 336 &width, &height, &ncolors, &cpp) != 4) { 337 av_log(avctx, AV_LOG_ERROR, "missing image parameters\n"); 338 return AVERROR_INVALIDDATA; 339 } 340 341 if ((ret = ff_set_dimensions(avctx, width, height)) < 0) 342 return ret; 343 344 if (cpp <= 0 || cpp >= 5) { 345 av_log(avctx, AV_LOG_ERROR, "unsupported/invalid number of chars per pixel: %d\n", cpp); 346 return AVERROR_INVALIDDATA; 347 } 348 349 size = 1; 350 for (i = 0; i < cpp; i++) 351 size *= NB_ELEMENTS; 352 353 if (ncolors <= 0 || ncolors > size) { 354 av_log(avctx, AV_LOG_ERROR, "invalid number of colors: %d\n", ncolors); 355 return AVERROR_INVALIDDATA; 356 } 357 358 if (size > SIZE_MAX / 4) 359 return AVERROR(ENOMEM); 360 361 size *= 4; 362 363 ptr += mod_strcspn(ptr, ",") + 1; 364 if (end - ptr < 1) 365 return AVERROR_INVALIDDATA; 366 367 if ((ret = ff_get_buffer(avctx, p, 0)) < 0) 368 return ret; 369 370 av_fast_padded_malloc(&x->pixels, &x->pixels_size, size); 371 if (!x->pixels) 372 return AVERROR(ENOMEM); 373 374 for (i = 0; i < ncolors; i++) { 375 const uint8_t *index; 376 int len; 377 378 ptr += mod_strcspn(ptr, "\"") + 1; 379 if (end - ptr < cpp) 380 return AVERROR_INVALIDDATA; 381 index = ptr; 382 ptr += cpp; 383 384 ptr = strstr(ptr, "c "); 385 if (ptr) { 386 ptr += 2; 387 } else { 388 return AVERROR_INVALIDDATA; 389 } 390 391 len = strcspn(ptr, "\" "); 392 393 if ((ret = ascii2index(index, cpp)) < 0) 394 return ret; 395 396 x->pixels[ret] = color_string_to_rgba(ptr, len); 397 ptr += mod_strcspn(ptr, ",") + 1; 398 if (end - ptr < 1) 399 return AVERROR_INVALIDDATA; 400 } 401 402 for (i = 0; i < avctx->height; i++) { 403 dst = (uint32_t *)(p->data[0] + i * p->linesize[0]); 404 if (end - ptr < 1) 405 return AVERROR_INVALIDDATA; 406 ptr += mod_strcspn(ptr, "\"") + 1; 407 if (end - ptr < 1) 408 return AVERROR_INVALIDDATA; 409 410 for (j = 0; j < avctx->width; j++) { 411 if (end - ptr < cpp) 412 return AVERROR_INVALIDDATA; 413 414 if ((ret = ascii2index(ptr, cpp)) < 0) 415 return ret; 416 417 *dst++ = x->pixels[ret]; 418 ptr += cpp; 419 } 420 ptr += mod_strcspn(ptr, ",") + 1; 421 } 422 423 p->key_frame = 1; 424 p->pict_type = AV_PICTURE_TYPE_I; 425 426 *got_frame = 1; 427 428 return avpkt->size; 429} 430 431static av_cold int xpm_decode_close(AVCodecContext *avctx) 432{ 433 XPMDecContext *x = avctx->priv_data; 434 av_freep(&x->pixels); 435 436 av_freep(&x->buf); 437 x->buf_size = 0; 438 439 return 0; 440} 441 442const FFCodec ff_xpm_decoder = { 443 .p.name = "xpm", 444 .p.long_name = NULL_IF_CONFIG_SMALL("XPM (X PixMap) image"), 445 .p.type = AVMEDIA_TYPE_VIDEO, 446 .p.id = AV_CODEC_ID_XPM, 447 .p.capabilities = AV_CODEC_CAP_DR1, 448 .priv_data_size = sizeof(XPMDecContext), 449 .close = xpm_decode_close, 450 FF_CODEC_DECODE_CB(xpm_decode_frame), 451}; 452