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