Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkJpegSegmentScan.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 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
9
10#include "include/core/SkData.h"
15
16#include <cstring>
17#include <utility>
18
19////////////////////////////////////////////////////////////////////////////////////////////////////
20// SkJpegSegmentScanner
21
22SkJpegSegmentScanner::SkJpegSegmentScanner(uint8_t stopMarker) : fStopMarker(stopMarker) {}
23
24const std::vector<SkJpegSegment>& SkJpegSegmentScanner::getSegments() const { return fSegments; }
25
33
34void SkJpegSegmentScanner::onBytes(const void* data, size_t size) {
35 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
36 size_t bytesRemaining = size;
37
38 while (bytesRemaining > 0) {
39 // Process the data byte-by-byte, unless we are in kSegmentParam or kEntropyCodedData, in
40 // which case, perform some optimizations to avoid examining every byte.
41 size_t bytesToMoveForward = 0;
42 switch (fState) {
43 case State::kSegmentParam: {
44 // Skip forward through payloads.
45 SkASSERT(fSegmentParamBytesRemaining > 0);
46 bytesToMoveForward = std::min(fSegmentParamBytesRemaining, bytesRemaining);
47 fSegmentParamBytesRemaining -= bytesToMoveForward;
48 if (fSegmentParamBytesRemaining == 0) {
49 fState = State::kEntropyCodedData;
50 }
51 break;
52 }
53 case State::kEntropyCodedData: {
54 // Skip through entropy-coded data, only looking at sentinel characters.
55 const uint8_t* sentinel =
56 reinterpret_cast<const uint8_t*>(memchr(bytes, 0xFF, bytesRemaining));
57 if (sentinel) {
58 bytesToMoveForward = (sentinel - bytes) + 1;
59 fState = State::kEntropyCodedDataSentinel;
60 } else {
61 bytesToMoveForward = bytesRemaining;
62 }
63 break;
64 }
65 case State::kDone:
66 // Skip all data after we have hit our stop marker.
67 bytesToMoveForward = bytesRemaining;
68 break;
69 default: {
70 onByte(*bytes);
71 bytesToMoveForward = 1;
72 break;
73 }
74 }
75 SkASSERT(bytesToMoveForward > 0);
76 fOffset += bytesToMoveForward;
77 bytes += bytesToMoveForward;
78 bytesRemaining -= bytesToMoveForward;
79 }
80}
81
82void SkJpegSegmentScanner::saveCurrentSegment(uint16_t length) {
83 SkJpegSegment s = {fCurrentSegmentOffset, fCurrentSegmentMarker, length};
84 fSegments.push_back(s);
85
86 fCurrentSegmentMarker = 0;
87 fCurrentSegmentOffset = 0;
88}
89
90void SkJpegSegmentScanner::onMarkerSecondByte(uint8_t byte) {
91 SkASSERT(fState == State::kStartOfImageByte1 || fState == State::kSecondMarkerByte1 ||
92 fState == State::kEntropyCodedDataSentinel ||
93 fState == State::kPostEntropyCodedDataFill);
94
95 fCurrentSegmentMarker = byte;
96 fCurrentSegmentOffset = fOffset - 1;
97
98 if (byte == fStopMarker) {
99 saveCurrentSegment(0);
100 fState = State::kDone;
101 } else if (byte == kJpegMarkerStartOfImage) {
102 saveCurrentSegment(0);
103 fState = State::kSecondMarkerByte0;
104 } else if (MarkerStandsAlone(byte)) {
105 saveCurrentSegment(0);
106 fState = State::kEntropyCodedData;
107 } else {
108 fCurrentSegmentMarker = byte;
109 fState = State::kSegmentParamLengthByte0;
110 }
111}
112
113void SkJpegSegmentScanner::onByte(uint8_t byte) {
114 switch (fState) {
115 case State::kStartOfImageByte0:
116 if (byte != 0xFF) {
117 SkCodecPrintf("First byte was %02x, not 0xFF", byte);
118 fState = State::kError;
119 return;
120 }
121 fState = State::kStartOfImageByte1;
122 break;
123 case State::kStartOfImageByte1:
124 if (byte != kJpegMarkerStartOfImage) {
125 SkCodecPrintf("Second byte was %02x, not %02x", byte, kJpegMarkerStartOfImage);
126 fState = State::kError;
127 return;
128 }
129 onMarkerSecondByte(byte);
130 break;
131 case State::kSecondMarkerByte0:
132 if (byte != 0xFF) {
133 SkCodecPrintf("Third byte was %02x, not 0xFF", byte);
134 fState = State::kError;
135 return;
136 }
137 fState = State::kSecondMarkerByte1;
138 break;
139 case State::kSecondMarkerByte1:
140 // See section B.1.1.3: All markers are assigned two-byte codes: a 0xFF byte followed by
141 // a byte which is not equal to 0x00 or 0xFF.
142 if (byte == 0xFF || byte == 0x00) {
143 SkCodecPrintf("SkJpegSegment marker was 0xFF,0xFF or 0xFF,0x00");
144 fState = State::kError;
145 return;
146 }
147 onMarkerSecondByte(byte);
148 break;
149 case State::kSegmentParamLengthByte0:
150 fSegmentParamLengthByte0 = byte;
151 fState = State::kSegmentParamLengthByte1;
152 break;
153 case State::kSegmentParamLengthByte1: {
154 uint16_t paramLength = 256u * fSegmentParamLengthByte0 + byte;
155 fSegmentParamLengthByte0 = 0;
156
157 // See section B.1.1.4: A marker segment consists of a marker followed by a sequence
158 // of related parameters. The first parameter in a marker segment is the two-byte length
159 // parameter. This length parameter encodes the number of bytes in the marker segment,
160 // including the length parameter and excluding the two-byte marker.
161 if (paramLength < kJpegSegmentParameterLengthSize) {
162 SkCodecPrintf("SkJpegSegment payload length was %u < 2 bytes", paramLength);
163 fState = State::kError;
164 return;
165 }
166 saveCurrentSegment(paramLength);
167 fSegmentParamBytesRemaining = paramLength - kJpegSegmentParameterLengthSize;
168 if (fSegmentParamBytesRemaining > 0) {
169 fState = State::kSegmentParam;
170 } else {
171 fState = State::kEntropyCodedData;
172 }
173 break;
174 }
175 case State::kSegmentParam:
176 SkASSERT(fSegmentParamBytesRemaining > 0);
177 fSegmentParamBytesRemaining -= 1;
178 if (fSegmentParamBytesRemaining == 0) {
179 fState = State::kEntropyCodedData;
180 }
181 break;
182 case State::kEntropyCodedData:
183 if (byte == 0xFF) {
184 fState = State::kEntropyCodedDataSentinel;
185 }
186 break;
187 case State::kEntropyCodedDataSentinel:
188 if (byte == 0x00) {
189 fState = State::kEntropyCodedData;
190 } else if (byte == 0xFF) {
191 fState = State::kPostEntropyCodedDataFill;
192 } else {
193 onMarkerSecondByte(byte);
194 }
195 break;
196 case State::kPostEntropyCodedDataFill:
197 // See section B.1.1.3: Any marker may optionally be preceded by any number of fill
198 // bytes, which are bytes assigned code 0xFF. Skip past any 0xFF fill bytes that may be
199 // present at the end of the entropy-coded data.
200 if (byte == 0xFF) {
201 fState = State::kPostEntropyCodedDataFill;
202 } else if (byte == 0x00) {
203 SkCodecPrintf("Post entropy coded data had 0xFF,0x00");
204 fState = State::kError;
205 return;
206 } else {
207 onMarkerSecondByte(byte);
208 }
209 break;
210 case State::kDone:
211 break;
212 case State::kError:
213 break;
214 }
215}
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkCodecPrintf(...)
Definition SkCodecPriv.h:23
static constexpr uint8_t kJpegMarkerStartOfImage
static constexpr size_t kJpegSegmentParameterLengthSize
static constexpr size_t kJpegMarkerCodeSize
static sk_sp< SkData > MakeSubset(const SkData *src, size_t offset, size_t length)
Definition SkData.cpp:173
const std::vector< SkJpegSegment > & getSegments() const
static sk_sp< SkData > GetParameters(const SkData *scannedData, const SkJpegSegment &segment)
SkJpegSegmentScanner(uint8_t stopMarker=kJpegMarkerEndOfImage)
void onBytes(const void *data, size_t size)
struct MyStruct s
size_t length
uint16_t parameterLength