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 "src/pdf/SkJpegInfo.h"
9 
10 #include "include/private/SkTo.h"
11 
12 #ifndef SK_HAS_JPEG_LIBRARY
13 
14 namespace {
15 class JpegSegment {
16 public:
JpegSegment(const void * data,size_t size)17     JpegSegment(const void* data, size_t size)
18         : fData(static_cast<const char*>(data))
19         , fSize(size)
20         , fOffset(0)
21         , fLength(0) {}
read()22     bool read() {
23         if (!this->readBigendianUint16(&fMarker)) {
24             return false;
25         }
26         if (JpegSegment::StandAloneMarker(fMarker)) {
27             fLength = 0;
28             fBuffer = nullptr;
29             return true;
30         }
31         if (!this->readBigendianUint16(&fLength) || fLength < 2) {
32             return false;
33         }
34         fLength -= 2;  // Length includes itself for some reason.
35         if (fOffset + fLength > fSize) {
36             return false;  // Segment too long.
37         }
38         fBuffer = &fData[fOffset];
39         fOffset += fLength;
40         return true;
41     }
42 
isSOF()43     bool isSOF() {
44         return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
45                fMarker != 0xFFC8 && fMarker != 0xFFCC;
46     }
marker()47     uint16_t marker() { return fMarker; }
length()48     uint16_t length() { return fLength; }
data()49     const char* data() { return fBuffer; }
50 
GetBigendianUint16(const char * ptr)51     static uint16_t GetBigendianUint16(const char* ptr) {
52         // "the most significant byte shall come first"
53         return (static_cast<uint8_t>(ptr[0]) << 8) |
54             static_cast<uint8_t>(ptr[1]);
55     }
56 
57 private:
58     const char* const fData;
59     const size_t fSize;
60     size_t fOffset;
61     const char* fBuffer;
62     uint16_t fMarker;
63     uint16_t fLength;
64 
readBigendianUint16(uint16_t * value)65     bool readBigendianUint16(uint16_t* value) {
66         if (fOffset + 2 > fSize) {
67             return false;
68         }
69         *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
70         fOffset += 2;
71         return true;
72     }
StandAloneMarker(uint16_t marker)73     static bool StandAloneMarker(uint16_t marker) {
74         // RST[m] markers or SOI, EOI, TEM
75         return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
76                marker == 0xFFD9 || marker == 0xFF01;
77     }
78 };
79 }  // namespace
80 
SkGetJpegInfo(const void * data,size_t len,SkISize * size,SkEncodedInfo::Color * colorType,SkEncodedOrigin * orientation)81 bool SkGetJpegInfo(const void* data, size_t len,
82                    SkISize* size,
83                    SkEncodedInfo::Color* colorType,
84                    SkEncodedOrigin* orientation) {
85     static const uint16_t kSOI = 0xFFD8;
86     static const uint16_t kAPP0 = 0xFFE0;
87     JpegSegment segment(data, len);
88     if (!segment.read() || segment.marker() != kSOI) {
89         return false;  // not a JPEG
90     }
91     if (!segment.read() || segment.marker() != kAPP0) {
92         return false;  // not an APP0 segment
93     }
94     static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
95     SkASSERT(segment.data());
96     if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
97         0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
98         return false;  // Not JFIF JPEG
99     }
100     do {
101         if (!segment.read()) {
102             return false;  // malformed JPEG
103         }
104     } while (!segment.isSOF());
105     if (segment.length() < 6) {
106         return false;  // SOF segment is short
107     }
108     if (8 != segment.data()[0]) {
109         return false;  // Only support 8-bit precision
110     }
111     int numberOfComponents = segment.data()[5];
112     if (numberOfComponents != 1 && numberOfComponents != 3) {
113         return false;  // Invalid JFIF
114     }
115     if (size) {
116         *size = {JpegSegment::GetBigendianUint16(&segment.data()[3]),
117                  JpegSegment::GetBigendianUint16(&segment.data()[1])};
118     }
119     if (colorType) {
120         *colorType = numberOfComponents == 3 ? SkEncodedInfo::kYUV_Color
121                                              : SkEncodedInfo::kGray_Color;
122     }
123     if (orientation) {
124         *orientation = kTopLeft_SkEncodedOrigin;
125     }
126     return true;
127 }
128 #endif  // SK_HAS_JPEG_LIBRARY
129