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