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/decode_to_jpeg.h"
7 
8 namespace jxl {
9 
10 #if JPEGXL_ENABLE_TRANSCODE_JPEG
11 
Process(const uint8_t ** next_in,size_t * avail_in)12 JxlDecoderStatus JxlToJpegDecoder::Process(const uint8_t** next_in,
13                                            size_t* avail_in) {
14   if (!inside_box_) {
15     JXL_ABORT(
16         "processing of JPEG reconstruction data outside JPEG reconstruction "
17         "box");
18   }
19   Span<const uint8_t> to_decode;
20   if (box_until_eof_) {
21     // Until EOF means consume all data.
22     to_decode = Span<const uint8_t>(*next_in, *avail_in);
23     *next_in += *avail_in;
24     *avail_in = 0;
25   } else {
26     // Defined size means consume min(available, needed).
27     size_t avail_recon_in =
28         std::min<size_t>(*avail_in, box_size_ - buffer_.size());
29     to_decode = Span<const uint8_t>(*next_in, avail_recon_in);
30     *next_in += avail_recon_in;
31     *avail_in -= avail_recon_in;
32   }
33   bool old_data_exists = !buffer_.empty();
34   if (old_data_exists) {
35     // Append incoming data to buffer if we already had data in the buffer.
36     buffer_.insert(buffer_.end(), to_decode.data(),
37                    to_decode.data() + to_decode.size());
38     to_decode = Span<const uint8_t>(buffer_.data(), buffer_.size());
39   }
40   if (!box_until_eof_ && to_decode.size() > box_size_) {
41     JXL_ABORT("JPEG reconstruction data to decode larger than expected");
42   }
43   if (box_until_eof_ || to_decode.size() == box_size_) {
44     // If undefined size, or the right size, try to decode.
45     jpeg_data_ = make_unique<jpeg::JPEGData>();
46     const auto status = jpeg::DecodeJPEGData(to_decode, jpeg_data_.get());
47     if (status.IsFatalError()) return JXL_DEC_ERROR;
48     if (status) {
49       // Successful decoding, emit event after updating state to track that we
50       // are no longer parsing JPEG reconstruction data.
51       inside_box_ = false;
52       return JXL_DEC_JPEG_RECONSTRUCTION;
53     }
54     if (box_until_eof_) {
55       // Unsuccessful decoding and undefined size, assume incomplete data. Copy
56       // the data if we haven't already.
57       if (!old_data_exists) {
58         buffer_.insert(buffer_.end(), to_decode.data(),
59                        to_decode.data() + to_decode.size());
60       }
61     } else {
62       // Unsuccessful decoding of correct amount of data, assume error.
63       return JXL_DEC_ERROR;
64     }
65   } else {
66     // Not enough data, copy the data if we haven't already.
67     if (!old_data_exists) {
68       buffer_.insert(buffer_.end(), to_decode.data(),
69                      to_decode.data() + to_decode.size());
70     }
71   }
72   return JXL_DEC_NEED_MORE_INPUT;
73 }
74 
NumExifMarkers(const jpeg::JPEGData & jpeg_data)75 size_t JxlToJpegDecoder::NumExifMarkers(const jpeg::JPEGData& jpeg_data) {
76   size_t num = 0;
77   for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
78     if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
79       num++;
80     }
81   }
82   return num;
83 }
84 
NumXmpMarkers(const jpeg::JPEGData & jpeg_data)85 size_t JxlToJpegDecoder::NumXmpMarkers(const jpeg::JPEGData& jpeg_data) {
86   size_t num = 0;
87   for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
88     if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
89       num++;
90     }
91   }
92   return num;
93 }
94 
ExifBoxContentSize(const jpeg::JPEGData & jpeg_data,size_t * size)95 JxlDecoderStatus JxlToJpegDecoder::ExifBoxContentSize(
96     const jpeg::JPEGData& jpeg_data, size_t* size) {
97   for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
98     if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
99       if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kExifTag)) {
100         // too small for app marker header
101         return JXL_DEC_ERROR;
102       }
103       // The first 4 bytes are the TIFF header from the box contents, and are
104       // not included in the JPEG
105       *size = jpeg_data.app_data[i].size() + 4 - 3 - sizeof(jpeg::kExifTag);
106       return JXL_DEC_SUCCESS;
107     }
108   }
109   return JXL_DEC_ERROR;
110 }
111 
XmlBoxContentSize(const jpeg::JPEGData & jpeg_data,size_t * size)112 JxlDecoderStatus JxlToJpegDecoder::XmlBoxContentSize(
113     const jpeg::JPEGData& jpeg_data, size_t* size) {
114   for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
115     if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
116       if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kXMPTag)) {
117         // too small for app marker header
118         return JXL_DEC_ERROR;
119       }
120       *size = jpeg_data.app_data[i].size() - 3 - sizeof(jpeg::kXMPTag);
121       return JXL_DEC_SUCCESS;
122     }
123   }
124   return JXL_DEC_ERROR;
125 }
126 
SetExif(const uint8_t * data,size_t size,jpeg::JPEGData * jpeg_data)127 JxlDecoderStatus JxlToJpegDecoder::SetExif(const uint8_t* data, size_t size,
128                                            jpeg::JPEGData* jpeg_data) {
129   for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) {
130     if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
131       if (jpeg_data->app_data[i].size() !=
132           size + 3 + sizeof(jpeg::kExifTag) - 4)
133         return JXL_DEC_ERROR;
134       // The first 9 bytes are used for JPEG marker header.
135       jpeg_data->app_data[i][0] = 0xE1;
136       // The second and third byte are already filled in correctly
137       memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kExifTag,
138              sizeof(jpeg::kExifTag));
139       // The first 4 bytes are the TIFF header from the box contents, and are
140       // not included in the JPEG
141       memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kExifTag),
142              data + 4, size - 4);
143       return JXL_DEC_SUCCESS;
144     }
145   }
146   return JXL_DEC_ERROR;
147 }
SetXmp(const uint8_t * data,size_t size,jpeg::JPEGData * jpeg_data)148 JxlDecoderStatus JxlToJpegDecoder::SetXmp(const uint8_t* data, size_t size,
149                                           jpeg::JPEGData* jpeg_data) {
150   for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) {
151     if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
152       if (jpeg_data->app_data[i].size() != size + 3 + sizeof(jpeg::kXMPTag))
153         return JXL_DEC_ERROR;
154       // The first 9 bytes are used for JPEG marker header.
155       jpeg_data->app_data[i][0] = 0xE1;
156       // The second and third byte are already filled in correctly
157       memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kXMPTag,
158              sizeof(jpeg::kXMPTag));
159       memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kXMPTag), data,
160              size);
161       return JXL_DEC_SUCCESS;
162     }
163   }
164   return JXL_DEC_ERROR;
165 }
166 
167 #endif  // JPEGXL_ENABLE_TRANSCODE_JPEG
168 
169 }  // namespace jxl
170