TGALoader.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * Copyright (c) 2022, Tom Needham <06needhamt@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Span.h>
  7. #include <AK/StdLibExtraDetails.h>
  8. #include <AK/String.h>
  9. #include <LibGfx/TGALoader.h>
  10. namespace Gfx {
  11. enum TGADataType : u8 {
  12. None = 0,
  13. UncompressedColorMapped = 1,
  14. UncompressedRGB = 2,
  15. UncompressedBlackAndWhite = 3,
  16. RunLengthEncodedColorMapped = 9,
  17. RunLengthEncodedRGB = 10,
  18. CompressedBlackAndWhite = 11,
  19. CompressedColorMapped = 32,
  20. CompressedColorMappedFourPass = 33
  21. };
  22. struct [[gnu::packed]] TGAHeader {
  23. u8 id_length;
  24. u8 color_map_type;
  25. TGADataType data_type_code;
  26. i16 color_map_origin;
  27. i16 color_map_length;
  28. u8 color_map_depth;
  29. i16 x_origin;
  30. i16 y_origin;
  31. u16 width;
  32. u16 height;
  33. u8 bits_per_pixel;
  34. u8 image_descriptor;
  35. };
  36. static_assert(sizeof(TGAHeader) == 18);
  37. union [[gnu::packed]] TGAPixel {
  38. struct TGAColor {
  39. u8 blue;
  40. u8 green;
  41. u8 red;
  42. u8 alpha;
  43. } components;
  44. u32 data;
  45. };
  46. static_assert(AssertSize<TGAPixel, 4>());
  47. class TGAReader {
  48. public:
  49. TGAReader(ReadonlyBytes data)
  50. : m_data(move(data))
  51. {
  52. }
  53. ALWAYS_INLINE u8 read_u8()
  54. {
  55. u8 value = m_data[m_index];
  56. m_index++;
  57. return value;
  58. }
  59. ALWAYS_INLINE i8 read_i8()
  60. {
  61. return static_cast<i8>(read_u8());
  62. }
  63. ALWAYS_INLINE u16 read_u16()
  64. {
  65. return read_u8() | read_u8() << 8;
  66. }
  67. ALWAYS_INLINE i16 read_i16()
  68. {
  69. return read_i8() | read_i8() << 8;
  70. }
  71. ALWAYS_INLINE u32 read_u32()
  72. {
  73. return read_u16() | read_u16() << 16;
  74. }
  75. ALWAYS_INLINE i32 read_i32()
  76. {
  77. return read_i16() | read_i16() << 16;
  78. }
  79. ALWAYS_INLINE TGAPixel read_pixel(u8 bits_per_pixel)
  80. {
  81. auto pixel = TGAPixel();
  82. switch (bits_per_pixel) {
  83. case 24:
  84. pixel.components.blue = read_u8();
  85. pixel.components.green = read_u8();
  86. pixel.components.red = read_u8();
  87. pixel.components.alpha = 0xFF;
  88. return pixel;
  89. case 32:
  90. pixel.components.blue = read_u8();
  91. pixel.components.green = read_u8();
  92. pixel.components.red = read_u8();
  93. pixel.components.alpha = read_u8();
  94. return pixel;
  95. default:
  96. VERIFY_NOT_REACHED();
  97. }
  98. }
  99. size_t index() const
  100. {
  101. return m_index;
  102. }
  103. ReadonlyBytes data() const
  104. {
  105. return m_data;
  106. }
  107. private:
  108. ReadonlyBytes m_data;
  109. size_t m_index { 0 };
  110. };
  111. struct TGALoadingContext {
  112. TGAHeader header;
  113. ReadonlyBytes bytes;
  114. size_t file_size;
  115. OwnPtr<TGAReader> reader = { nullptr };
  116. RefPtr<Gfx::Bitmap> bitmap;
  117. };
  118. TGAImageDecoderPlugin::TGAImageDecoderPlugin(u8 const* file_data, size_t file_size)
  119. {
  120. m_context = make<TGALoadingContext>();
  121. m_context->bytes = ReadonlyBytes(file_data, file_size);
  122. m_context->file_size = move(file_size);
  123. m_context->reader = make<TGAReader>(m_context->bytes);
  124. }
  125. TGAImageDecoderPlugin::~TGAImageDecoderPlugin() = default;
  126. IntSize TGAImageDecoderPlugin::size()
  127. {
  128. return IntSize { m_context->header.width, m_context->header.height };
  129. }
  130. void TGAImageDecoderPlugin::set_volatile()
  131. {
  132. if (m_context->bitmap)
  133. m_context->bitmap->set_volatile();
  134. }
  135. bool TGAImageDecoderPlugin::set_nonvolatile(bool& was_purged)
  136. {
  137. if (!m_context->bitmap)
  138. return false;
  139. return m_context->bitmap->set_nonvolatile(was_purged);
  140. }
  141. bool TGAImageDecoderPlugin::decode_tga_header()
  142. {
  143. auto& reader = m_context->reader;
  144. m_context->header = TGAHeader();
  145. m_context->header.id_length = reader->read_u8();
  146. m_context->header.color_map_type = reader->read_u8();
  147. m_context->header.data_type_code = static_cast<TGADataType>(reader->read_u8());
  148. m_context->header.color_map_origin = reader->read_i16();
  149. m_context->header.color_map_length = reader->read_i16();
  150. m_context->header.color_map_depth = reader->read_u8();
  151. m_context->header.x_origin = reader->read_i16();
  152. m_context->header.y_origin = reader->read_i16();
  153. m_context->header.width = reader->read_u16();
  154. m_context->header.height = reader->read_u16();
  155. m_context->header.bits_per_pixel = reader->read_u8();
  156. m_context->header.image_descriptor = reader->read_u8();
  157. auto bytes_remaining = reader->data().size() - reader->index();
  158. if (bytes_remaining < (m_context->header.width * m_context->header.height * (m_context->header.bits_per_pixel / 8)))
  159. return false;
  160. if (m_context->header.bits_per_pixel < 8 || m_context->header.bits_per_pixel > 32)
  161. return false;
  162. return true;
  163. }
  164. bool TGAImageDecoderPlugin::sniff()
  165. {
  166. return decode_tga_header();
  167. }
  168. bool TGAImageDecoderPlugin::is_animated()
  169. {
  170. return false;
  171. }
  172. size_t TGAImageDecoderPlugin::loop_count()
  173. {
  174. return 0;
  175. }
  176. size_t TGAImageDecoderPlugin::frame_count()
  177. {
  178. return 1;
  179. }
  180. ErrorOr<ImageFrameDescriptor> TGAImageDecoderPlugin::frame(size_t index)
  181. {
  182. auto bits_per_pixel = m_context->header.bits_per_pixel;
  183. auto color_map = m_context->header.color_map_type;
  184. auto data_type = m_context->header.data_type_code;
  185. auto width = m_context->header.width;
  186. auto height = m_context->header.height;
  187. auto x_origin = m_context->header.x_origin;
  188. auto y_origin = m_context->header.y_origin;
  189. if (index != 0)
  190. return Error::from_string_literal("TGAImageDecoderPlugin: frame index must be 0");
  191. if (color_map > 1)
  192. return Error::from_string_literal("TGAImageDecoderPlugin: Invalid color map type");
  193. switch (bits_per_pixel) {
  194. case 24:
  195. m_context->bitmap = TRY(Bitmap::try_create(BitmapFormat::BGRx8888, { m_context->header.width, m_context->header.height }));
  196. break;
  197. case 32:
  198. m_context->bitmap = TRY(Bitmap::try_create(BitmapFormat::BGRA8888, { m_context->header.width, m_context->header.height }));
  199. break;
  200. default:
  201. // FIXME: Implement other TGA bit depths
  202. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle 24 and 32 bits per pixel");
  203. }
  204. // FIXME: Try to understand the Image origin (instead of X and Y origin coordinates)
  205. // based on the Image descriptor, Field 5.6, bits 4 and 5.
  206. // NOTE: If Y origin is set to a negative number, just assume the generating software
  207. // meant that we start with Y origin at the top height of the picture.
  208. // At least this is the observed behavior when generating some pictures in GIMP.
  209. if (y_origin < 0)
  210. y_origin = height;
  211. if (y_origin != 0 && y_origin != height)
  212. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle Y origin which is 0 or the entire height");
  213. if (x_origin != 0 && x_origin != width)
  214. return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle X origin which is 0 or the entire width");
  215. switch (data_type) {
  216. case TGADataType::UncompressedRGB: {
  217. for (int row = 0; row < height; ++row) {
  218. for (int col = 0; col < width; ++col) {
  219. auto pixel = m_context->reader->read_pixel(bits_per_pixel);
  220. auto actual_row = row;
  221. if (y_origin < height)
  222. actual_row = height - 1 - row;
  223. auto actual_col = col;
  224. if (x_origin > width)
  225. actual_col = width - 1 - col;
  226. m_context->bitmap->scanline(actual_row)[actual_col] = pixel.data;
  227. }
  228. }
  229. break;
  230. }
  231. default:
  232. // FIXME: Implement other TGA data types
  233. return Error::from_string_literal("TGAImageDecoderPlugin: Can currently only handle the UncompressedRGB data type");
  234. }
  235. VERIFY(m_context->bitmap);
  236. return ImageFrameDescriptor { m_context->bitmap, 0 };
  237. }
  238. }