You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
5.9KB

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