Home | History | Annotate | Download | only in pdf
      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 "SkData.h"
      9 #include "SkJpegInfo.h"
     10 
     11 namespace {
     12 class JpegSegment {
     13 public:
     14     JpegSegment(const SkData* skdata)
     15         : fData(static_cast<const char*>(skdata->data()))
     16         , fSize(skdata->size())
     17         , fOffset(0)
     18         , fLength(0) {}
     19     bool read() {
     20         if (!this->readBigendianUint16(&fMarker)) {
     21             return false;
     22         }
     23         if (JpegSegment::StandAloneMarker(fMarker)) {
     24             fLength = 0;
     25             fBuffer = nullptr;
     26             return true;
     27         }
     28         if (!this->readBigendianUint16(&fLength) || fLength < 2) {
     29             return false;
     30         }
     31         fLength -= 2;  // Length includes itself for some reason.
     32         if (fOffset + fLength > fSize) {
     33             return false;  // Segment too long.
     34         }
     35         fBuffer = &fData[fOffset];
     36         fOffset += fLength;
     37         return true;
     38     }
     39 
     40     bool isSOF() {
     41         return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
     42                fMarker != 0xFFC8 && fMarker != 0xFFCC;
     43     }
     44     uint16_t marker() { return fMarker; }
     45     uint16_t length() { return fLength; }
     46     const char* data() { return fBuffer; }
     47 
     48     static uint16_t GetBigendianUint16(const char* ptr) {
     49         // "the most significant byte shall come first"
     50         return (static_cast<uint8_t>(ptr[0]) << 8) |
     51             static_cast<uint8_t>(ptr[1]);
     52     }
     53 
     54 private:
     55     const char* const fData;
     56     const size_t fSize;
     57     size_t fOffset;
     58     const char* fBuffer;
     59     uint16_t fMarker;
     60     uint16_t fLength;
     61 
     62     bool readBigendianUint16(uint16_t* value) {
     63         if (fOffset + 2 > fSize) {
     64             return false;
     65         }
     66         *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
     67         fOffset += 2;
     68         return true;
     69     }
     70     static bool StandAloneMarker(uint16_t marker) {
     71         // RST[m] markers or SOI, EOI, TEM
     72         return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
     73                marker == 0xFFD9 || marker == 0xFF01;
     74     }
     75 };
     76 }  // namespace
     77 
     78 bool SkIsJFIF(const SkData* skdata, SkJFIFInfo* info) {
     79     static const uint16_t kSOI = 0xFFD8;
     80     static const uint16_t kAPP0 = 0xFFE0;
     81     JpegSegment segment(skdata);
     82     if (!segment.read() || segment.marker() != kSOI) {
     83         return false;  // not a JPEG
     84     }
     85     if (!segment.read() || segment.marker() != kAPP0) {
     86         return false;  // not an APP0 segment
     87     }
     88     static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
     89     SkASSERT(segment.data());
     90     if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
     91         0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
     92         return false;  // Not JFIF JPEG
     93     }
     94     do {
     95         if (!segment.read()) {
     96             return false;  // malformed JPEG
     97         }
     98     } while (!segment.isSOF());
     99     if (segment.length() < 6) {
    100         return false;  // SOF segment is short
    101     }
    102     if (8 != segment.data()[0]) {
    103         return false;  // Only support 8-bit precision
    104     }
    105     int numberOfComponents = segment.data()[5];
    106     if (numberOfComponents != 1 && numberOfComponents != 3) {
    107         return false;  // Invalid JFIF
    108     }
    109     if (info) {
    110         info->fSize.set(JpegSegment::GetBigendianUint16(&segment.data()[3]),
    111                         JpegSegment::GetBigendianUint16(&segment.data()[1]));
    112         if (numberOfComponents == 3) {
    113             info->fType = SkJFIFInfo::kYCbCr;
    114         } else {
    115             info->fType = SkJFIFInfo::kGrayscale;
    116         }
    117     }
    118     return true;
    119 }
    120