summaryrefslogtreecommitdiff
path: root/includes/image_io/jpeg
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2018-11-15 16:07:46 -0800
committerEino-Ville Talvala <etalvala@google.com>2018-11-15 16:07:46 -0800
commit2d6d3250dcb304c8ad081dedc8eef6ea48fd669d (patch)
tree68cc8d5a9bf5a558f46025d740c47cb292eea9f0 /includes/image_io/jpeg
parent840fc3b66a9e6593d542ada6fe14d91107fab98d (diff)
downloadimage_io-2d6d3250dcb304c8ad081dedc8eef6ea48fd669d.tar.gz
Initial commit of libimage_io
Image_io is a library for manipulating image files, especially XMP metadata within them. Test: m libimage_io Bug: 109735087 Bug: 119211681 Change-Id: I657f307be0459fe40154806c7cd388b97bcb0ea5
Diffstat (limited to 'includes/image_io/jpeg')
-rw-r--r--includes/image_io/jpeg/jpeg_apple_depth_builder.h102
-rw-r--r--includes/image_io/jpeg/jpeg_image_extractor.h73
-rw-r--r--includes/image_io/jpeg/jpeg_info.h153
-rw-r--r--includes/image_io/jpeg/jpeg_info_builder.h133
-rw-r--r--includes/image_io/jpeg/jpeg_marker.h86
-rw-r--r--includes/image_io/jpeg/jpeg_scanner.h100
-rw-r--r--includes/image_io/jpeg/jpeg_segment.h178
-rw-r--r--includes/image_io/jpeg/jpeg_segment_builder.h140
-rw-r--r--includes/image_io/jpeg/jpeg_segment_info.h85
-rw-r--r--includes/image_io/jpeg/jpeg_segment_lister.h35
-rw-r--r--includes/image_io/jpeg/jpeg_segment_processor.h44
-rw-r--r--includes/image_io/jpeg/jpeg_xmp_data_extractor.h66
-rw-r--r--includes/image_io/jpeg/jpeg_xmp_info.h92
-rw-r--r--includes/image_io/jpeg/jpeg_xmp_info_builder.h42
14 files changed, 1329 insertions, 0 deletions
diff --git a/includes/image_io/jpeg/jpeg_apple_depth_builder.h b/includes/image_io/jpeg/jpeg_apple_depth_builder.h
new file mode 100644
index 0000000..7f5c595
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_apple_depth_builder.h
@@ -0,0 +1,102 @@
+#ifndef IMAGE_IO_JPEG_JPEG_APPLE_DEPTH_BUILDER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_APPLE_DEPTH_BUILDER_H_ // NOLINT
+
+#include <vector>
+
+#include "image_io/base/data_destination.h"
+#include "image_io/base/data_range.h"
+#include "image_io/base/data_source.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// Builds an Apple depth file containing a (possibly scaled down) primary image
+/// and original depth image.
+class JpegAppleDepthBuilder {
+ public:
+ JpegAppleDepthBuilder()
+ : primary_image_data_source_(nullptr),
+ depth_image_data_source_(nullptr),
+ data_destination_(nullptr) {}
+
+ /// @param primary_image_data_source The data source containing the primary
+ /// image. The builder uses the first image in this data source.
+ /// @param depth_image_data_source The data source containing the depth image.
+ /// The builder finds the depth image using a JpegInfoBuilder and the
+ /// JpegInfo::GetAppleDepthImageRange() function. Consequently, this
+ /// image source must refer a valid Apple depth file.
+ /// @param data_destination The data destination for the combined primary
+ /// and depth images.
+ /// @return Whether the building and transfer was successful.
+ bool Run(DataSource* primary_image_data_source,
+ DataSource* depth_image_data_source,
+ DataDestination* data_destination);
+
+ private:
+ /// Gets the data associated with the primary image its data source.
+ /// @return Whether the primary image data was gotten successfully.
+ bool GetPrimaryImageData();
+
+ /// Gets the data associated with the depth image from its data source.
+ /// @return Whether the depth image data was gotten successfully.
+ bool GetDepthImageData();
+
+ /// Transfers the primary image from its data source to the data destination,
+ /// adding and transforming the jpeg segments it needs to make the resulting
+ /// data destination a valid Apple depth file.
+ /// @return Whether the transfer was successful or not.
+ bool TransferPrimaryImage();
+
+ /// Transfers the depth image from its data source to the data destination.
+ /// @return Whether the transfer was successful or not.
+ bool TransferDepthImage();
+
+ /// Modifies the existing primary Jfif segment to contain the information
+ /// needed for a valid Apple depth file, and transfers the result to the data
+ /// destination.
+ /// @param jfif_length_delta The increased size of the Jfif segment.
+ /// @return Whether the transfer was successful or not.
+ bool TransferNewJfifSegment(size_t *jfif_length_delta);
+
+ /// Creates a new Mpf segment needed for a valid Apple depth file and
+ /// transfers the result to the data destination.
+ /// @param jfif_length_delta The increased size of the Jfif segment.
+ /// @return Whether the transfer was successful or not.
+ bool TransferNewMpfSegment(size_t jfif_length_delta);
+
+ /// @param data_source The data source from which to transfer bytes to the
+ /// data destination.
+ /// @param data_range The data range in the data source to transfer.
+ bool TransferData(DataSource *data_source, const DataRange& data_range);
+
+ /// The data source containing the primary image.
+ DataSource* primary_image_data_source_;
+
+ /// The data source representing a valid Apple depth file.
+ DataSource* depth_image_data_source_;
+
+ /// The final destination of the new Apple depth data.
+ DataDestination* data_destination_;
+
+ /// The range in the primary image data source containing the primary image.
+ DataRange primary_image_range_;
+
+ /// The range in the primary image data source containing the primary image's
+ /// Jfif segment.
+ DataRange primary_image_jfif_segment_range_;
+
+ /// The bytes of the primary image's Jfif segment.
+ std::vector<Byte> primary_image_jfif_segment_bytes_;
+
+ /// The range in the primary image data source containing the primary images's
+ /// Mpf segment, or the location at a new Mpf segment should be written.
+ DataRange primary_image_mpf_segment_range_;
+
+ /// The range in the depth image data source containing the depth image.
+ DataRange depth_image_range_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_APPLE_DEPTH_BUILDER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_image_extractor.h b/includes/image_io/jpeg/jpeg_image_extractor.h
new file mode 100644
index 0000000..91237e5
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_image_extractor.h
@@ -0,0 +1,73 @@
+#ifndef IMAGE_IO_JPEG_JPEG_IMAGE_EXTRACTOR_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_IMAGE_EXTRACTOR_H_ // NOLINT
+
+#include "image_io/base/data_destination.h"
+#include "image_io/base/data_source.h"
+#include "image_io/jpeg/jpeg_info.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A class that can make use of the data in a JpegInfo instance to transfer
+/// Apple depth and GDepth/GImage images from a DataSource and ship it to a
+/// DataDestination.
+class JpegImageExtractor {
+ public:
+ /// @param jpeg_info The JpegInfo instance containing depth/image data.
+ /// @param data_source The DataSource from which to transfer depth/image data.
+ JpegImageExtractor(const JpegInfo& jpeg_info, DataSource* data_source)
+ : jpeg_info_(jpeg_info), data_source_(data_source) {}
+
+ /// This function extracts the Apple depth image from the DataSource and sends
+ /// the bytes to the DataDestination.
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if an image was extracted.
+ bool ExtractAppleDepthImage(DataDestination* image_destination);
+
+ /// This function extracts the Apple matte image from the DataSource and sends
+ /// the bytes to the DataDestination.
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if an image was extracted.
+ bool ExtractAppleMatteImage(DataDestination* image_destination);
+
+ /// This function extracts the GDepth type image from the DataSource and
+ /// sends the bytes to the DataDestination.
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if an image was extracted.
+ bool ExtractGDepthImage(DataDestination* image_destination);
+
+ /// This function extracts the GImage type image from the DataSource and
+ /// sends the bytes to the DataDestination.
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if an image was extracted.
+ bool ExtractGImageImage(DataDestination* image_destination);
+
+ private:
+ /// Worker function called for GDepth/GImage type image extraction.
+ /// @param xmp_info_type The type of image to extract.
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if an image was extracted.
+ bool ExtractImage(JpegXmpInfo::Type xmp_info_type,
+ DataDestination* image_destination);
+
+ /// Worker function called for Apple depth/matte type image extraction.
+ /// @param image_range The range of the image data to extract. If invalid,
+ /// the image_destination's StartTransfer/FinishTransfer functions are
+ /// still called, and this function will return true (i.e., zero bytes
+ /// "successfully" transferred).
+ /// @param image_destination The DataDestination to receive the image data.
+ /// @return True if the transfer succeeded.
+ bool ExtractImage(const DataRange& image_range,
+ DataDestination* image_destination);
+
+ /// The jpeg info object contains the location of the Apple and Google images.
+ JpegInfo jpeg_info_;
+
+ /// The data source from which the images are extracted.
+ DataSource* data_source_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_IMAGE_EXTRACTOR_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_info.h b/includes/image_io/jpeg/jpeg_info.h
new file mode 100644
index 0000000..8aedf9e
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_info.h
@@ -0,0 +1,153 @@
+#ifndef IMAGE_IO_JPEG_JPEG_INFO_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_INFO_H_ // NOLINT
+
+#include <vector>
+
+#include "image_io/base/data_range.h"
+#include "image_io/jpeg/jpeg_segment_info.h"
+#include "image_io/jpeg/jpeg_xmp_info.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A class to represent interesting depth and image information in a JPEG file,
+/// and where it is located so that it can be efficiently extracted.
+class JpegInfo {
+ public:
+ JpegInfo() { JpegXmpInfo::InitializeVector(&xmp_info_vector_); }
+ JpegInfo(const JpegInfo&) = default;
+ JpegInfo& operator=(const JpegInfo&) = default;
+
+ /// @return The vector of data ranges indicating the locations of the images.
+ const std::vector<DataRange>& GetImageRanges() const { return image_ranges_; }
+
+ /// @return The vector of interesting segment info structures.
+ const std::vector<JpegSegmentInfo>& GetSegmentInfos() const {
+ return segment_infos_;
+ }
+
+ /// @param image_index The image containing the sought after segment info.
+ /// @param type The type of segment info to get.
+ /// @return The segment info, or one that is invalid if not found.
+ JpegSegmentInfo GetSegmentInfo(size_t image_index,
+ const std::string& type) const {
+ for (const auto& segment_info : GetSegmentInfos()) {
+ if (segment_info.GetImageIndex() == image_index &&
+ segment_info.GetType() == type) {
+ return segment_info;
+ }
+ }
+ return JpegSegmentInfo(0, DataRange(), "");
+ }
+
+ /// @return True if there is Apple depth information.
+ bool HasAppleDepth() const { return apple_depth_image_range_.IsValid(); }
+
+ /// @return True if there is Apple matte information.
+ bool HasAppleMatte() const { return apple_matte_image_range_.IsValid(); }
+
+ /// @return True if there is GDepth type depth information.
+ bool HasGDepth() const {
+ return HasImage(JpegXmpInfo::kGDepthInfoType);
+ }
+
+ /// @return True if there is GImage information.
+ bool HasGImage() const {
+ return HasImage(JpegXmpInfo::kGImageInfoType);
+ }
+
+ /// @return True if there is either Apple or GDepth information.
+ bool HasDepth() const { return HasAppleDepth() || HasGDepth(); }
+
+ /// @return True if there is an extratable image present.
+ bool HasExtractableImage() const {
+ return HasAppleDepth() || HasAppleMatte() || HasGDepth() || HasGImage();
+ }
+
+ /// @param xmp_info_type The type of xmp image information desired.
+ /// @return True if there is information of the given type.
+ bool HasImage(JpegXmpInfo::Type xmp_info_type) const {
+ return !GetSegmentDataRanges(xmp_info_type).empty();
+ }
+
+ /// @return The DataRange where the Apple depth information is located.
+ const DataRange& GetAppleDepthImageRange() const {
+ return apple_depth_image_range_;
+ }
+
+ /// @return The DataRange where the Apple matte information is located.
+ const DataRange& GetAppleMatteImageRange() const {
+ return apple_matte_image_range_;
+ }
+
+ /// @param type The type of Xmp data to get the mime type of.
+ /// @return The mime type for the Xmp data of the given type.
+ std::string GetMimeType(JpegXmpInfo::Type type) const {
+ return xmp_info_vector_[type].GetMimeType();
+ }
+
+ /// @param type The type of Xmp data to get the segment data ranges of.
+ /// @return The segment data ranges containing the Xmp data of the given type.
+ const std::vector<DataRange>& GetSegmentDataRanges(
+ JpegXmpInfo::Type type) const {
+ return xmp_info_vector_[type].GetSegmentDataRanges();
+ }
+
+ /// Adds a DataRange to the vector of image DataRanges.
+ /// @param image_range The data range of an image.
+ void AddImageRange(const DataRange& image_range) {
+ image_ranges_.push_back(image_range);
+ }
+
+ /// Adds a JpegSegmentInfo to the vector of JpegSegmentInfos.
+ /// @param jpeg_segment_info The info structure to add.
+ void AddSegmentInfo(const JpegSegmentInfo& segment_info) {
+ segment_infos_.push_back(segment_info);
+ }
+
+ /// @param data_range The DataRange where Apple depth information is located.
+ void SetAppleDepthImageRange(const DataRange& data_range) {
+ apple_depth_image_range_ = data_range;
+ }
+
+ /// @param data_range The DataRange where Apple matte information is located.
+ void SetAppleMatteImageRange(const DataRange& data_range) {
+ apple_matte_image_range_ = data_range;
+ }
+
+ /// @param type The type of Xmp data to set the mime type of.
+ /// @param mime_type The mime type of the Xmp data.
+ void SetMimeType(JpegXmpInfo::Type type, const std::string& mime_type) {
+ xmp_info_vector_[type].SetMimeType(mime_type);
+ }
+
+ /// @param type The type of Xmp data to set segment data ranges of.
+ /// @param segment_data_ranges The segment that contain the Xmp data.
+ void SetSegmentDataRanges(JpegXmpInfo::Type type,
+ const std::vector<DataRange>& segment_data_ranges) {
+ xmp_info_vector_[type].SetSegmentDataRanges(segment_data_ranges);
+ }
+
+ private:
+ /// The DataRanges of all images.
+ std::vector<DataRange> image_ranges_;
+
+ /// Interesting segment information. Currently information about APP0/JFIF,
+ /// APP1/EXIF and APP2/MPF segments are saved here.
+ std::vector<JpegSegmentInfo> segment_infos_;
+
+ /// The DataRange of the Apple depth information.
+ DataRange apple_depth_image_range_;
+
+ /// The DataRange of the Apple depth information.
+ DataRange apple_matte_image_range_;
+
+ /// A vector holding information about the Xmp segments containing GDepth and
+ /// GImage data.
+ std::vector<JpegXmpInfo> xmp_info_vector_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_INFO_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_info_builder.h b/includes/image_io/jpeg/jpeg_info_builder.h
new file mode 100644
index 0000000..ee4d611
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_info_builder.h
@@ -0,0 +1,133 @@
+#ifndef IMAGE_IO_JPEG_JPEG_INFO_BUILDER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_INFO_BUILDER_H_ // NOLINT
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "image_io/base/data_range.h"
+#include "image_io/jpeg/jpeg_info.h"
+#include "image_io/jpeg/jpeg_segment_processor.h"
+#include "image_io/jpeg/jpeg_xmp_info_builder.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// JpegInfoBuilder is JpegSegmentProcessor that collects the location and type
+/// of depth information in the JPEG file so that subsequent operations can
+/// efficiently maniuplate it.
+class JpegInfoBuilder : public JpegSegmentProcessor {
+ public:
+ JpegInfoBuilder();
+
+ /// @return The JpegInfo with the depth information obtained from the
+ /// scanner as a result of processing the segments it processes.
+ const JpegInfo& GetInfo() const { return jpeg_info_; }
+
+ /// @param image_limit The max number of images to process. By default there
+ /// is no limit on the number of images processed.
+ void SetImageLimit(int image_limit) { image_limit_ = image_limit; }
+
+ /// By default the info builder does not capture the value of the segment in
+ /// the segment infos contained in the @c JpegInfo object. Call this function
+ /// to capture the bytes of the indicated segment types.
+ /// @param type The type of segment info to capture the value of.
+ void SetCaptureSegmentBytes(const std::string& segment_info_type);
+
+ void Start(JpegScanner* scanner) override;
+ void Process(JpegScanner* scanner, const JpegSegment& segment) override;
+ void Finish(JpegScanner* scanner) override;
+
+ private:
+ /// @return True if the data members indicate Apple depth is present.
+ bool HasAppleDepth() const;
+
+ /// @return True if the data members indicate Apple matte is present.
+ bool HasAppleMatte() const;
+
+ /// @return True if the segment is a primary Xmp segment.
+ bool IsPrimaryXmpSegment(const JpegSegment& segment) const;
+
+ /// @return True if the segment is an extended Xmp segment.
+ bool IsExtendedXmpSegment(const JpegSegment& segment) const;
+
+ /// @return True if the segment is an Mpf segment.
+ bool IsMpfSegment(const JpegSegment& segment) const;
+
+ /// @return True if the segment is an Exif segment.
+ bool IsExifSegment(const JpegSegment& segment) const;
+
+ /// @return True if the segment is an Jfif segment.
+ bool IsJfifSegment(const JpegSegment& segment) const;
+
+ /// Captures the segment bytes into the a JpegSegmentInfo's byte vector if
+ /// the SetCaptureSegmentBytes() has been called for the segment info type.
+ /// @param type The type of segment info being processed.
+ /// @param segment The segment being processed.
+ /// @param bytes A vector to hold the segment bytes.
+ void MaybeCaptureSegmentBytes(const std::string& type,
+ const JpegSegment& segment,
+ std::vector<Byte>* bytes) const;
+
+ /// @return True if the segment's extended xmp guid matches the one from the
+ /// primary xmp segment.
+ bool HasMatchingExtendedXmpGuid(const JpegSegment& segment) const;
+
+ /// @return True if the segment contains the given id.
+ bool HasId(const JpegSegment& segment, const char* id) const;
+
+ /// Sets the primary segment guid value using properties in the given segment.
+ /// @param The segment from which to obtain the primary xmp guid value.
+ void SetPrimaryXmpGuid(const JpegSegment& segment);
+
+ /// Sets the Xmp mime type using property values in the given segment.
+ /// @param The segment from which to obtain the mime property value.
+ /// @param xmp_info_type The type of xmp data that determines the mime
+ /// property name to look for.
+ void SetXmpMimeType(const JpegSegment& segment,
+ JpegXmpInfo::Type xmp_info_type);
+
+ /// The limit on the number of images to process. After this many images have
+ /// been found, the Process() function will tell the JpegScanner to stop.
+ int image_limit_;
+
+ /// The number of images encountered in the JPEG file so far.
+ int image_count_;
+
+ /// The number of APP2/MPF segments encountered per image. One criterial used
+ /// to determine if Apple depth data is present is that the first image has
+ /// an APP2/MPF segment.
+ std::vector<int> image_mpf_count_;
+
+ /// The number of APP1/XMP segments encountered per image. Another criteria
+ /// used to determine if Apple depth data is present is that the second or
+ /// following image contains one of these segments.
+ std::vector<int> image_xmp_apple_depth_count_;
+
+ /// The number of APP1/XMP segments encountered per image. Another criteria
+ /// used to determine if Apple matte data is present is that the second or
+ /// following image contains one of these segments.
+ std::vector<int> image_xmp_apple_matte_count_;
+
+ /// The DataRange of the most recent SOI type segment. This is used to compute
+ /// the range of the image that represents the Apple depth data.
+ DataRange most_recent_soi_marker_range_;
+
+ /// The GUID value of the APP1/XMP segments that contain GDepth/GImage data.
+ std::string primary_xmp_guid_;
+
+ /// Builder helpers for gdepth and gimage xmp type segments.
+ JpegXmpInfoBuilder gdepth_info_builder_;
+ JpegXmpInfoBuilder gimage_info_builder_;
+
+ /// The collected data describing the type/location of data in the JPEG file.
+ JpegInfo jpeg_info_;
+
+ /// The types of the segment info type to capture the bytes of.
+ std::set<std::string> capture_segment_bytes_types_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_INFO_BUILDER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_marker.h b/includes/image_io/jpeg/jpeg_marker.h
new file mode 100644
index 0000000..507382d
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_marker.h
@@ -0,0 +1,86 @@
+#ifndef IMAGE_IO_JPEG_JPEG_MARKER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_MARKER_H_ // NOLINT
+
+#include <bitset>
+#include <string>
+
+#include "image_io/base/types.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// The size of the array that would be needed to reference all marker types.
+const size_t kJpegMarkerArraySize = 256;
+
+/// A JpegMarker begins each JpegSegment in a JPEG file. The first byte of a
+/// marker is 0xFF, and the second byte is the marker type value. Bytes with
+/// values 0x00 and 0xFF indicate not a JpegMarker, but a zero byte or fill
+/// byte, respectively. That is the sequence FF00 must be interpreted as a
+/// single byte with a 0 value. The specification says that multiple fill bytes
+/// may appear before a valid marker start: FFFFFFDA - the leading FFFF should
+/// be ignored.
+class JpegMarker {
+ public:
+ /// The length of the marker in the JPEG file. One byte for the 0xFF value,
+ /// and one byte for the marker type.
+ static const size_t kLength = 2;
+
+ /// The offset from the start of the JpegMarker that contains the marker type.
+ static const size_t kTypeOffset = 1;
+
+ /// The special byte value that may start a marker.
+ static const Byte kStart = 0xFF;
+
+ /// Special marker type values referenced elsewhere in the code.
+ static const Byte kZERO = 0;
+ static const Byte kSOS = 0xDA;
+ static const Byte kSOI = 0xD8;
+ static const Byte kEOI = 0xD9;
+ static const Byte kAPP0 = 0xE0;
+ static const Byte kAPP1 = 0xE1;
+ static const Byte kAPP2 = 0xE2;
+ static const Byte kFILL = 0xFF;
+
+ /// A set of bits, one for each type of marker.
+ using Flags = std::bitset<kJpegMarkerArraySize>;
+
+ /// Creates a JpegMarker with the given type value.
+ explicit JpegMarker(Byte type) : type_(type) {}
+
+ JpegMarker() = delete;
+
+ /// Not all byte values are used to represent markers. Bytes with values 0x00
+ /// and 0xFF indicate a zero byte or fill byte, respectively.
+ /// @return Whether this is a valid marker.
+ bool IsValid() const { return type_ != kZERO && type_ != kFILL; }
+
+ /// @return The type of the marker.
+ Byte GetType() const { return type_; }
+
+ /// @return The name of the marker type.
+ const std::string GetName() const;
+
+ /// @param prefix A prefix for the returned string.
+ /// @return The <prefix>XX hex string representation of the type.
+ const std::string GetHexString(const std::string& prefix) const;
+
+ /// Some markers have two extra bytes that indicate the size of the segment's
+ /// data payload. See https://www.w3.org/Graphics/JPEG/itu-t81.pdf, Table B-2.
+ /// @return Whether this marker type has such a variable length payload.
+ bool HasVariablePayloadSize() const;
+
+ /// Some markers are delimiters in an otherwise continuous stream of bytes in
+ /// the JPEG file. See https://www.w3.org/Graphics/JPEG/itu-t81.pdf, Section
+ /// B.2.1.
+ /// @return Whether this is an entropy segment delimiter marker.
+ bool IsEntropySegmentDelimiter() const;
+
+ private:
+ /// The type value of the marker.
+ Byte type_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_MARKER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_scanner.h b/includes/image_io/jpeg/jpeg_scanner.h
new file mode 100644
index 0000000..0ab0488
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_scanner.h
@@ -0,0 +1,100 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SCANNER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SCANNER_H_ // NOLINT
+
+#include <memory>
+
+#include "image_io/base/data_segment.h"
+#include "image_io/base/data_source.h"
+#include "image_io/jpeg/jpeg_marker.h"
+#include "image_io/jpeg/jpeg_segment_processor.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// JpegScanner reads DataSegments from a DataSource, finds interesting
+/// JpegSegments and passes them on to a JpegSegmentProcessor for further
+/// examination.
+class JpegScanner {
+ public:
+ JpegScanner()
+ : data_source_(nullptr),
+ segment_processor_(nullptr),
+ current_location_(0),
+ done_(false),
+ has_error_(false) {}
+
+ /// Called to start and run the scanner.
+ /// @param data_source The DataSource from which to obtain DataSegments.
+ /// @param segment_processor The processor of the JpegSegment instances.
+ void Run(DataSource* data_source, JpegSegmentProcessor* segment_processor);
+
+ /// If the JpegSegmentProcessor determines that it has seen enough JpegSegment
+ /// instances, it can call this function to terminate the scanner prematurely.
+ void SetDone() { done_ = true; }
+
+ /// @return True if the done flag was set by SetDone(), else false.
+ bool IsDone() const { return done_; }
+
+ /// @return True if the scanner encountered errors.
+ bool HasError() const { return has_error_; }
+
+ /// @return The DataSource from which DataSegments are being read.
+ DataSource* GetDataSource() const { return data_source_; }
+
+ /// JpegSegmentProcessor instances can call this function to inform the
+ /// scanner about the types of JpegSegment instances it is interested in.
+ /// The JpegScanner will not send any uninteresting segments to the processor.
+ void UpdateInterestingMarkerFlags(const JpegMarker::Flags& marker_flags) {
+ interesting_marker_flags_ = marker_flags;
+ }
+
+ private:
+ /// Called from the Run() function to do the heavy lifting.
+ void FindAndProcessSegments();
+
+ /// @param marker The marker of the JpegSegment under construction.
+ /// @param begin_location The start of the JpegSegment under construction.
+ /// @return The size of the segment payload of given marker type that starts
+ /// at the specified location.
+ size_t GetPayloadSize(const JpegMarker& marker, size_t begin_location);
+
+ /// @return The validated byte value at the given location.
+ ValidatedByte GetValidatedByte(size_t location);
+
+ /// Calls GetValidatedByte() and returns its value if the byte is valid, else
+ /// sets the get_byte_error_ flag.
+ /// @return the byte value at the given location, or 0 if the byte is invalid.
+ Byte GetByte(size_t location);
+
+ /// Asks the DataSource for the next DataSegment.
+ void GetNextSegment();
+
+ private:
+ /// The DataSource from which DataSegments are obtained.
+ DataSource* data_source_;
+
+ /// The JpegSegmentProcessor to which JpegSegments are sent.
+ JpegSegmentProcessor* segment_processor_;
+
+ /// The JpegSegment types of interest to the JpegSegmentProcessor.
+ JpegMarker::Flags interesting_marker_flags_;
+
+ /// Depending on the DataSource, a given JpegSegment may span up to two
+ /// DataSegments. These are they.
+ std::shared_ptr<DataSegment> current_segment_;
+ std::shared_ptr<DataSegment> next_segment_;
+
+ /// The current location of the scanner in the DataSource.
+ size_t current_location_;
+
+ /// A flag that indicates the scanner is done, naturally or prematurely.
+ bool done_;
+
+ /// A flag that indicates an error occurred while getting Byte data.
+ bool has_error_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SCANNER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_segment.h b/includes/image_io/jpeg/jpeg_segment.h
new file mode 100644
index 0000000..c4a79fd
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_segment.h
@@ -0,0 +1,178 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SEGMENT_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SEGMENT_H_ // NOLINT
+
+#include "image_io/base/data_range.h"
+#include "image_io/base/data_segment.h"
+#include "image_io/jpeg/jpeg_marker.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A JpegSegment is an entity in a JPEG file that starts with a JpegMarker and
+/// is followed by zero or more payload bytes. The JpegSegment has a DataRange
+/// that indicates the position of the segment in the originating DataSource.
+/// A JpegScanner obtains DataSegment instances from a DataSource in such a way
+/// that it can guarantee that a JpegSegment will span at most two DataSegment
+/// instances. Clients of JpegSegment need not be concerned with the number of
+/// underlying DataSegments if they use the member functions defined here to
+/// access the segment's bytes.
+class JpegSegment {
+ public:
+ /// If a JpegSegment has a variable length data payload, the payload data is
+ /// located at this offset from the start of the payload.
+ static constexpr size_t kVariablePayloadDataOffset = 2;
+
+ /// Constructs a JpegSegment starting and ending at the indicated points in
+ /// the given DataSegment instances, the second of which may be null.
+ /// @param begin The start of JpegSegment range.
+ /// @param end The end of JpegSegment range.
+ /// @param begin_segment The DataSegment that contains the begin location of
+ /// the JpegSegment and the end if the end_segment is null.
+ /// @param end_segment The DataSegment that contains the end location of the
+ /// JpegSegment if it is not null.
+ JpegSegment(size_t begin, size_t end, const DataSegment* begin_segment,
+ const DataSegment* end_segment)
+ : data_range_(begin, end),
+ begin_segment_(begin_segment),
+ end_segment_(end_segment){}
+ ~JpegSegment() = default;
+
+ /// @return The DataRange of the data in the segment.
+ const DataRange& GetDataRange() const { return data_range_; }
+
+ /// @return The begin location of the segment's data range.
+ size_t GetBegin() const { return data_range_.GetBegin(); }
+
+ /// @return The end location of the segment's data range.
+ size_t GetEnd() const { return data_range_.GetEnd(); }
+
+ /// @return The length of the segment's data range.
+ size_t GetLength() const { return data_range_.GetLength(); }
+
+ /// @return True if the segment's range contains the location, else false.
+ bool Contains(size_t location) const {
+ return data_range_.Contains(location);
+ }
+
+ /// @return The location of the segment's JpegMarker.
+ size_t GetMarkerLocation() const { return GetBegin(); }
+
+ /// @return The location of the segment's payload, which includes the payload
+ /// length if applicable for the type of segment.
+ size_t GetPayloadLocation() const { return GetBegin() + JpegMarker::kLength; }
+
+ /// @return The location of the segment's payload's data.
+ size_t GetPayloadDataLocation() const {
+ return GetMarker().HasVariablePayloadSize()
+ ? GetPayloadLocation() + kVariablePayloadDataOffset
+ : GetPayloadLocation();
+ }
+
+ /// @param The location at which to obtain the byte value.
+ /// @return The validated byte value at the location, or 0/false if the
+ /// segment's range does not contain the location.
+ ValidatedByte GetValidatedByte(size_t location) const {
+ return DataSegment::GetValidatedByte(location, begin_segment_,
+ end_segment_);
+ }
+
+ /// @return The payload size or zero if the segment's marker indicates the
+ /// segment does not have a payload. The payload size includes the two
+ /// bytes that encode the length of the payload. I.e., the payload data
+ /// size is two less than the value returned by this function.
+ size_t GetVariablePayloadSize() const;
+
+ /// @param location The start location of the compare operation.
+ /// @param str The string to compare the bytes with.
+ /// @return True if the segment's bytes at the given location equals the str.
+ bool BytesAtLocationStartWith(size_t location, const char* str) const;
+
+ /// @param location The start location of the search operation.
+ /// @param str The string to search for.
+ /// @return True if the segment's contains the string, starting at location.
+ bool BytesAtLocationContain(size_t location, const char* str) const;
+
+ /// @param start_location The location at which to start the search.
+ /// @param value The byte value to search for.
+ /// @return The location in the segment's bytes of the next occurrence of the
+ /// given byte value, starting at the indicated location, or the segment's
+ /// range's GetEnd() location if not found.
+ size_t Find(size_t start_location, Byte value) const;
+
+ /// @param start_location The location at which to start the search.
+ /// @param str The string to search for.
+ /// @return the location in the segment's bytes of the next occurrence of the
+ /// given string value, starting at the indicated location, or the
+ /// segment's range's GetEnd() location if not found.
+ size_t Find(size_t location, const char* str) const;
+
+ /// XMP property names have the syntax property_name="property_value".
+ /// @param segment The segment in which to look for the property name/value.
+ /// @param start_location Where to start looking for the property name.
+ /// @param property_name The name of the property to look for.
+ /// @return The string value associated with the xmp property name, or an
+ /// empty string if the property was not found.
+ std::string ExtractXmpPropertyValue(size_t start_location,
+ const char* property_name) const;
+
+ /// XMP property names have the syntax property_name="property_value".
+ /// @start_location The location in the segment to begin looking for the
+ /// property_name=" syntax.
+ /// @return The location of the next byte following the quote, or GetEnd() if
+ /// the property_name=" syntax was not found.
+ size_t FindXmpPropertyValueBegin(size_t start_location,
+ const char* property_name) const;
+
+ /// XMP property names have the syntax property_name="property_value".
+ /// @start_location The location in the segment to begin looking for the final
+ /// quote of the property value.
+ /// @return The location of quote that terminates the property_value, or
+ /// GetEnd() if the final quote was not found.
+ size_t FindXmpPropertyValueEnd(size_t start_location) const;
+
+ /// @param The DataRange to use to extract a string from the segment's bytes.
+ /// @return The string extracted from the segment at locations indicated by
+ /// the data_range, or an empty string if the data_range is not contained
+ /// in the segment's range, or any invalid or zero bytes are encountered.
+ std::string ExtractString(const DataRange& data_range) const;
+
+ /// @return the JpegMarker of this segment.
+ JpegMarker GetMarker() const {
+ size_t marker_type_location = GetMarkerLocation() + 1;
+ // An invalid ValidatedByte has a value of 0, and a JpegMarker with a 0
+ // type value is invalid, so its ok to just grab the ValidatedByte's value.
+ return JpegMarker(GetValidatedByte(marker_type_location).value);
+ }
+
+ /// Fills two strings with byte_count bytes from the start of the segment's
+ /// payload in a form suitable for creating a "hex dump" of the segment. Note
+ /// that if the jpeg segment has a entropy delimiter type marker, there is
+ /// technically no payload to dump. However in this case, as long as a valid
+ /// byte can be obtained from the jpeg segment's underlying data segments, a
+ /// byte value will be dumped to the strings.
+ /// @param byte_count The number of bytes to dump from the segment's payload.
+ /// @param hex_string A string that will be at most 2 * byte_count in length
+ /// that will contain the hex values of the bytes.
+ /// @param ascii_string A string that will be at most byte_count in length
+ /// that will contain the printable character of the bytes, or a '.' for
+ /// non-printable byte values.
+ void GetPayloadHexDumpStrings(size_t byte_count, std::string* hex_string,
+ std::string* ascii_string) const;
+
+ private:
+ /// The DataRange of the JpegSegment.
+ DataRange data_range_;
+
+ /// The DataSegment that contains the begin of the range and possibly the
+ /// end. This DataSegment will never be null.
+ const DataSegment* begin_segment_;
+
+ /// The DataSegment, that if not null, will contain the end location of the
+ /// JPegSegment's DataRange.
+ const DataSegment* end_segment_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SEGMENT_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_segment_builder.h b/includes/image_io/jpeg/jpeg_segment_builder.h
new file mode 100644
index 0000000..e8e714f
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_segment_builder.h
@@ -0,0 +1,140 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SEGMENT_BUILDER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SEGMENT_BUILDER_H_ // NOLINT
+
+#include <string>
+#include <vector>
+
+#include "image_io/base/byte_buffer.h"
+#include "image_io/jpeg/jpeg_xmp_info.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A helper to assemble the data in a JpegSegment. Currently this is only used
+/// for testing purposes, but in the future may prove useful in the image_io
+/// library itself.
+class JpegSegmentBuilder {
+ public:
+ /// Sets the payload size value of the JpegSegment data in the byte buffer.
+ /// This function assumes that the byte buffer contains the data for exactly
+ /// one JpegSegment, and that the segment type has a variable payload size.
+ /// The byte buffer must have a size in the range [4:65535] for this to work.
+ /// @param byte_buffer The data defining the JpegSegment.
+ /// @return Whether the byte buffer's size was valid and the payload size set.
+ static bool SetPayloadSize(ByteBuffer* byte_buffer);
+
+ /// @return The vector of ByteData.
+ const std::vector<ByteData>& GetByteData() const { return byte_data_; }
+
+ /// @return The concatenated string values of all byte data, or an empty
+ /// string if there are invalid byte data entries. Note that the string
+ /// may have embedded null characters if there are any kAscii0 type
+ /// byte data elements present.
+ std::string GetByteDataValues() const;
+
+ /// Adds the byte data to the vector.
+ /// @param byte_data The byte data to add.
+ void AddByteData(const ByteData& byte_data) {
+ byte_data_.push_back(byte_data);
+ }
+
+ /// Adds a segment marker of the given type and payload size.
+ /// @param marker_type The type of segment marker to add.
+ /// @param size The size of the payload if the marker has a variable
+ /// size payload. This value must be in the range [2:65535], although no
+ /// check is performed to ensure that is the case.
+ void AddMarkerAndSize(Byte marker_type, size_t size);
+
+ /// Adds a segment marker of the given type, and "0000" placeholder value if
+ /// the type has a variable payload size. The SetSizePlaceholder() function
+ /// can be called later to set the actual size of the segment.
+ /// @param marker_type The type of segment marker to add.
+ /// @return The index in the vector of ByteData where the marker was added.
+ size_t AddMarkerAndSizePlaceholder(Byte marker_type);
+
+ /// Replacess the size of the segment marker that was previously added using
+ /// the AddMarkerAndSizePlaceholder() function. The first two bytes of the
+ /// ByteData at the given index must represent a valid JpegMarker that has
+ /// a variable length payload size.
+ /// @param index The index in the vector of ByteData set the size of.
+ /// @param size The size of the segment, including the size field itself.
+ /// This value must be in the range [2:65535].
+ /// @return Whether the size was set successfully.
+ bool ReplaceSizePlaceholder(size_t index, size_t size);
+
+ /// Adds the bytes that define an XMP header.
+ /// @param xmp_guid The guid value of the XMP data. If this value is not 32
+ /// bytes long, it is either truncated or extended with 0s.
+ void AddExtendedXmpHeader(const std::string& xmp_guid);
+
+ /// Adds the XMP syntax that appears at the start of an XMP segment. This
+ /// syntax appears after the XMP header in a segment, so this function should
+ /// be called after the AddExtendedXmpHeader() function.
+ void AddXmpMetaPrefix();
+
+ /// Adds the XMP syntax that appears at the end of an XMP segment. This syntax
+ /// finishes the XMP data, so it should be the last function called when
+ /// assembling the data for such a segment.
+ void AddXmpMetaSuffix();
+
+ /// Adds the RDF prefix that appears within the body of an XMP segment. This
+ /// syntax should be added before any XMP property names and values are added.
+ void AddRdfPrefix();
+
+ /// Adds the RDF suffix that appears within the body of an XMP segment. This
+ /// syntax should be added after all XMP property names and values are added.
+ void AddRdfSuffix();
+
+ /// Adds the RDF:Description prefix that appears within the body of an XMP
+ /// segment. This syntax should be added after the RDF prefix is added, but
+ /// before any XMP property names and values are added.
+ void AddRdfDescriptionPrefix();
+
+ /// Adds the RDF:Description suffix that appears within the body of an XMP
+ /// segment. This syntax should be added after after all XMP property names
+ /// and values are added, but before the RDF syntax is added.
+ void AddRdfDescriptionSuffix();
+
+ /// Adds the property name, and the '="' string that defines
+ /// the start of the name="value" string. After this call, you can
+ /// add the property value to the byte data vector, and then call the
+ /// AddXmpPropertySuffix() function to finish the definition.
+ /// @param property_name The name of the property to add.
+ void AddXmpPropertyPrefix(const std::string& property_name);
+
+ /// Adds a final quote to finish off the definition of a name="value" string.
+ void AddXmpPropertySuffix();
+
+ /// Adds the name="value" strings to define the XMP property name and value.
+ /// @param property_name The name of the property to add.
+ /// @param property_value The value of the property to add.
+ void AddXmpPropertyNameAndValue(const std::string& property_name,
+ const std::string& property_value);
+
+ /// Adds segment marker and the extended XMP header for an APP1/XMP type
+ /// segment that as extended XMP data. After this call you can either all the
+ /// AddXmpAndRdfPrefixes() function (if this is the first extended segment, or
+ /// just continue adding the property value contained in this segment.
+ /// @param xmp_guid The guid value of the XMP data. If this value is not 32
+ /// bytes long, it is either truncated or extended with 0s.
+ void AddApp1XmpMarkerAndXmpExtendedHeader(const std::string& xmp_guid);
+
+ /// Adds segment marker and all the prefixes to start the xmpmeta/rdf section
+ /// of the segment. After this call property names and values can be added,
+ /// and optionally the section can be completed by calling the
+ /// AddXmpAndRdfSuffixes() function.
+ void AddXmpAndRdfPrefixes();
+
+ /// Adds the suffixes to complete the definition of an APP1/XMP segment. Call
+ /// this function after the AddApp1XmpPrefixes() and after adding property
+ /// names and values to the byte data.
+ void AddXmpAndRdfSuffixes();
+
+ private:
+ std::vector<ByteData> byte_data_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SEGMENT_BUILDER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_segment_info.h b/includes/image_io/jpeg/jpeg_segment_info.h
new file mode 100644
index 0000000..6fb25db
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_segment_info.h
@@ -0,0 +1,85 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SEGMENT_INFO_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SEGMENT_INFO_H_ // NOLINT
+
+#include <string>
+#include <vector>
+
+#include "image_io/base/data_range.h"
+#include "image_io/base/types.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// Interesting segment types.
+const char kExif[] = "Exif";
+const char kJfif[] = "JFIF";
+const char kMpf[] = "MPF";
+
+/// A class that holds interesting information about a JpegSegment.
+class JpegSegmentInfo {
+ public:
+ /// @param image_index The index of the image in a @c DataSource that contains
+ /// the segment.
+ /// @param data_range The range in the segment in the @c DataSource.
+ /// @param type The type of segment.
+ JpegSegmentInfo(size_t image_index, const DataRange& data_range,
+ const std::string& type)
+ : image_index_(image_index), data_range_(data_range), type_(type) {}
+
+ /// Constructs an empty, invalid segment info.
+ JpegSegmentInfo() : image_index_(0) {}
+
+ JpegSegmentInfo(const JpegSegmentInfo&) = default;
+ JpegSegmentInfo& operator=(const JpegSegmentInfo&) = default;
+
+ /// @param rhs The segment info to compare with this one.
+ /// @return Whether the segment infos are equal
+ bool operator==(const JpegSegmentInfo& rhs) const {
+ return image_index_ == rhs.image_index_ && data_range_ == rhs.data_range_ &&
+ type_ == rhs.type_ && bytes_ == rhs.bytes_;
+ }
+
+ /// @param rhs The segment info to compare with this one.
+ /// @return Whether the segment infos are not equal
+ bool operator!=(const JpegSegmentInfo& rhs) const {
+ return !(*this == rhs);
+ }
+
+ /// @return Whether the segment info is valid.
+ bool IsValid() const { return !type_.empty() && data_range_.IsValid(); }
+
+ /// @return The image index of the segment info.
+ size_t GetImageIndex() const { return image_index_; }
+
+ /// @return The data range of the segment info.
+ const DataRange& GetDataRange() const { return data_range_; }
+
+ /// @return The type of the segment info.
+ const std::string& GetType() const { return type_; }
+
+ /// @return The (optional) bytes of the segment to which the info refers. The
+ /// vector will be empty unless the GetMutableBytes() function has been
+ /// and the vector filled with the segment contents.
+ const std::vector<Byte>& GetBytes() const { return bytes_; }
+
+ /// @return A non-const pointer to the bytes vector.
+ std::vector<Byte>* GetMutableBytes() { return &bytes_; }
+
+ private:
+ // The image index where the segment is located.
+ size_t image_index_;
+
+ // The data range of the segment.
+ DataRange data_range_;
+
+ // The type of segment.
+ std::string type_;
+
+ // The (optional) bytes of the segment.
+ std::vector<Byte> bytes_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SEGMENT_INFO_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_segment_lister.h b/includes/image_io/jpeg/jpeg_segment_lister.h
new file mode 100644
index 0000000..ca2a19d
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_segment_lister.h
@@ -0,0 +1,35 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SEGMENT_LISTER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SEGMENT_LISTER_H_ // NOLINT
+
+#include <string>
+#include <vector>
+
+#include "image_io/jpeg/jpeg_segment_processor.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// JpegSegmentLister is an implementation of JpegSegmentProcesor that creates
+/// a listing (in the form of a vector of strings) describing the segments.
+class JpegSegmentLister : public JpegSegmentProcessor {
+ public:
+ JpegSegmentLister();
+ void Start(JpegScanner* scanner) override;
+ void Process(JpegScanner* scanner, const JpegSegment& segment) override;
+ void Finish(JpegScanner* scanner) override;
+
+ /// @return The lines representing the listing of the segments.
+ const std::vector<std::string>& GetLines() const { return lines_; }
+
+ private:
+ /// The number of occurences of the various segment types.
+ std::vector<int> marker_type_counts_;
+
+ /// The lines representing the listing output.
+ std::vector<std::string> lines_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SEGMENT_LISTER_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_segment_processor.h b/includes/image_io/jpeg/jpeg_segment_processor.h
new file mode 100644
index 0000000..a193797
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_segment_processor.h
@@ -0,0 +1,44 @@
+#ifndef IMAGE_IO_JPEG_JPEG_SEGMENT_PROCESSOR_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_SEGMENT_PROCESSOR_H_ // NOLINT
+
+#include "image_io/jpeg/jpeg_segment.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+class JpegScanner;
+
+/// JpegSegmentProcessor is the abstract base class for implementations that do
+/// something with the JPEG segments that the JpegScanner identifies.
+class JpegSegmentProcessor {
+ public:
+ virtual ~JpegSegmentProcessor() = default;
+
+ /// This function is called at the start of the JPegScanner::Run() function to
+ /// allow this JpegProcessor to initialize its data structures. It can also
+ /// inform the JpegScanner about preferences for the types of segments it is
+ /// interested in by calling the JpegScanner::UpdateInterestingMarkerFlags()
+ /// function.
+ /// @param scanner The scanner that is starting the JpegProcessor.
+ virtual void Start(JpegScanner* scanner) = 0;
+
+ /// This function is called repeatedly by the JpegScanner as it identifies
+ /// segments in the JPEG file. The JpegProcessor can access the data in the
+ /// segment to do interesting things, or can update the scanner's preferences
+ /// like in the Start() function.
+ /// @param scanner The scanner that is providing the segment to the processor.
+ /// @param segment The segment provided by the scanner to the processor.
+ virtual void Process(JpegScanner* scanner, const JpegSegment& segment) = 0;
+
+ /// This function is called after the JpegScanner has provided all the
+ /// segments to the JpegProcessor to allow the processor to finish its work
+ /// processing the segments.
+ /// @param scanner The scanner that is informing the processor that it is done
+ /// finding segments.
+ virtual void Finish(JpegScanner* scanner) = 0;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_SEGMENT_PROCESSOR_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_xmp_data_extractor.h b/includes/image_io/jpeg/jpeg_xmp_data_extractor.h
new file mode 100644
index 0000000..30d62a1
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_xmp_data_extractor.h
@@ -0,0 +1,66 @@
+#ifndef IMAGE_IO_JPEG_JPEG_XMP_DATA_EXTRACTOR_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_XMP_DATA_EXTRACTOR_H_ // NOLINT
+
+#include "image_io/base/data_destination.h"
+#include "image_io/jpeg/jpeg_info.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A class that can make use of the data in a JpegInfo instance to extract
+/// the xmp data JpegSegments passed to it and forward it to a DataDestination.
+class JpegXmpDataExtractor : public DataDestination {
+ public:
+ /// @param xmp_info_type The type of xmp data being extracted.
+ /// @param segment_count The number of segment ranges over which the xmp
+ /// data is spread.
+ /// @param data_destination The destination to which the extracted xmp data
+ /// is to be sent.
+ JpegXmpDataExtractor(JpegXmpInfo::Type xmp_info_type, size_t segment_count,
+ DataDestination* data_destination)
+ : xmp_info_type_(xmp_info_type),
+ last_segment_index_(segment_count - 1),
+ data_destination_(data_destination),
+ has_error_(false) {}
+
+ /// Set the current segment index to the given value.
+ /// @param segment_index The index of the segment currently being processed.
+ void SetSegmentIndex(size_t segment_index) { segment_index_ = segment_index; }
+
+ /// @return True if there was an error in the extraction process.
+ bool HasError() const { return has_error_; }
+
+ void StartTransfer() override;
+ TransferStatus Transfer(const DataRange& transfer_range,
+ const DataSegment& data_segment) override;
+ void FinishTransfer() override;
+
+ /// @return The number of bytes written not to this extractor destination, but
+ /// to the next destination. Returns zero if the next destination is null.
+ size_t GetBytesTransferred() const override {
+ return data_destination_ ? data_destination_->GetBytesTransferred() : 0;
+ }
+
+ private:
+ /// The type of xmp data being extracted.
+ JpegXmpInfo::Type xmp_info_type_;
+
+ /// The xmp data require special processing when the last segment is being
+ /// transferred. This value is the index of the last segment.
+ size_t last_segment_index_;
+
+ /// The DataDestination that the extracted xmp data is sent to.
+ DataDestination* data_destination_;
+
+ /// The xmp data is spread over one or more segments in the DataSource. This
+ /// index tracks which one is being transferred.
+ size_t segment_index_;
+
+ /// A true value indicates that an error occurred in the decoding process.
+ bool has_error_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_XMP_DATA_EXTRACTOR_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_xmp_info.h b/includes/image_io/jpeg/jpeg_xmp_info.h
new file mode 100644
index 0000000..2bda3f5
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_xmp_info.h
@@ -0,0 +1,92 @@
+#ifndef IMAGE_IO_JPEG_JPEG_XMP_INFO_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_XMP_INFO_H_ // NOLINT
+
+#include <string>
+#include <vector>
+
+#include "image_io/base/data_range.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+const size_t kXmpGuidSize = 32;
+const char kXmpId[] = "http://ns.adobe.com/xap/1.0/";
+const char kXmpExtendedId[] = "http://ns.adobe.com/xmp/extension/";
+const size_t kXmpExtendedHeaderSize =
+ sizeof(kXmpExtendedId) + kXmpGuidSize + 2 * sizeof(std::uint32_t);
+
+/// Constants used to find and process information in APP1/XMP type segments.
+const char kXmpAppleDepthId[] = "http://ns.apple.com/depthData/1.0";
+const char kXmpAppleMatteId[] = "http://ns.apple.com/portraitEffectsMatte/1.0/";
+const char kXmpGDepthV1Id[] = "http://ns.google.com/photos/1.0/depthmap/";
+const char kXmpGImageV1Id[] = "http://ns.google.com/photos/1.0/image/";
+const char kXmpHasExtendedId[] = "xmpNote:HasExtendedXMP";
+
+/// JpegXmpInfo maintains information about the data in an Xmp property, such as
+/// are used to store the GDepth and GImage data.
+class JpegXmpInfo {
+ public:
+ /// The possible types of Xmp information.
+ enum Type {
+ /// GDepth:Data type information.
+ kGDepthInfoType,
+
+ /// GImage:Data type information.
+ kGImageInfoType,
+ };
+
+ /// Initializes a vector of JpegXmpinfo instances, indexed by their type.
+ /// @param xmp_info_vector The vector to initialize.
+ static void InitializeVector(std::vector<JpegXmpInfo>* xmp_info_vector);
+
+ /// @param xmp_info_type The type to get the identifier of.
+ /// @return The identfier that appears at the start of the Xmp segment.
+ static std::string GetIdentifier(Type jpeg_xmp_info_type);
+
+ /// @param xmp_info_type The type to get the data property name of.
+ /// @return The name of the data property that appears in the Xmp segment.
+ static std::string GetDataPropertyName(Type jpeg_xmp_info_type);
+
+ /// @param xmp_info_type The type to get the mime property name of.
+ /// @return The name of the mime property that appears in the primary
+ /// Xmp segment.
+ static std::string GetMimePropertyName(Type jpeg_xmp_info_type);
+
+ explicit JpegXmpInfo(Type type) : type_(type) {}
+ JpegXmpInfo(const JpegXmpInfo&) = default;
+ JpegXmpInfo& operator=(const JpegXmpInfo&) = default;
+
+ /// @return The type of the Xmp property information.
+ Type GetType() const { return type_; }
+
+ /// @return The mime type of the Xmp data.
+ std::string GetMimeType() const { return mime_type_; }
+
+ /// @param mime_type The mime type to assign to this instance.
+ void SetMimeType(const std::string& mime_type) { mime_type_ = mime_type; }
+
+ /// @return The segment's data ranges where this Xmp data occurs.
+ const std::vector<DataRange>& GetSegmentDataRanges() const {
+ return segment_data_ranges_;
+ }
+
+ /// @param The segment data ranges to assign to this instance.
+ void SetSegmentDataRanges(const std::vector<DataRange>& segment_data_ranges) {
+ segment_data_ranges_ = segment_data_ranges;
+ }
+
+ private:
+ /// The type of the Xmp information.
+ Type type_;
+
+ /// The mime type of the Xmp data.
+ std::string mime_type_;
+
+ /// The segment data ranges that contain the Xmp data.
+ std::vector<DataRange> segment_data_ranges_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_XMP_INFO_H_ // NOLINT
diff --git a/includes/image_io/jpeg/jpeg_xmp_info_builder.h b/includes/image_io/jpeg/jpeg_xmp_info_builder.h
new file mode 100644
index 0000000..62b3ac4
--- /dev/null
+++ b/includes/image_io/jpeg/jpeg_xmp_info_builder.h
@@ -0,0 +1,42 @@
+#ifndef IMAGE_IO_JPEG_JPEG_XMP_INFO_BUILDER_H_ // NOLINT
+#define IMAGE_IO_JPEG_JPEG_XMP_INFO_BUILDER_H_ // NOLINT
+
+#include <vector>
+
+#include "image_io/jpeg/jpeg_segment.h"
+#include "image_io/jpeg/jpeg_xmp_info.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A helper class for building information about the segments that contain
+/// extended xmp data of various types.
+class JpegXmpInfoBuilder {
+ public:
+ /// @param xmp_info_type The type of xmp information to build.
+ explicit JpegXmpInfoBuilder(JpegXmpInfo::Type xmp_info_type)
+ : xmp_info_type_(xmp_info_type) {}
+
+ /// @param segment The segment to examine for xmp data.
+ void ProcessSegment(const JpegSegment& segment);
+
+ /// @return The vector of segment data ranges that contains xmp property data.
+ const std::vector<DataRange>& GetPropertySegmentRanges() const {
+ return property_segment_ranges_;
+ }
+
+ private:
+ /// The type of xmp data to collect.
+ JpegXmpInfo::Type xmp_info_type_;
+
+ /// The vector of segment data ranges that contains xmp property data.
+ std::vector<DataRange> property_segment_ranges_;
+
+ /// The segment data range that contains the xmp property data end.
+ DataRange property_end_segment_range_;
+};
+
+} // namespace image_io
+} // namespace photos_editing_formats
+
+#endif // IMAGE_IO_JPEG_JPEG_XMP_INFO_BUILDER_H_ // NOLINT