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