PGMLoader.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "PGMLoader.h"
  7. #include "PortableImageLoaderCommon.h"
  8. #include "Streamer.h"
  9. #include <AK/Endian.h>
  10. #include <AK/LexicalPath.h>
  11. #include <AK/MappedFile.h>
  12. #include <AK/StringBuilder.h>
  13. #include <string.h>
  14. namespace Gfx {
  15. struct PGMLoadingContext {
  16. enum Type {
  17. Unknown,
  18. ASCII,
  19. RAWBITS
  20. };
  21. enum State {
  22. NotDecoded = 0,
  23. Error,
  24. MagicNumber,
  25. Width,
  26. Height,
  27. Maxval,
  28. Bitmap,
  29. Decoded
  30. };
  31. static constexpr auto ascii_magic_number = '2';
  32. static constexpr auto binary_magic_number = '5';
  33. static constexpr auto image_type = "PGM";
  34. Type type { Type::Unknown };
  35. State state { State::NotDecoded };
  36. const u8* data { nullptr };
  37. size_t data_size { 0 };
  38. u16 width { 0 };
  39. u16 height { 0 };
  40. u16 max_val { 0 };
  41. RefPtr<Gfx::Bitmap> bitmap;
  42. };
  43. static void set_adjusted_pixels(PGMLoadingContext& context, const Vector<Gfx::Color>& color_data)
  44. {
  45. size_t index = 0;
  46. for (size_t y = 0; y < context.height; ++y) {
  47. for (size_t x = 0; x < context.width; ++x) {
  48. Color color = color_data.at(index);
  49. if (context.max_val < 255) {
  50. color = adjust_color(context.max_val, color);
  51. }
  52. context.bitmap->set_pixel(x, y, color);
  53. ++index;
  54. }
  55. }
  56. }
  57. static bool read_image_data(PGMLoadingContext& context, Streamer& streamer)
  58. {
  59. Vector<Gfx::Color> color_data;
  60. if (context.type == PGMLoadingContext::ASCII) {
  61. u16 value;
  62. while (true) {
  63. if (!read_number(streamer, &value))
  64. break;
  65. if (!read_whitespace(context, streamer))
  66. break;
  67. color_data.append({ (u8)value, (u8)value, (u8)value });
  68. }
  69. } else if (context.type == PGMLoadingContext::RAWBITS) {
  70. u8 pixel;
  71. while (streamer.read(pixel)) {
  72. color_data.append({ pixel, pixel, pixel });
  73. }
  74. }
  75. size_t context_size = (u32)context.width * (u32)context.height;
  76. if (context_size != color_data.size()) {
  77. dbgln("Not enough color data in image.");
  78. return false;
  79. }
  80. if (!create_bitmap(context))
  81. return false;
  82. set_adjusted_pixels(context, color_data);
  83. context.state = PGMLoadingContext::State::Bitmap;
  84. return true;
  85. }
  86. RefPtr<Gfx::Bitmap> load_pgm(const StringView& path)
  87. {
  88. return load<PGMLoadingContext>(path);
  89. }
  90. RefPtr<Gfx::Bitmap> load_pgm_from_memory(const u8* data, size_t length)
  91. {
  92. auto bitmap = load_impl<PGMLoadingContext>(data, length);
  93. if (bitmap)
  94. bitmap->set_mmap_name(String::formatted("Gfx::Bitmap [{}] - Decoded PGM: <memory>", bitmap->size()));
  95. return bitmap;
  96. }
  97. PGMImageDecoderPlugin::PGMImageDecoderPlugin(const u8* data, size_t size)
  98. {
  99. m_context = make<PGMLoadingContext>();
  100. m_context->data = data;
  101. m_context->data_size = size;
  102. }
  103. PGMImageDecoderPlugin::~PGMImageDecoderPlugin()
  104. {
  105. }
  106. IntSize PGMImageDecoderPlugin::size()
  107. {
  108. if (m_context->state == PGMLoadingContext::State::Error)
  109. return {};
  110. if (m_context->state < PGMLoadingContext::State::Decoded) {
  111. bool success = decode(*m_context);
  112. if (!success)
  113. return {};
  114. }
  115. return { m_context->width, m_context->height };
  116. }
  117. RefPtr<Gfx::Bitmap> PGMImageDecoderPlugin::bitmap()
  118. {
  119. if (m_context->state == PGMLoadingContext::State::Error)
  120. return nullptr;
  121. if (m_context->state < PGMLoadingContext::State::Decoded) {
  122. bool success = decode(*m_context);
  123. if (!success)
  124. return nullptr;
  125. }
  126. VERIFY(m_context->bitmap);
  127. return m_context->bitmap;
  128. }
  129. void PGMImageDecoderPlugin::set_volatile()
  130. {
  131. if (m_context->bitmap)
  132. m_context->bitmap->set_volatile();
  133. }
  134. bool PGMImageDecoderPlugin::set_nonvolatile()
  135. {
  136. if (!m_context->bitmap)
  137. return false;
  138. return m_context->bitmap->set_nonvolatile();
  139. }
  140. bool PGMImageDecoderPlugin::sniff()
  141. {
  142. if (m_context->data_size < 2)
  143. return false;
  144. if (m_context->data[0] == 'P' && m_context->data[1] == '2')
  145. return true;
  146. if (m_context->data[0] == 'P' && m_context->data[1] == '5')
  147. return true;
  148. return false;
  149. }
  150. bool PGMImageDecoderPlugin::is_animated()
  151. {
  152. return false;
  153. }
  154. size_t PGMImageDecoderPlugin::loop_count()
  155. {
  156. return 0;
  157. }
  158. size_t PGMImageDecoderPlugin::frame_count()
  159. {
  160. return 1;
  161. }
  162. ImageFrameDescriptor PGMImageDecoderPlugin::frame(size_t i)
  163. {
  164. if (i > 0) {
  165. return { bitmap(), 0 };
  166. }
  167. return {};
  168. }
  169. }