• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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