• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkData.h"
9 #include "SkJpegInfo.h"
10 
11 namespace {
12 class JpegSegment {
13 public:
JpegSegment(const SkData * skdata)14     JpegSegment(const SkData* skdata)
15         : fData(static_cast<const char*>(skdata->data()))
16         , fSize(skdata->size())
17         , fOffset(0)
18         , fLength(0) {}
read()19     bool read() {
20         if (!this->readBigendianUint16(&fMarker)) {
21             return false;
22         }
23         if (JpegSegment::StandAloneMarker(fMarker)) {
24             fLength = 0;
25             fBuffer = nullptr;
26             return true;
27         }
28         if (!this->readBigendianUint16(&fLength) || fLength < 2) {
29             return false;
30         }
31         fLength -= 2;  // Length includes itself for some reason.
32         if (fOffset + fLength > fSize) {
33             return false;  // Segment too long.
34         }
35         fBuffer = &fData[fOffset];
36         fOffset += fLength;
37         return true;
38     }
39 
isSOF()40     bool isSOF() {
41         return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
42                fMarker != 0xFFC8 && fMarker != 0xFFCC;
43     }
marker()44     uint16_t marker() { return fMarker; }
length()45     uint16_t length() { return fLength; }
data()46     const char* data() { return fBuffer; }
47 
GetBigendianUint16(const char * ptr)48     static uint16_t GetBigendianUint16(const char* ptr) {
49         // "the most significant byte shall come first"
50         return (static_cast<uint8_t>(ptr[0]) << 8) |
51             static_cast<uint8_t>(ptr[1]);
52     }
53 
54 private:
55     const char* const fData;
56     const size_t fSize;
57     size_t fOffset;
58     const char* fBuffer;
59     uint16_t fMarker;
60     uint16_t fLength;
61 
readBigendianUint16(uint16_t * value)62     bool readBigendianUint16(uint16_t* value) {
63         if (fOffset + 2 > fSize) {
64             return false;
65         }
66         *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
67         fOffset += 2;
68         return true;
69     }
StandAloneMarker(uint16_t marker)70     static bool StandAloneMarker(uint16_t marker) {
71         // RST[m] markers or SOI, EOI, TEM
72         return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
73                marker == 0xFFD9 || marker == 0xFF01;
74     }
75 };
76 }  // namespace
77 
SkIsJFIF(const SkData * skdata,SkJFIFInfo * info)78 bool SkIsJFIF(const SkData* skdata, SkJFIFInfo* info) {
79     static const uint16_t kSOI = 0xFFD8;
80     static const uint16_t kAPP0 = 0xFFE0;
81     JpegSegment segment(skdata);
82     if (!segment.read() || segment.marker() != kSOI) {
83         return false;  // not a JPEG
84     }
85     if (!segment.read() || segment.marker() != kAPP0) {
86         return false;  // not an APP0 segment
87     }
88     static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
89     SkASSERT(segment.data());
90     if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
91         0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
92         return false;  // Not JFIF JPEG
93     }
94     do {
95         if (!segment.read()) {
96             return false;  // malformed JPEG
97         }
98     } while (!segment.isSOF());
99     if (segment.length() < 6) {
100         return false;  // SOF segment is short
101     }
102     if (8 != segment.data()[0]) {
103         return false;  // Only support 8-bit precision
104     }
105     int numberOfComponents = segment.data()[5];
106     if (numberOfComponents != 1 && numberOfComponents != 3) {
107         return false;  // Invalid JFIF
108     }
109     if (info) {
110         info->fSize.set(JpegSegment::GetBigendianUint16(&segment.data()[3]),
111                         JpegSegment::GetBigendianUint16(&segment.data()[1]));
112         if (numberOfComponents == 3) {
113             info->fType = SkJFIFInfo::kYCbCr;
114         } else {
115             info->fType = SkJFIFInfo::kGrayscale;
116         }
117     }
118     return true;
119 }
120