blob: 5e5ec792ea2240d40cc9e0d3af6321305a8987b9 [file] [log] [blame]
halcanary96287f72015-05-07 11:46:59 -07001/*
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
11namespace {
12class JpegSegment {
13public:
14 JpegSegment(const SkData* skdata)
15 : fData(static_cast<const char*>(skdata->data()))
16 , fSize(skdata->size())
17 , fOffset(0)
18 , fLength(0) {}
19 bool read() {
20 if (!this->readBigendianUint16(&fMarker)) {
21 return false;
22 }
23 if (JpegSegment::StandAloneMarker(fMarker)) {
24 fLength = 0;
halcanary96fcdcc2015-08-27 07:41:13 -070025 fBuffer = nullptr;
halcanary96287f72015-05-07 11:46:59 -070026 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
40 bool isSOF() {
41 return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
42 fMarker != 0xFFC8 && fMarker != 0xFFCC;
43 }
44 uint16_t marker() { return fMarker; }
45 uint16_t length() { return fLength; }
46 const char* data() { return fBuffer; }
47
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
54private:
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
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 }
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
78bool 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) {
halcanary7a14b312015-10-01 07:28:13 -0700110 info->fSize.set(JpegSegment::GetBigendianUint16(&segment.data()[3]),
111 JpegSegment::GetBigendianUint16(&segment.data()[1]));
halcanary96287f72015-05-07 11:46:59 -0700112 if (numberOfComponents == 3) {
113 info->fType = SkJFIFInfo::kYCbCr;
114 } else {
115 info->fType = SkJFIFInfo::kGrayscale;
116 }
117 }
118 return true;
119}