• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "image_io/jpeg/jpeg_info_builder.h"
2 
3 #include <sstream>
4 #include <string>
5 
6 #include "image_io/base/message_handler.h"
7 #include "image_io/jpeg/jpeg_marker.h"
8 #include "image_io/jpeg/jpeg_scanner.h"
9 #include "image_io/jpeg/jpeg_segment.h"
10 
11 namespace photos_editing_formats {
12 namespace image_io {
13 
14 using std::string;
15 using std::stringstream;
16 using std::vector;
17 
JpegInfoBuilder()18 JpegInfoBuilder::JpegInfoBuilder()
19     : image_limit_(std::numeric_limits<int>::max()), image_count_(0),
20       gdepth_info_builder_(JpegXmpInfo::kGDepthInfoType),
21       gimage_info_builder_(JpegXmpInfo::kGImageInfoType) {}
22 
SetCaptureSegmentBytes(const std::string & segment_info_type)23 void JpegInfoBuilder::SetCaptureSegmentBytes(
24     const std::string& segment_info_type) {
25   capture_segment_bytes_types_.insert(segment_info_type);
26 }
27 
Start(JpegScanner * scanner)28 void JpegInfoBuilder::Start(JpegScanner* scanner) {
29   JpegMarker::Flags marker_flags;
30   marker_flags[JpegMarker::kSOI] = true;
31   marker_flags[JpegMarker::kEOI] = true;
32   marker_flags[JpegMarker::kAPP0] = true;
33   marker_flags[JpegMarker::kAPP1] = true;
34   marker_flags[JpegMarker::kAPP2] = true;
35   scanner->UpdateInterestingMarkerFlags(marker_flags);
36 }
37 
Process(JpegScanner * scanner,const JpegSegment & segment)38 void JpegInfoBuilder::Process(JpegScanner* scanner,
39                               const JpegSegment& segment) {
40   // SOI segments are used to track of the number of images in the JPEG file.
41   // Apple depth images start with a SOI marker, so store its range for later.
42   JpegMarker marker = segment.GetMarker();
43   if (marker.GetType() == JpegMarker::kSOI) {
44     image_count_++;
45     image_mpf_count_.push_back(0);
46     image_xmp_apple_depth_count_.push_back(0);
47     image_xmp_apple_matte_count_.push_back(0);
48     most_recent_soi_marker_range_ =
49         DataRange(segment.GetBegin(), segment.GetBegin() + JpegMarker::kLength);
50   } else if (marker.GetType() == JpegMarker::kEOI) {
51     if (most_recent_soi_marker_range_.IsValid()) {
52       DataRange image_range(most_recent_soi_marker_range_.GetBegin(),
53                             segment.GetBegin() + JpegMarker::kLength);
54       jpeg_info_.AddImageRange(image_range);
55       // This image range might represent the Apple depth or matte image if
56       // other info indicates such an image is in progress and the apple image
57       // range has not yet been set.
58       if (HasAppleDepth() && !jpeg_info_.GetAppleDepthImageRange().IsValid()) {
59         jpeg_info_.SetAppleDepthImageRange(image_range);
60       }
61       if (HasAppleMatte() && !jpeg_info_.GetAppleMatteImageRange().IsValid()) {
62         jpeg_info_.SetAppleMatteImageRange(image_range);
63       }
64       if (image_count_ >= image_limit_) {
65         scanner->SetDone();
66       }
67     }
68   } else if (marker.GetType() == JpegMarker::kAPP0) {
69     // APP0/JFIF segments are interesting.
70     if (image_count_ > 0 && IsJfifSegment(segment)) {
71       const auto& data_range = segment.GetDataRange();
72       JpegSegmentInfo segment_info(image_count_ - 1, data_range, kJfif);
73       MaybeCaptureSegmentBytes(kJfif, segment, segment_info.GetMutableBytes());
74       jpeg_info_.AddSegmentInfo(segment_info);
75     }
76   } else if (marker.GetType() == JpegMarker::kAPP2) {
77     // APP2/MPF segments. JPEG files with Apple depth information have this
78     // segment in the primary (first) image of the file, but note their presence
79     // where ever they are found.
80     if (image_count_ > 0 && IsMpfSegment(segment)) {
81       ++image_mpf_count_[image_count_ - 1];
82       const auto& data_range = segment.GetDataRange();
83       JpegSegmentInfo segment_info(image_count_ - 1, data_range, kMpf);
84       MaybeCaptureSegmentBytes(kMpf, segment, segment_info.GetMutableBytes());
85       jpeg_info_.AddSegmentInfo(segment_info);
86     }
87   } else if (marker.GetType() == JpegMarker::kAPP1) {
88     // APP1/XMP segments. Both Apple depth and GDepthV1 image formats have
89     // APP1/XMP segments with important information in them. There are two types
90     // of XMP segments, a primary one (that starts with kXmpId) and an extended
91     // one (that starts with kExtendedXmpId). Apple depth information is only in
92     // the former, while GDepthV1/GImageV1 information is in both.
93     if (IsPrimaryXmpSegment(segment)) {
94       // The primary XMP segment in a non-primary image (i.e., not the first
95       // image in the file) may contain Apple depth/matte information.
96       if (image_count_ > 1 && HasId(segment, kXmpAppleDepthId)) {
97         ++image_xmp_apple_depth_count_[image_count_ - 1];
98       } else if (image_count_ > 1 && HasId(segment, kXmpAppleMatteId)) {
99         ++image_xmp_apple_matte_count_[image_count_ - 1];
100       } else if (image_count_ == 1 && (HasId(segment, kXmpGDepthV1Id) ||
101                                        HasId(segment, kXmpGImageV1Id))) {
102         // The primary XMP segment in the primary image may contain GDepthV1
103         // and/or GImageV1 data.
104         SetPrimaryXmpGuid(segment);
105         SetXmpMimeType(segment, JpegXmpInfo::kGDepthInfoType);
106         SetXmpMimeType(segment, JpegXmpInfo::kGImageInfoType);
107       }
108     } else if (image_count_ == 1 && IsExtendedXmpSegment(segment)) {
109       // The extended XMP segment in the primary image may contain GDepth and/or
110       // GImage data.
111       if (HasMatchingExtendedXmpGuid(segment)) {
112         gdepth_info_builder_.ProcessSegment(segment);
113         gimage_info_builder_.ProcessSegment(segment);
114       }
115     } else if (image_count_ > 0 && IsExifSegment(segment)) {
116       const auto& data_range = segment.GetDataRange();
117       JpegSegmentInfo segment_info(image_count_ - 1, data_range, kExif);
118       MaybeCaptureSegmentBytes(kExif, segment, segment_info.GetMutableBytes());
119       jpeg_info_.AddSegmentInfo(segment_info);
120     }
121   }
122 }
123 
Finish(JpegScanner * scanner)124 void JpegInfoBuilder::Finish(JpegScanner* scanner) {
125   jpeg_info_.SetSegmentDataRanges(
126       JpegXmpInfo::kGDepthInfoType,
127       gdepth_info_builder_.GetPropertySegmentRanges());
128   jpeg_info_.SetSegmentDataRanges(
129       JpegXmpInfo::kGImageInfoType,
130       gimage_info_builder_.GetPropertySegmentRanges());
131 }
132 
HasAppleDepth() const133 bool JpegInfoBuilder::HasAppleDepth() const {
134   if (image_count_ > 1 && image_mpf_count_[0]) {
135     for (size_t image = 1; image < image_xmp_apple_depth_count_.size();
136          ++image) {
137       if (image_xmp_apple_depth_count_[image]) {
138         return true;
139       }
140     }
141   }
142   return false;
143 }
144 
HasAppleMatte() const145 bool JpegInfoBuilder::HasAppleMatte() const {
146   if (image_count_ > 1 && image_mpf_count_[0]) {
147     for (size_t image = 1; image < image_xmp_apple_matte_count_.size();
148          ++image) {
149       if (image_xmp_apple_matte_count_[image]) {
150         return true;
151       }
152     }
153   }
154   return false;
155 }
156 
IsPrimaryXmpSegment(const JpegSegment & segment) const157 bool JpegInfoBuilder::IsPrimaryXmpSegment(const JpegSegment& segment) const {
158   size_t location = segment.GetPayloadDataLocation();
159   return segment.BytesAtLocationStartWith(location, kXmpId);
160 }
161 
IsExtendedXmpSegment(const JpegSegment & segment) const162 bool JpegInfoBuilder::IsExtendedXmpSegment(const JpegSegment& segment) const {
163   size_t location = segment.GetPayloadDataLocation();
164   return segment.BytesAtLocationStartWith(location, kXmpExtendedId);
165 }
166 
IsMpfSegment(const JpegSegment & segment) const167 bool JpegInfoBuilder::IsMpfSegment(const JpegSegment& segment) const {
168   size_t payload_data_location = segment.GetPayloadDataLocation();
169   return segment.BytesAtLocationStartWith(payload_data_location, kMpf);
170 }
171 
IsExifSegment(const JpegSegment & segment) const172 bool JpegInfoBuilder::IsExifSegment(const JpegSegment& segment) const {
173   size_t payload_data_location = segment.GetPayloadDataLocation();
174   return segment.BytesAtLocationStartWith(payload_data_location, kExif);
175 }
176 
IsJfifSegment(const JpegSegment & segment) const177 bool JpegInfoBuilder::IsJfifSegment(const JpegSegment& segment) const {
178   size_t payload_data_location = segment.GetPayloadDataLocation();
179   return segment.BytesAtLocationStartWith(payload_data_location, kJfif);
180 }
181 
MaybeCaptureSegmentBytes(const std::string & type,const JpegSegment & segment,std::vector<Byte> * bytes) const182 void JpegInfoBuilder::MaybeCaptureSegmentBytes(const std::string& type,
183                                                const JpegSegment& segment,
184                                                std::vector<Byte>* bytes) const {
185   if (capture_segment_bytes_types_.count(type) == 0) {
186     return;
187   }
188   bytes->clear();
189   bytes->reserve(segment.GetLength());
190   size_t segment_begin = segment.GetBegin();
191   size_t segment_end = segment.GetEnd();
192   for (size_t location = segment_begin; location < segment_end; ++location) {
193     ValidatedByte validated_byte = segment.GetValidatedByte(location);
194     if (!validated_byte.is_valid) {
195       bytes->clear();
196       return;
197     }
198     bytes->emplace_back(validated_byte.value);
199   }
200 }
201 
HasMatchingExtendedXmpGuid(const JpegSegment & segment) const202 bool JpegInfoBuilder::HasMatchingExtendedXmpGuid(
203     const JpegSegment& segment) const {
204   if (primary_xmp_guid_.empty()) {
205     return false;
206   }
207   if (segment.GetLength() <= kXmpExtendedHeaderSize) {
208     return false;
209   }
210   size_t start = segment.GetPayloadDataLocation() + sizeof(kXmpExtendedId);
211   return segment.BytesAtLocationStartWith(start, primary_xmp_guid_.c_str());
212 }
213 
HasId(const JpegSegment & segment,const char * id) const214 bool JpegInfoBuilder::HasId(const JpegSegment& segment, const char* id) const {
215   return segment.BytesAtLocationContain(segment.GetPayloadDataLocation(), id);
216 }
217 
SetPrimaryXmpGuid(const JpegSegment & segment)218 void JpegInfoBuilder::SetPrimaryXmpGuid(const JpegSegment& segment) {
219   primary_xmp_guid_ = segment.ExtractXmpPropertyValue(
220       segment.GetPayloadDataLocation(), kXmpHasExtendedId);
221 }
222 
SetXmpMimeType(const JpegSegment & segment,JpegXmpInfo::Type xmp_info_type)223 void JpegInfoBuilder::SetXmpMimeType(const JpegSegment& segment,
224                                      JpegXmpInfo::Type xmp_info_type) {
225   string property_name = JpegXmpInfo::GetMimePropertyName(xmp_info_type);
226   jpeg_info_.SetMimeType(xmp_info_type, segment.ExtractXmpPropertyValue(
227                                             segment.GetPayloadDataLocation(),
228                                             property_name.c_str()));
229 }
230 
231 }  // namespace image_io
232 }  // namespace photos_editing_formats
233