blob: 5e5ec792ea2240d40cc9e0d3af6321305a8987b9 [file] [log] [blame]
/*
* 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;
}