libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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