1 #include "image_io/jpeg/jpeg_apple_depth_builder.h"
2
3 #include <cstring>
4 #include <sstream>
5
6 #include "image_io/base/byte_buffer.h"
7 #include "image_io/base/data_segment_data_source.h"
8 #include "image_io/base/message.h"
9 #include "image_io/base/message_handler.h"
10 #include "image_io/jpeg/jpeg_info.h"
11 #include "image_io/jpeg/jpeg_info_builder.h"
12 #include "image_io/jpeg/jpeg_scanner.h"
13 #include "image_io/jpeg/jpeg_segment_info.h"
14
15 namespace photos_editing_formats {
16 namespace image_io {
17
18 using std::string;
19 using std::vector;
20
21 namespace {
22
23 /// The special Apple depth JFIF segment suffix and length. The -1 in the
24 /// kAmpfLength compuration is because the size of kAmpf is 5 bytes, including
25 /// the terminating null character, but the kAmpfLength should be 4. Can't use
26 /// strlen (which would be better) because it is not constexpr-able.
27 const char kAmpf[] = "AMPF";
28 constexpr size_t kAmpfLength = sizeof(kAmpf) - 1;
29
30 /// The contents of the MPF segment length and value in three parts. For more
31 /// information, see go/photos-image-io-phase2-summary.
32 const size_t kMpfSegmentLength = 0x5A;
33 const char kMpfHex0[] =
34 "FFE200584D5046004D4D002A000000080003B00000070000000430313030B0010004000000"
35 "0100000002B002000700000020000000320000000000030000";
36 // Four byte primary image length value
37 const char kMpfHex1[] = "000000000000000000000000";
38 // Four byte depth image length value
39 // Four byte depth image offset value
40 const char kMpfHex2[] = "00000000";
41
42 /// The optimum size to use for the DataSource::TransferData() function.
43 constexpr size_t kBestDataSize = 0x10000;
44
45 /// @param image_limit The limit on the number of images to get info of.
46 /// @param data_source The data source from which to get info.
47 /// @param info A pointer to the jpeg_info object to receive the info.
48 /// @param message_handler For use when reporting messages.
49 /// @return Whether the info was obtained successfully or not.
GetJpegInfo(int image_limit,DataSource * data_source,JpegInfo * info,MessageHandler * message_handler)50 bool GetJpegInfo(int image_limit, DataSource* data_source, JpegInfo* info,
51 MessageHandler* message_handler) {
52 JpegInfoBuilder info_builder;
53 info_builder.SetImageLimit(image_limit);
54 info_builder.SetCaptureSegmentBytes(kJfif);
55 JpegScanner scanner(message_handler);
56 scanner.Run(data_source, &info_builder);
57 if (scanner.HasError()) {
58 return false;
59 }
60 *info = info_builder.GetInfo();
61 return true;
62 }
63
64 } // namespace
65
Run(DataSource * primary_image_data_source,DataSource * depth_image_data_source,DataDestination * data_destination)66 bool JpegAppleDepthBuilder::Run(DataSource* primary_image_data_source,
67 DataSource* depth_image_data_source,
68 DataDestination* data_destination) {
69 primary_image_data_source_ = primary_image_data_source;
70 depth_image_data_source_ = depth_image_data_source;
71 data_destination_ = data_destination;
72 if (!GetPrimaryImageData()) {
73 if (message_handler_) {
74 message_handler_->ReportMessage(Message::kDecodingError,
75 "Primary image data");
76 }
77 return false;
78 }
79 if (!GetDepthImageData()) {
80 if (message_handler_) {
81 message_handler_->ReportMessage(Message::kDecodingError,
82 "Depth image data");
83 }
84 return false;
85 }
86 data_destination->StartTransfer();
87 bool status = TransferPrimaryImage();
88 if (status) {
89 status = TransferDepthImage();
90 }
91 data_destination->FinishTransfer();
92 return status;
93 }
94
GetPrimaryImageData()95 bool JpegAppleDepthBuilder::GetPrimaryImageData() {
96 JpegInfo info;
97 if (!GetJpegInfo(1, primary_image_data_source_, &info, message_handler_)) {
98 return false;
99 }
100 if (info.GetImageRanges().empty()) {
101 return false;
102 }
103 primary_image_range_ = info.GetImageRanges()[0];
104 JpegSegmentInfo jfif_segment_info = info.GetSegmentInfo(0, kJfif);
105 if (!jfif_segment_info.IsValid() ||
106 jfif_segment_info.GetBytes().size() < kAmpfLength) {
107 return false;
108 }
109 primary_image_jfif_segment_range_ = jfif_segment_info.GetDataRange();
110 primary_image_jfif_segment_bytes_ = jfif_segment_info.GetBytes();
111
112 JpegSegmentInfo exif_info = info.GetSegmentInfo(0, kExif);
113 if (!exif_info.IsValid()) {
114 return false;
115 }
116 JpegSegmentInfo mpf_info = info.GetSegmentInfo(0, kMpf);
117 if (mpf_info.IsValid()) {
118 primary_image_mpf_segment_range_ = mpf_info.GetDataRange();
119 } else {
120 size_t exif_end = exif_info.GetDataRange().GetEnd();
121 primary_image_mpf_segment_range_ = DataRange(exif_end, exif_end);
122 }
123 return true;
124 }
125
GetDepthImageData()126 bool JpegAppleDepthBuilder::GetDepthImageData() {
127 JpegInfo info;
128 if (!GetJpegInfo(2, depth_image_data_source_, &info, message_handler_)) {
129 return false;
130 }
131 if (!info.HasAppleDepth()) {
132 return false;
133 }
134 depth_image_range_ = info.GetAppleDepthImageRange();
135 return true;
136 }
137
TransferPrimaryImage()138 bool JpegAppleDepthBuilder::TransferPrimaryImage() {
139 // The first move involves all from the start of the data source to the
140 // mpf location or the beginning of the jfif segment, which ever comes first.
141 size_t first_end = std::min(primary_image_jfif_segment_range_.GetBegin(),
142 primary_image_mpf_segment_range_.GetBegin());
143 DataRange first_range(0, first_end);
144 if (!TransferData(primary_image_data_source_, first_range)) {
145 return false;
146 }
147
148 // Move the new Jfif segment. If the primary image jfif came right after the
149 // SOI then the first_end is positioned at the start of the jfif segment. So
150 // move it to the end so that the original jfif segment does not get copied
151 // to the output destination.
152 size_t jfif_length_delta = 0;
153 if (!TransferNewJfifSegment(&jfif_length_delta)) {
154 return false;
155 }
156 if (first_end == primary_image_jfif_segment_range_.GetBegin()) {
157 first_end = primary_image_jfif_segment_range_.GetEnd();
158 }
159
160 // The second move is from the end of the first move or the end of the jfif
161 // segment, which ever comes first to the mpf location.
162 size_t second_begin =
163 std::min(first_end, primary_image_jfif_segment_range_.GetEnd());
164 DataRange second_range(second_begin,
165 primary_image_mpf_segment_range_.GetBegin());
166 if (second_range.IsValid()) {
167 if (!TransferData(primary_image_data_source_, second_range)) {
168 return false;
169 }
170 }
171
172 // Move the new Mpf segment.
173 if (!TransferNewMpfSegment(jfif_length_delta)) {
174 return false;
175 }
176
177 // The third move is from from the end of the mpf to the end of the image.
178 DataRange mpf_eoi_range(primary_image_mpf_segment_range_.GetEnd(),
179 primary_image_range_.GetEnd());
180 if (!mpf_eoi_range.IsValid()) {
181 return false;
182 }
183 return TransferData(primary_image_data_source_, mpf_eoi_range);
184 }
185
TransferNewJfifSegment(size_t * jfif_length_delta)186 bool JpegAppleDepthBuilder::TransferNewJfifSegment(size_t* jfif_length_delta) {
187 *jfif_length_delta = 0;
188 size_t jfif_size = primary_image_jfif_segment_bytes_.size();
189 Byte* jfif_bytes = new Byte[jfif_size + kAmpfLength];
190 memcpy(jfif_bytes, primary_image_jfif_segment_bytes_.data(), jfif_size);
191 if (memcmp(jfif_bytes + jfif_size - kAmpfLength, kAmpf, kAmpfLength) != 0) {
192 memcpy(jfif_bytes + jfif_size, kAmpf, kAmpfLength);
193 *jfif_length_delta = kAmpfLength;
194 jfif_size += kAmpfLength;
195 size_t jfif_data_length = jfif_size - 2;
196 jfif_bytes[2] = ((jfif_data_length >> 8) & 0xFF);
197 jfif_bytes[3] = (jfif_data_length & 0xFF);
198 }
199 DataRange jfif_range(0, jfif_size);
200 auto jfif_segment = DataSegment::Create(jfif_range, jfif_bytes);
201 DataSegmentDataSource jfif_data_source(jfif_segment);
202 return TransferData(&jfif_data_source, jfif_range);
203 }
204
TransferNewMpfSegment(size_t jfif_length_delta)205 bool JpegAppleDepthBuilder::TransferNewMpfSegment(size_t jfif_length_delta) {
206 size_t primary_image_length =
207 primary_image_range_.GetLength() + jfif_length_delta -
208 primary_image_mpf_segment_range_.GetLength() + kMpfSegmentLength;
209 size_t depth_image_length = depth_image_range_.GetLength();
210 size_t depth_image_offset =
211 primary_image_length - primary_image_mpf_segment_range_.GetBegin() - 8;
212 vector<ByteData> mpf_bytes;
213 mpf_bytes.reserve(5);
214 mpf_bytes.emplace_back(ByteData::kHex, kMpfHex0);
215 mpf_bytes.emplace_back(ByteData::kHex,
216 ByteData::Size2BigEndianHex(primary_image_length));
217 mpf_bytes.emplace_back(ByteData::kHex, kMpfHex1);
218 mpf_bytes.emplace_back(ByteData::kHex,
219 ByteData::Size2BigEndianHex(depth_image_length));
220 mpf_bytes.emplace_back(ByteData::kHex,
221 ByteData::Size2BigEndianHex(depth_image_offset));
222 mpf_bytes.emplace_back(ByteData::kHex, kMpfHex2);
223 ByteBuffer mpf_byte_buffer(mpf_bytes);
224 size_t mpf_segment_size = mpf_byte_buffer.GetSize();
225 if (!mpf_byte_buffer.IsValid() || mpf_segment_size != kMpfSegmentLength) {
226 return false;
227 }
228 DataRange mpf_range(0, mpf_segment_size);
229 auto mpf_segment = DataSegment::Create(mpf_range, mpf_byte_buffer.Release());
230 DataSegmentDataSource mpf_data_source(mpf_segment);
231 return TransferData(&mpf_data_source, mpf_range);
232 }
233
TransferDepthImage()234 bool JpegAppleDepthBuilder::TransferDepthImage() {
235 return TransferData(depth_image_data_source_, depth_image_range_);
236 }
237
TransferData(DataSource * data_source,const DataRange & data_range)238 bool JpegAppleDepthBuilder::TransferData(DataSource* data_source,
239 const DataRange& data_range) {
240 size_t old_byte_count = data_destination_->GetBytesTransferred();
241 DataSource::TransferDataResult result =
242 data_source->TransferData(data_range, kBestDataSize, data_destination_);
243 if (result == DataSource::kTransferDataSuccess) {
244 size_t bytes_transferred =
245 data_destination_->GetBytesTransferred() - old_byte_count;
246 if (bytes_transferred != data_range.GetLength()) {
247 result = DataSource::kTransferDataError;
248 if (message_handler_) {
249 std::stringstream ss;
250 ss << "JpegAppleDepthBuilder:data source transferred "
251 << bytes_transferred << " bytes instead of "
252 << data_range.GetLength();
253 message_handler_->ReportMessage(Message::kInternalError, ss.str());
254 }
255 }
256 }
257 return result == DataSource::kTransferDataSuccess;
258 }
259
260 } // namespace image_io
261 } // namespace photos_editing_formats
262