| /* | |
| * Copyright 2023 Google LLC | |
| * | |
| * 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/SkAssert.h" | |
| #include "src/codec/SkCodecPriv.h" | |
| #include "src/codec/SkJpegConstants.h" | |
| #include <cstring> | |
| #include <utility> | |
| //////////////////////////////////////////////////////////////////////////////////////////////////// | |
| // SkJpegSegmentScanner | |
| SkJpegSegmentScanner::SkJpegSegmentScanner(uint8_t stopMarker) : fStopMarker(stopMarker) {} | |
| const std::vector<SkJpegSegment>& SkJpegSegmentScanner::getSegments() const { return fSegments; } | |
| sk_sp<SkData> 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<const uint8_t*>(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<const uint8_t*>(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; | |
| } | |
| } |