/* * Copyright 2023 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/codec/SkJpegSegmentScan.h" #include "include/core/SkData.h" #include "include/core/SkStream.h" #include "include/private/base/SkAssert.h" #include "src/codec/SkCodecPriv.h" #include "src/codec/SkJpegConstants.h" #include #include //////////////////////////////////////////////////////////////////////////////////////////////////// // SkJpegSegmentScanner SkJpegSegmentScanner::SkJpegSegmentScanner(uint8_t stopMarker) : fStopMarker(stopMarker) {} const std::vector& SkJpegSegmentScanner::getSegments() const { return fSegments; } sk_sp SkJpegSegmentScanner::GetParameters(const SkData* scannedData, const SkJpegSegment& segment) { return SkData::MakeSubset( scannedData, segment.offset + kJpegMarkerCodeSize + kJpegSegmentParameterLengthSize, segment.parameterLength - kJpegSegmentParameterLengthSize); } void SkJpegSegmentScanner::onBytes(const void* data, size_t size) { const uint8_t* bytes = reinterpret_cast(data); size_t bytesRemaining = size; while (bytesRemaining > 0) { // Process the data byte-by-byte, unless we are in kSegmentParam or kEntropyCodedData, in // which case, perform some optimizations to avoid examining every byte. size_t bytesToMoveForward = 0; switch (fState) { case State::kSegmentParam: { // Skip forward through payloads. SkASSERT(fSegmentParamBytesRemaining > 0); bytesToMoveForward = std::min(fSegmentParamBytesRemaining, bytesRemaining); fSegmentParamBytesRemaining -= bytesToMoveForward; if (fSegmentParamBytesRemaining == 0) { fState = State::kEntropyCodedData; } break; } case State::kEntropyCodedData: { // Skip through entropy-coded data, only looking at sentinel characters. const uint8_t* sentinel = reinterpret_cast(memchr(bytes, 0xFF, bytesRemaining)); if (sentinel) { bytesToMoveForward = (sentinel - bytes) + 1; fState = State::kEntropyCodedDataSentinel; } else { bytesToMoveForward = bytesRemaining; } break; } case State::kDone: // Skip all data after we have hit our stop marker. bytesToMoveForward = bytesRemaining; break; default: { onByte(*bytes); bytesToMoveForward = 1; break; } } SkASSERT(bytesToMoveForward > 0); fOffset += bytesToMoveForward; bytes += bytesToMoveForward; bytesRemaining -= bytesToMoveForward; } } void SkJpegSegmentScanner::saveCurrentSegment(uint16_t length) { SkJpegSegment s = {fCurrentSegmentOffset, fCurrentSegmentMarker, length}; fSegments.push_back(s); fCurrentSegmentMarker = 0; fCurrentSegmentOffset = 0; } void SkJpegSegmentScanner::onMarkerSecondByte(uint8_t byte) { SkASSERT(fState == State::kStartOfImageByte1 || fState == State::kSecondMarkerByte1 || fState == State::kEntropyCodedDataSentinel || fState == State::kPostEntropyCodedDataFill); fCurrentSegmentMarker = byte; fCurrentSegmentOffset = fOffset - 1; if (byte == fStopMarker) { saveCurrentSegment(0); fState = State::kDone; } else if (byte == kJpegMarkerStartOfImage) { saveCurrentSegment(0); fState = State::kSecondMarkerByte0; } else if (MarkerStandsAlone(byte)) { saveCurrentSegment(0); fState = State::kEntropyCodedData; } else { fCurrentSegmentMarker = byte; fState = State::kSegmentParamLengthByte0; } } void SkJpegSegmentScanner::onByte(uint8_t byte) { switch (fState) { case State::kStartOfImageByte0: if (byte != 0xFF) { SkCodecPrintf("First byte was %02x, not 0xFF", byte); fState = State::kError; return; } fState = State::kStartOfImageByte1; break; case State::kStartOfImageByte1: if (byte != kJpegMarkerStartOfImage) { SkCodecPrintf("Second byte was %02x, not %02x", byte, kJpegMarkerStartOfImage); fState = State::kError; return; } onMarkerSecondByte(byte); break; case State::kSecondMarkerByte0: if (byte != 0xFF) { SkCodecPrintf("Third byte was %02x, not 0xFF", byte); fState = State::kError; return; } fState = State::kSecondMarkerByte1; break; case State::kSecondMarkerByte1: // See section B.1.1.3: All markers are assigned two-byte codes: a 0xFF byte followed by // a byte which is not equal to 0x00 or 0xFF. if (byte == 0xFF || byte == 0x00) { SkCodecPrintf("SkJpegSegment marker was 0xFF,0xFF or 0xFF,0x00"); fState = State::kError; return; } onMarkerSecondByte(byte); break; case State::kSegmentParamLengthByte0: fSegmentParamLengthByte0 = byte; fState = State::kSegmentParamLengthByte1; break; case State::kSegmentParamLengthByte1: { uint16_t paramLength = 256u * fSegmentParamLengthByte0 + byte; fSegmentParamLengthByte0 = 0; // See section B.1.1.4: A marker segment consists of a marker followed by a sequence // of related parameters. The first parameter in a marker segment is the two-byte length // parameter. This length parameter encodes the number of bytes in the marker segment, // including the length parameter and excluding the two-byte marker. if (paramLength < kJpegSegmentParameterLengthSize) { SkCodecPrintf("SkJpegSegment payload length was %u < 2 bytes", paramLength); fState = State::kError; return; } saveCurrentSegment(paramLength); fSegmentParamBytesRemaining = paramLength - kJpegSegmentParameterLengthSize; if (fSegmentParamBytesRemaining > 0) { fState = State::kSegmentParam; } else { fState = State::kEntropyCodedData; } break; } case State::kSegmentParam: SkASSERT(fSegmentParamBytesRemaining > 0); fSegmentParamBytesRemaining -= 1; if (fSegmentParamBytesRemaining == 0) { fState = State::kEntropyCodedData; } break; case State::kEntropyCodedData: if (byte == 0xFF) { fState = State::kEntropyCodedDataSentinel; } break; case State::kEntropyCodedDataSentinel: if (byte == 0x00) { fState = State::kEntropyCodedData; } else if (byte == 0xFF) { fState = State::kPostEntropyCodedDataFill; } else { onMarkerSecondByte(byte); } break; case State::kPostEntropyCodedDataFill: // See section B.1.1.3: Any marker may optionally be preceded by any number of fill // bytes, which are bytes assigned code 0xFF. Skip past any 0xFF fill bytes that may be // present at the end of the entropy-coded data. if (byte == 0xFF) { fState = State::kPostEntropyCodedDataFill; } else if (byte == 0x00) { SkCodecPrintf("Post entropy coded data had 0xFF,0x00"); fState = State::kError; return; } else { onMarkerSecondByte(byte); } break; case State::kDone: break; case State::kError: break; } }