| /* |
| * 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 "SkJpegInfo.h" |
| |
| #include "SkTo.h" |
| |
| #ifndef SK_HAS_JPEG_LIBRARY |
| |
| 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_HAS_JPEG_LIBRARY |