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