| // 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 <algorithm> |
| #include <cassert> |
| #include <string> |
| #include <vector> |
| |
| #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 5000; } |
| |
| // 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 = 5; |
| 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 |
| }; |
| 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 (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<string> 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 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<TypeChecker*> checkers_; |
| }; |
| |
| } // namespace |
| |
| bool IsRaw(const RawImageTypes type) { |
| switch (type) { |
| // Non-RAW-image type |
| case kNonRawImage: { |
| return false; |
| } |
| |
| // Raw image types |
| case kArwImage: |
| 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 |