diff options
Diffstat (limited to 'gfx/skia/skia/src/pdf/SkJpegInfo.cpp')
-rw-r--r-- | gfx/skia/skia/src/pdf/SkJpegInfo.cpp | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/pdf/SkJpegInfo.cpp b/gfx/skia/skia/src/pdf/SkJpegInfo.cpp new file mode 100644 index 0000000000..b0c72d011c --- /dev/null +++ b/gfx/skia/skia/src/pdf/SkJpegInfo.cpp @@ -0,0 +1,128 @@ +/* + * 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 "src/pdf/SkJpegInfo.h" + +#include "include/private/base/SkTo.h" + +#ifndef SK_CODEC_DECODES_JPEG + +namespace { +class JpegSegment { +public: + JpegSegment(const void* data, size_t size) + : fData(static_cast<const char*>(data)) + , fSize(size) + , fOffset(0) + , fLength(0) {} + bool read() { + if (!this->readBigendianUint16(&fMarker)) { + return false; + } + if (JpegSegment::StandAloneMarker(fMarker)) { + fLength = 0; + fBuffer = nullptr; + 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 SkGetJpegInfo(const void* data, size_t len, + SkISize* size, + SkEncodedInfo::Color* colorType, + SkEncodedOrigin* orientation) { + static const uint16_t kSOI = 0xFFD8; + static const uint16_t kAPP0 = 0xFFE0; + JpegSegment segment(data, len); + 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 (size) { + *size = {JpegSegment::GetBigendianUint16(&segment.data()[3]), + JpegSegment::GetBigendianUint16(&segment.data()[1])}; + } + if (colorType) { + *colorType = numberOfComponents == 3 ? SkEncodedInfo::kYUV_Color + : SkEncodedInfo::kGray_Color; + } + if (orientation) { + *orientation = kTopLeft_SkEncodedOrigin; + } + return true; +} +#endif // SK_CODEC_DECODES_JPEG |