|
1 // Copyright (c) 2003-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 #include "NQColorQuantizer.h" |
|
17 #include "FastColorQuantizer.h" |
|
18 #include "GifScalerBody.h" |
|
19 #include "GifScalerMain.h" |
|
20 #include "gifscaler.h" |
|
21 |
|
22 // Default transparency threshold. |
|
23 const TUint8 KDefaultTransparencyThreshold = 128; |
|
24 |
|
25 // Number of palette entries (8bpp) |
|
26 const TInt KMaxPaletteEntries = 256; |
|
27 |
|
28 |
|
29 /* |
|
30 * NewL |
|
31 * The function NewL constructs a CGifScalerBody |
|
32 * |
|
33 * @return CGifScalerBody* |
|
34 * |
|
35 */ |
|
36 CGifScaler::CBody* CGifScaler::CBody::NewL(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions) |
|
37 { |
|
38 // Assert valid source bitmap. |
|
39 __ASSERT_ALWAYS(aSource.Handle() != 0, Panic(ENoSourceBitmap)); |
|
40 |
|
41 if(aSource.ExtendedBitmapType()!=KNullUid) |
|
42 { |
|
43 User::Leave(KErrNotSupported); |
|
44 } |
|
45 |
|
46 // If any dimension is zero then send back an error. |
|
47 TSize sourceSize = aSource.SizeInPixels(); |
|
48 if ((sourceSize.iWidth == 0) || (sourceSize.iHeight == 0)) |
|
49 User::Leave(KErrArgument); |
|
50 |
|
51 if (aSourceMask) |
|
52 { |
|
53 // Assert valid source mask bitmap. |
|
54 __ASSERT_ALWAYS(aSourceMask->Handle() != 0, Panic(ENoSourceMaskBitmap)); |
|
55 |
|
56 if(aSourceMask->ExtendedBitmapType()!=KNullUid) |
|
57 { |
|
58 User::Leave(KErrNotSupported); |
|
59 } |
|
60 |
|
61 // If the mask dimensions are not the same as the source then send back an error. |
|
62 TSize maskSize = aSourceMask->SizeInPixels(); |
|
63 if (maskSize != sourceSize) |
|
64 User::Leave(KErrArgument); |
|
65 |
|
66 // If the mask bitmap isn't in the correct display mode then send back an error. |
|
67 TDisplayMode maskMode = aSourceMask->DisplayMode(); |
|
68 if ((maskMode != EGray2) && (maskMode != EGray256)) |
|
69 User::Leave(KErrArgument); |
|
70 } |
|
71 |
|
72 // If the options are invalid send back an error. |
|
73 if ((aOptions<CGifScaler::ELowQualityQuantization) || (aOptions>CGifScaler::EMaximumQualityQuantization)) |
|
74 User::Leave(KErrArgument); |
|
75 |
|
76 CBody* self = new(ELeave) CBody(aSource, aSourceMask, aOptions); |
|
77 CleanupStack::PushL(self); |
|
78 self->ConstructL(); |
|
79 |
|
80 CleanupStack::Pop(); |
|
81 return self; |
|
82 } |
|
83 |
|
84 |
|
85 /* |
|
86 * |
|
87 * CGifScalerBody |
|
88 * Constructor for this class. Adds itself to <code>CActiveScheduler</code>. |
|
89 * |
|
90 */ |
|
91 CGifScaler::CBody::CBody(CFbsBitmap& aSource, CFbsBitmap* aSourceMask, CGifScaler::TOptions aOptions) |
|
92 : CActive(CActive::EPriorityIdle), iSource(aSource), iSourceMask(aSourceMask), iOptions(aOptions) |
|
93 { |
|
94 CActiveScheduler::Add(this); |
|
95 } |
|
96 |
|
97 |
|
98 /* |
|
99 * ConstructL |
|
100 * Performs second phase of contruction |
|
101 * |
|
102 */ |
|
103 void CGifScaler::CBody::ConstructL() |
|
104 { |
|
105 ASSERT(iCurrentState == EInactiveState); |
|
106 ASSERT(iBitmapScaler == NULL); |
|
107 iBitmapScaler = CBitmapScaler::NewL(); |
|
108 } |
|
109 |
|
110 |
|
111 /* |
|
112 * |
|
113 * ~CGifScalerBody |
|
114 * This is the destructor for the CGifScalerBody |
|
115 * and is resposible for deallocating all resources |
|
116 * allocated by the CGifScalerBody |
|
117 * |
|
118 */ |
|
119 CGifScaler::CBody::~CBody() |
|
120 { |
|
121 // Parent object should have cancelled the scaling, |
|
122 // so assert that we are not active here. |
|
123 ASSERT(!IsActive()); |
|
124 |
|
125 ASSERT(iCurrentState == EInactiveState); |
|
126 ASSERT(iDestinationMask == NULL); |
|
127 delete iBitmapScaler; |
|
128 } |
|
129 |
|
130 // Implementation of CGifScaler::Scale. |
|
131 void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TBool aMaintainAspectRatio) |
|
132 { |
|
133 Scale(aStatus, aDestination, aPalette, KDefaultTransparencyThreshold, aMaintainAspectRatio); |
|
134 } |
|
135 |
|
136 // Implementation of CGifScaler::Scale (second version). |
|
137 void CGifScaler::CBody::Scale(TRequestStatus* aStatus, CFbsBitmap& aDestination, CPalette& aPalette, TUint8 aTransparencyThreshold, TBool aMaintainAspectRatio) |
|
138 { |
|
139 // Panic if aStatus is NULL. |
|
140 __ASSERT_ALWAYS(aStatus != NULL, Panic(EBadRequestStatus)); |
|
141 iRequestStatus = aStatus; |
|
142 *iRequestStatus = KRequestPending; |
|
143 |
|
144 // Panic if the target bitmap hasn't been created. |
|
145 __ASSERT_ALWAYS(aDestination.Handle() != 0, Panic(ENoDestinationBitmap)); |
|
146 |
|
147 // If the target bitmap is not 256 color then send back an error. |
|
148 if (aDestination.DisplayMode() != EColor256) |
|
149 { |
|
150 RequestComplete(KErrArgument); |
|
151 return; |
|
152 } |
|
153 |
|
154 if(aDestination.ExtendedBitmapType()!=KNullUid) |
|
155 { |
|
156 RequestComplete(KErrNotSupported); |
|
157 return; |
|
158 } |
|
159 |
|
160 // If the number of palette entries is not correct then send back an error. |
|
161 if (aPalette.Entries() != KMaxPaletteEntries) |
|
162 { |
|
163 RequestComplete(KErrArgument); |
|
164 return; |
|
165 } |
|
166 |
|
167 // Current state must be inactive. |
|
168 if (iCurrentState != EInactiveState) |
|
169 { |
|
170 RequestComplete(KErrGeneral); |
|
171 return; |
|
172 } |
|
173 |
|
174 // If any dimension is zero then send back KErrNone. |
|
175 TSize destSize = aDestination.SizeInPixels(); |
|
176 if ((destSize.iWidth == 0) || (destSize.iHeight == 0)) |
|
177 { |
|
178 RequestComplete(KErrNone); |
|
179 return; |
|
180 } |
|
181 |
|
182 iPalette = &aPalette; |
|
183 iPalette->Clear(); |
|
184 iDestination = &aDestination; |
|
185 iTransparencyThreshold = aTransparencyThreshold; |
|
186 iMaintainAspectRatio = aMaintainAspectRatio; |
|
187 |
|
188 TInt err; |
|
189 if (iSourceMask != NULL) |
|
190 { |
|
191 ASSERT(iDestinationMask == NULL); |
|
192 iDestinationMask = new CFbsBitmap; |
|
193 if (!iDestinationMask) |
|
194 { |
|
195 RequestComplete(KErrNoMemory); |
|
196 return; |
|
197 } |
|
198 |
|
199 err = iDestinationMask->Create(destSize, EGray256); |
|
200 if (err != KErrNone) |
|
201 { |
|
202 delete iDestinationMask; |
|
203 iDestinationMask = NULL; |
|
204 |
|
205 RequestComplete(err); |
|
206 return; |
|
207 } |
|
208 } |
|
209 |
|
210 ASSERT(iIntermediate == NULL); |
|
211 iIntermediate = new CFbsBitmap; |
|
212 if (!iIntermediate) |
|
213 { |
|
214 delete iDestinationMask; |
|
215 iDestinationMask = NULL; |
|
216 |
|
217 RequestComplete(KErrNoMemory); |
|
218 return; |
|
219 } |
|
220 |
|
221 if (destSize == iSource.SizeInPixels()) |
|
222 { |
|
223 // no scaling required |
|
224 iIntermediate->Duplicate(iSource.Handle()); |
|
225 if (iSourceMask != NULL) |
|
226 { |
|
227 iDestinationMask->Duplicate(iSourceMask->Handle()); |
|
228 } |
|
229 iCurrentState = EStartColorQuantization; |
|
230 |
|
231 SelfComplete(KErrNone); |
|
232 return; |
|
233 } |
|
234 |
|
235 err = iIntermediate->Create(destSize, EColor16M); |
|
236 if (err != KErrNone) |
|
237 { |
|
238 delete iDestinationMask; |
|
239 iDestinationMask = NULL; |
|
240 |
|
241 delete iIntermediate; |
|
242 iIntermediate = NULL; |
|
243 |
|
244 RequestComplete(err); |
|
245 return; |
|
246 } |
|
247 |
|
248 iCurrentState = EScalingState; |
|
249 |
|
250 // Start by scaling the source. |
|
251 iBitmapScaler->Scale(&iStatus, iSource, *iIntermediate, iMaintainAspectRatio); |
|
252 SetActive(); |
|
253 } |
|
254 |
|
255 |
|
256 /* |
|
257 * |
|
258 * RunL |
|
259 * Handle the next chunk of scaling/color quantization. |
|
260 */ |
|
261 void CGifScaler::CBody::RunL() |
|
262 { |
|
263 if (iStatus.Int() != KErrNone) |
|
264 { |
|
265 iCurrentState = ECleanUpPending; |
|
266 } |
|
267 |
|
268 switch (iCurrentState) |
|
269 { |
|
270 case EScalingState: |
|
271 // Scaling of source successful, so |
|
272 // start scaling the mask if there is one. |
|
273 iCurrentState = EStartColorQuantization; |
|
274 if (iSourceMask) |
|
275 { |
|
276 iBitmapScaler->Scale(&iStatus, *iSourceMask, *iDestinationMask); |
|
277 SetActive(); |
|
278 break; |
|
279 } |
|
280 |
|
281 case EStartColorQuantization: |
|
282 // Scaling is done, |
|
283 // so start color quantization if required. |
|
284 { |
|
285 // Resize the destination. |
|
286 // (The scaler may have changed the size) |
|
287 TSize intermediateSize = iIntermediate->SizeInPixels(); |
|
288 if (intermediateSize != iDestination->SizeInPixels()) |
|
289 { |
|
290 if ((intermediateSize.iWidth == 0) || (intermediateSize.iHeight == 0)) |
|
291 { |
|
292 // Complete the operation if the scaler |
|
293 // has reduced either dimension to zero. |
|
294 iCurrentState = ECleanUpState; |
|
295 break; |
|
296 } |
|
297 |
|
298 User::LeaveIfError(iDestination->Resize(intermediateSize)); |
|
299 if (iDestinationMask) |
|
300 { |
|
301 User::LeaveIfError(iDestinationMask->Resize(intermediateSize)); |
|
302 } |
|
303 } |
|
304 |
|
305 // Initialize settings for color quantizer. |
|
306 TInt numPaletteEntries = iSourceMask ? KMaxPaletteEntries-1 : KMaxPaletteEntries; |
|
307 TInt sampleFactor = 1; |
|
308 switch (iOptions) |
|
309 { |
|
310 case CGifScaler::ELowQualityQuantization: |
|
311 sampleFactor = 10; |
|
312 break; |
|
313 |
|
314 case CGifScaler::EMediumQualityQuantization: |
|
315 sampleFactor = 6; |
|
316 break; |
|
317 |
|
318 case CGifScaler::EHighQualityQuantization: |
|
319 sampleFactor = 3; |
|
320 break; |
|
321 |
|
322 case CGifScaler::EMaximumQualityQuantization: |
|
323 sampleFactor = 1; |
|
324 break; |
|
325 |
|
326 default: |
|
327 ASSERT(0); |
|
328 } |
|
329 |
|
330 // decide which color quantizer to use |
|
331 // CFastColorQuantiser can be used if the number of unique colors in |
|
332 // the image data is less than numPaletteEntries |
|
333 TBool useFastColorQuantiser = EFalse; |
|
334 TInt totalPixels = intermediateSize.iWidth * intermediateSize.iHeight; |
|
335 TInt nonTransparentPixels = totalPixels; |
|
336 if (totalPixels <= numPaletteEntries) |
|
337 { |
|
338 useFastColorQuantiser = ETrue; |
|
339 } |
|
340 else |
|
341 { |
|
342 // check the number of unique colors in the image data |
|
343 // if a transparency mask is supplied: |
|
344 // - exclude transparent pixels from the count |
|
345 // - update nonTransparentPixels |
|
346 |
|
347 CPalette* colorPalette = CPalette::NewL(numPaletteEntries); |
|
348 |
|
349 TPoint pixelPos(0, 0); |
|
350 TRgb pixelColor; |
|
351 TInt paletteIndex = 0; |
|
352 TBool tooManyColors = EFalse; |
|
353 TBool finished = EFalse; |
|
354 TBitmapUtil bitmapUtil(iIntermediate); |
|
355 bitmapUtil.Begin(pixelPos); |
|
356 |
|
357 TInt scanlineLengthMask = 0; |
|
358 TUint8* basePosMask = NULL; |
|
359 |
|
360 if (iDestinationMask) |
|
361 { |
|
362 scanlineLengthMask = CFbsBitmap::ScanLineLength(intermediateSize.iWidth, iDestinationMask->DisplayMode()); |
|
363 basePosMask = reinterpret_cast<TUint8*>(iDestinationMask->DataAddress()); |
|
364 } |
|
365 |
|
366 for (; (pixelPos.iY < intermediateSize.iHeight) && !finished; pixelPos.iY++) |
|
367 { |
|
368 pixelPos.iX = 0; |
|
369 bitmapUtil.SetPos(pixelPos); |
|
370 for (; (pixelPos.iX < intermediateSize.iWidth) && !finished; pixelPos.iX++) |
|
371 { |
|
372 pixelColor.SetInternal(bitmapUtil.GetPixel()); |
|
373 TBool pixelTransparent = iDestinationMask && (basePosMask[pixelPos.iX] <= iTransparencyThreshold); |
|
374 |
|
375 if (pixelTransparent) |
|
376 { |
|
377 nonTransparentPixels--; |
|
378 } |
|
379 else if (colorPalette->NearestEntry(pixelColor) != pixelColor) |
|
380 { |
|
381 if (paletteIndex < numPaletteEntries) |
|
382 { |
|
383 // current pixel color is not in the palette so add it |
|
384 colorPalette->SetEntry(paletteIndex, pixelColor); |
|
385 paletteIndex++; |
|
386 } |
|
387 else |
|
388 { |
|
389 // there are more colors in the image data than palette spaces |
|
390 tooManyColors = ETrue; |
|
391 if (!iDestinationMask) |
|
392 { |
|
393 // not counting nontransparent pixels so can finish |
|
394 finished = ETrue; |
|
395 } |
|
396 } |
|
397 } |
|
398 |
|
399 bitmapUtil.IncXPos(); |
|
400 } |
|
401 |
|
402 basePosMask += scanlineLengthMask; |
|
403 } |
|
404 bitmapUtil.End(); |
|
405 delete colorPalette; |
|
406 |
|
407 if (!tooManyColors) |
|
408 { |
|
409 useFastColorQuantiser = ETrue; |
|
410 } |
|
411 } |
|
412 |
|
413 |
|
414 // Create the color quantizer. |
|
415 if (useFastColorQuantiser) |
|
416 { |
|
417 // The number of pixels in the image is small, |
|
418 // so we can color quantize the image perfectly and quickly. |
|
419 iColorQuantizer = CFastColorQuantizer::NewL(iPalette, iTransparencyThreshold); |
|
420 } |
|
421 else |
|
422 { |
|
423 iColorQuantizer = CNQColorQuantizer::NewL(iPalette, numPaletteEntries, sampleFactor, iTransparencyThreshold); |
|
424 } |
|
425 |
|
426 // Kick off the color quantization. |
|
427 iColorQuantizer->Quantize(&iStatus, *iIntermediate, *iDestination, iDestinationMask, nonTransparentPixels); |
|
428 |
|
429 // Move to the cleanup state when the quantization is done. |
|
430 iCurrentState = ECleanUpPending; |
|
431 SetActive(); |
|
432 } |
|
433 |
|
434 break; |
|
435 |
|
436 case ECleanUpPending: |
|
437 // All operations are complete, |
|
438 // so move to the cleanup state. |
|
439 iCurrentState = ECleanUpState; |
|
440 break; |
|
441 |
|
442 default: |
|
443 ASSERT(0); |
|
444 break; |
|
445 } |
|
446 |
|
447 if (iCurrentState == ECleanUpState) |
|
448 { |
|
449 CleanUp(); |
|
450 iCurrentState = EScalingComplete; |
|
451 } |
|
452 |
|
453 if (iCurrentState == EScalingComplete) |
|
454 { |
|
455 RequestComplete(iStatus.Int()); |
|
456 iCurrentState = EInactiveState; |
|
457 } |
|
458 } |
|
459 |
|
460 /* |
|
461 * |
|
462 * RunError |
|
463 * Handle leaving errors from RunL. |
|
464 */ |
|
465 TInt CGifScaler::CBody::RunError(TInt aError) |
|
466 { |
|
467 CleanUp(); |
|
468 RequestComplete(aError); |
|
469 iCurrentState = EInactiveState; |
|
470 |
|
471 return KErrNone; |
|
472 } |
|
473 |
|
474 /* |
|
475 * |
|
476 * DoCancel |
|
477 * Called by active object to prematurely terminate this bitmap scaling. |
|
478 * |
|
479 */ |
|
480 void CGifScaler::CBody::DoCancel() |
|
481 { |
|
482 // Cancel scaling. |
|
483 ASSERT(iBitmapScaler); |
|
484 iBitmapScaler->Cancel(); |
|
485 |
|
486 // Cancel color quantization. |
|
487 if (iColorQuantizer) |
|
488 iColorQuantizer->Cancel(); |
|
489 |
|
490 CleanUp(); |
|
491 iCurrentState = EInactiveState; |
|
492 RequestComplete(KErrCancel); |
|
493 } |
|
494 |
|
495 /* |
|
496 * |
|
497 * CleanUp |
|
498 * Delete all the memory allocated during scaling. |
|
499 * |
|
500 */ |
|
501 void CGifScaler::CBody::CleanUp() |
|
502 { |
|
503 // Delete the intermediate. |
|
504 delete iIntermediate; |
|
505 iIntermediate = NULL; |
|
506 |
|
507 // Delete the color quantizer. |
|
508 delete iColorQuantizer; |
|
509 iColorQuantizer = NULL; |
|
510 |
|
511 // Delete the destination mask. |
|
512 delete iDestinationMask; |
|
513 iDestinationMask = NULL; |
|
514 } |
|
515 |
|
516 /* |
|
517 * |
|
518 * RequestComplete |
|
519 * @param "TInt aReason" |
|
520 * |
|
521 */ |
|
522 void CGifScaler::CBody::RequestComplete(TInt aReason) |
|
523 { |
|
524 TRequestStatus* stat = iRequestStatus; |
|
525 User::RequestComplete(stat, aReason); |
|
526 } |
|
527 |
|
528 /* |
|
529 * |
|
530 * SelfComplete |
|
531 * This function activates the active object and |
|
532 * signals completion of the current asynchronous operation |
|
533 * |
|
534 * @param "TInt aReason" |
|
535 * |
|
536 */ |
|
537 void CGifScaler::CBody::SelfComplete(TInt aReason) |
|
538 { |
|
539 SetActive(); |
|
540 |
|
541 TRequestStatus* stat = &iStatus; |
|
542 User::RequestComplete(stat, aReason); |
|
543 } |