|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 #include <e32base.h> |
|
21 #include "icl/ImageCodec.h" |
|
22 |
|
23 #include "JpegTypes.h" |
|
24 #include "rgbbufferptr.h" |
|
25 #include "jpgimageframeprocessor.h" |
|
26 |
|
27 #include "jpegwritecodec.h" |
|
28 |
|
29 const TInt KDataUnitSafeBufferSz = sizeof(TDataUnit) + ( sizeof(TDataUnit) >> 1 ); // sizeof(TDataUnit) * 1.5 |
|
30 |
|
31 // Encoder tables. |
|
32 static const TUint8 KLuminanceQTableValues[] = { |
|
33 16, 11, 12, 14, 12, 10, 16, 14, |
|
34 13, 14, 18, 17, 16, 19, 24, 40, |
|
35 26, 24, 22, 22, 24, 49, 35, 37, |
|
36 29, 40, 58, 51, 61, 60, 57, 51, |
|
37 56, 55, 64, 72, 92, 78, 64, 68, |
|
38 87, 69, 55, 56, 80, 109, 81, 87, |
|
39 95, 98, 103, 104, 103, 62, 77, 113, |
|
40 121, 112, 100, 120, 92, 101, 103, 99 }; |
|
41 |
|
42 static const TUint8 KChrominanceQTableValues[] = { |
|
43 17, 18, 18, 24, 21, 24, 47, 26, |
|
44 26, 47, 99, 66, 56, 66, 99, 99, |
|
45 99, 99, 99, 99, 99, 99, 99, 99, |
|
46 99, 99, 99, 99, 99, 99, 99, 99, |
|
47 99, 99, 99, 99, 99, 99, 99, 99, |
|
48 99, 99, 99, 99, 99, 99, 99, 99, |
|
49 99, 99, 99, 99, 99, 99, 99, 99, |
|
50 99, 99, 99, 99, 99, 99, 99, 99 }; |
|
51 |
|
52 static const TUint8 KLuminanceDCHuffmanValues[] = { |
|
53 // Number of codes per bit-length |
|
54 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
55 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, |
|
56 // source byte definitions |
|
57 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
|
58 |
|
59 |
|
60 |
|
61 static const TUint8 KChrominanceDCHuffmanValues[] = { |
|
62 // Number of codes per bit-length |
|
63 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
64 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, |
|
65 // source byte definitions |
|
66 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
|
67 |
|
68 |
|
69 |
|
70 static const TUint8 KLuminanceACHuffmanValues[] = { |
|
71 // Number of codes per bit-length |
|
72 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
73 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d, |
|
74 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, |
|
75 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, |
|
76 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, |
|
77 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, |
|
78 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, |
|
79 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, |
|
80 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, |
|
81 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, |
|
82 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, |
|
83 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|
84 0xf9, 0xfa }; |
|
85 |
|
86 static const TUint8 KChrominanceACHuffmanValues[] = { |
|
87 // Number of codes per bit-length |
|
88 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77, |
|
89 // Code value definitions |
|
90 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, |
|
91 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, |
|
92 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, |
|
93 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, |
|
94 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, |
|
95 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, |
|
96 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, |
|
97 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, |
|
98 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, |
|
99 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, |
|
100 0xf9, 0xfa }; |
|
101 |
|
102 #if defined(PROFILE_JPEG_ENCODER) |
|
103 |
|
104 #define PROFILE_START(a)\ |
|
105 TUint a##_Start=User::FastCounter(); |
|
106 |
|
107 #define PROFILE_END(aa) aa += User::FastCounter() - aa##_Start; |
|
108 |
|
109 #define PROFILE_MARK(aa) aa = User::FastCounter() - aa; |
|
110 |
|
111 #define PROFILE_REPORT(a)\ |
|
112 RDebug::Printf("--Profile: %s\t%d",#a, a); |
|
113 #else |
|
114 |
|
115 #define PROFILE_START(a) |
|
116 #define PROFILE_END(a) |
|
117 #define PROFILE_REPORT(a) |
|
118 #define PROFILE_MARK(a) |
|
119 #endif |
|
120 |
|
121 |
|
122 // CJpegWriteCodec |
|
123 CJpgWriteCodec::CJpgWriteCodec(const TJpgFrameInfo& aFrameInfo,TInt aQualityFactor,TQTable* aReplacementLumaQTable,TQTable* aReplacementChromaQTable,const RPointerArray<HBufC8>& aComment) |
|
124 : iQualityFactor(aQualityFactor), iReplacementLumaQTable(aReplacementLumaQTable), iReplacementChromaQTable(aReplacementChromaQTable), iFrameInfo(aFrameInfo), iComment(aComment) |
|
125 { |
|
126 iWrittingDUIdx = KJpgMaxNumOfDataUnits; |
|
127 // check that CJpgWriteCodec::TSpecialDataUnit has the right size, as we rely on that |
|
128 // if it doesn't throw a compile-time error |
|
129 ASSERT( ( _FOFF(CJpgWriteCodec::TSpecialDataUnit,iExtraElement) == sizeof(TDataUnit) ) ); |
|
130 } |
|
131 |
|
132 CJpgWriteCodec::~CJpgWriteCodec() |
|
133 { |
|
134 iOperationsRequested.Close(); |
|
135 delete iRgbInputBuffer; |
|
136 delete iDataUnitProcessor; |
|
137 delete iImageFrameCodecPtr; |
|
138 } |
|
139 |
|
140 CJpgWriteCodec* CJpgWriteCodec::NewL(const TJpgFrameInfo& aFrameInfo,TInt aQualityFactor,TQTable* aReplacementLumaQTable,TQTable* aReplacementChromaQTable,const RPointerArray<HBufC8>& aComment,RArray<TUint>* aOperationsRequested) |
|
141 { |
|
142 CJpgWriteCodec* self = new(ELeave) CJpgWriteCodec(aFrameInfo, aQualityFactor, aReplacementLumaQTable, aReplacementChromaQTable, aComment); |
|
143 CleanupStack::PushL(self); |
|
144 self->ConstructL(aOperationsRequested); |
|
145 CleanupStack::Pop(self); |
|
146 return self; |
|
147 } |
|
148 |
|
149 void CJpgWriteCodec::ConstructL(RArray<TUint>* aOperationsRequested) |
|
150 { |
|
151 if(!aOperationsRequested) |
|
152 { |
|
153 iOperationsRequested.Reset(); |
|
154 } |
|
155 else |
|
156 { |
|
157 TInt count = aOperationsRequested->Count(); |
|
158 for (TInt i = 0; i<count; i++) |
|
159 { |
|
160 iOperationsRequested.AppendL((*aOperationsRequested)[i]); |
|
161 } |
|
162 } |
|
163 } |
|
164 |
|
165 void CJpgWriteCodec::SetHighSpeedMode(TBool aHighSpeedMode) |
|
166 { |
|
167 iHighSpeedMode = aHighSpeedMode; |
|
168 } |
|
169 |
|
170 TFrameState CJpgWriteCodec::ProcessFrameL(TBufPtr8& aDst) |
|
171 { |
|
172 |
|
173 TFrameState state; |
|
174 TUint8* destStartPtr = const_cast<TUint8*>(aDst.Ptr()); |
|
175 iDestPtr = destStartPtr; |
|
176 iDestPtrLimit = iDestPtr + aDst.MaxLength() - sizeof(iBitBuffer) * 2; // Subtract four to give a safety buffer |
|
177 |
|
178 if(iStreamConfig.iIsFrameComplete) |
|
179 { |
|
180 CompleteFrame(); |
|
181 aDst.SetLength(iDestPtr - destStartPtr); |
|
182 state = EFrameComplete; |
|
183 if(iStreamConfig.iNumOfMCUsProcessed < ((iFrameInfo.iSizeInPixels.iWidth * iFrameInfo.iSizeInPixels.iHeight) / (iMCUSizeInPixels.iWidth * iMCUSizeInPixels.iHeight))) |
|
184 { |
|
185 User::Leave(KErrUnderflow); |
|
186 } |
|
187 return state; |
|
188 } |
|
189 |
|
190 TRAPD(err,DoProcessL()); |
|
191 |
|
192 if (err != KErrNone) |
|
193 { |
|
194 if (err != KErrCompletion) |
|
195 { |
|
196 User::Leave(err); |
|
197 } |
|
198 } |
|
199 |
|
200 state = EFrameIncomplete; |
|
201 |
|
202 if(iIsBlockStreaming) |
|
203 { |
|
204 if (iPosProcessor.IsEndOfImage()) |
|
205 { |
|
206 state = EBlockComplete; |
|
207 } |
|
208 } |
|
209 else |
|
210 { |
|
211 if (iPosProcessor.IsEndOfImage()) |
|
212 { |
|
213 CompleteFrame(); |
|
214 state = EFrameComplete; |
|
215 } |
|
216 } |
|
217 |
|
218 aDst.SetLength(iDestPtr - destStartPtr); |
|
219 return state; |
|
220 } |
|
221 |
|
222 void CJpgWriteCodec::SetCompleteFrame() |
|
223 { |
|
224 iStreamConfig.iIsFrameComplete = ETrue; |
|
225 } |
|
226 |
|
227 void CJpgWriteCodec::CompleteFrame() |
|
228 { |
|
229 |
|
230 if (iBitsUsed >= 8) |
|
231 { |
|
232 do |
|
233 { |
|
234 TUint8 byte = (TUint8)(iBitBuffer >> 24); |
|
235 iBitBuffer <<= 8; |
|
236 |
|
237 *iDestPtr++ = byte; |
|
238 if (byte == 0xff) |
|
239 { |
|
240 *iDestPtr++ = 0; |
|
241 } |
|
242 |
|
243 iBitsUsed -= 8; |
|
244 } while (iBitsUsed >= 8); |
|
245 } |
|
246 |
|
247 if (iBitsUsed > 0) |
|
248 { |
|
249 iDestPtr[0] = TUint8( iBitBuffer >> 24 ); |
|
250 ++iDestPtr; |
|
251 } |
|
252 |
|
253 PROFILE_MARK(iOverallTime); |
|
254 PROFILE_REPORT(iGetPixelsTime); |
|
255 PROFILE_REPORT(iImageFrameTime); |
|
256 PROFILE_REPORT(iDataUnitProcessTime); |
|
257 PROFILE_REPORT(iTransforTime); |
|
258 PROFILE_REPORT(iQuantizeTime); |
|
259 PROFILE_REPORT(iWriteDataUnitTime); |
|
260 PROFILE_REPORT(iOverallTime); |
|
261 } |
|
262 |
|
263 #if defined(__ARMCC__) |
|
264 #pragma push |
|
265 #pragma thumb |
|
266 #endif |
|
267 |
|
268 //Bitmap encoding |
|
269 void CJpgWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource) |
|
270 { |
|
271 PROFILE_MARK(iOverallTime); |
|
272 |
|
273 TUint8* destStartPtr = const_cast<TUint8*>(aDst.Ptr()); |
|
274 iDestPtr = destStartPtr; |
|
275 iDestPtrLimit = iDestPtr + aDst.MaxLength(); |
|
276 |
|
277 SetSource(&aSource); |
|
278 |
|
279 iSourceRect = TRect(aSource.SizeInPixels()); |
|
280 iPos.SetXY(0,0); |
|
281 |
|
282 iFrameInfo.iSizeInPixels = iSourceRect.Size(); |
|
283 iMCUSizeInPixels.SetSize(iFrameInfo.iMaxHorzSampleFactor,iFrameInfo.iMaxVertSampleFactor); |
|
284 iMCUSizeInPixels.iWidth *= KJpgDCTBlockWidth; |
|
285 iMCUSizeInPixels.iHeight *= KJpgDCTBlockWidth; |
|
286 |
|
287 delete iRgbInputBuffer; |
|
288 iRgbInputBuffer = NULL; |
|
289 iRgbInputBuffer = CRgbBufferPtr::NewL(const_cast<CFbsBitmap&>(aSource), iMCUSizeInPixels); |
|
290 |
|
291 if (iFrameInfo.iNumberOfComponents == 1) |
|
292 { |
|
293 iDataUnitCount = 1; |
|
294 } |
|
295 else |
|
296 { |
|
297 iDataUnitCount = 0; |
|
298 for (TInt index = 0; index < iFrameInfo.iNumberOfComponents; index++) |
|
299 { |
|
300 TJpgFrameInfo::TComponentInfo& component = iFrameInfo.iComponent[index]; |
|
301 iDataUnitCount += component.iHorzSampleFactor * component.iVertSampleFactor; |
|
302 } |
|
303 } |
|
304 |
|
305 ASSERT( iDataUnitCount <= KJpgMaxNumOfDataUnits ); |
|
306 |
|
307 switch (iDataUnitCount) |
|
308 { |
|
309 case KJpgMonochromeDataUnitCount: // Monochrome |
|
310 iDataUnitProcessor = new(ELeave) TMonoProcessor; |
|
311 break; |
|
312 case KJpgEColor444DataUnitCount: // 4:4:4 |
|
313 iDataUnitProcessor = new(ELeave) T444Processor; |
|
314 break; |
|
315 case KJpgColor422DataUnitCount: // 4:2:2 |
|
316 iDataUnitProcessor = new(ELeave) T422Processor; |
|
317 break; |
|
318 case KJpgColor420DataUnitCount: // 4:2:0 |
|
319 iDataUnitProcessor = new(ELeave) T420Processor; |
|
320 break; |
|
321 default: |
|
322 User::Leave(KErrNotSupported); |
|
323 } |
|
324 |
|
325 iDataUnitProcessor->SetBufferPtrUtil( *iRgbInputBuffer ); |
|
326 |
|
327 InitTransformationL(iPosProcessor, iSourceRect, iMCUSizeInPixels, iFrameInfo, iDataUnitCount); |
|
328 |
|
329 CodecInfoL(); |
|
330 InitCompConfig(); |
|
331 |
|
332 aDst.SetLength(iDestPtr - destStartPtr); |
|
333 } |
|
334 |
|
335 //ImageFrame encoding |
|
336 void CJpgWriteCodec::InitFrameL(TBufPtr8& aDst, const CImageFrame* aFrame, const CFrameImageData* aFrameImageData) |
|
337 { |
|
338 TUint8* destStartPtr = const_cast<TUint8*>(aDst.Ptr()); |
|
339 iDestPtr = destStartPtr; |
|
340 iDestPtrLimit = iDestPtr + aDst.MaxLength(); |
|
341 |
|
342 iSourceRect = TRect(aFrame->FrameSizeInPixels()); |
|
343 iPos.SetXY(0,0); |
|
344 |
|
345 iFrameInfo.iSizeInPixels = iSourceRect.Size(); |
|
346 |
|
347 // Create JPEG write codec extension and the appropriate image processor |
|
348 ASSERT( iImageFrameCodecPtr == NULL ); |
|
349 delete iImageFrameCodecPtr; |
|
350 iImageFrameCodecPtr = NULL; |
|
351 iImageFrameCodecPtr = CJpgImageFrameWriteCodec::NewL(aFrame); |
|
352 iImageFrameCodecPtr->PrepareFrameInfoL(iFrameInfo, aFrameImageData); |
|
353 iImageFrameCodecPtr->CreateImageProcessorL(); |
|
354 |
|
355 iDataUnitCount = iImageFrameCodecPtr->DataUnitCount(); |
|
356 |
|
357 iMCUSizeInPixels.SetSize(iFrameInfo.iMaxHorzSampleFactor, iFrameInfo.iMaxVertSampleFactor); |
|
358 iMCUSizeInPixels.iWidth *= KJpgDCTBlockWidth; |
|
359 iMCUSizeInPixels.iHeight *= KJpgDCTBlockWidth; |
|
360 |
|
361 InitTransformationL(iPosProcessor, iSourceRect, iMCUSizeInPixels, iFrameInfo, iDataUnitCount); |
|
362 |
|
363 CodecInfoL(); |
|
364 InitCompConfig(); |
|
365 |
|
366 aDst.SetLength(iDestPtr - destStartPtr); |
|
367 } |
|
368 |
|
369 //ImageFrame stream encoding |
|
370 void CJpgWriteCodec::InitFrameL(TBufPtr8& aDst, TUid aFormat, TInt aFrameNumber, const TSize& aFrameSizeInPixels, const TSize& aBlockSizeInPixels, const CFrameImageData* aFrameImageData) |
|
371 { |
|
372 TUint8* destStartPtr = const_cast<TUint8*>(aDst.Ptr()); |
|
373 iDestPtr = destStartPtr; |
|
374 iDestPtrLimit = iDestPtr + aDst.MaxLength(); |
|
375 |
|
376 iSourceRect = TRect(aFrameSizeInPixels); |
|
377 iPos.SetXY(0,0); |
|
378 |
|
379 iFrameInfo.iSizeInPixels = iSourceRect.Size(); |
|
380 |
|
381 // Create JPEG write codec extension and the appropriate image processor |
|
382 ASSERT( iImageFrameCodecPtr == NULL ); |
|
383 delete iImageFrameCodecPtr; |
|
384 iImageFrameCodecPtr = NULL; |
|
385 iImageFrameCodecPtr = CJpgImageFrameWriteCodec::NewL(NULL); |
|
386 iImageFrameCodecPtr->PrepareFrameImageDataInfoL(iFrameInfo, aFormat, aFrameNumber, aFrameImageData); |
|
387 |
|
388 iDataUnitCount = iImageFrameCodecPtr->DataUnitCount(); |
|
389 |
|
390 iMCUSizeInPixels.SetSize(iFrameInfo.iMaxHorzSampleFactor, iFrameInfo.iMaxVertSampleFactor); |
|
391 iMCUSizeInPixels.iWidth *= KJpgDCTBlockWidth; |
|
392 iMCUSizeInPixels.iHeight *= KJpgDCTBlockWidth; |
|
393 |
|
394 InitTransformationL(iPosProcessor, TRect(aBlockSizeInPixels), iMCUSizeInPixels, iFrameInfo, iDataUnitCount); |
|
395 |
|
396 iIsBlockStreaming = ETrue; |
|
397 iStreamConfig.iIsFrameComplete = EFalse; |
|
398 |
|
399 CodecInfoL(); |
|
400 InitCompConfig(); |
|
401 |
|
402 aDst.SetLength(iDestPtr - destStartPtr); |
|
403 } |
|
404 |
|
405 void CJpgWriteCodec::AppendFrameBlockL(const CImageFrame& aBlocks, TInt aNumBlocksToAdd) |
|
406 { |
|
407 ValidateBlockSizeL(aBlocks.FrameSizeInPixels(), iMCUSizeInPixels); |
|
408 |
|
409 if (aBlocks.FrameSizeInPixels().iHeight != iMCUSizeInPixels.iHeight) |
|
410 { |
|
411 User::Leave(KErrNotSupported); |
|
412 } |
|
413 |
|
414 iStreamConfig.iNumOfBlocksToAppend = aNumBlocksToAdd; |
|
415 iStreamConfig.iNumOfBlocksAppended = 0; |
|
416 |
|
417 iImageFrameCodecPtr->AppendImageFrameBlockL(&aBlocks); |
|
418 |
|
419 InitTransformationL(iPosProcessor, TRect(aBlocks.FrameSizeInPixels()), iMCUSizeInPixels, iFrameInfo, iDataUnitCount); |
|
420 } |
|
421 |
|
422 //validates the stream blocks passed and checks that the height is equal to the mcu height. |
|
423 void CJpgWriteCodec::ValidateBlockSizeL(const TSize& aBlockSizeInPixels, const TSize& aRefSizeInPixels) |
|
424 { |
|
425 TInt oddPixelsWidth = aBlockSizeInPixels.iWidth % aRefSizeInPixels.iWidth; |
|
426 TInt oddPixelsHeight = aBlockSizeInPixels.iHeight % aRefSizeInPixels.iHeight; |
|
427 |
|
428 if (oddPixelsWidth != 0 || oddPixelsHeight != 0) |
|
429 { |
|
430 User::Leave(KErrNotSupported); |
|
431 } |
|
432 } |
|
433 |
|
434 void CJpgWriteCodec::InitTransformationL(TPositionProcessor& aPosProcessor, const TRect& aSourceRect, const TSize& aMCUSize, TJpgFrameInfo& aFrameInfo, const TInt aDataUnitCount) |
|
435 { |
|
436 InitTransformCoordinates(aPosProcessor, aSourceRect, aMCUSize, iOperationsRequested); |
|
437 |
|
438 InitTransformDataUnitIndex(aDataUnitCount); |
|
439 |
|
440 if(aPosProcessor.IsTransformNeeded()) |
|
441 { |
|
442 ValidateBlockSizeL(aFrameInfo.iSizeInPixels, aMCUSize); |
|
443 |
|
444 if(aPosProcessor.SwapDimensions()) |
|
445 { |
|
446 TInt tempDimention; |
|
447 tempDimention = aFrameInfo.iSizeInPixels.iHeight; |
|
448 aFrameInfo.iSizeInPixels.iHeight = aFrameInfo.iSizeInPixels.iWidth; // Y |
|
449 aFrameInfo.iSizeInPixels.iWidth = tempDimention; // X |
|
450 |
|
451 if(aDataUnitCount == KJpgColor422DataUnitCount) |
|
452 { |
|
453 tempDimention = aFrameInfo.iComponent[0].iHorzSampleFactor; |
|
454 aFrameInfo.iComponent[0].iHorzSampleFactor = aFrameInfo.iComponent[0].iVertSampleFactor; |
|
455 aFrameInfo.iComponent[0].iVertSampleFactor = tempDimention; |
|
456 } |
|
457 } |
|
458 } |
|
459 |
|
460 InitTransformDataUnitCoeff(iTransformDUCoeffIdx); |
|
461 } |
|
462 |
|
463 |
|
464 void CJpgWriteCodec::InitCompConfig() |
|
465 { |
|
466 iCompConfig[ KYComp ].iFirstDUIdx = 0; |
|
467 |
|
468 switch (iDataUnitCount) |
|
469 { |
|
470 case KJpgMonochromeDataUnitCount: // Monochrome |
|
471 // in fact we don't have these comps for mono, so assign them last index |
|
472 iCompConfig[ KUComp ].iFirstDUIdx = KJpgMaxNumOfDataUnits - 1; |
|
473 iCompConfig[ KVComp ].iFirstDUIdx = KJpgMaxNumOfDataUnits - 1; |
|
474 break; |
|
475 case KJpgEColor444DataUnitCount: // 4:4:4 |
|
476 iCompConfig[ KUComp ].iFirstDUIdx = 1; |
|
477 iCompConfig[ KVComp ].iFirstDUIdx = 2; |
|
478 break; |
|
479 case KJpgColor422DataUnitCount: // 4:2:2 |
|
480 iCompConfig[ KUComp ].iFirstDUIdx = 2; |
|
481 iCompConfig[ KVComp ].iFirstDUIdx = 3; |
|
482 break; |
|
483 case KJpgColor420DataUnitCount: // 4:2:0 |
|
484 iCompConfig[ KUComp ].iFirstDUIdx = 4; |
|
485 iCompConfig[ KVComp ].iFirstDUIdx = 5; |
|
486 break; |
|
487 default: |
|
488 ASSERT( EFalse ); // shan't happen |
|
489 } |
|
490 |
|
491 iCompConfig[ KYComp ].iDCPredictorIdx = KYComp; |
|
492 iCompConfig[ KUComp ].iDCPredictorIdx = KUComp; |
|
493 iCompConfig[ KVComp ].iDCPredictorIdx = KVComp; |
|
494 |
|
495 TJpgFrameInfo::TComponentInfo& yComp = iFrameInfo.iComponent[ KYComp ]; |
|
496 iCompConfig[ KYComp ].iDataUnitsCount = yComp.iHorzSampleFactor * yComp.iVertSampleFactor; |
|
497 |
|
498 if (iCompConfig[ KYComp ].iDataUnitsCount == 3) |
|
499 { |
|
500 TJpgFrameInfo::TComponentInfo& uComp = iFrameInfo.iComponent[ KUComp ]; |
|
501 iCompConfig[ KUComp ].iDataUnitsCount = uComp.iHorzSampleFactor * uComp.iVertSampleFactor; |
|
502 |
|
503 TJpgFrameInfo::TComponentInfo& vComp = iFrameInfo.iComponent[ KVComp ]; |
|
504 iCompConfig[ KVComp ].iDataUnitsCount = vComp.iHorzSampleFactor * vComp.iVertSampleFactor; |
|
505 } |
|
506 else |
|
507 { |
|
508 iCompConfig[ KVComp ].iDataUnitsCount = iCompConfig[ KUComp ].iDataUnitsCount = 0; |
|
509 } |
|
510 |
|
511 iCompConfig[ KYComp ].iACTable = &iLumaACHuffmanTable; |
|
512 iCompConfig[ KYComp ].iDCTable = &iLumaDCHuffmanTable; |
|
513 |
|
514 // we use the same tables for U and V comps |
|
515 iCompConfig[ KUComp ].iACTable = &iChromaACHuffmanTable; |
|
516 iCompConfig[ KUComp ].iDCTable = &iChromaDCHuffmanTable; |
|
517 |
|
518 iCompConfig[ KVComp ].iACTable = &iChromaACHuffmanTable; |
|
519 iCompConfig[ KVComp ].iDCTable = &iChromaDCHuffmanTable; |
|
520 |
|
521 iCompConfig[ KYComp ].iQTTable = &iLumaQTable; |
|
522 // we use the same tables for U and V comps |
|
523 iCompConfig[ KUComp ].iQTTable = &iChromaQTable; |
|
524 iCompConfig[ KVComp ].iQTTable = &iChromaQTable; |
|
525 |
|
526 iBitsUsed = 0; |
|
527 iBitBuffer= 0; |
|
528 } |
|
529 |
|
530 void CJpgWriteCodec::CodecInfoL() |
|
531 { |
|
532 iDCT.SetPrecision(8); |
|
533 |
|
534 if (iReplacementLumaQTable) |
|
535 iLumaQTable = *iReplacementLumaQTable; |
|
536 else |
|
537 iLumaQTable.Set(KLuminanceQTableValues,EFalse); |
|
538 iLumaQTable.SetQualityFactor(iQualityFactor); |
|
539 |
|
540 const TUint8* dataPtr = KLuminanceDCHuffmanValues; |
|
541 const TUint8* dataPtrLimit = dataPtr + sizeof(KLuminanceDCHuffmanValues); |
|
542 iLumaDCHuffmanTable.SetL(dataPtr,dataPtrLimit); |
|
543 iLumaDCHuffmanTable.MakeDerivedTableL(); |
|
544 |
|
545 dataPtr = KLuminanceACHuffmanValues; |
|
546 dataPtrLimit = dataPtr + sizeof(KLuminanceACHuffmanValues); |
|
547 iLumaACHuffmanTable.SetL(dataPtr,dataPtrLimit); |
|
548 iLumaACHuffmanTable.MakeDerivedTableL(); |
|
549 |
|
550 if (iFrameInfo.iNumberOfComponents == 3) |
|
551 { |
|
552 if (iReplacementChromaQTable) |
|
553 iChromaQTable = *iReplacementChromaQTable; |
|
554 else |
|
555 iChromaQTable.Set(KChrominanceQTableValues,EFalse); |
|
556 iChromaQTable.SetQualityFactor(iQualityFactor); |
|
557 |
|
558 dataPtr = KChrominanceDCHuffmanValues; |
|
559 dataPtrLimit = dataPtr + sizeof(KChrominanceDCHuffmanValues); |
|
560 iChromaDCHuffmanTable.SetL(dataPtr,dataPtrLimit); |
|
561 iChromaDCHuffmanTable.MakeDerivedTableL(); |
|
562 |
|
563 dataPtr = KChrominanceACHuffmanValues; |
|
564 dataPtrLimit = dataPtr + sizeof(KChrominanceACHuffmanValues); |
|
565 iChromaACHuffmanTable.SetL(dataPtr,dataPtrLimit); |
|
566 iChromaACHuffmanTable.MakeDerivedTableL(); |
|
567 } |
|
568 |
|
569 iDCPredictor[0] = 0; |
|
570 iDCPredictor[1] = 0; |
|
571 iDCPredictor[2] = 0; |
|
572 |
|
573 WriteInfoL(); |
|
574 } |
|
575 |
|
576 void CJpgWriteCodec::WriteInfoL() |
|
577 { |
|
578 for (TInt index = 0; index < iComment.Count(); index++) |
|
579 { |
|
580 if (!iComment[index]) |
|
581 continue; |
|
582 |
|
583 const TInt commentLength = iComment[index]->Length(); |
|
584 WriteBigEndianUint16(iDestPtr,KJpgCommentSignature); |
|
585 WriteBigEndianUint16(iDestPtr,commentLength + 2); |
|
586 Mem::Copy(iDestPtr,iComment[index]->Ptr(),commentLength); |
|
587 iDestPtr += commentLength; |
|
588 } |
|
589 |
|
590 |
|
591 if (iFrameInfo.iNumberOfComponents == 1) |
|
592 { |
|
593 WriteBigEndianUint16(iDestPtr,KJpgDQTSignature); // Luminance quantization table |
|
594 WriteBigEndianUint16(iDestPtr,3 + KJpgQTableEntries); // Lq |
|
595 *iDestPtr++ = 0; // (Pq << 4) | Tq |
|
596 iDestPtr += iLumaQTable.Get(iDestPtr); // Qk |
|
597 } |
|
598 else if (iFrameInfo.iNumberOfComponents == 3) |
|
599 { |
|
600 WriteBigEndianUint16(iDestPtr,KJpgDQTSignature); // Luminance and Chrominance quantization tables |
|
601 WriteBigEndianUint16(iDestPtr,2 + ((KJpgQTableEntries + 1) * 2)); // Lq |
|
602 |
|
603 *iDestPtr++ = 0; // (Pq << 4) | Tq |
|
604 iDestPtr += iLumaQTable.Get(iDestPtr); // Qk |
|
605 |
|
606 *iDestPtr++ = 1; // (Pq << 4) | Tq |
|
607 iDestPtr += iChromaQTable.Get(iDestPtr); // Qk |
|
608 } |
|
609 else |
|
610 User::Leave(KErrNotSupported); |
|
611 |
|
612 WriteBigEndianUint16(iDestPtr,KJpgBaselineDCTSOFSignature); // Frame header |
|
613 WriteBigEndianUint16(iDestPtr,8 + (3 * iFrameInfo.iNumberOfComponents)); // Lf |
|
614 *iDestPtr++ = 8; // P |
|
615 WriteBigEndianUint16(iDestPtr,iFrameInfo.iSizeInPixels.iHeight); // Y |
|
616 WriteBigEndianUint16(iDestPtr,iFrameInfo.iSizeInPixels.iWidth); // X |
|
617 *iDestPtr++ = TUint8(iFrameInfo.iNumberOfComponents); // Nf |
|
618 |
|
619 if (iFrameInfo.iNumberOfComponents == 3) |
|
620 { |
|
621 for (TInt i = 0; i < iFrameInfo.iNumberOfComponents; i++) |
|
622 { |
|
623 *iDestPtr++ = TUint8(i + 1); // Ci |
|
624 *iDestPtr++ = TUint8((iFrameInfo.iComponent[i].iHorzSampleFactor << 4) | iFrameInfo.iComponent[i].iVertSampleFactor); |
|
625 *iDestPtr++ = TUint8((i == 0) ? 0 : 1); // Tqi |
|
626 } |
|
627 } |
|
628 else |
|
629 { |
|
630 *iDestPtr++ = 1; // Ci |
|
631 *iDestPtr++ = 0x11; // (Hi << 4) | Vi |
|
632 *iDestPtr++ = 0; // Tqi |
|
633 } |
|
634 |
|
635 if (iFrameInfo.iNumberOfComponents == 1) |
|
636 { |
|
637 WriteBigEndianUint16(iDestPtr,KJpgDHTSignature); // Huffman table |
|
638 |
|
639 TUint8* LhPtr = iDestPtr; // Remember Lh location |
|
640 WriteBigEndianUint16(iDestPtr,0); // Dummy Lh value |
|
641 |
|
642 *iDestPtr++ = 0x00; // (Tc << 4) | Th |
|
643 const TInt lumaDCTableSize = iLumaDCHuffmanTable.GetL(iDestPtr); |
|
644 iDestPtr += lumaDCTableSize; |
|
645 |
|
646 *iDestPtr++ = 0x10; // (Tc << 4) | Th |
|
647 const TInt lumaACTableSize = iLumaACHuffmanTable.GetL(iDestPtr); |
|
648 iDestPtr += lumaACTableSize; |
|
649 |
|
650 WriteBigEndianUint16(LhPtr,iDestPtr - LhPtr); // Real Lh value |
|
651 } |
|
652 else if (iFrameInfo.iNumberOfComponents == 3) |
|
653 { |
|
654 WriteBigEndianUint16(iDestPtr,KJpgDHTSignature); // Huffman tables |
|
655 |
|
656 TUint8* LhPtr = iDestPtr; // Remember Lh location |
|
657 WriteBigEndianUint16(iDestPtr,0); // Dummy Lh value |
|
658 |
|
659 *iDestPtr++ = 0x00; // (Tc << 4) | Th |
|
660 const TInt lumaDCTableSize = iLumaDCHuffmanTable.GetL(iDestPtr); |
|
661 iDestPtr += lumaDCTableSize; |
|
662 |
|
663 *iDestPtr++ = 0x10; // (Tc << 4) | Th |
|
664 const TInt lumaACTableSize = iLumaACHuffmanTable.GetL(iDestPtr); |
|
665 iDestPtr += lumaACTableSize; |
|
666 |
|
667 *iDestPtr++ = 0x01; // (Tc << 4) | Th |
|
668 const TInt chromaDCTableSize = iChromaDCHuffmanTable.GetL(iDestPtr); |
|
669 iDestPtr += chromaDCTableSize; |
|
670 |
|
671 *iDestPtr++ = 0x11; // (Tc << 4) | Th |
|
672 const TInt chromaACTableSize = iChromaACHuffmanTable.GetL(iDestPtr); |
|
673 iDestPtr += chromaACTableSize; |
|
674 |
|
675 WriteBigEndianUint16(LhPtr,iDestPtr - LhPtr); // Real Lh value |
|
676 } |
|
677 else |
|
678 { |
|
679 User::Leave(KErrNotSupported); |
|
680 } |
|
681 |
|
682 WriteBigEndianUint16(iDestPtr,KJpgSOSSignature); // Scan header |
|
683 WriteBigEndianUint16(iDestPtr,6 + (2 * iFrameInfo.iNumberOfComponents)); // Ls |
|
684 *iDestPtr++ = TUint8(iFrameInfo.iNumberOfComponents); // Ns |
|
685 for (TInt j = 0; j < iFrameInfo.iNumberOfComponents; j++) |
|
686 { |
|
687 *iDestPtr++ = TUint8(j + 1); // Csj |
|
688 *iDestPtr++ = TUint8((j == 0) ? 0x00 : 0x11); // (Tdj << 4) | Taj |
|
689 } |
|
690 *iDestPtr++ = 0; // Ss |
|
691 *iDestPtr++ = 63; // Se |
|
692 *iDestPtr++ = 0; // (Ah << 4) | Al |
|
693 |
|
694 *iDestPtr = 0; // Initialize first byte of scan data buffer |
|
695 } |
|
696 |
|
697 #if defined(__ARMCC__) |
|
698 #pragma pop |
|
699 #endif |
|
700 |
|
701 void CJpgWriteCodec::DoProcessL() |
|
702 { |
|
703 ASSERT ( iDataUnitCount > 0 ); |
|
704 // only process a maximum of KMaxRgbBufferLines |
|
705 // y position is a multiple of iMCUSizeInPixels.iHeight due to the matrix |
|
706 // way in which pixels are gathered [see GetPixels()]. |
|
707 // KMaxRgbBufferLines * iMCUSizeInPixels.iHeight = no of scanlines |
|
708 |
|
709 // Maximum number of matrix lines to process in one process call |
|
710 // we have no limit for CImageFrame since it doesn't lock |
|
711 // FBs heap |
|
712 |
|
713 TComponentConfig* compCfg[KJpgMaxNumOfDataUnits];//no more then 10 data units is supported now |
|
714 for (TInt dataUnit = 0; dataUnit < iDataUnitCount; ++dataUnit) |
|
715 { |
|
716 compCfg[dataUnit] = (dataUnit < iCompConfig[ KUComp ].iFirstDUIdx ? iCompConfig + KYComp: |
|
717 (dataUnit < iCompConfig[ KVComp ].iFirstDUIdx ? iCompConfig + KUComp: |
|
718 iCompConfig + KVComp) |
|
719 ); |
|
720 } |
|
721 |
|
722 |
|
723 do |
|
724 { |
|
725 if ( iWrittingDUIdx >= iDataUnitCount) // we have no blocks to write, so start from processing phase |
|
726 { |
|
727 |
|
728 TDataUnit* dataUnitPtr = NULL; |
|
729 |
|
730 iPosProcessor.GetCurrentPosition(iPos); |
|
731 |
|
732 if (iImageFrameCodecPtr==NULL) |
|
733 { |
|
734 PROFILE_START(iGetPixelsTime); |
|
735 // get destination scanline |
|
736 CRgbBufferPtr::TConstRgbBufferPtr bufPtr = GetPixels(iPos); |
|
737 PROFILE_END(iGetPixelsTime); |
|
738 |
|
739 PROFILE_START(iDataUnitProcessTime); |
|
740 dataUnitPtr = iDataUnitProcessor->Process( bufPtr ); |
|
741 iRgbInputBuffer->UnlockBuffer(); |
|
742 PROFILE_END(iDataUnitProcessTime); |
|
743 } |
|
744 else |
|
745 { |
|
746 PROFILE_START(iImageFrameTime); |
|
747 TPoint imageFramePos; |
|
748 //set position before processing Image Frame Codec |
|
749 if(iImageFrameCodecPtr->SamplingScheme() == KUidSamplingColor422) |
|
750 { |
|
751 imageFramePos = iPos; |
|
752 //the code below makes it possible to navigate the image in the multiples of 2 blocks. To be removed if the image frame write codec navigate in the multiple of a block. |
|
753 imageFramePos.iX = imageFramePos.iX * 2; |
|
754 } |
|
755 else |
|
756 { |
|
757 imageFramePos = iPos; |
|
758 } |
|
759 iImageFrameCodecPtr->SetPosition(imageFramePos); |
|
760 dataUnitPtr = iImageFrameCodecPtr->ProcessL(); |
|
761 PROFILE_END(iImageFrameTime); |
|
762 if(iIsBlockStreaming) |
|
763 { |
|
764 iStreamConfig.iNumOfBlocksAppended++; |
|
765 iStreamConfig.iNumOfMCUsProcessed++; |
|
766 if(iStreamConfig.iNumOfBlocksAppended >= iStreamConfig.iNumOfBlocksToAppend) |
|
767 { |
|
768 iPosProcessor.SetEndOfImage(ETrue); |
|
769 } |
|
770 |
|
771 if(iStreamConfig.iNumOfMCUsProcessed > ((iFrameInfo.iSizeInPixels.iWidth * iFrameInfo.iSizeInPixels.iHeight) / (iMCUSizeInPixels.iWidth * iMCUSizeInPixels.iHeight))) |
|
772 { |
|
773 User::Leave(KErrOverflow); |
|
774 } |
|
775 } |
|
776 } |
|
777 |
|
778 iWrittingDUIdx = 0; |
|
779 TDataUnit transformedDataUnitCoeff; |
|
780 TDataUnit transformedDataUnit; |
|
781 // here we create a special "data unit" and place a non-zero value at the end, |
|
782 // as that is required by the WriteDataUnitL() |
|
783 TSpecialDataUnit* quantTarget = iQuantizeTarget; |
|
784 |
|
785 for (TInt dataUnit = 0; dataUnit < iDataUnitCount; ++dataUnit) |
|
786 { |
|
787 PROFILE_START(iTransforTime); |
|
788 if(iPosProcessor.IsTransformNeeded()) |
|
789 { |
|
790 //Xform Coefficients in DU |
|
791 TransformDataUnitCoeff(transformedDataUnitCoeff, dataUnitPtr[iTransformedDUIdx[dataUnit]]); |
|
792 |
|
793 //Xform DCT |
|
794 iDCT.Transform(transformedDataUnit, transformedDataUnitCoeff, iHighSpeedMode); |
|
795 } |
|
796 else |
|
797 { |
|
798 //Xform DCT |
|
799 iDCT.Transform(transformedDataUnit, dataUnitPtr[iTransformedDUIdx[dataUnit]], iHighSpeedMode); |
|
800 } |
|
801 PROFILE_END(iTransforTime); |
|
802 |
|
803 //Quantize |
|
804 PROFILE_START(iQuantizeTime); |
|
805 compCfg[dataUnit]->iQTTable->Quantize(*quantTarget, transformedDataUnit, iHighSpeedMode); |
|
806 quantTarget->iExtraElement = KErrNotFound; // place a non-zero value at the end |
|
807 quantTarget++; |
|
808 PROFILE_END(iQuantizeTime); |
|
809 } |
|
810 } |
|
811 |
|
812 TSpecialDataUnit* quantTarget = iQuantizeTarget; |
|
813 |
|
814 PROFILE_START(iWriteDataUnitTime); |
|
815 /* Coverity may report about overrun of array compCfg. This is false positive. The value of iDataUnitCount is <= KJpgMaxNumOfDataUnits, |
|
816 so the array access is safe. */ |
|
817 for (; iWrittingDUIdx < iDataUnitCount; ++iWrittingDUIdx) |
|
818 { |
|
819 WriteDataUnitL( *(quantTarget + iWrittingDUIdx), *(compCfg[iWrittingDUIdx]->iDCTable), *(compCfg[iWrittingDUIdx]->iACTable), |
|
820 iDCPredictor[ compCfg[iWrittingDUIdx]->iDCPredictorIdx ] |
|
821 ); |
|
822 } |
|
823 PROFILE_END(iWriteDataUnitTime); |
|
824 iWrittingDUIdx = KJpgMaxNumOfDataUnits; |
|
825 |
|
826 iPosProcessor.MoveNext(); |
|
827 |
|
828 } while(!iPosProcessor.IsEndOfImage()); |
|
829 |
|
830 } |
|
831 |
|
832 // CJpgImageFrameWriteCodec |
|
833 CJpgImageFrameWriteCodec::CJpgImageFrameWriteCodec(const CImageFrame* aFrame) |
|
834 { |
|
835 iSource = aFrame; |
|
836 } |
|
837 |
|
838 CJpgImageFrameWriteCodec::~CJpgImageFrameWriteCodec() |
|
839 { |
|
840 delete iImageFrameProcessorPtr; |
|
841 } |
|
842 |
|
843 CJpgImageFrameWriteCodec* CJpgImageFrameWriteCodec::NewL(const CImageFrame* aFrame) |
|
844 { |
|
845 CJpgImageFrameWriteCodec* self = new(ELeave) CJpgImageFrameWriteCodec(aFrame); |
|
846 return self; |
|
847 } |
|
848 |
|
849 void CJpgImageFrameWriteCodec::CreateImageProcessorL() |
|
850 { |
|
851 iImageFrameProcessorPtr = CJpgImageFrameProcessorUtility::NewL(*(const_cast<CImageFrame*>(iSource))); |
|
852 } |
|
853 |
|
854 TInt CJpgImageFrameWriteCodec::DataUnitCount() const |
|
855 { |
|
856 ASSERT( iDataUnitCount > 0 ); |
|
857 return iDataUnitCount; |
|
858 } |
|
859 |
|
860 TSize CJpgImageFrameWriteCodec::ImageFrameSize() const |
|
861 { |
|
862 return iSource->FrameSizeInPixels(); |
|
863 } |
|
864 |
|
865 TUid CJpgImageFrameWriteCodec::SamplingScheme() const |
|
866 { |
|
867 return iSampleScheme; |
|
868 } |
|
869 |
|
870 void CJpgImageFrameWriteCodec::PrepareFrameInfoL(TJpgFrameInfo& aFrameInfo, const CFrameImageData* aFrameImageData) |
|
871 { |
|
872 // First, validate image frame |
|
873 CImageFrame* aImageFrame = const_cast<CImageFrame*>(iSource); |
|
874 CJpgImageFrameProcessorUtility::ValidateImageFrameL(*aImageFrame, ETrue); |
|
875 |
|
876 // If CFrameImageData has been provided aFrameInfo contains some encoder |
|
877 // parameters already. These need to be the same as the imageFrame parameters. |
|
878 // Therefore, validate the image frame parameters against the frame image |
|
879 // data provided by the application if any |
|
880 TUid aSampling = static_cast<const TFrameFormat&>(iSource->FrameFormat()).Sampling(); |
|
881 |
|
882 TInt count = (aFrameImageData) ? aFrameImageData->ImageDataCount() : 0; |
|
883 |
|
884 TBool samplingSchemeSet = EFalse; |
|
885 // look for Frame Image Data |
|
886 for (TInt index = 0 ; index<count ; index++) |
|
887 { |
|
888 const TImageDataBlock& encoderData = *aFrameImageData->GetImageData(index); |
|
889 if (encoderData.DataType() == KJPGImageDataUid) |
|
890 { |
|
891 const TJpegImageData& jpegImageData = static_cast<const TJpegImageData&>(encoderData); |
|
892 |
|
893 TJpegImageData::TColorSampling sampleScheme = jpegImageData.iSampleScheme; |
|
894 CJpgImageFrameProcessorUtility::ValidateFrameImageDataL(sampleScheme, *aImageFrame); |
|
895 |
|
896 switch (sampleScheme) |
|
897 { |
|
898 case TJpegImageData::EMonochrome: |
|
899 iDataUnitCount = 1; |
|
900 break; |
|
901 case TJpegImageData::EColor420: |
|
902 iDataUnitCount = 6; |
|
903 break; |
|
904 case TJpegImageData::EColor422: |
|
905 iDataUnitCount = 4; |
|
906 break; |
|
907 case TJpegImageData::EColor444: |
|
908 iDataUnitCount = 3; |
|
909 break; |
|
910 default: |
|
911 iDataUnitCount = 0; |
|
912 User::Leave(KErrNotSupported); |
|
913 break; |
|
914 }; |
|
915 |
|
916 samplingSchemeSet = ETrue; |
|
917 break; |
|
918 } |
|
919 } |
|
920 |
|
921 if (!samplingSchemeSet) |
|
922 { |
|
923 iSampleScheme = aSampling; |
|
924 InitFrameCompInfoL(aFrameInfo); |
|
925 } |
|
926 } |
|
927 |
|
928 void CJpgImageFrameWriteCodec::Position(TPoint& aPos) |
|
929 { |
|
930 iImageFrameProcessorPtr->GetCurrentPosition(aPos); |
|
931 } |
|
932 |
|
933 void CJpgImageFrameWriteCodec::SetPosition(TPoint& aPos) |
|
934 { |
|
935 iImageFrameProcessorPtr->SetCurrentPosition(aPos); |
|
936 } |
|
937 |
|
938 TDataUnit* CJpgImageFrameWriteCodec::ProcessL() |
|
939 { |
|
940 return (iImageFrameProcessorPtr->ReadBlockL()); |
|
941 } |
|
942 |
|
943 |
|
944 // for streaming |
|
945 void CJpgImageFrameWriteCodec::AppendImageFrameBlockL(const CImageFrame* aFrame) |
|
946 { |
|
947 if(aFrame == NULL) |
|
948 { |
|
949 User::Leave(KErrNotSupported); |
|
950 } |
|
951 |
|
952 iSource = aFrame; |
|
953 |
|
954 if(iImageFrameProcessorPtr) |
|
955 { |
|
956 delete iImageFrameProcessorPtr; |
|
957 iImageFrameProcessorPtr = NULL; |
|
958 } |
|
959 |
|
960 iImageFrameProcessorPtr = CJpgImageFrameProcessorUtility::NewL(*(const_cast<CImageFrame*>(iSource))); |
|
961 |
|
962 // First, validate image frame |
|
963 CImageFrame* aImageFrame = const_cast<CImageFrame*>(iSource); |
|
964 CJpgImageFrameProcessorUtility::ValidateImageFrameL(*aImageFrame, ETrue); |
|
965 CJpgImageFrameProcessorUtility::ValidateFrameImageDataL(GetColorSamplingL(iSampleScheme), *aImageFrame); |
|
966 } |
|
967 |
|
968 TJpegImageData::TColorSampling CJpgImageFrameWriteCodec::GetColorSamplingL(TUid aFormat) |
|
969 { |
|
970 TJpegImageData::TColorSampling aColorSampling = TJpegImageData::EColor420; |
|
971 switch (aFormat.iUid) |
|
972 { |
|
973 case KSamplingMonochromeUidValue: |
|
974 aColorSampling = TJpegImageData::EMonochrome; |
|
975 break; |
|
976 case KSamplingColor422UidValue: |
|
977 aColorSampling = TJpegImageData::EColor422; |
|
978 break; |
|
979 case KSamplingColor420UidValue: |
|
980 aColorSampling = TJpegImageData::EColor420; |
|
981 break; |
|
982 default: |
|
983 User::Leave(KErrNotSupported); |
|
984 } |
|
985 return aColorSampling; |
|
986 } |
|
987 |
|
988 TUid CJpgImageFrameWriteCodec::GetSamplingSchemeL(TUid aFormat) |
|
989 { |
|
990 TUid samplingScheme = KUidSamplingColor420; |
|
991 switch (aFormat.iUid) |
|
992 { |
|
993 case KFormatYUVMonochromeUidValue: |
|
994 samplingScheme = KUidSamplingMonochrome; |
|
995 break; |
|
996 case KFormatYUV422InterleavedUidValue: |
|
997 samplingScheme = KUidSamplingColor422; |
|
998 break; |
|
999 case KFormatYUV420PlanarReversedUidValue: |
|
1000 case KFormatYUV420PlanarUidValue: |
|
1001 samplingScheme = KUidSamplingColor420; |
|
1002 break; |
|
1003 default: |
|
1004 User::Leave(KErrNotSupported); |
|
1005 } |
|
1006 return samplingScheme; |
|
1007 } |
|
1008 |
|
1009 void CJpgImageFrameWriteCodec::PrepareFrameImageDataInfoL(TJpgFrameInfo& aFrameInfo, TUid aFormat, TInt /*aFrameNumber*/, const CFrameImageData* /*aFrameImageData*/) |
|
1010 { |
|
1011 iSampleScheme = GetSamplingSchemeL(aFormat); |
|
1012 InitFrameCompInfoL(aFrameInfo); |
|
1013 } |
|
1014 |
|
1015 void CJpgImageFrameWriteCodec::InitFrameCompInfoL(TJpgFrameInfo& aFrameInfo) |
|
1016 { |
|
1017 aFrameInfo.iComponent[0].iQTable = 0; |
|
1018 if (iSampleScheme == KUidSamplingMonochrome ) |
|
1019 { |
|
1020 aFrameInfo.iNumberOfComponents = 1; |
|
1021 aFrameInfo.iComponent[0].iHorzSampleFactor = 1; |
|
1022 aFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1023 iDataUnitCount = 1; |
|
1024 } |
|
1025 else |
|
1026 { |
|
1027 aFrameInfo.iNumberOfComponents = 3; |
|
1028 switch (iSampleScheme.iUid) |
|
1029 { |
|
1030 case KSamplingColor420UidValue: |
|
1031 aFrameInfo.iComponent[0].iHorzSampleFactor = 2; |
|
1032 aFrameInfo.iComponent[0].iVertSampleFactor = 2; |
|
1033 aFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1034 aFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1035 iDataUnitCount = 6; |
|
1036 break; |
|
1037 case KSamplingColor422UidValue: |
|
1038 aFrameInfo.iComponent[0].iHorzSampleFactor = 2; |
|
1039 aFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1040 aFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1041 aFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1042 iDataUnitCount = 4; |
|
1043 break; |
|
1044 case KSamplingColor444UidValue: |
|
1045 aFrameInfo.iComponent[0].iHorzSampleFactor = 1; |
|
1046 aFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1047 aFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1048 aFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1049 iDataUnitCount = 3; |
|
1050 break; |
|
1051 default: |
|
1052 iDataUnitCount = 0; |
|
1053 User::Leave(KErrNotSupported); |
|
1054 } |
|
1055 |
|
1056 aFrameInfo.iComponent[1].iHorzSampleFactor = 1; |
|
1057 aFrameInfo.iComponent[1].iQTable = 1; |
|
1058 aFrameInfo.iComponent[2].iHorzSampleFactor = 1; |
|
1059 aFrameInfo.iComponent[2].iQTable = 1; |
|
1060 } |
|
1061 |
|
1062 aFrameInfo.iMaxHorzSampleFactor = aFrameInfo.iComponent[0].iHorzSampleFactor; |
|
1063 aFrameInfo.iMaxVertSampleFactor = aFrameInfo.iComponent[0].iVertSampleFactor; |
|
1064 } |
|
1065 |
|
1066 void CJpgWriteCodec::InitTransformCoordinates(TPositionProcessor& aPosProcessor, const TRect& aSourceRect, const TSize& aMCUSize, const RArray<TUint>& aOperationsRequested) |
|
1067 { |
|
1068 |
|
1069 aPosProcessor.SetDimensions(aSourceRect,aMCUSize); |
|
1070 |
|
1071 TInt count = aOperationsRequested.Count(); |
|
1072 for (TInt i=0; i<count; i++) |
|
1073 { |
|
1074 switch(aOperationsRequested[i]) |
|
1075 { |
|
1076 case KRotation90DegreesClockwise: |
|
1077 { |
|
1078 aPosProcessor.RotateCoordinates(); |
|
1079 break; |
|
1080 } |
|
1081 case KRotation180DegreesClockwise: |
|
1082 { |
|
1083 aPosProcessor.RotateCoordinates(); |
|
1084 aPosProcessor.RotateCoordinates(); |
|
1085 break; |
|
1086 } |
|
1087 case KRotation270DegreesClockwise: |
|
1088 { |
|
1089 aPosProcessor.RotateCoordinates(); |
|
1090 aPosProcessor.RotateCoordinates(); |
|
1091 aPosProcessor.RotateCoordinates(); |
|
1092 break; |
|
1093 } |
|
1094 case KMirrorHorizontalAxis: |
|
1095 { |
|
1096 aPosProcessor.HorFlipCoordinates(); |
|
1097 break; |
|
1098 } |
|
1099 case KMirrorVerticalAxis: |
|
1100 { |
|
1101 aPosProcessor.VerFlipCoordinates(); |
|
1102 break; |
|
1103 } |
|
1104 default: |
|
1105 ASSERT( EFalse ); |
|
1106 } |
|
1107 } |
|
1108 |
|
1109 aPosProcessor.MoveFirst(); |
|
1110 |
|
1111 } |
|
1112 |
|
1113 void CJpgWriteCodec::TransformDataUnitCoeff(TDataUnit& aDestination,const TDataUnit& aSource) |
|
1114 { |
|
1115 const TDataUnit::TDataUnitElemType* srcPtr = aSource.iCoeff; |
|
1116 TDataUnit::TDataUnitElemType* dstPtr = aDestination.iCoeff; |
|
1117 TUint8* idxCoeff = iTransformDUCoeffIdx; |
|
1118 |
|
1119 TInt count = 0; |
|
1120 do |
|
1121 { |
|
1122 TUint32 fourIndeces = *(TUint32 *)(idxCoeff+count); |
|
1123 |
|
1124 dstPtr[count++] = srcPtr[fourIndeces & 0xFF]; |
|
1125 fourIndeces >>= 8; |
|
1126 |
|
1127 dstPtr[count++] = srcPtr[fourIndeces & 0xFF]; |
|
1128 fourIndeces >>= 8; |
|
1129 |
|
1130 dstPtr[count++] = srcPtr[fourIndeces & 0xFF]; |
|
1131 fourIndeces >>= 8; |
|
1132 |
|
1133 dstPtr[count++] = srcPtr[fourIndeces]; |
|
1134 } while (count < KJpgDCTBlockSize); |
|
1135 |
|
1136 /*do |
|
1137 { |
|
1138 dstPtr[count++] = srcPtr[*idxCoeff++]; |
|
1139 } while (count < KJpgDCTBlockSize);*/ |
|
1140 |
|
1141 } |
|
1142 |
|
1143 void CJpgWriteCodec::InitTransformDataUnitCoeff(TUint8* aDestination) //TODO |
|
1144 { |
|
1145 TPositionProcessor posProcess; |
|
1146 TPoint pos; |
|
1147 |
|
1148 TUint8* dstPtr = aDestination; |
|
1149 |
|
1150 InitTransformCoordinates(posProcess, TRect(TPoint(0,0),TPoint(KJpgDCTBlockWidth, KJpgDCTBlockHeight)), TSize(KJpgPixelRatio, KJpgPixelRatio), iOperationsRequested); |
|
1151 |
|
1152 do |
|
1153 { |
|
1154 posProcess.GetCurrentPosition(pos); |
|
1155 TUint8 elementPos = ((pos.iY * KJpgDCTBlockWidth) + pos.iX); |
|
1156 *dstPtr++ = elementPos; |
|
1157 posProcess.MoveNext(); |
|
1158 } while (!posProcess.IsEndOfImage()); |
|
1159 } |
|
1160 |
|
1161 void CJpgWriteCodec::InitTransformDataUnitIndex(TInt aDataUnitCount) |
|
1162 { |
|
1163 |
|
1164 TUint8 tempTransformedDUIdx[KJpgMaxNumOfDataUnits]; |
|
1165 TPositionProcessor posProcess; |
|
1166 TPoint pos; |
|
1167 |
|
1168 TUint8* dstPtr = iTransformedDUIdx; |
|
1169 for(TInt i = 0; i < aDataUnitCount; i++) |
|
1170 { |
|
1171 iTransformedDUIdx[i] = i; |
|
1172 tempTransformedDUIdx[i] = i; |
|
1173 } |
|
1174 |
|
1175 ASSERT( aDataUnitCount > 0 && aDataUnitCount <= KJpgMaxNumOfDataUnits ); // Defensive programming |
|
1176 |
|
1177 switch (aDataUnitCount) |
|
1178 { |
|
1179 case KJpgMonochromeDataUnitCount: |
|
1180 return; |
|
1181 case KJpgEColor444DataUnitCount: // 4:4:4 |
|
1182 return; |
|
1183 case KJpgColor422DataUnitCount: // 4:2:2 |
|
1184 InitTransformCoordinates(posProcess, TRect(TPoint(0,0),TPoint(2,1)), TSize(KJpgPixelRatio, KJpgPixelRatio), iOperationsRequested); |
|
1185 break; |
|
1186 case KJpgColor420DataUnitCount: // 4:2:0 |
|
1187 InitTransformCoordinates(posProcess, TRect(TPoint(0,0),TPoint(2,2)), TSize(KJpgPixelRatio, KJpgPixelRatio), iOperationsRequested); |
|
1188 break; |
|
1189 default: |
|
1190 ASSERT( EFalse ); |
|
1191 } |
|
1192 do |
|
1193 { |
|
1194 posProcess.GetCurrentPosition(pos); |
|
1195 TInt elementPos = ((pos.iY * 2) + pos.iX); |
|
1196 *dstPtr++ = tempTransformedDUIdx[elementPos]; |
|
1197 posProcess.MoveNext(); |
|
1198 } while (!posProcess.IsEndOfImage()); |
|
1199 } |
|
1200 |
|
1201 // |
|
1202 // this section contains many performance-critical code, so use ARM instruction set for it |
|
1203 // |
|
1204 #ifdef __ARMCC__ |
|
1205 #pragma push |
|
1206 #pragma arm |
|
1207 #pragma O3 |
|
1208 #pragma Otime |
|
1209 #endif |
|
1210 |
|
1211 inline |
|
1212 CRgbBufferPtr::TConstRgbBufferPtr CJpgWriteCodec::GetPixels(const TPoint& aPos) |
|
1213 { |
|
1214 CRgbBufferPtr::TConstRgbBufferPtr ptr = iRgbInputBuffer->LockBuffer(aPos); |
|
1215 return ptr; |
|
1216 } |
|
1217 |
|
1218 #if defined(__ARMCC__) && defined(__MARM_ARMV5__)//armv6 and armv7 should be added later |
|
1219 __asm int count_lz(int reserved, int x) |
|
1220 { |
|
1221 clz r0, r1 |
|
1222 bx lr |
|
1223 endp |
|
1224 } |
|
1225 |
|
1226 inline |
|
1227 TInt MaxPowerOf2(TUint aValue) |
|
1228 { |
|
1229 return 32 - count_lz(0, aValue); |
|
1230 } |
|
1231 #else |
|
1232 const TUint8 KNumBitsTable[64]= |
|
1233 { |
|
1234 // 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F |
|
1235 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 |
|
1236 ,6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 |
|
1237 }; |
|
1238 |
|
1239 inline |
|
1240 TInt MaxPowerOf2(TUint aValue) |
|
1241 { |
|
1242 register TInt powerOf2 = 0; |
|
1243 do |
|
1244 { |
|
1245 powerOf2 += (aValue >= 64? 6 : KNumBitsTable[aValue]); |
|
1246 aValue >>= 6; |
|
1247 } while (aValue); |
|
1248 return powerOf2; |
|
1249 } |
|
1250 #endif |
|
1251 |
|
1252 void CJpgWriteCodec::WriteDataUnitL(const TDataUnit& aDataUnit,const TEncHuffmanTable<KEncDCHTSize+1>& aDCTable, const TEncHuffmanTable<KEncACHTSize+1>& aACTable, TInt& aDCPredictor) |
|
1253 { |
|
1254 # define MASKED_VALUE(a, num_bit) (TUint(a) & ((1 << num_bit) - 1))//TO optimize |
|
1255 |
|
1256 // make sure that we will never exceed the available buffer size |
|
1257 // as we may have all the 0xFF which will result in size doubled |
|
1258 // |
|
1259 if ( TUint(iDestPtrLimit - iDestPtr) < KDataUnitSafeBufferSz) |
|
1260 { |
|
1261 User::Leave(KErrCompletion); |
|
1262 } |
|
1263 TInt value = aDataUnit.iCoeff[0] - aDCPredictor; |
|
1264 aDCPredictor = aDataUnit.iCoeff[0]; |
|
1265 TInt valueComp = value; |
|
1266 |
|
1267 if (value < 0) |
|
1268 { |
|
1269 value = -value; |
|
1270 valueComp--; |
|
1271 } |
|
1272 |
|
1273 |
|
1274 TInt numBits = MaxPowerOf2(value); |
|
1275 |
|
1276 TInt size; |
|
1277 TInt code=aDCTable.GetCode(size,numBits); |
|
1278 |
|
1279 register TUint32 bitBuffer = iBitBuffer; |
|
1280 register TInt bitsUsed = iBitsUsed; |
|
1281 |
|
1282 bitsUsed = WriteBitsFast(code, size, bitBuffer, bitsUsed); |
|
1283 bitsUsed = WriteBitsFast(MASKED_VALUE(valueComp, numBits), numBits, bitBuffer, bitsUsed); |
|
1284 |
|
1285 const TInt16* acPtr = aDataUnit.iCoeff + 1; |
|
1286 const TInt16* acPtrLimit = aDataUnit.iCoeff + KJpgDCTBlockSize; |
|
1287 TInt runLength = 0; |
|
1288 |
|
1289 FOREVER |
|
1290 { |
|
1291 do |
|
1292 { |
|
1293 // here we assume that there is always a non-zero value |
|
1294 // at the end, the caller must do that. |
|
1295 value = *acPtr++; |
|
1296 if (value == 0) |
|
1297 { |
|
1298 runLength++; |
|
1299 } |
|
1300 } while (value == 0); |
|
1301 |
|
1302 if (acPtr > acPtrLimit) |
|
1303 { |
|
1304 break; |
|
1305 } |
|
1306 |
|
1307 while (runLength > 15) |
|
1308 { |
|
1309 code = aACTable.GetCode(size, KJpgZeroRunValue); |
|
1310 bitsUsed = WriteBitsFast(code, size, bitBuffer, bitsUsed); |
|
1311 runLength -= 16; |
|
1312 } |
|
1313 |
|
1314 valueComp = value; |
|
1315 if (value < 0) |
|
1316 { |
|
1317 value = -value; |
|
1318 valueComp--; |
|
1319 } |
|
1320 |
|
1321 numBits = MaxPowerOf2( (value | 1) ); |
|
1322 |
|
1323 code = aACTable.GetCode(size,(runLength << 4) | numBits); |
|
1324 bitsUsed = WriteBitsFast(code, size, bitBuffer, bitsUsed); |
|
1325 bitsUsed = WriteBitsFast(MASKED_VALUE(valueComp,numBits), numBits, bitBuffer, bitsUsed); |
|
1326 runLength = 0; |
|
1327 |
|
1328 } |
|
1329 |
|
1330 if (runLength > 0) |
|
1331 { |
|
1332 code = aACTable.GetCode(size,0); |
|
1333 bitsUsed = WriteBitsFast(code, size, bitBuffer, bitsUsed); |
|
1334 } |
|
1335 |
|
1336 iBitBuffer = bitBuffer; |
|
1337 iBitsUsed = bitsUsed; |
|
1338 |
|
1339 ASSERT( iDestPtr < iDestPtrLimit ); |
|
1340 |
|
1341 # undef MASKED_VALUE |
|
1342 } |
|
1343 |
|
1344 FORCEDINLINE TUint32 CJpgWriteCodec::WriteBitsFast(TUint32 aValue, TInt aNumBits, TUint32& aBitBuffer, TInt aBitsUsed) |
|
1345 { |
|
1346 if ((aBitsUsed + aNumBits) > 32) |
|
1347 { |
|
1348 aNumBits -= (32 - aBitsUsed); |
|
1349 aBitBuffer |= (aValue >> aNumBits); |
|
1350 WriteBits(aBitBuffer); |
|
1351 aBitBuffer = (aValue << (32 - aNumBits)); |
|
1352 return aNumBits; |
|
1353 } |
|
1354 else |
|
1355 { |
|
1356 aBitsUsed += aNumBits; |
|
1357 aBitBuffer |= (aValue << (32 - aBitsUsed)); |
|
1358 return aBitsUsed; |
|
1359 } |
|
1360 } |
|
1361 |
|
1362 void CJpgWriteCodec::WriteBits(TUint32 aBitBuffer) |
|
1363 { |
|
1364 register TUint8* bytePtr = (TUint8*)(&aBitBuffer); |
|
1365 register TUint8 byte; |
|
1366 |
|
1367 byte = *(bytePtr+3); |
|
1368 *iDestPtr++ = byte; |
|
1369 if (byte == 0xff) |
|
1370 { |
|
1371 *iDestPtr++ = 0; |
|
1372 } |
|
1373 |
|
1374 byte = *(bytePtr+2); |
|
1375 *iDestPtr++ = byte; |
|
1376 if (byte == 0xff) |
|
1377 { |
|
1378 *iDestPtr++ = 0; |
|
1379 } |
|
1380 |
|
1381 byte = *(bytePtr+1); |
|
1382 *iDestPtr++ = byte; |
|
1383 if (byte == 0xff) |
|
1384 { |
|
1385 *iDestPtr++ = 0; |
|
1386 } |
|
1387 |
|
1388 byte = *bytePtr; |
|
1389 *iDestPtr++ = byte; |
|
1390 if (byte == 0xff) |
|
1391 { |
|
1392 *iDestPtr++ = 0; |
|
1393 } |
|
1394 } |
|
1395 |
|
1396 #ifdef __ARMCC__ |
|
1397 #pragma pop |
|
1398 #endif |