diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2018-11-15 16:07:46 -0800 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2018-11-15 16:07:46 -0800 |
commit | 2d6d3250dcb304c8ad081dedc8eef6ea48fd669d (patch) | |
tree | 68cc8d5a9bf5a558f46025d740c47cb292eea9f0 /includes/image_io/jpeg | |
parent | 840fc3b66a9e6593d542ada6fe14d91107fab98d (diff) | |
download | image_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.h | 102 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_image_extractor.h | 73 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_info.h | 153 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_info_builder.h | 133 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_marker.h | 86 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_scanner.h | 100 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_segment.h | 178 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_segment_builder.h | 140 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_segment_info.h | 85 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_segment_lister.h | 35 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_segment_processor.h | 44 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_xmp_data_extractor.h | 66 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_xmp_info.h | 92 | ||||
-rw-r--r-- | includes/image_io/jpeg/jpeg_xmp_info_builder.h | 42 |
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 |