TGALoader.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * Copyright (c) 2022, Tom Needham <06needhamt@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/MemoryStream.h>
  7. #include <AK/Span.h>
  8. #include <AK/StdLibExtraDetails.h>
  9. #include <AK/String.h>
  10. #include <AK/Traits.h>
  11. #include <LibGfx/ImageFormats/TGALoader.h>
  12. namespace Gfx {
  13. enum TGADataType : u8 {
  14. None = 0,
  15. UncompressedColorMapped = 1,
  16. UncompressedRGB = 2,
  17. UncompressedBlackAndWhite = 3,
  18. RunLengthEncodedColorMapped = 9,
  19. RunLengthEncodedRGB = 10,
  20. CompressedBlackAndWhite = 11,
  21. CompressedColorMapped = 32,
  22. CompressedColorMappedFourPass = 33
  23. };
  24. struct [[gnu::packed]] TGAHeader {
  25. u8 id_length;
  26. u8 color_map_type;
  27. TGADataType data_type_code;
  28. i16 color_map_origin;
  29. i16 color_map_length;
  30. u8 color_map_depth;
  31. i16 x_origin;
  32. i16 y_origin;
  33. u16 width;
  34. u16 height;
  35. u8 bits_per_pixel;
  36. u8 image_descriptor;
  37. };
  38. static_assert(sizeof(TGAHeader) == 18);
  39. }
  40. template<>
  41. struct AK::Traits<Gfx::TGAHeader> : public DefaultTraits<Gfx::TGAHeader> {
  42. static constexpr bool is_trivially_serializable() { return true; }
  43. };
  44. namespace Gfx {
  45. struct TGALoadingContext {
  46. TGALoadingContext(ReadonlyBytes bytes, FixedMemoryStream stream)
  47. : bytes(bytes)
  48. , stream(move(stream))
  49. {
  50. }
  51. ReadonlyBytes bytes;
  52. FixedMemoryStream stream;
  53. TGAHeader header {};
  54. RefPtr<Gfx::Bitmap> bitmap;
  55. };
  56. TGAImageDecoderPlugin::TGAImageDecoderPlugin(NonnullOwnPtr<TGALoadingContext> context)
  57. : m_context(move(context))
  58. {
  59. }
  60. TGAImageDecoderPlugin::~TGAImageDecoderPlugin() = default;
  61. IntSize TGAImageDecoderPlugin::size()
  62. {
  63. return IntSize { m_context->header.width, m_context->header.height };
  64. }
  65. static ErrorOr<void> ensure_header_validity(TGAHeader const& header, size_t whole_image_stream_size)
  66. {
  67. auto bytes_remaining = whole_image_stream_size - sizeof(TGAHeader);
  68. if ((header.bits_per_pixel % 8) != 0 || header.bits_per_pixel < 8 || header.bits_per_pixel > 32)
  69. return Error::from_string_literal("Invalid bit depth");
  70. if (header.data_type_code == TGADataType::UncompressedRGB && bytes_remaining < static_cast<u64>(header.width) * header.height * (header.bits_per_pixel / 8))
  71. return Error::from_string_literal("Not enough data to read an image with the expected size");
  72. return {};
  73. }
  74. ErrorOr<void> TGAImageDecoderPlugin::decode_tga_header()
  75. {
  76. m_context->header = TRY(m_context->stream.read_value<TGAHeader>());
  77. TRY(ensure_header_validity(m_context->header, m_context->bytes.size()));
  78. return {};
  79. }
  80. ErrorOr<bool> TGAImageDecoderPlugin::validate_before_create(ReadonlyBytes data)
  81. {
  82. FixedMemoryStream stream { data };
  83. auto header = TRY(stream.read_value<Gfx::TGAHeader>());
  84. return !ensure_header_validity(header, data.size()).is_error();
  85. }
  86. ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> TGAImageDecoderPlugin::create(ReadonlyBytes data)
  87. {
  88. FixedMemoryStream stream { data };
  89. auto context = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGALoadingContext(data, move(stream))));
  90. auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGAImageDecoderPlugin(move(context))));
  91. TRY(plugin->decode_tga_header());
  92. return plugin;
  93. }
  94. static ErrorOr<ARGB32> read_pixel_from_stream(Stream& stream, size_t bytes_size)
  95. {
  96. // NOTE: We support 24-bit color pixels and 32-bit color pixels
  97. VERIFY(bytes_size == 3 || bytes_size == 4);
  98. if (bytes_size == 3) {
  99. Array<u8, 3> raw;
  100. TRY(stream.read_until_filled(raw.span()));
  101. return Color(raw[2], raw[1], raw[0]).value();
  102. }
  103. return stream.read_value<ARGB32>();
  104. }
  105. struct TGAPixelPacketHeader {
  106. bool raw { false };
  107. u8 pixels_count { 0 };
  108. };
  109. static ErrorOr<TGAPixelPacketHeader> read_pixel_packet_header(Stream& stream)
  110. {
  111. auto const pixel_packet_header = TRY(stream.read_value<u8>());
  112. bool pixels_raw_in_packet = !(pixel_packet_header & 0x80);
  113. u8 pixels_count_in_packet = (pixel_packet_header & 0x7f);
  114. // NOTE: Run-length-encoded/Raw pixel packets cannot encode zero pixels,
  115. // so value 0 stands for 1 pixel, 1 stands for 2, etc...
  116. pixels_count_in_packet++;
  117. VERIFY(pixels_count_in_packet > 0);
  118. return TGAPixelPacketHeader { pixels_raw_in_packet, pixels_count_in_packet };
  119. }
  120. ErrorOr<ImageFrameDescriptor> TGAImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
  121. {
  122. auto bits_per_pixel = m_context->header.bits_per_pixel;
  123. auto color_map = m_context->header.color_map_type;
  124. auto data_type = m_context->header.data_type_code;
  125. auto width = m_context->header.width;
  126. auto height = m_context->header.height;
  127. auto x_origin = m_context->header.x_origin;
  128. auto y_origin = m_context->header.y_origin;
  129. if (index != 0)
  130. return Error::from_string_literal("TGAImageDecoderPlugin: frame index must be 0");
  131. if (color_map > 1)
  132. return Error::from_string_literal("TGAImageDecoderPlugin: Invalid color map type");
  133. if (m_context->bitmap)
  134. return ImageFrameDescriptor { m_context->bitmap, 0 };
  135. RefPtr<Gfx::Bitmap> bitmap;
  136. switch (bits_per_pixel) {
  137. case 24:
  138. bitmap = TRY(Bitmap::create(BitmapFormat::BGRx8888, { m_context->header.width, m_context->header.height }));
  139. break;
  140. case 32:
  141. bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { m_context->header.width, m_context->header.height }));
  142. break;
  143. default:
  144. // FIXME: Implement other TGA bit depths
  145. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle 24 and 32 bits per pixel");
  146. }
  147. // FIXME: Try to understand the Image origin (instead of X and Y origin coordinates)
  148. // based on the Image descriptor, Field 5.6, bits 4 and 5.
  149. // NOTE: If Y origin is set to a negative number, just assume the generating software
  150. // meant that we start with Y origin at the top height of the picture.
  151. // At least this is the observed behavior when generating some pictures in GIMP.
  152. if (y_origin < 0)
  153. y_origin = height;
  154. if (y_origin != 0 && y_origin != height)
  155. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle Y origin which is 0 or the entire height");
  156. if (x_origin != 0 && x_origin != width)
  157. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle X origin which is 0 or the entire width");
  158. VERIFY((bits_per_pixel % 8) == 0);
  159. auto bytes_per_pixel = bits_per_pixel / 8;
  160. switch (data_type) {
  161. case TGADataType::UncompressedRGB: {
  162. for (int row = 0; row < height; ++row) {
  163. for (int col = 0; col < width; ++col) {
  164. auto actual_row = row;
  165. if (y_origin < height)
  166. actual_row = height - 1 - row;
  167. auto actual_col = col;
  168. if (x_origin > width)
  169. actual_col = width - 1 - col;
  170. bitmap->scanline(actual_row)[actual_col] = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  171. }
  172. }
  173. break;
  174. }
  175. case TGADataType::RunLengthEncodedRGB: {
  176. size_t pixel_index = 0;
  177. size_t pixel_count = height * width;
  178. while (pixel_index < pixel_count) {
  179. auto pixel_packet_header = TRY(read_pixel_packet_header(m_context->stream));
  180. VERIFY(pixel_packet_header.pixels_count > 0);
  181. auto pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  182. auto max_pixel_index = min(pixel_index + pixel_packet_header.pixels_count, pixel_count);
  183. for (size_t current_pixel_index = pixel_index; current_pixel_index < max_pixel_index; ++current_pixel_index) {
  184. int row = current_pixel_index / width;
  185. int col = current_pixel_index % width;
  186. auto actual_row = row;
  187. if (y_origin < height)
  188. actual_row = height - 1 - row;
  189. auto actual_col = col;
  190. if (x_origin > width)
  191. actual_col = width - 1 - col;
  192. bitmap->scanline(actual_row)[actual_col] = pixel;
  193. if (pixel_packet_header.raw && (current_pixel_index + 1) < max_pixel_index)
  194. pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  195. }
  196. pixel_index += pixel_packet_header.pixels_count;
  197. }
  198. break;
  199. }
  200. default:
  201. // FIXME: Implement other TGA data types
  202. return Error::from_string_literal("TGAImageDecoderPlugin: Can currently only handle the UncompressedRGB or CompressedRGB data type");
  203. }
  204. m_context->bitmap = bitmap;
  205. return ImageFrameDescriptor { m_context->bitmap, 0 };
  206. }
  207. }