1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5
6 #include "lib/jxl/jpeg/dec_jpeg_data.h"
7
8 #include <brotli/decode.h>
9
10 #include "lib/jxl/base/span.h"
11 #include "lib/jxl/base/status.h"
12 #include "lib/jxl/dec_bit_reader.h"
13
14 namespace jxl {
15 namespace jpeg {
DecodeJPEGData(Span<const uint8_t> encoded,JPEGData * jpeg_data)16 Status DecodeJPEGData(Span<const uint8_t> encoded, JPEGData* jpeg_data) {
17 Status ret = true;
18 const uint8_t* in = encoded.data();
19 size_t available_in = encoded.size();
20 {
21 BitReader br(encoded);
22 BitReaderScopedCloser br_closer(&br, &ret);
23 JXL_RETURN_IF_ERROR(Bundle::Read(&br, jpeg_data));
24 JXL_RETURN_IF_ERROR(br.JumpToByteBoundary());
25 in += br.TotalBitsConsumed() / 8;
26 available_in -= br.TotalBitsConsumed() / 8;
27 }
28 JXL_RETURN_IF_ERROR(ret);
29
30 BrotliDecoderState* brotli_dec =
31 BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
32
33 struct BrotliDecDeleter {
34 BrotliDecoderState* brotli_dec;
35 ~BrotliDecDeleter() { BrotliDecoderDestroyInstance(brotli_dec); }
36 } brotli_dec_deleter{brotli_dec};
37
38 BrotliDecoderResult result =
39 BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS;
40
41 auto br_read = [&](std::vector<uint8_t>& data) -> Status {
42 size_t available_out = data.size();
43 uint8_t* out = data.data();
44 while (available_out != 0) {
45 if (BrotliDecoderIsFinished(brotli_dec)) {
46 return JXL_FAILURE("Not enough decompressed output");
47 }
48 result = BrotliDecoderDecompressStream(brotli_dec, &available_in, &in,
49 &available_out, &out, nullptr);
50 if (result !=
51 BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT &&
52 result != BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS) {
53 return JXL_FAILURE(
54 "Brotli decoding error: %s\n",
55 BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_dec)));
56 }
57 }
58 return true;
59 };
60 size_t num_icc = 0;
61 for (size_t i = 0; i < jpeg_data->app_data.size(); i++) {
62 auto& marker = jpeg_data->app_data[i];
63 if (jpeg_data->app_marker_type[i] != AppMarkerType::kUnknown) {
64 // Set the size of the marker.
65 size_t size_minus_1 = marker.size() - 1;
66 marker[1] = size_minus_1 >> 8;
67 marker[2] = size_minus_1 & 0xFF;
68 if (jpeg_data->app_marker_type[i] == AppMarkerType::kICC) {
69 if (marker.size() < 17) {
70 return JXL_FAILURE("ICC markers must be at least 17 bytes");
71 }
72 marker[0] = 0xE2;
73 memcpy(&marker[3], kIccProfileTag, sizeof kIccProfileTag);
74 marker[15] = ++num_icc;
75 }
76 } else {
77 JXL_RETURN_IF_ERROR(br_read(marker));
78 if (marker[1] * 256u + marker[2] + 1u != marker.size()) {
79 return JXL_FAILURE("Incorrect marker size");
80 }
81 }
82 }
83 for (size_t i = 0; i < jpeg_data->app_data.size(); i++) {
84 auto& marker = jpeg_data->app_data[i];
85 if (jpeg_data->app_marker_type[i] == AppMarkerType::kICC) {
86 marker[16] = num_icc;
87 }
88 if (jpeg_data->app_marker_type[i] == AppMarkerType::kExif) {
89 marker[0] = 0xE1;
90 if (marker.size() < 3 + sizeof kExifTag) {
91 return JXL_FAILURE("Incorrect Exif marker size");
92 }
93 memcpy(&marker[3], kExifTag, sizeof kExifTag);
94 }
95 if (jpeg_data->app_marker_type[i] == AppMarkerType::kXMP) {
96 marker[0] = 0xE1;
97 if (marker.size() < 3 + sizeof kXMPTag) {
98 return JXL_FAILURE("Incorrect XMP marker size");
99 }
100 memcpy(&marker[3], kXMPTag, sizeof kXMPTag);
101 }
102 }
103 // TODO(eustas): actually inject ICC profile and check it fits perfectly.
104 for (size_t i = 0; i < jpeg_data->com_data.size(); i++) {
105 auto& marker = jpeg_data->com_data[i];
106 JXL_RETURN_IF_ERROR(br_read(marker));
107 if (marker[1] * 256u + marker[2] + 1u != marker.size()) {
108 return JXL_FAILURE("Incorrect marker size");
109 }
110 }
111 for (size_t i = 0; i < jpeg_data->inter_marker_data.size(); i++) {
112 JXL_RETURN_IF_ERROR(br_read(jpeg_data->inter_marker_data[i]));
113 }
114 JXL_RETURN_IF_ERROR(br_read(jpeg_data->tail_data));
115
116 // Check if there is more decompressed output.
117 size_t available_out = 1;
118 uint64_t dummy;
119 uint8_t* next_out = reinterpret_cast<uint8_t*>(&dummy);
120 result = BrotliDecoderDecompressStream(brotli_dec, &available_in, &in,
121 &available_out, &next_out, nullptr);
122 if (available_out == 0 ||
123 result == BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
124 return JXL_FAILURE("Excess data in compressed stream");
125 }
126 if (result == BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
127 return JXL_FAILURE("Incomplete brotli-stream");
128 }
129 if (!BrotliDecoderIsFinished(brotli_dec) ||
130 result != BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS) {
131 return JXL_FAILURE("Corrupted brotli-stream");
132 }
133 if (available_in != 0) {
134 return JXL_FAILURE("Unused data after brotli stream");
135 }
136
137 return true;
138 }
139 } // namespace jpeg
140 } // namespace jxl
141