JPEG2000Loader.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2024, Nico Weber <thakis@chromium.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/MemoryStream.h>
  7. #include <LibGfx/ImageFormats/ISOBMFF/JPEG2000Boxes.h>
  8. #include <LibGfx/ImageFormats/ISOBMFF/Reader.h>
  9. #include <LibGfx/ImageFormats/JPEG2000Loader.h>
  10. // Core coding system spec (.jp2 format): T-REC-T.800-201511-S!!PDF-E.pdf available here:
  11. // https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.800-201511-S!!PDF-E&type=items
  12. // Extensions (.jpx format): T-REC-T.801-202106-S!!PDF-E.pdf available here:
  13. // https://handle.itu.int/11.1002/1000/14666-en?locatt=format:pdf&auth
  14. // rfc3745 lists the MIME type. It only mentions the jp2_id_string as magic number.
  15. namespace Gfx {
  16. // A JPEG2000 image can be stored in a codestream with markers, similar to a JPEG image,
  17. // or in a JP2 file, which is a container format based on boxes similar to ISOBMFF.
  18. // This is the marker for the codestream version. We don't support this yet.
  19. // If we add support, add a second `"image/jp2"` line to MimeData.cpp for this magic number.
  20. // T.800 Annex A, Codestream syntax, A.2 Information in the marker segments and A.3 Construction of the codestream
  21. [[maybe_unused]] static constexpr u8 marker_id_string[] = { 0xFF, 0x4F, 0xFF, 0x51 };
  22. // This is the marker for the box version.
  23. // T.800 Annex I, JP2 file format syntax, I.5.1 JPEG 2000 Signature box
  24. static constexpr u8 jp2_id_string[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
  25. struct JPEG2000LoadingContext {
  26. enum class State {
  27. NotDecoded = 0,
  28. Error,
  29. };
  30. State state { State::NotDecoded };
  31. ReadonlyBytes codestream_data;
  32. Optional<ReadonlyBytes> icc_data;
  33. IntSize size;
  34. ISOBMFF::BoxList boxes;
  35. };
  36. static ErrorOr<void> decode_jpeg2000_header(JPEG2000LoadingContext& context, ReadonlyBytes data)
  37. {
  38. if (!JPEG2000ImageDecoderPlugin::sniff(data))
  39. return Error::from_string_literal("JBIG2LoadingContext: Invalid JBIG2 header");
  40. auto reader = TRY(Gfx::ISOBMFF::Reader::create(TRY(try_make<FixedMemoryStream>(data))));
  41. context.boxes = TRY(reader.read_entire_file());
  42. // I.2.2 File organization
  43. // "A particular order of those boxes in the file is not generally implied. However, the JPEG 2000 Signature box
  44. // shall be the first box in a JP2 file, the File Type box shall immediately follow the JPEG 2000 Signature box
  45. // and the JP2 Header box shall fall before the Contiguous Codestream box."
  46. if (context.boxes.size() < 4)
  47. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected at least four boxes");
  48. // Required toplevel boxes: signature box, file type box, jp2 header box, contiguous codestream box.
  49. if (context.boxes[0]->box_type() != ISOBMFF::BoxType::JPEG2000SignatureBox)
  50. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected JPEG2000SignatureBox as first box");
  51. if (context.boxes[1]->box_type() != ISOBMFF::BoxType::FileTypeBox)
  52. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected FileTypeBox as second box");
  53. Optional<size_t> jp2_header_box_index;
  54. Optional<size_t> contiguous_codestream_box_index;
  55. for (size_t i = 2; i < context.boxes.size(); ++i) {
  56. if (context.boxes[i]->box_type() == ISOBMFF::BoxType::JPEG2000HeaderBox) {
  57. // "Within a JP2 file, there shall be one and only one JP2 Header box."
  58. if (jp2_header_box_index.has_value())
  59. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple JP2 Header boxes");
  60. jp2_header_box_index = i;
  61. }
  62. if (context.boxes[i]->box_type() == ISOBMFF::BoxType::JPEG2000ContiguousCodestreamBox && !contiguous_codestream_box_index.has_value()) {
  63. // "a conforming reader shall ignore all codestreams after the first codestream found in the file.
  64. // Contiguous Codestream boxes may be found anywhere in the file except before the JP2 Header box."
  65. contiguous_codestream_box_index = i;
  66. if (!jp2_header_box_index.has_value() || contiguous_codestream_box_index.value() < jp2_header_box_index.value())
  67. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: JP2 Header box must come before Contiguous Codestream box");
  68. }
  69. }
  70. if (!jp2_header_box_index.has_value())
  71. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected JP2 Header box");
  72. if (!contiguous_codestream_box_index.has_value())
  73. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Contiguous Codestream box");
  74. // FIXME: JPEG2000ContiguousCodestreamBox makes a copy of the codestream data. That's too heavy for header scanning.
  75. // Add a mode to ISOBMFF::Reader where it only stores offsets for the codestream data and the ICC profile.
  76. auto const& codestream_box = static_cast<ISOBMFF::JPEG2000ContiguousCodestreamBox const&>(*context.boxes[contiguous_codestream_box_index.value()]);
  77. context.codestream_data = codestream_box.codestream.bytes();
  78. // Required child boxes of the jp2 header box: image header box, color box.
  79. Optional<size_t> image_header_box_index;
  80. Optional<size_t> color_header_box_index;
  81. auto const& header_box = static_cast<ISOBMFF::JPEG2000HeaderBox const&>(*context.boxes[jp2_header_box_index.value()]);
  82. for (size_t i = 0; i < header_box.child_boxes().size(); ++i) {
  83. auto const& subbox = header_box.child_boxes()[i];
  84. if (subbox->box_type() == ISOBMFF::BoxType::JPEG2000ImageHeaderBox) {
  85. if (image_header_box_index.has_value())
  86. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple Image Header boxes");
  87. image_header_box_index = i;
  88. }
  89. if (subbox->box_type() == ISOBMFF::BoxType::JPEG2000ColorSpecificationBox) {
  90. // T.800 says there should be just one 'colr' box, but T.801 allows several and says to pick the one with highest precedence.
  91. bool use_this_color_box;
  92. if (!color_header_box_index.has_value()) {
  93. use_this_color_box = true;
  94. } else {
  95. auto const& new_header_box = static_cast<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*header_box.child_boxes()[i]);
  96. auto const& current_color_box = static_cast<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*header_box.child_boxes()[color_header_box_index.value()]);
  97. use_this_color_box = new_header_box.precedence > current_color_box.precedence;
  98. }
  99. if (use_this_color_box)
  100. color_header_box_index = i;
  101. }
  102. }
  103. if (!image_header_box_index.has_value())
  104. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Image Header box");
  105. if (!color_header_box_index.has_value())
  106. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected Color Specification box");
  107. auto const& image_header_box = static_cast<ISOBMFF::JPEG2000ImageHeaderBox const&>(*header_box.child_boxes()[image_header_box_index.value()]);
  108. context.size = { image_header_box.width, image_header_box.height };
  109. auto const& color_header_box = static_cast<ISOBMFF::JPEG2000ColorSpecificationBox const&>(*header_box.child_boxes()[color_header_box_index.value()]);
  110. if (color_header_box.method == 2 || color_header_box.method == 3)
  111. context.icc_data = color_header_box.icc_data.bytes();
  112. return {};
  113. }
  114. bool JPEG2000ImageDecoderPlugin::sniff(ReadonlyBytes data)
  115. {
  116. return data.starts_with(jp2_id_string);
  117. }
  118. JPEG2000ImageDecoderPlugin::JPEG2000ImageDecoderPlugin()
  119. {
  120. m_context = make<JPEG2000LoadingContext>();
  121. }
  122. IntSize JPEG2000ImageDecoderPlugin::size()
  123. {
  124. return m_context->size;
  125. }
  126. ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JPEG2000ImageDecoderPlugin::create(ReadonlyBytes data)
  127. {
  128. auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JPEG2000ImageDecoderPlugin()));
  129. TRY(decode_jpeg2000_header(*plugin->m_context, data));
  130. return plugin;
  131. }
  132. ErrorOr<ImageFrameDescriptor> JPEG2000ImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
  133. {
  134. if (index != 0)
  135. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Invalid frame index");
  136. if (m_context->state == JPEG2000LoadingContext::State::Error)
  137. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Decoding failed");
  138. return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Draw the rest of the owl");
  139. }
  140. ErrorOr<Optional<ReadonlyBytes>> JPEG2000ImageDecoderPlugin::icc_data()
  141. {
  142. return m_context->icc_data;
  143. }
  144. }