| /* | |
| * 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 = 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 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->fSize.set(JpegSegment::GetBigendianUint16(&segment.data()[3]), | |
| JpegSegment::GetBigendianUint16(&segment.data()[1])); | |
| if (numberOfComponents == 3) { | |
| info->fType = SkJFIFInfo::kYCbCr; | |
| } else { | |
| info->fType = SkJFIFInfo::kGrayscale; | |
| } | |
| } | |
| return true; | |
| } | |