TGALoader.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. if ((header.bits_per_pixel % 8) != 0 || header.bits_per_pixel < 8 || header.bits_per_pixel > 32)
  68. return Error::from_string_literal("Invalid bit depth");
  69. auto bytes_remaining = whole_image_stream_size - sizeof(TGAHeader);
  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. bool TGAImageDecoderPlugin::validate_before_create(ReadonlyBytes data)
  81. {
  82. FixedMemoryStream stream { data };
  83. auto header_or_err = stream.read_value<Gfx::TGAHeader>();
  84. if (header_or_err.is_error())
  85. return false;
  86. return !ensure_header_validity(header_or_err.release_value(), data.size()).is_error();
  87. }
  88. ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> TGAImageDecoderPlugin::create(ReadonlyBytes data)
  89. {
  90. FixedMemoryStream stream { data };
  91. auto context = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGALoadingContext(data, move(stream))));
  92. auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGAImageDecoderPlugin(move(context))));
  93. TRY(plugin->decode_tga_header());
  94. return plugin;
  95. }
  96. static ErrorOr<ARGB32> read_pixel_from_stream(Stream& stream, size_t bytes_size)
  97. {
  98. // NOTE: We support 24-bit color pixels and 32-bit color pixels
  99. VERIFY(bytes_size == 3 || bytes_size == 4);
  100. if (bytes_size == 3) {
  101. Array<u8, 3> raw;
  102. TRY(stream.read_until_filled(raw.span()));
  103. return Color(raw[2], raw[1], raw[0]).value();
  104. }
  105. return stream.read_value<ARGB32>();
  106. }
  107. struct TGAPixelPacketHeader {
  108. bool raw { false };
  109. u8 pixels_count { 0 };
  110. };
  111. static ErrorOr<TGAPixelPacketHeader> read_pixel_packet_header(Stream& stream)
  112. {
  113. auto const pixel_packet_header = TRY(stream.read_value<u8>());
  114. bool pixels_raw_in_packet = !(pixel_packet_header & 0x80);
  115. u8 pixels_count_in_packet = (pixel_packet_header & 0x7f);
  116. // NOTE: Run-length-encoded/Raw pixel packets cannot encode zero pixels,
  117. // so value 0 stands for 1 pixel, 1 stands for 2, etc...
  118. pixels_count_in_packet++;
  119. VERIFY(pixels_count_in_packet > 0);
  120. return TGAPixelPacketHeader { pixels_raw_in_packet, pixels_count_in_packet };
  121. }
  122. ErrorOr<ImageFrameDescriptor> TGAImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
  123. {
  124. auto bits_per_pixel = m_context->header.bits_per_pixel;
  125. auto color_map = m_context->header.color_map_type;
  126. auto data_type = m_context->header.data_type_code;
  127. auto width = m_context->header.width;
  128. auto height = m_context->header.height;
  129. auto x_origin = m_context->header.x_origin;
  130. auto y_origin = m_context->header.y_origin;
  131. if (index != 0)
  132. return Error::from_string_literal("TGAImageDecoderPlugin: frame index must be 0");
  133. if (color_map > 1)
  134. return Error::from_string_literal("TGAImageDecoderPlugin: Invalid color map type");
  135. if (m_context->bitmap)
  136. return ImageFrameDescriptor { m_context->bitmap, 0 };
  137. RefPtr<Gfx::Bitmap> bitmap;
  138. switch (bits_per_pixel) {
  139. case 24:
  140. bitmap = TRY(Bitmap::create(BitmapFormat::BGRx8888, { m_context->header.width, m_context->header.height }));
  141. break;
  142. case 32:
  143. bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { m_context->header.width, m_context->header.height }));
  144. break;
  145. default:
  146. // FIXME: Implement other TGA bit depths
  147. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle 24 and 32 bits per pixel");
  148. }
  149. // FIXME: Try to understand the Image origin (instead of X and Y origin coordinates)
  150. // based on the Image descriptor, Field 5.6, bits 4 and 5.
  151. // NOTE: If Y origin is set to a negative number, just assume the generating software
  152. // meant that we start with Y origin at the top height of the picture.
  153. // At least this is the observed behavior when generating some pictures in GIMP.
  154. if (y_origin < 0)
  155. y_origin = height;
  156. if (y_origin != 0 && y_origin != height)
  157. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle Y origin which is 0 or the entire height");
  158. if (x_origin != 0 && x_origin != width)
  159. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle X origin which is 0 or the entire width");
  160. VERIFY((bits_per_pixel % 8) == 0);
  161. auto bytes_per_pixel = bits_per_pixel / 8;
  162. switch (data_type) {
  163. case TGADataType::UncompressedRGB: {
  164. for (int row = 0; row < height; ++row) {
  165. for (int col = 0; col < width; ++col) {
  166. auto actual_row = row;
  167. if (y_origin < height)
  168. actual_row = height - 1 - row;
  169. auto actual_col = col;
  170. if (x_origin > width)
  171. actual_col = width - 1 - col;
  172. bitmap->scanline(actual_row)[actual_col] = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  173. }
  174. }
  175. break;
  176. }
  177. case TGADataType::RunLengthEncodedRGB: {
  178. size_t pixel_index = 0;
  179. size_t pixel_count = height * width;
  180. while (pixel_index < pixel_count) {
  181. auto pixel_packet_header = TRY(read_pixel_packet_header(m_context->stream));
  182. VERIFY(pixel_packet_header.pixels_count > 0);
  183. auto pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  184. auto max_pixel_index = min(pixel_index + pixel_packet_header.pixels_count, pixel_count);
  185. for (size_t current_pixel_index = pixel_index; current_pixel_index < max_pixel_index; ++current_pixel_index) {
  186. int row = current_pixel_index / width;
  187. int col = current_pixel_index % width;
  188. auto actual_row = row;
  189. if (y_origin < height)
  190. actual_row = height - 1 - row;
  191. auto actual_col = col;
  192. if (x_origin > width)
  193. actual_col = width - 1 - col;
  194. bitmap->scanline(actual_row)[actual_col] = pixel;
  195. if (pixel_packet_header.raw && (current_pixel_index + 1) < max_pixel_index)
  196. pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel));
  197. }
  198. pixel_index += pixel_packet_header.pixels_count;
  199. }
  200. break;
  201. }
  202. default:
  203. // FIXME: Implement other TGA data types
  204. return Error::from_string_literal("TGAImageDecoderPlugin: Can currently only handle the UncompressedRGB or CompressedRGB data type");
  205. }
  206. m_context->bitmap = bitmap;
  207. return ImageFrameDescriptor { m_context->bitmap, 0 };
  208. }
  209. }