xref: /third_party/skia/src/pdf/SkJpegInfo.cpp (revision cb93a386)
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_CODEC_DECODES_JPEG
13
14namespace {
15class JpegSegment {
16public:
17    JpegSegment(const void* data, size_t size)
18        : fData(static_cast<const char*>(data))
19        , fSize(size)
20        , fOffset(0)
21        , fLength(0) {}
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
43    bool isSOF() {
44        return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
45               fMarker != 0xFFC8 && fMarker != 0xFFCC;
46    }
47    uint16_t marker() { return fMarker; }
48    uint16_t length() { return fLength; }
49    const char* data() { return fBuffer; }
50
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
57private:
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
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    }
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
81bool 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_CODEC_DECODES_JPEG
129