xref: /aosp_15_r20/external/skia/src/codec/SkJpegSegmentScan.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
8 #include "src/codec/SkJpegSegmentScan.h"
9 
10 #include "include/core/SkData.h"
11 #include "include/core/SkStream.h"
12 #include "include/private/base/SkAssert.h"
13 #include "src/codec/SkCodecPriv.h"
14 #include "src/codec/SkJpegConstants.h"
15 
16 #include <cstring>
17 #include <utility>
18 
19 ////////////////////////////////////////////////////////////////////////////////////////////////////
20 // SkJpegSegmentScanner
21 
SkJpegSegmentScanner(uint8_t stopMarker)22 SkJpegSegmentScanner::SkJpegSegmentScanner(uint8_t stopMarker) : fStopMarker(stopMarker) {}
23 
getSegments() const24 const std::vector<SkJpegSegment>& SkJpegSegmentScanner::getSegments() const { return fSegments; }
25 
GetParameters(const SkData * scannedData,const SkJpegSegment & segment)26 sk_sp<SkData> SkJpegSegmentScanner::GetParameters(const SkData* scannedData,
27                                                   const SkJpegSegment& segment) {
28     return SkData::MakeSubset(
29             scannedData,
30             segment.offset + kJpegMarkerCodeSize + kJpegSegmentParameterLengthSize,
31             segment.parameterLength - kJpegSegmentParameterLengthSize);
32 }
33 
onBytes(const void * data,size_t size)34 void 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 
saveCurrentSegment(uint16_t length)82 void 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 
onMarkerSecondByte(uint8_t byte)90 void 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 
onByte(uint8_t byte)113 void 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 }
216