| Index: src/pdf/SkJpegInfo.cpp
|
| diff --git a/src/pdf/SkJpegInfo.cpp b/src/pdf/SkJpegInfo.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..85cd3251cdee9cf42c9e4c750fefedd77b3d2444
|
| --- /dev/null
|
| +++ b/src/pdf/SkJpegInfo.cpp
|
| @@ -0,0 +1,121 @@
|
| +/*
|
| + * Copyright 2015 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkData.h"
|
| +#include "SkJpegInfo.h"
|
| +
|
| +namespace {
|
| +class JpegSegment {
|
| +public:
|
| + JpegSegment(const SkData* skdata)
|
| + : fData(static_cast<const char*>(skdata->data()))
|
| + , fSize(skdata->size())
|
| + , fOffset(0)
|
| + , fLength(0) {}
|
| + bool read() {
|
| + if (!this->readBigendianUint16(&fMarker)) {
|
| + return false;
|
| + }
|
| + if (JpegSegment::StandAloneMarker(fMarker)) {
|
| + fLength = 0;
|
| + fBuffer = NULL;
|
| + return true;
|
| + }
|
| + if (!this->readBigendianUint16(&fLength) || fLength < 2) {
|
| + return false;
|
| + }
|
| + fLength -= 2; // Length includes itself for some reason.
|
| + if (fOffset + fLength > fSize) {
|
| + return false; // Segment too long.
|
| + }
|
| + fBuffer = &fData[fOffset];
|
| + fOffset += fLength;
|
| + return true;
|
| + }
|
| +
|
| + bool isSOF() {
|
| + return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
|
| + fMarker != 0xFFC8 && fMarker != 0xFFCC;
|
| + }
|
| + uint16_t marker() { return fMarker; }
|
| + uint16_t length() { return fLength; }
|
| + const char* data() { return fBuffer; }
|
| +
|
| + static uint16_t GetBigendianUint16(const char* ptr) {
|
| + // "the most significant byte shall come first"
|
| + return (static_cast<uint8_t>(ptr[0]) << 8) |
|
| + static_cast<uint8_t>(ptr[1]);
|
| + }
|
| +
|
| +private:
|
| + const char* const fData;
|
| + const size_t fSize;
|
| + size_t fOffset;
|
| + const char* fBuffer;
|
| + uint16_t fMarker;
|
| + uint16_t fLength;
|
| +
|
| + bool readBigendianUint16(uint16_t* value) {
|
| + if (fOffset + 2 > fSize) {
|
| + return false;
|
| + }
|
| + *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
|
| + fOffset += 2;
|
| + return true;
|
| + }
|
| + static bool StandAloneMarker(uint16_t marker) {
|
| + // RST[m] markers or SOI, EOI, TEM
|
| + return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
|
| + marker == 0xFFD9 || marker == 0xFF01;
|
| + }
|
| +};
|
| +} // namespace
|
| +
|
| +bool SkIsJFIF(const SkData* skdata, SkJFIFInfo* info) {
|
| + static const uint16_t kSOI = 0xFFD8;
|
| + static const uint16_t kAPP0 = 0xFFE0;
|
| + JpegSegment segment(skdata);
|
| + if (!segment.read() || segment.marker() != kSOI) {
|
| + return false; // not a JPEG
|
| + }
|
| + if (!segment.read() || segment.marker() != kAPP0) {
|
| + return false; // not an APP0 segment
|
| + }
|
| + static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
|
| + SkASSERT(segment.data());
|
| + if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
|
| + 0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
|
| + return false; // Not JFIF JPEG
|
| + }
|
| + do {
|
| + if (!segment.read()) {
|
| + return false; // malformed JPEG
|
| + }
|
| + } while (!segment.isSOF());
|
| + if (segment.length() < 6) {
|
| + return false; // SOF segment is short
|
| + }
|
| + if (8 != segment.data()[0]) {
|
| + return false; // Only support 8-bit precision
|
| + }
|
| + int numberOfComponents = segment.data()[5];
|
| + if (numberOfComponents != 1 && numberOfComponents != 3) {
|
| + return false; // Invalid JFIF
|
| + }
|
| + if (info) {
|
| + info->fHeight = JpegSegment::GetBigendianUint16(&segment.data()[1]);
|
| + info->fWidth = JpegSegment::GetBigendianUint16(&segment.data()[3]);
|
| + if (numberOfComponents == 3) {
|
| + info->fType = SkJFIFInfo::kYCbCr;
|
| + } else {
|
| + info->fType = SkJFIFInfo::kGrayscale;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
|
|