// Copyright 2015 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// // // This file implements the image type recognition algorithm. Functions, which // will check each single image type, are implemented based on the comparisons // of magic numbers or signature strings. Other checks (e.g endianness, general // tiff magic number "42", etc.) could also be used in some of those functions // to make the type recognition more stable. Those checks are designed // according to the format spcifications and our own experiments. Notice that // the magic numbers and signature strings may have different binary values // according to different endiannesses. #include "src/image_type_recognition/image_type_recognition_lite.h" #include #include #include #include #include "src/binary_parse/range_checked_byte_ptr.h" namespace piex { namespace image_type_recognition { namespace { using std::string; using binary_parse::MemoryStatus; using binary_parse::RangeCheckedBytePtr; // Base class for checking image type. For each image type, one should create an // inherited class and do the implementation. class TypeChecker { public: // Comparing function, whihc is used for sorting. static bool Compare(const TypeChecker* a, const TypeChecker* b) { assert(a); assert(b); return a->RequestedSize() < b->RequestedSize(); } virtual ~TypeChecker() {} // Returns the type of current checker. virtual RawImageTypes Type() const = 0; // Returns the requested data size (in bytes) for current checker. The checker // guarantees that it will not read more than this size. virtual size_t RequestedSize() const = 0; // Checks if source data belongs to current checker type. virtual bool IsMyType(const RangeCheckedBytePtr& source) const = 0; protected: // Limits the source length to the RequestedSize(), using it guarantees that // we will not read more than this size from the source. RangeCheckedBytePtr LimitSource(const RangeCheckedBytePtr& source) const { return source.pointerToSubArray(0 /* pos */, RequestedSize()); } }; // Check if the uint16 value at (source + offset) is equal to the target value. bool CheckUInt16Value(const RangeCheckedBytePtr& source, const size_t source_offset, const bool use_big_endian, const unsigned short target_value) { // NOLINT MemoryStatus status = binary_parse::RANGE_CHECKED_BYTE_SUCCESS; const unsigned short value = binary_parse::Get16u( // NOLINT source + source_offset, use_big_endian, &status); if (status != binary_parse::RANGE_CHECKED_BYTE_SUCCESS) { return false; } return (target_value == value); } // Check if the uint32 value at (source + offset) is equal to the target value. bool CheckUInt32Value(const RangeCheckedBytePtr& source, const size_t source_offset, const bool use_big_endian, const unsigned int target_value) { MemoryStatus status = binary_parse::RANGE_CHECKED_BYTE_SUCCESS; const unsigned int value = binary_parse::Get32u(source + source_offset, use_big_endian, &status); if (status != binary_parse::RANGE_CHECKED_BYTE_SUCCESS) { return false; } return (target_value == value); } // Determine the endianness. The return value is NOT the endianness indicator, // it's just that this function was successful. bool DetermineEndianness(const RangeCheckedBytePtr& source, bool* is_big_endian) { if (source.remainingLength() < 2) { return false; } if (source[0] == 0x49 && source[1] == 0x49) { *is_big_endian = false; } else if (source[0] == 0x4D && source[1] == 0x4D) { *is_big_endian = true; } else { return false; } return true; } // Check if signature string can match to the same length string start from // (source + offset). The signature string will be used as longer magic number // series. bool IsSignatureMatched(const RangeCheckedBytePtr& source, const size_t source_offset, const string& signature) { return source.substr(source_offset, signature.size()) == signature; } // Check if signature is found in [source + offset, source + offset + range]. bool IsSignatureFound(const RangeCheckedBytePtr& source, const size_t search_offset, const size_t search_range, const string& signature, size_t* first_matched) { if (source.remainingLength() < search_offset + search_range) { return false; } // The index must be in range [offset, offset + range - sizeof(signature)], so // that it can guarantee that it will not read outside of range. for (size_t i = search_offset; i < search_offset + search_range - signature.size(); ++i) { if (IsSignatureMatched(source, i, signature)) { if (first_matched) { *first_matched = i; } return true; } } return false; } // Sony RAW format. class ArwTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kArwImage; } virtual size_t RequestedSize() const { return 10000; } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. correct tiff magic number at the (offset == 8) position of the file; // 3. signature "SONY" in first requested bytes; // 4. correct signature for (section + version) in first requested bytes. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const unsigned short kTiffMagic = 0x2A; // NOLINT const unsigned int kTiffOffset = 8; if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTiffMagic) || !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian, kTiffOffset)) { return false; } // Search for kSignatureSony in first requested bytes const string kSignatureSony("SONY"); if (!IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kSignatureSony, NULL)) { return false; } // Search for (kSignatureFileTypeSection + kSignatureVersions[i]) in first // requested bytes const string kSignatureSection("\x00\xb0\x01\x00\x04\x00\x00\x00", 8); const int kSignatureVersionsSize = 6; const string kSignatureVersions[kSignatureVersionsSize] = { string("\x02\x00", 2), // ARW 1.0 string("\x03\x00", 2), // ARW 2.0 string("\x03\x01", 2), // ARW 2.1 string("\x03\x02", 2), // ARW 2.2 string("\x03\x03", 2), // ARW 2.3 string("\x04\x00", 2), // ARW 4.0 }; bool matched = false; for (int i = 0; i < kSignatureVersionsSize; ++i) { matched = matched || IsSignatureFound( limited_source, 0 /* offset */, RequestedSize(), kSignatureSection + kSignatureVersions[i], NULL); } return matched; } }; // Canon RAW (CR3 extension). class Cr3TypeChecker : public TypeChecker { public: static constexpr size_t kSignatureOffset = 4; static constexpr const char* kSignature = "ftypcrx "; virtual RawImageTypes Type() const { return kCr3Image; } virtual size_t RequestedSize() const { return kSignatureOffset + strlen(kSignature); } // Checks for the ftyp box w/ brand 'crx '. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); return IsSignatureMatched(limited_source, kSignatureOffset, kSignature); } }; // Canon RAW (CR2 extension). class Cr2TypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kCr2Image; } virtual size_t RequestedSize() const { return 16; } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. magic number "42" at the (offset == 2) position of the file; // 3. signature "CR2" at the (offset == 8) position of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const unsigned short kTag = 42; // NOLINT if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTag)) { return false; } const string kSignature("CR\2\0", 4); return IsSignatureMatched(limited_source, 8 /* offset */, kSignature); } }; // Canon RAW (CRW extension). class CrwTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kCrwImage; } virtual size_t RequestedSize() const { return 14; } // Check only the signature at the (offset == 6) position of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } string signature; if (use_big_endian) { signature = string("\x00\x10\xba\xb0\xac\xbb\x00\x02", 8); } else { signature = string("HEAPCCDR"); } return IsSignatureMatched(limited_source, 6 /* offset */, signature); } }; // Kodak RAW. class DcrTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kDcrImage; } virtual size_t RequestedSize() const { return 5000; } // Check two different cases, only need to fulfill one of the two: // 1. signature at the (offset == 16) position of the file; // 2. two tags (OriginalFileName and FirmwareVersion) can be found in the // first requested bytes of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } // Case 1: has signature const string kSignature( "\x4b\x4f\x44\x41\x4b\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20", 16); if (IsSignatureMatched(limited_source, 16 /* offset */, kSignature)) { return true; } // Case 2: search for tags in first requested bytes string kIfdTags[2]; if (use_big_endian) { kIfdTags[0] = string("\x03\xe9\x00\x02", 4); // OriginalFileName kIfdTags[1] = string("\x0c\xe5\x00\x02", 4); // FirmwareVersion } else { kIfdTags[0] = string("\xe9\x03\x02\x00", 4); // OriginalFileName kIfdTags[1] = string("\xe5\x0c\x02\x00", 4); // FirmwareVersion } return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kIfdTags[0], NULL) && IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kIfdTags[1], NULL); } }; // Digital Negative RAW. class DngTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kDngImage; } virtual size_t RequestedSize() const { return 1024; } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. at least two dng specific tags in the first requested bytes of the // file virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } // Search tags in first requested bytes and verify the order of them. const int kTagsCount = 5; string dng_tags[kTagsCount]; if (use_big_endian) { dng_tags[0] = string("\xc6\x12\x00\x01\x00\x00\x00\x04", 8); // tag: 50706 dng_tags[1] = string("\xc6\x13\x00\x01\x00\x00\x00\x04", 8); // tag: 50707 dng_tags[2] = string("\xc6\x14\x00\x02", 4); // tag: 50708 dng_tags[3] = string("\xc6\x20", 2); // tag: 50720 dng_tags[4] = string("\xc6\x2d\x00\x04\x00\x00\x00\x01", 8); // tag: 50733 } else { dng_tags[0] = string("\x12\xc6\x01\x00\x04\x00\x00\x00", 8); // tag: 50706 dng_tags[1] = string("\x13\xc6\x01\x00\x04\x00\x00\x00", 8); // tag: 50707 dng_tags[2] = string("\x14\xc6\x02\x00", 4); // tag: 50708 dng_tags[3] = string("\x20\xc6", 2); // tag: 50720 dng_tags[4] = string("\x2d\xc6\x04\x00\x01\x00\x00\x00", 8); // tag: 50733 } int tags_found = 0; for (int i = 0; i < kTagsCount; ++i) { if (IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), dng_tags[i], NULL)) { tags_found++; } } return tags_found >= 2; } }; // Kodak RAW. class KdcTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kKdcImage; } virtual size_t RequestedSize() const { return 5000; } // Check two points: // 1. valid endianness at the beginning of the file; // 2. two tags (WhiteBalance and SerialNumber) in the first requested bytes. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } // Search in first requested bytes const size_t kIfdTagsSize = 2; string kIfdTags[kIfdTagsSize]; if (use_big_endian) { kIfdTags[0] = string("\xfa\x0d\x00\x01", 4); // WhiteBalance kIfdTags[1] = string("\xfa\x00\x00\x02", 4); // SerialNumber } else { kIfdTags[0] = string("\x0d\xfa\x01\x00", 4); // WhiteBalance kIfdTags[1] = string("\x00\xfa\x02\x00", 4); // SerialNumber } return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kIfdTags[0], NULL) && IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kIfdTags[1], NULL); } }; // Leaf RAW. class MosTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kMosImage; } virtual size_t RequestedSize() const { return 5000; } // Check two points: // 1. valid endianness at the beginning of the file; // 2. signature "PKTS " in the first requested bytes. Note the // "whitespace". It's important as they are special binary values. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(source, &use_big_endian)) { return false; } // Search kSignaturePKTS in first requested bytes const string kSignaturePKTS("PKTS\x00\x00\x00\x001", 8); return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kSignaturePKTS, NULL); } }; // Minolta RAW. class MrwTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kMrwImage; } virtual size_t RequestedSize() const { return 4; } // Check only the signature at the beginning of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { // Limits the source length to the RequestedSize(), using it guarantees that // we will not read more than this size from the source. RangeCheckedBytePtr limited_source = source.pointerToSubArray(0 /* pos */, RequestedSize()); const string kSignature("\0MRM", 4); return IsSignatureMatched(limited_source, 0 /* offset */, kSignature); } }; // Check if the file contains a NRW signature "NRW " in the first requested // bytes. Note the "whitespace". It's important as they are special binary // values. const size_t kRequestedSizeForNrwSignature = 4000; bool ContainsNrwSignature(const RangeCheckedBytePtr& source) { // Search for kSignatureNrw. const string kSignatureNrw("NRW\x20\x20\x20", 6); return IsSignatureFound(source, 0 /* offset */, kRequestedSizeForNrwSignature, kSignatureNrw, NULL); } // Checks if the file contains the signatures for Nikon formats: // * the general Nikon singature "NIKON" string. // * the ReferenceBlackWhite tag. const size_t kRequestedSizeForNikonSignatures = 4000; bool ContainsNikonSignatures(const RangeCheckedBytePtr& source, const bool use_big_endian) { const string kSignatureNikon("NIKON"); const string kReferenceBlackWhiteTag = use_big_endian ? string("\x02\x14\x00\x05", 4) : string("\x14\x02\x05\x00", 4); const std::vector kSignatures = {kSignatureNikon, kReferenceBlackWhiteTag}; for (auto const& signature : kSignatures) { if (!IsSignatureFound(source, 0, kRequestedSizeForNikonSignatures, signature, NULL)) { return false; } } return true; } // Nikon RAW (NEF extension). class NefTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kNefImage; } virtual size_t RequestedSize() const { return std::max(kRequestedSizeForNikonSignatures, kRequestedSizeForNrwSignature); } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. magic number at the (offset == 2) position of the file; // 3. the signature "NIKON" in the requested bytes of the file; // 4. the ReferenceBlackWhite tag in the requested bytes of the file; // 5. does not contain the NRW signature. We may also check a special // signature "RAW " similar to the NRW case, but we got issues in some // special images that the signature locates in the middle of the file, and it // costs too long time to check; virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const unsigned short kTiffMagic = 0x2A; // NOLINT if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTiffMagic)) { return false; } return ContainsNikonSignatures(limited_source, use_big_endian) && !ContainsNrwSignature(limited_source); // not NRW } }; // Nikon RAW (NRW extension). class NrwTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kNrwImage; } virtual size_t RequestedSize() const { return std::max(kRequestedSizeForNikonSignatures, kRequestedSizeForNrwSignature); } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. magic numbers at the (offset == 2 and offset == 4) positions of the // file; // 3. the signature "NIKON" in the first requested bytes of the file; // 4. the ReferenceBlackWhite tag in the requested bytes of the file; // 5. contains the NRW signature; virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const unsigned short kTiffMagic = 0x2A; // NOLINT const unsigned int kTiffOffset = 8; if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTiffMagic) || !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian, kTiffOffset)) { return false; } return ContainsNikonSignatures(limited_source, use_big_endian) && ContainsNrwSignature(limited_source); } }; // Olympus RAW. class OrfTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kOrfImage; } virtual size_t RequestedSize() const { return 3000; } // Check multiple points: // 1. valid endianness at the beginning of the file; // 2. tag at the (offset == 2) position of the file; // 3. signature "OLYMP" in the first requested bytes. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const size_t kTagSize = 2; const unsigned short kTag[kTagSize] = {0x4F52, 0x5352}; // NOLINT if (!(CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTag[0]) || CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTag[1]))) { return false; } // Search for kSignatureOlymp in first requested bytes const string kSignatureOlymp("OLYMP"); return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kSignatureOlymp, NULL); } }; // Pentax RAW. class PefTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kPefImage; } virtual size_t RequestedSize() const { return 1280; } // Check multiple points: // 1. valid big endianness at the beginning of the file; // 2. magic numbers at the (offset == 2 and offset==4) positions of the file; // 3. signature "AOC " or "PENTAX " in first requested bytes. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(limited_source, &use_big_endian)) { return false; } const unsigned short kTiffMagic = 0x2A; // NOLINT const unsigned int kTiffOffset = 8; if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTiffMagic) || !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian, kTiffOffset)) { return false; } // Search for kSignatureAOC or kSignaturePENTAX in first requested bytes const string kSignatureAOC("\x41\x4f\x43\x00\x4d\x4d", 6); const string kSignaturePENTAX("\x50\x45\x4e\x54\x41\x58\x20\x00", 8); return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kSignatureAOC, NULL) || IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(), kSignaturePENTAX, NULL); } }; // Apple format. class QtkTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kQtkImage; } virtual size_t RequestedSize() const { return 8; } // Check only the signature at the beginning of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); const size_t kSignatureSize = 2; const string kSignature[kSignatureSize] = { string("qktk\x00\x00\x00\x08", 8), string("qktn\x00\x00\x00\x08", 8), }; return IsSignatureMatched(limited_source, 0 /* offset */, kSignature[0]) || IsSignatureMatched(limited_source, 0 /* offset */, kSignature[1]); } }; // Fuji RAW. class RafTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kRafImage; } virtual size_t RequestedSize() const { return 8; } // Check only the signature at the beginning of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); const string kSignature("FUJIFILM"); return IsSignatureMatched(limited_source, 0 /* offset */, kSignature); } }; // Contax N RAW. class RawContaxNTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kRawContaxNImage; } virtual size_t RequestedSize() const { return 36; } // Check only the signature at the (offset == 25) position of the // file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); const string kSignature("ARECOYK"); return IsSignatureMatched(limited_source, 25, kSignature); } }; // Panasonic RAW. class Rw2TypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kRw2Image; } virtual size_t RequestedSize() const { return 4; } // Check two points: 1. valid endianness at the beginning of the // file; 2. tag at the (offset == 2) position of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(source, &use_big_endian)) { return false; } const unsigned short kTag = 0x55; // NOLINT return CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTag); } }; // Samsung RAW. class SrwTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kSrwImage; } virtual size_t RequestedSize() const { return 256; } // Check multiple points: // 1. valid big endianness at the beginning of the file; // 2. magic numbers at the (offset == 2 and offset==4) positions of the file; // 3. the signature "SAMSUNG" in the requested bytes of the file; virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); bool use_big_endian; if (!DetermineEndianness(source, &use_big_endian)) { return false; } const unsigned short kTiffMagic = 0x2A; // NOLINT const unsigned int kTiffOffset = 8; if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian, kTiffMagic) || !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian, kTiffOffset)) { return false; } const string kSignature("SAMSUNG"); if (!IsSignatureFound(source, 0, RequestedSize(), kSignature, NULL)) { return false; } return true; } }; // Sigma / Polaroid RAW. class X3fTypeChecker : public TypeChecker { public: virtual RawImageTypes Type() const { return kX3fImage; } virtual size_t RequestedSize() const { return 4; } // Check only the signature at the beginning of the file. virtual bool IsMyType(const RangeCheckedBytePtr& source) const { RangeCheckedBytePtr limited_source = LimitSource(source); const string kSignature("FOVb", 4); return IsSignatureMatched(limited_source, 0 /* offset */, kSignature); } }; // This class contains the list of all type checkers. One should used this list // as a whole to execute the image type recognition. class TypeCheckerList { public: TypeCheckerList() { // Add all supported RAW type checkers here. checkers_.push_back(new ArwTypeChecker()); checkers_.push_back(new Cr3TypeChecker()); checkers_.push_back(new Cr2TypeChecker()); checkers_.push_back(new CrwTypeChecker()); checkers_.push_back(new DcrTypeChecker()); checkers_.push_back(new DngTypeChecker()); checkers_.push_back(new KdcTypeChecker()); checkers_.push_back(new MosTypeChecker()); checkers_.push_back(new MrwTypeChecker()); checkers_.push_back(new NefTypeChecker()); checkers_.push_back(new NrwTypeChecker()); checkers_.push_back(new OrfTypeChecker()); checkers_.push_back(new PefTypeChecker()); checkers_.push_back(new QtkTypeChecker()); checkers_.push_back(new RafTypeChecker()); checkers_.push_back(new RawContaxNTypeChecker()); checkers_.push_back(new Rw2TypeChecker()); checkers_.push_back(new SrwTypeChecker()); checkers_.push_back(new X3fTypeChecker()); // Sort the checkers by the ascending RequestedSize() to get better // performance when checking type. std::sort(checkers_.begin(), checkers_.end(), TypeChecker::Compare); } ~TypeCheckerList() { for (size_t i = 0; i < checkers_.size(); ++i) { delete checkers_[i]; checkers_[i] = NULL; } } // Returns the type of source data. If it can not be identified, returns // kNonRawImage. RawImageTypes GetType(const RangeCheckedBytePtr& source) const { for (size_t i = 0; i < checkers_.size(); ++i) { if (checkers_[i]->IsMyType(source)) { return checkers_[i]->Type(); } } return kNonRawImage; } // Returns the maximum size of requested size of data for identifying image // type using this class. The class guarantees that it will not read more than // this size. size_t RequestedSize() const { assert(!checkers_.empty()); // The checkers_ is ascending sorted. The last element is the maximum. return checkers_.back()->RequestedSize(); } bool IsOfType(const RangeCheckedBytePtr& source, const RawImageTypes type) { const TypeChecker* type_checker = GetTypeCheckerForType(type); if (type_checker) { return type_checker->IsMyType(source); } else { return false; } } size_t RequestedSizeForType(const RawImageTypes type) { const TypeChecker* type_checker = GetTypeCheckerForType(type); if (type_checker) { return type_checker->RequestedSize(); } else { return 0; } } private: const TypeChecker* GetTypeCheckerForType(const RawImageTypes type) { for (const auto* type_checker : checkers_) { if (type_checker->Type() == type) { return type_checker; } } return nullptr; } std::vector checkers_; }; } // namespace bool IsRaw(const RawImageTypes type) { switch (type) { // Non-RAW-image type case kNonRawImage: { return false; } // Raw image types case kArwImage: case kCr3Image: case kCr2Image: case kCrwImage: case kDcrImage: case kDngImage: case kKdcImage: case kMosImage: case kMrwImage: case kNefImage: case kNrwImage: case kOrfImage: case kPefImage: case kQtkImage: case kRafImage: case kRawContaxNImage: case kRw2Image: case kSrwImage: case kX3fImage: { return true; } default: { // Unsupported type! assert(false); } } return false; } bool IsOfType(const RangeCheckedBytePtr& source, const RawImageTypes type) { return TypeCheckerList().IsOfType(source, type); } RawImageTypes RecognizeRawImageTypeLite(const RangeCheckedBytePtr& source) { return TypeCheckerList().GetType(source); } size_t GetNumberOfBytesForIsRawLite() { return TypeCheckerList().RequestedSize(); } size_t GetNumberOfBytesForIsOfType(const RawImageTypes type) { return TypeCheckerList().RequestedSizeForType(type); } bool IsRawLite(const RangeCheckedBytePtr& source) { return IsRaw(RecognizeRawImageTypeLite(source)); } } // namespace image_type_recognition } // namespace piex