1 // Copyright 2015 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 //
17 // This file implements the image type recognition algorithm. Functions, which
18 // will check each single image type, are implemented based on the comparisons
19 // of magic numbers or signature strings. Other checks (e.g endianness, general
20 // tiff magic number "42", etc.) could also be used in some of those functions
21 // to make the type recognition more stable. Those checks are designed
22 // according to the format spcifications and our own experiments. Notice that
23 // the magic numbers and signature strings may have different binary values
24 // according to different endiannesses.
25 #include "src/image_type_recognition/image_type_recognition_lite.h"
26
27 #include <algorithm>
28 #include <cassert>
29 #include <string>
30 #include <vector>
31
32 #include "src/binary_parse/range_checked_byte_ptr.h"
33
34 namespace piex {
35 namespace image_type_recognition {
36 namespace {
37
38 using std::string;
39 using binary_parse::MemoryStatus;
40 using binary_parse::RangeCheckedBytePtr;
41
42 // Base class for checking image type. For each image type, one should create an
43 // inherited class and do the implementation.
44 class TypeChecker {
45 public:
46 // Comparing function, whihc is used for sorting.
Compare(const TypeChecker * a,const TypeChecker * b)47 static bool Compare(const TypeChecker* a, const TypeChecker* b) {
48 assert(a);
49 assert(b);
50 return a->RequestedSize() < b->RequestedSize();
51 }
52
~TypeChecker()53 virtual ~TypeChecker() {}
54
55 // Returns the type of current checker.
56 virtual RawImageTypes Type() const = 0;
57
58 // Returns the requested data size (in bytes) for current checker. The checker
59 // guarantees that it will not read more than this size.
60 virtual size_t RequestedSize() const = 0;
61
62 // Checks if source data belongs to current checker type.
63 virtual bool IsMyType(const RangeCheckedBytePtr& source) const = 0;
64
65 protected:
66 // Limits the source length to the RequestedSize(), using it guarantees that
67 // we will not read more than this size from the source.
LimitSource(const RangeCheckedBytePtr & source) const68 RangeCheckedBytePtr LimitSource(const RangeCheckedBytePtr& source) const {
69 return source.pointerToSubArray(0 /* pos */, RequestedSize());
70 }
71 };
72
73 // Check if the uint16 value at (source + offset) is equal to the target value.
CheckUInt16Value(const RangeCheckedBytePtr & source,const size_t source_offset,const bool use_big_endian,const unsigned short target_value)74 bool CheckUInt16Value(const RangeCheckedBytePtr& source,
75 const size_t source_offset, const bool use_big_endian,
76 const unsigned short target_value) { // NOLINT
77 MemoryStatus status = binary_parse::RANGE_CHECKED_BYTE_SUCCESS;
78 const unsigned short value = binary_parse::Get16u( // NOLINT
79 source + source_offset, use_big_endian, &status);
80 if (status != binary_parse::RANGE_CHECKED_BYTE_SUCCESS) {
81 return false;
82 }
83 return (target_value == value);
84 }
85
86 // Check if the uint32 value at (source + offset) is equal to the target value.
CheckUInt32Value(const RangeCheckedBytePtr & source,const size_t source_offset,const bool use_big_endian,const unsigned int target_value)87 bool CheckUInt32Value(const RangeCheckedBytePtr& source,
88 const size_t source_offset, const bool use_big_endian,
89 const unsigned int target_value) {
90 MemoryStatus status = binary_parse::RANGE_CHECKED_BYTE_SUCCESS;
91 const unsigned int value =
92 binary_parse::Get32u(source + source_offset, use_big_endian, &status);
93 if (status != binary_parse::RANGE_CHECKED_BYTE_SUCCESS) {
94 return false;
95 }
96 return (target_value == value);
97 }
98
99 // Determine the endianness. The return value is NOT the endianness indicator,
100 // it's just that this function was successful.
DetermineEndianness(const RangeCheckedBytePtr & source,bool * is_big_endian)101 bool DetermineEndianness(const RangeCheckedBytePtr& source,
102 bool* is_big_endian) {
103 if (source.remainingLength() < 2) {
104 return false;
105 }
106
107 if (source[0] == 0x49 && source[1] == 0x49) {
108 *is_big_endian = false;
109 } else if (source[0] == 0x4D && source[1] == 0x4D) {
110 *is_big_endian = true;
111 } else {
112 return false;
113 }
114 return true;
115 }
116
117 // Check if signature string can match to the same length string start from
118 // (source + offset). The signature string will be used as longer magic number
119 // series.
IsSignatureMatched(const RangeCheckedBytePtr & source,const size_t source_offset,const string & signature)120 bool IsSignatureMatched(const RangeCheckedBytePtr& source,
121 const size_t source_offset, const string& signature) {
122 return source.substr(source_offset, signature.size()) == signature;
123 }
124
125 // Check if signature is found in [source + offset, source + offset + range].
IsSignatureFound(const RangeCheckedBytePtr & source,const size_t search_offset,const size_t search_range,const string & signature,size_t * first_matched)126 bool IsSignatureFound(const RangeCheckedBytePtr& source,
127 const size_t search_offset, const size_t search_range,
128 const string& signature, size_t* first_matched) {
129 if (source.remainingLength() < search_offset + search_range) {
130 return false;
131 }
132
133 // The index must be in range [offset, offset + range - sizeof(signature)], so
134 // that it can guarantee that it will not read outside of range.
135 for (size_t i = search_offset;
136 i < search_offset + search_range - signature.size(); ++i) {
137 if (IsSignatureMatched(source, i, signature)) {
138 if (first_matched) {
139 *first_matched = i;
140 }
141 return true;
142 }
143 }
144 return false;
145 }
146
147 // Sony RAW format.
148 class ArwTypeChecker : public TypeChecker {
149 public:
Type() const150 virtual RawImageTypes Type() const { return kArwImage; }
151
RequestedSize() const152 virtual size_t RequestedSize() const { return 5000; }
153
154 // Check multiple points:
155 // 1. valid endianness at the beginning of the file;
156 // 2. correct tiff magic number at the (offset == 8) position of the file;
157 // 3. signature "SONY" in first requested bytes;
158 // 4. correct signature for (section + version) in first requested bytes.
IsMyType(const RangeCheckedBytePtr & source) const159 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
160 RangeCheckedBytePtr limited_source = LimitSource(source);
161
162 bool use_big_endian;
163 if (!DetermineEndianness(limited_source, &use_big_endian)) {
164 return false;
165 }
166
167 const unsigned short kTiffMagic = 0x2A; // NOLINT
168 const unsigned int kTiffOffset = 8;
169 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
170 kTiffMagic) ||
171 !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian,
172 kTiffOffset)) {
173 return false;
174 }
175
176 // Search for kSignatureSony in first requested bytes
177 const string kSignatureSony("SONY");
178 if (!IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
179 kSignatureSony, NULL)) {
180 return false;
181 }
182
183 // Search for (kSignatureFileTypeSection + kSignatureVersions[i]) in first
184 // requested bytes
185 const string kSignatureSection("\x00\xb0\x01\x00\x04\x00\x00\x00", 8);
186 const int kSignatureVersionsSize = 5;
187 const string kSignatureVersions[kSignatureVersionsSize] = {
188 string("\x02\x00", 2), // ARW 1.0
189 string("\x03\x00", 2), // ARW 2.0
190 string("\x03\x01", 2), // ARW 2.1
191 string("\x03\x02", 2), // ARW 2.2
192 string("\x03\x03", 2), // ARW 2.3
193 };
194 bool matched = false;
195 for (int i = 0; i < kSignatureVersionsSize; ++i) {
196 matched = matched || IsSignatureFound(
197 limited_source, 0 /* offset */, RequestedSize(),
198 kSignatureSection + kSignatureVersions[i], NULL);
199 }
200 return matched;
201 }
202 };
203
204 // Canon RAW (CR2 extension).
205 class Cr2TypeChecker : public TypeChecker {
206 public:
Type() const207 virtual RawImageTypes Type() const { return kCr2Image; }
208
RequestedSize() const209 virtual size_t RequestedSize() const { return 16; }
210
211 // Check multiple points:
212 // 1. valid endianness at the beginning of the file;
213 // 2. magic number "42" at the (offset == 2) position of the file;
214 // 3. signature "CR2" at the (offset == 8) position of the file.
IsMyType(const RangeCheckedBytePtr & source) const215 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
216 RangeCheckedBytePtr limited_source = LimitSource(source);
217
218 bool use_big_endian;
219 if (!DetermineEndianness(limited_source, &use_big_endian)) {
220 return false;
221 }
222
223 const unsigned short kTag = 42; // NOLINT
224 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
225 kTag)) {
226 return false;
227 }
228
229 const string kSignature("CR\2\0", 4);
230 return IsSignatureMatched(limited_source, 8 /* offset */, kSignature);
231 }
232 };
233
234 // Canon RAW (CRW extension).
235 class CrwTypeChecker : public TypeChecker {
236 public:
Type() const237 virtual RawImageTypes Type() const { return kCrwImage; }
238
RequestedSize() const239 virtual size_t RequestedSize() const { return 14; }
240
241 // Check only the signature at the (offset == 6) position of the file.
IsMyType(const RangeCheckedBytePtr & source) const242 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
243 RangeCheckedBytePtr limited_source = LimitSource(source);
244
245 bool use_big_endian;
246 if (!DetermineEndianness(limited_source, &use_big_endian)) {
247 return false;
248 }
249
250 string signature;
251 if (use_big_endian) {
252 signature = string("\x00\x10\xba\xb0\xac\xbb\x00\x02", 8);
253 } else {
254 signature = string("HEAPCCDR");
255 }
256 return IsSignatureMatched(limited_source, 6 /* offset */, signature);
257 }
258 };
259
260 // Kodak RAW.
261 class DcrTypeChecker : public TypeChecker {
262 public:
Type() const263 virtual RawImageTypes Type() const { return kDcrImage; }
264
RequestedSize() const265 virtual size_t RequestedSize() const { return 5000; }
266
267 // Check two different cases, only need to fulfill one of the two:
268 // 1. signature at the (offset == 16) position of the file;
269 // 2. two tags (OriginalFileName and FirmwareVersion) can be found in the
270 // first requested bytes of the file.
IsMyType(const RangeCheckedBytePtr & source) const271 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
272 RangeCheckedBytePtr limited_source = LimitSource(source);
273
274 bool use_big_endian;
275 if (!DetermineEndianness(limited_source, &use_big_endian)) {
276 return false;
277 }
278
279 // Case 1: has signature
280 const string kSignature(
281 "\x4b\x4f\x44\x41\x4b\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20", 16);
282 if (IsSignatureMatched(limited_source, 16 /* offset */, kSignature)) {
283 return true;
284 }
285
286 // Case 2: search for tags in first requested bytes
287 string kIfdTags[2];
288 if (use_big_endian) {
289 kIfdTags[0] = string("\x03\xe9\x00\x02", 4); // OriginalFileName
290 kIfdTags[1] = string("\x0c\xe5\x00\x02", 4); // FirmwareVersion
291 } else {
292 kIfdTags[0] = string("\xe9\x03\x02\x00", 4); // OriginalFileName
293 kIfdTags[1] = string("\xe5\x0c\x02\x00", 4); // FirmwareVersion
294 }
295 return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
296 kIfdTags[0], NULL) &&
297 IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
298 kIfdTags[1], NULL);
299 }
300 };
301
302 // Digital Negative RAW.
303 class DngTypeChecker : public TypeChecker {
304 public:
Type() const305 virtual RawImageTypes Type() const { return kDngImage; }
306
RequestedSize() const307 virtual size_t RequestedSize() const { return 1024; }
308
309 // Check multiple points:
310 // 1. valid endianness at the beginning of the file;
311 // 2. at least two dng specific tags in the first requested bytes of the
312 // file
IsMyType(const RangeCheckedBytePtr & source) const313 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
314 RangeCheckedBytePtr limited_source = LimitSource(source);
315
316 bool use_big_endian;
317 if (!DetermineEndianness(limited_source, &use_big_endian)) {
318 return false;
319 }
320
321 // Search tags in first requested bytes and verify the order of them.
322 const int kTagsCount = 5;
323 string dng_tags[kTagsCount];
324 if (use_big_endian) {
325 dng_tags[0] =
326 string("\xc6\x12\x00\x01\x00\x00\x00\x04", 8); // tag: 50706
327 dng_tags[1] =
328 string("\xc6\x13\x00\x01\x00\x00\x00\x04", 8); // tag: 50707
329 dng_tags[2] = string("\xc6\x14\x00\x02", 4); // tag: 50708
330 dng_tags[3] = string("\xc6\x20", 2); // tag: 50720
331 dng_tags[4] =
332 string("\xc6\x2d\x00\x04\x00\x00\x00\x01", 8); // tag: 50733
333 } else {
334 dng_tags[0] =
335 string("\x12\xc6\x01\x00\x04\x00\x00\x00", 8); // tag: 50706
336 dng_tags[1] =
337 string("\x13\xc6\x01\x00\x04\x00\x00\x00", 8); // tag: 50707
338 dng_tags[2] = string("\x14\xc6\x02\x00", 4); // tag: 50708
339 dng_tags[3] = string("\x20\xc6", 2); // tag: 50720
340 dng_tags[4] =
341 string("\x2d\xc6\x04\x00\x01\x00\x00\x00", 8); // tag: 50733
342 }
343 int tags_found = 0;
344 for (int i = 0; i < kTagsCount; ++i) {
345 if (IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
346 dng_tags[i], NULL)) {
347 tags_found++;
348 }
349 }
350 return tags_found >= 2;
351 }
352 };
353
354 // Kodak RAW.
355 class KdcTypeChecker : public TypeChecker {
356 public:
Type() const357 virtual RawImageTypes Type() const { return kKdcImage; }
358
RequestedSize() const359 virtual size_t RequestedSize() const { return 5000; }
360
361 // Check two points:
362 // 1. valid endianness at the beginning of the file;
363 // 2. two tags (WhiteBalance and SerialNumber) in the first requested bytes.
IsMyType(const RangeCheckedBytePtr & source) const364 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
365 RangeCheckedBytePtr limited_source = LimitSource(source);
366
367 bool use_big_endian;
368 if (!DetermineEndianness(limited_source, &use_big_endian)) {
369 return false;
370 }
371
372 // Search in first requested bytes
373 const size_t kIfdTagsSize = 2;
374 string kIfdTags[kIfdTagsSize];
375 if (use_big_endian) {
376 kIfdTags[0] = string("\xfa\x0d\x00\x01", 4); // WhiteBalance
377 kIfdTags[1] = string("\xfa\x00\x00\x02", 4); // SerialNumber
378 } else {
379 kIfdTags[0] = string("\x0d\xfa\x01\x00", 4); // WhiteBalance
380 kIfdTags[1] = string("\x00\xfa\x02\x00", 4); // SerialNumber
381 }
382
383 return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
384 kIfdTags[0], NULL) &&
385 IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
386 kIfdTags[1], NULL);
387 }
388 };
389
390 // Leaf RAW.
391 class MosTypeChecker : public TypeChecker {
392 public:
Type() const393 virtual RawImageTypes Type() const { return kMosImage; }
394
RequestedSize() const395 virtual size_t RequestedSize() const { return 5000; }
396
397 // Check two points:
398 // 1. valid endianness at the beginning of the file;
399 // 2. signature "PKTS " in the first requested bytes. Note the
400 // "whitespace". It's important as they are special binary values.
IsMyType(const RangeCheckedBytePtr & source) const401 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
402 RangeCheckedBytePtr limited_source = LimitSource(source);
403
404 bool use_big_endian;
405 if (!DetermineEndianness(source, &use_big_endian)) {
406 return false;
407 }
408
409 // Search kSignaturePKTS in first requested bytes
410 const string kSignaturePKTS("PKTS\x00\x00\x00\x001", 8);
411 return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
412 kSignaturePKTS, NULL);
413 }
414 };
415
416 // Minolta RAW.
417 class MrwTypeChecker : public TypeChecker {
418 public:
Type() const419 virtual RawImageTypes Type() const { return kMrwImage; }
420
RequestedSize() const421 virtual size_t RequestedSize() const { return 4; }
422
423 // Check only the signature at the beginning of the file.
IsMyType(const RangeCheckedBytePtr & source) const424 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
425 // Limits the source length to the RequestedSize(), using it guarantees that
426 // we will not read more than this size from the source.
427 RangeCheckedBytePtr limited_source =
428 source.pointerToSubArray(0 /* pos */, RequestedSize());
429
430 const string kSignature("\0MRM", 4);
431 return IsSignatureMatched(limited_source, 0 /* offset */, kSignature);
432 }
433 };
434
435 // Check if the file contains a NRW signature "NRW " in the first requested
436 // bytes. Note the "whitespace". It's important as they are special binary
437 // values.
438 const size_t kRequestedSizeForNrwSignature = 4000;
ContainsNrwSignature(const RangeCheckedBytePtr & source)439 bool ContainsNrwSignature(const RangeCheckedBytePtr& source) {
440 // Search for kSignatureNrw.
441 const string kSignatureNrw("NRW\x20\x20\x20", 6);
442 return IsSignatureFound(source, 0 /* offset */, kRequestedSizeForNrwSignature,
443 kSignatureNrw, NULL);
444 }
445
446 // Checks if the file contains the signatures for Nikon formats:
447 // * the general Nikon singature "NIKON" string.
448 // * the ReferenceBlackWhite tag.
449 const size_t kRequestedSizeForNikonSignatures = 4000;
ContainsNikonSignatures(const RangeCheckedBytePtr & source,const bool use_big_endian)450 bool ContainsNikonSignatures(const RangeCheckedBytePtr& source,
451 const bool use_big_endian) {
452 const string kSignatureNikon("NIKON");
453 const string kReferenceBlackWhiteTag = use_big_endian
454 ? string("\x02\x14\x00\x05", 4)
455 : string("\x14\x02\x05\x00", 4);
456 const std::vector<string> kSignatures = {kSignatureNikon,
457 kReferenceBlackWhiteTag};
458 for (auto const& signature : kSignatures) {
459 if (!IsSignatureFound(source, 0, kRequestedSizeForNikonSignatures,
460 signature, NULL)) {
461 return false;
462 }
463 }
464 return true;
465 }
466
467 // Nikon RAW (NEF extension).
468 class NefTypeChecker : public TypeChecker {
469 public:
Type() const470 virtual RawImageTypes Type() const { return kNefImage; }
471
RequestedSize() const472 virtual size_t RequestedSize() const {
473 return std::max(kRequestedSizeForNikonSignatures,
474 kRequestedSizeForNrwSignature);
475 }
476
477 // Check multiple points:
478 // 1. valid endianness at the beginning of the file;
479 // 2. magic number at the (offset == 2) position of the file;
480 // 3. the signature "NIKON" in the requested bytes of the file;
481 // 4. the ReferenceBlackWhite tag in the requested bytes of the file;
482 // 5. does not contain the NRW signature. We may also check a special
483 // signature "RAW " similar to the NRW case, but we got issues in some
484 // special images that the signature locates in the middle of the file, and it
485 // costs too long time to check;
IsMyType(const RangeCheckedBytePtr & source) const486 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
487 RangeCheckedBytePtr limited_source = LimitSource(source);
488
489 bool use_big_endian;
490 if (!DetermineEndianness(limited_source, &use_big_endian)) {
491 return false;
492 }
493
494 const unsigned short kTiffMagic = 0x2A; // NOLINT
495 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
496 kTiffMagic)) {
497 return false;
498 }
499
500 return ContainsNikonSignatures(limited_source, use_big_endian) &&
501 !ContainsNrwSignature(limited_source); // not NRW
502 }
503 };
504
505 // Nikon RAW (NRW extension).
506 class NrwTypeChecker : public TypeChecker {
507 public:
Type() const508 virtual RawImageTypes Type() const { return kNrwImage; }
509
RequestedSize() const510 virtual size_t RequestedSize() const {
511 return std::max(kRequestedSizeForNikonSignatures,
512 kRequestedSizeForNrwSignature);
513 }
514
515 // Check multiple points:
516 // 1. valid endianness at the beginning of the file;
517 // 2. magic numbers at the (offset == 2 and offset == 4) positions of the
518 // file;
519 // 3. the signature "NIKON" in the first requested bytes of the file;
520 // 4. the ReferenceBlackWhite tag in the requested bytes of the file;
521 // 5. contains the NRW signature;
IsMyType(const RangeCheckedBytePtr & source) const522 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
523 RangeCheckedBytePtr limited_source = LimitSource(source);
524
525 bool use_big_endian;
526 if (!DetermineEndianness(limited_source, &use_big_endian)) {
527 return false;
528 }
529
530 const unsigned short kTiffMagic = 0x2A; // NOLINT
531 const unsigned int kTiffOffset = 8;
532 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
533 kTiffMagic) ||
534 !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian,
535 kTiffOffset)) {
536 return false;
537 }
538
539 return ContainsNikonSignatures(limited_source, use_big_endian) &&
540 ContainsNrwSignature(limited_source);
541 }
542 };
543
544 // Olympus RAW.
545 class OrfTypeChecker : public TypeChecker {
546 public:
Type() const547 virtual RawImageTypes Type() const { return kOrfImage; }
548
RequestedSize() const549 virtual size_t RequestedSize() const { return 3000; }
550
551 // Check multiple points:
552 // 1. valid endianness at the beginning of the file;
553 // 2. tag at the (offset == 2) position of the file;
554 // 3. signature "OLYMP" in the first requested bytes.
IsMyType(const RangeCheckedBytePtr & source) const555 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
556 RangeCheckedBytePtr limited_source = LimitSource(source);
557
558 bool use_big_endian;
559 if (!DetermineEndianness(limited_source, &use_big_endian)) {
560 return false;
561 }
562
563 const size_t kTagSize = 2;
564 const unsigned short kTag[kTagSize] = {0x4F52, 0x5352}; // NOLINT
565 if (!(CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
566 kTag[0]) ||
567 CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
568 kTag[1]))) {
569 return false;
570 }
571
572 // Search for kSignatureOlymp in first requested bytes
573 const string kSignatureOlymp("OLYMP");
574 return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
575 kSignatureOlymp, NULL);
576 }
577 };
578
579 // Pentax RAW.
580 class PefTypeChecker : public TypeChecker {
581 public:
Type() const582 virtual RawImageTypes Type() const { return kPefImage; }
583
RequestedSize() const584 virtual size_t RequestedSize() const { return 1280; }
585
586 // Check multiple points:
587 // 1. valid big endianness at the beginning of the file;
588 // 2. magic numbers at the (offset == 2 and offset==4) positions of the file;
589 // 3. signature "AOC " or "PENTAX " in first requested bytes.
IsMyType(const RangeCheckedBytePtr & source) const590 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
591 RangeCheckedBytePtr limited_source = LimitSource(source);
592
593 bool use_big_endian;
594 if (!DetermineEndianness(limited_source, &use_big_endian)) {
595 return false;
596 }
597
598 const unsigned short kTiffMagic = 0x2A; // NOLINT
599 const unsigned int kTiffOffset = 8;
600 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
601 kTiffMagic) ||
602 !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian,
603 kTiffOffset)) {
604 return false;
605 }
606
607 // Search for kSignatureAOC or kSignaturePENTAX in first requested bytes
608 const string kSignatureAOC("\x41\x4f\x43\x00\x4d\x4d", 6);
609 const string kSignaturePENTAX("\x50\x45\x4e\x54\x41\x58\x20\x00", 8);
610 return IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
611 kSignatureAOC, NULL) ||
612 IsSignatureFound(limited_source, 0 /* offset */, RequestedSize(),
613 kSignaturePENTAX, NULL);
614 }
615 };
616
617 // Apple format.
618 class QtkTypeChecker : public TypeChecker {
619 public:
Type() const620 virtual RawImageTypes Type() const { return kQtkImage; }
621
RequestedSize() const622 virtual size_t RequestedSize() const { return 8; }
623
624 // Check only the signature at the beginning of the file.
IsMyType(const RangeCheckedBytePtr & source) const625 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
626 RangeCheckedBytePtr limited_source = LimitSource(source);
627
628 const size_t kSignatureSize = 2;
629 const string kSignature[kSignatureSize] = {
630 string("qktk\x00\x00\x00\x08", 8), string("qktn\x00\x00\x00\x08", 8),
631 };
632 return IsSignatureMatched(limited_source, 0 /* offset */, kSignature[0]) ||
633 IsSignatureMatched(limited_source, 0 /* offset */, kSignature[1]);
634 }
635 };
636
637 // Fuji RAW.
638 class RafTypeChecker : public TypeChecker {
639 public:
Type() const640 virtual RawImageTypes Type() const { return kRafImage; }
641
RequestedSize() const642 virtual size_t RequestedSize() const { return 8; }
643
644 // Check only the signature at the beginning of the file.
IsMyType(const RangeCheckedBytePtr & source) const645 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
646 RangeCheckedBytePtr limited_source = LimitSource(source);
647
648 const string kSignature("FUJIFILM");
649 return IsSignatureMatched(limited_source, 0 /* offset */, kSignature);
650 }
651 };
652
653 // Contax N RAW.
654 class RawContaxNTypeChecker : public TypeChecker {
655 public:
Type() const656 virtual RawImageTypes Type() const { return kRawContaxNImage; }
657
RequestedSize() const658 virtual size_t RequestedSize() const { return 36; }
659
660 // Check only the signature at the (offset == 25) position of the
661 // file.
IsMyType(const RangeCheckedBytePtr & source) const662 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
663 RangeCheckedBytePtr limited_source = LimitSource(source);
664
665 const string kSignature("ARECOYK");
666 return IsSignatureMatched(limited_source, 25, kSignature);
667 }
668 };
669
670 // Panasonic RAW.
671 class Rw2TypeChecker : public TypeChecker {
672 public:
Type() const673 virtual RawImageTypes Type() const { return kRw2Image; }
674
RequestedSize() const675 virtual size_t RequestedSize() const { return 4; }
676
677 // Check two points: 1. valid endianness at the beginning of the
678 // file; 2. tag at the (offset == 2) position of the file.
IsMyType(const RangeCheckedBytePtr & source) const679 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
680 RangeCheckedBytePtr limited_source = LimitSource(source);
681
682 bool use_big_endian;
683 if (!DetermineEndianness(source, &use_big_endian)) {
684 return false;
685 }
686
687 const unsigned short kTag = 0x55; // NOLINT
688 return CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
689 kTag);
690 }
691 };
692
693 // Samsung RAW.
694 class SrwTypeChecker : public TypeChecker {
695 public:
Type() const696 virtual RawImageTypes Type() const { return kSrwImage; }
697
RequestedSize() const698 virtual size_t RequestedSize() const { return 256; }
699
700 // Check multiple points:
701 // 1. valid big endianness at the beginning of the file;
702 // 2. magic numbers at the (offset == 2 and offset==4) positions of the file;
703 // 3. the signature "SAMSUNG" in the requested bytes of the file;
IsMyType(const RangeCheckedBytePtr & source) const704 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
705 RangeCheckedBytePtr limited_source = LimitSource(source);
706
707 bool use_big_endian;
708 if (!DetermineEndianness(source, &use_big_endian)) {
709 return false;
710 }
711
712 const unsigned short kTiffMagic = 0x2A; // NOLINT
713 const unsigned int kTiffOffset = 8;
714 if (!CheckUInt16Value(limited_source, 2 /* offset */, use_big_endian,
715 kTiffMagic) ||
716 !CheckUInt32Value(limited_source, 4 /* offset */, use_big_endian,
717 kTiffOffset)) {
718 return false;
719 }
720
721 const string kSignature("SAMSUNG");
722 if (!IsSignatureFound(source, 0, RequestedSize(), kSignature, NULL)) {
723 return false;
724 }
725 return true;
726 }
727 };
728
729 // Sigma / Polaroid RAW.
730 class X3fTypeChecker : public TypeChecker {
731 public:
Type() const732 virtual RawImageTypes Type() const { return kX3fImage; }
733
RequestedSize() const734 virtual size_t RequestedSize() const { return 4; }
735
736 // Check only the signature at the beginning of the file.
IsMyType(const RangeCheckedBytePtr & source) const737 virtual bool IsMyType(const RangeCheckedBytePtr& source) const {
738 RangeCheckedBytePtr limited_source = LimitSource(source);
739
740 const string kSignature("FOVb", 4);
741 return IsSignatureMatched(limited_source, 0 /* offset */, kSignature);
742 }
743 };
744
745 // This class contains the list of all type checkers. One should used this list
746 // as a whole to execute the image type recognition.
747 class TypeCheckerList {
748 public:
TypeCheckerList()749 TypeCheckerList() {
750 // Add all supported RAW type checkers here.
751 checkers_.push_back(new ArwTypeChecker());
752 checkers_.push_back(new Cr2TypeChecker());
753 checkers_.push_back(new CrwTypeChecker());
754 checkers_.push_back(new DcrTypeChecker());
755 checkers_.push_back(new DngTypeChecker());
756 checkers_.push_back(new KdcTypeChecker());
757 checkers_.push_back(new MosTypeChecker());
758 checkers_.push_back(new MrwTypeChecker());
759 checkers_.push_back(new NefTypeChecker());
760 checkers_.push_back(new NrwTypeChecker());
761 checkers_.push_back(new OrfTypeChecker());
762 checkers_.push_back(new PefTypeChecker());
763 checkers_.push_back(new QtkTypeChecker());
764 checkers_.push_back(new RafTypeChecker());
765 checkers_.push_back(new RawContaxNTypeChecker());
766 checkers_.push_back(new Rw2TypeChecker());
767 checkers_.push_back(new SrwTypeChecker());
768 checkers_.push_back(new X3fTypeChecker());
769
770 // Sort the checkers by the ascending RequestedSize() to get better
771 // performance when checking type.
772 std::sort(checkers_.begin(), checkers_.end(), TypeChecker::Compare);
773 }
774
~TypeCheckerList()775 ~TypeCheckerList() {
776 for (size_t i = 0; i < checkers_.size(); ++i) {
777 delete checkers_[i];
778 checkers_[i] = NULL;
779 }
780 }
781
782 // Returns the type of source data. If it can not be identified, returns
783 // kNonRawImage.
GetType(const RangeCheckedBytePtr & source) const784 RawImageTypes GetType(const RangeCheckedBytePtr& source) const {
785 for (size_t i = 0; i < checkers_.size(); ++i) {
786 if (checkers_[i]->IsMyType(source)) {
787 return checkers_[i]->Type();
788 }
789 }
790 return kNonRawImage;
791 }
792
793 // Returns the maximum size of requested size of data for identifying image
794 // type using this class. The class guarantees that it will not read more than
795 // this size.
RequestedSize() const796 size_t RequestedSize() const {
797 assert(!checkers_.empty());
798 // The checkers_ is ascending sorted. The last element is the maximum.
799 return checkers_.back()->RequestedSize();
800 }
801
IsOfType(const RangeCheckedBytePtr & source,const RawImageTypes type)802 bool IsOfType(const RangeCheckedBytePtr& source, const RawImageTypes type) {
803 const TypeChecker* type_checker = GetTypeCheckerForType(type);
804 if (type_checker) {
805 return type_checker->IsMyType(source);
806 } else {
807 return false;
808 }
809 }
810
RequestedSizeForType(const RawImageTypes type)811 size_t RequestedSizeForType(const RawImageTypes type) {
812 const TypeChecker* type_checker = GetTypeCheckerForType(type);
813 if (type_checker) {
814 return type_checker->RequestedSize();
815 } else {
816 return 0;
817 }
818 }
819
820 private:
GetTypeCheckerForType(const RawImageTypes type)821 const TypeChecker* GetTypeCheckerForType(const RawImageTypes type) {
822 for (const auto* type_checker : checkers_) {
823 if (type_checker->Type() == type) {
824 return type_checker;
825 }
826 }
827 return nullptr;
828 }
829
830 std::vector<TypeChecker*> checkers_;
831 };
832
833 } // namespace
834
IsRaw(const RawImageTypes type)835 bool IsRaw(const RawImageTypes type) {
836 switch (type) {
837 // Non-RAW-image type
838 case kNonRawImage: {
839 return false;
840 }
841
842 // Raw image types
843 case kArwImage:
844 case kCr2Image:
845 case kCrwImage:
846 case kDcrImage:
847 case kDngImage:
848 case kKdcImage:
849 case kMosImage:
850 case kMrwImage:
851 case kNefImage:
852 case kNrwImage:
853 case kOrfImage:
854 case kPefImage:
855 case kQtkImage:
856 case kRafImage:
857 case kRawContaxNImage:
858 case kRw2Image:
859 case kSrwImage:
860 case kX3fImage: {
861 return true;
862 }
863
864 default: {
865 // Unsupported type!
866 assert(false);
867 }
868 }
869 return false;
870 }
871
IsOfType(const RangeCheckedBytePtr & source,const RawImageTypes type)872 bool IsOfType(const RangeCheckedBytePtr& source, const RawImageTypes type) {
873 return TypeCheckerList().IsOfType(source, type);
874 }
875
RecognizeRawImageTypeLite(const RangeCheckedBytePtr & source)876 RawImageTypes RecognizeRawImageTypeLite(const RangeCheckedBytePtr& source) {
877 return TypeCheckerList().GetType(source);
878 }
879
GetNumberOfBytesForIsRawLite()880 size_t GetNumberOfBytesForIsRawLite() {
881 return TypeCheckerList().RequestedSize();
882 }
883
GetNumberOfBytesForIsOfType(const RawImageTypes type)884 size_t GetNumberOfBytesForIsOfType(const RawImageTypes type) {
885 return TypeCheckerList().RequestedSizeForType(type);
886 }
887
IsRawLite(const RangeCheckedBytePtr & source)888 bool IsRawLite(const RangeCheckedBytePtr& source) {
889 return IsRaw(RecognizeRawImageTypeLite(source));
890 }
891
892 } // namespace image_type_recognition
893 } // namespace piex
894