1 /*****************************************************************************\
2 filterhpa.cpp : Implimentation for the TErnieFilter class
4 Copyright (c) 1996 - 2001, Hewlett-Packard Co.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. Neither the name of Hewlett-Packard nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
22 NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 \*****************************************************************************/
32 #if defined(APDK_DJ9xxVIP) && defined(APDK_VIP_COLORFILTERING)
36 // copied from vob \di_research on 10/31/00
37 // MODIFICATIONS BY GE:
38 // 0. remove Windows header references
40 // 2. set iRastersReady, iRastersDelivered in submitrowtofilter
41 // 3. (constructor) allocate (and delete in destructor) buffers for fRowPtr
42 // (instead of setting it to input buffers, since we reuse input buffers)
43 // 4. copy data into fRowPtr in submitrowtofilter
45 //#define assert ASSERT
47 #include "ernieplatform.h"
48 #include "filterhpa.h"
51 extern int blockStats[];
54 #if ((kMemWritesOptimize != 1) && (kMemWritesOptimize != 0))
55 #error "kMemWritesOptimize must be 0 or 1"
60 inline void AverageNRound(bool roundGreenDown, int &rFinal, int &r0, int &r1, int &gFinal, int &g0, int &g1, int &bFinal, int &b0, int &b1);
61 inline void AverageNRound(bool roundGreenDown, int &rFinal, int &r0, int &r1, int &gFinal, int &g0, int &g1, int &bFinal, int &b0, int &b1)
63 // By rounding G in the other direction than R and B L* variations are minimized while mathematically alternate rounding is accomplished. EGW 2 Dec. 1999.
66 rFinal = (r0 + r1 + 1) / 2;
67 gFinal = (g0 + g1) / 2;
68 bFinal = (b0 + b1 + 1) / 2;
72 rFinal = (r0 + r1) / 2;
73 gFinal = (g0 + g1 + 1) / 2;
74 bFinal = (b0 + b1) / 2;
79 // Filter1RawRow. To be used to filter an odd row for which we don't have a pair,
80 // found at the bottom of bands that aren't divisable by 2. This routine
81 // filters its row horizontally forming 4x1 and 2x1 blocks.
82 void TErnieFilter::Filter1RawRow(unsigned char *currPtr, int rowWidthInPixels, unsigned int *flagsPtr)
85 ASSERT(rowWidthInPixels > 0);
87 int R0, G0, B0, R1, G1, B1, lastR, lastG, lastB;
88 const unsigned int maxErrorForFourPixels = fMaxErrorForTwoPixels / 2;
89 // const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
91 // int currPixel, lastPixel;
92 uint32_t currPixel, lastPixel;
93 bool lastPairAveraged = false;
94 bool last2by2Averaged = false;
97 for (int pixelNum = 0; pixelNum < rowWidthInPixels; pixelNum++)
99 if ((pixelNum & 0x03) == 0x00) // 0,4,8...
101 last2by2Averaged = false; // Reinitialize every four columns;
104 currPixel = get4Pixel(currPtr);
106 flagsPtr[0] = (e11n|e11s); // Initialize in case nothing is found for this column
108 if (isWhite(currPixel))
111 #if kGatherStats == 1
112 blockStats[esWhiteFound]++;
116 // Currently we bail entirely if there is white. Later we may still do RLE on the non white pixel if one is present.
117 if (flagsPtr[0] == (e11n|e11s))
119 R1 = GetRed(currPixel);
120 G1 = GetGreen(currPixel);
121 B1 = GetBlue(currPixel);
123 // Can only horizontally average every other pixel, much like the 2x2 blocks.
126 // do horizontal on current raster
127 lastPixel = get4Pixel(currPtr,-1);
128 lastR = GetRed(lastPixel);
129 lastG = GetGreen(lastPixel);
130 lastB = GetBlue(lastPixel);
131 if ((fMaxErrorForTwoPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1, lastB, B1, fMaxErrorForTwoPixels)))
137 int didNotBuild4by1 = true;
138 #if kGatherStats == 1
139 blockStats[es21nw]++;
141 AverageNRound(isOdd(pixelNum), lastR, lastR, R1, lastG, lastG, G1, lastB, lastB, B1);
142 if ((pixelNum >= 3) && (flagsPtr[-3] & e21nw)) // 4,5,6,7,12,13,14,15,20...
145 ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
147 lastPixel = get4Pixel(currPtr,-3);
148 R0 = GetRed(lastPixel);
149 G0 = GetGreen(lastPixel);
150 B0 = GetBlue(lastPixel);
151 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0, lastB, B0, maxErrorForFourPixels)))
157 #if kGatherStats == 1
158 blockStats[es41ni]++;
160 didNotBuild4by1 = false;
161 AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
164 currPixel = (lastR<<16) + (lastG<<8) + lastB;
166 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
168 #if kMemWritesOptimize == 0
169 put4Pixel(currPtr, -3, currPixel);
170 put4Pixel(currPtr, -2, currPixel);
171 put4Pixel(currPtr, -1, currPixel);
172 put4Pixel(currPtr, 0, currPixel);
174 put4Pixel(currPtr, -3, currPixel);
176 flagsPtr[-3] = (flagsPtr[-3] & ~eNorths) | e41ni;
177 flagsPtr[-2] = (flagsPtr[-2] & ~eNorths) | e41n;
178 flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e41n;
179 flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e41n;
183 if (didNotBuild4by1) // Not a 4x1 so output 2x1.
185 ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
188 currPixel = (lastR<<16) + (lastG<<8) + lastB;
190 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
192 #if kMemWritesOptimize == 0
193 put4Pixel(currPtr, -1, currPixel);
194 put4Pixel(currPtr, 0, currPixel);
196 put4Pixel(currPtr, -1, currPixel);
198 flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e21nw;
199 flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e21ne;
201 } // If DeltaE... Looking for two by one
204 else // no flag bits set.
206 lastPairAveraged = false; // EGW Fixes bug on business graphics. 11/24/97
209 currPtr += eBufferedPixelWidthInBytes;
211 } // for each pixel...
214 // Filter2RawRows: Looks filter two raw rows together to form blocks. Vertical
215 // blocks are prefered over horizontal ones. The routine will create 1x2 blocks
216 // before it will create 4x1's. In total this routine will create 1x2, 2x2, 4x2,
217 // 4x1, and 2x1 blocks sizes, with the potential for two seperate 4x1's or 2x1's
218 // in the upper and lower rasters.
219 void TErnieFilter::Filter2RawRows(unsigned char *currPtr, unsigned char *upPtr, int rowWidthInPixels, unsigned int *flagsPtr)
223 ASSERT(rowWidthInPixels > 0);
225 int R0, G0, B0, R1, G1, B1, lastR, lastG, lastB;
226 const unsigned int maxErrorForFourPixels = fMaxErrorForTwoPixels / 2;
227 const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
229 // int currPixel, upPixel, lastPixel;
230 uint32_t currPixel, upPixel, lastPixel;
231 bool lastPairAveraged = false;
232 bool last2by2Averaged = false;
234 for (int pixelNum = 0; pixelNum < rowWidthInPixels; pixelNum++)
236 if ((pixelNum & 0x03) == 0x00) // 0,4,8...
238 last2by2Averaged = false; // Reinitialize every four columns;
241 upPixel = get4Pixel(upPtr);
242 currPixel = get4Pixel(currPtr);
244 flagsPtr[0] = (e11n|e11s); // Initialize in case nothing is found for this column
246 if (isWhite(upPixel) && isWhite(currPixel)) // both white?
249 #if kGatherStats == 1
250 blockStats[esWhiteFound]++;
254 // Do vertical average on the current 2 pixel high column
256 // Currently we bail entirely if there is white. Later we may still do RLE on the non white pixel if one is present.
257 if (flagsPtr[0] == (e11n|e11s))
259 R1 = GetRed(currPixel);
260 G1 = GetGreen(currPixel);
261 B1 = GetBlue(currPixel);
263 R0 = GetRed(upPixel);
264 G0 = GetGreen(upPixel);
265 B0 = GetBlue(upPixel);
267 if ((fMaxErrorForTwoPixels >= 3) && (NewDeltaE(R0, R1, G0, G1, B0, B1, fMaxErrorForTwoPixels)))
274 ASSERT(flagsPtr[0] == (e11n|e11s));
276 #if kGatherStats == 1
279 R1 = GetRed(currPixel);
280 G1 = GetGreen(currPixel);
281 B1 = GetBlue(currPixel);
283 R0 = GetRed(upPixel);
284 G0 = GetGreen(upPixel);
285 B0 = GetBlue(upPixel);
287 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
289 // look for a 2x2 block average on every other column
291 { // It looks like we are at the end of a 2x2 block
292 if (lastPairAveraged)
294 // Last pair was averaged so it's ok to try to make a 2x2 block
295 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1,lastB, B1, maxErrorForFourPixels)))
302 ASSERT(flagsPtr[-1] == e12);
303 int didNotBuild4by2 = true;
304 #if kGatherStats == 1
310 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, lastR, G1, G1, lastG, B1, B1, lastB); // 2,3,6,7... Alternate between rounding up and down for these 2x2 blocks
312 if ((pixelNum & 0x03) == 0x03) // 3,7,11,15... Looking for a 4x2 block to average
314 if (last2by2Averaged)
317 | | | | We have two 2x2s.
322 lastPixel = get4Pixel(upPtr, -3); // Go back to previous 2x2 block and get the pixel
323 lastR = GetRed(lastPixel);
324 lastG = GetGreen(lastPixel);
325 lastB = GetBlue(lastPixel);
326 if ((maxErrorForEightPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1,lastB, B1, maxErrorForEightPixels)))
335 #if kGatherStats == 1
338 didNotBuild4by2 = false;
341 flagsPtr[-2] = flagsPtr[-1] = flagsPtr[0] = e42;
343 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, lastR, G1, G1, lastG, B1, B1, lastB); // 4,5,6,7,12,13,14,15,20... Alternate between rounding up down for these 4x2 blocks
346 currPixel = (R1<<16) + (G1<<8) + B1;
348 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
350 #if kMemWritesOptimize == 0
351 put4Pixel(upPtr, -3, currPixel);
352 put4Pixel(upPtr, -2, currPixel);
353 put4Pixel(upPtr, -1, currPixel);
354 put4Pixel(upPtr, 0, currPixel);
355 put4Pixel(currPtr, -3, currPixel);
356 put4Pixel(currPtr, -2, currPixel);
357 put4Pixel(currPtr, -1, currPixel);
358 put4Pixel(currPtr, 0, currPixel);
360 put4Pixel(upPtr, -3, currPixel);
366 { // The first 2x2 block of this pair of 2x2 blocks wasn't averaged.
368 |X X| | | not averaged block and averaged 2x2.
373 last2by2Averaged = true;
376 currPixel = (R1<<16) + (G1<<8) + B1;
378 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
380 #if kMemWritesOptimize == 0
381 put4Pixel(upPtr, -1, currPixel);
382 put4Pixel(upPtr, 0, currPixel);
383 put4Pixel(currPtr, -1, currPixel);
384 put4Pixel(currPtr, 0, currPixel);
386 put4Pixel(upPtr, -1, currPixel);
390 else // Not looking for a 4x2 block yet so just output this 2x2 block for now.
393 | | |? ?| 1st 2x2 and maybe another later.
398 last2by2Averaged = true;
401 currPixel = (R1<<16) + (G1<<8) + B1;
403 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
405 #if kMemWritesOptimize == 0
406 put4Pixel(upPtr, -1, currPixel);
407 put4Pixel(upPtr, 0, currPixel);
408 put4Pixel(currPtr, -1, currPixel);
409 put4Pixel(currPtr, 0, currPixel);
411 put4Pixel(upPtr, -1, currPixel);
415 else // The two averaged columns are not close enough in Delta E
423 last2by2Averaged = false;
426 currPixel = (R1<<16) + (G1<<8) + B1;
428 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
430 #if kMemWritesOptimize == 0
431 put4Pixel(upPtr, 0, currPixel);
432 put4Pixel(currPtr, 0, currPixel);
434 put4Pixel(upPtr,0, currPixel);
440 lastPairAveraged = true;
442 else // This is the right place for 2x2 averaging but the previous column wasn't averaged
445 X | | Two non averaged pixels and a 1x2.
449 last2by2Averaged = false;
450 lastPairAveraged = true;
456 currPixel = (R1<<16) + (G1<<8) + B1;
458 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
460 #if kMemWritesOptimize == 0
461 put4Pixel(upPtr, 0, currPixel);
462 put4Pixel(currPtr, 0, currPixel);
464 put4Pixel(upPtr, 0, currPixel);
468 else // Not on the boundary for a 2x2 block, so just output current averaged 1x2 column
476 lastPairAveraged = true;
482 currPixel = (R1<<16) + (G1<<8) + B1;
484 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
486 #if kMemWritesOptimize == 0
487 put4Pixel(upPtr, 0, currPixel);
488 put4Pixel(currPtr, 0, currPixel);
490 put4Pixel(upPtr, 0, currPixel);
494 else if (lastPairAveraged)
495 { // This is the case where we can't average current column and the last column was averaged.
496 // Don't do anything if last pair was averaged and this one can't be
499 | | X 1x2 averaged block and two non averaged pixels.
504 lastPairAveraged = false;
507 // can't vertically average current column so look for some horizontal averaging as a fallback
508 // Only do it if the last pair wasn't averaged either because we don't want to mess up a vertical averaging
509 // just to create a possible horizontal averaging.
511 // Can only horizontally average every other pixel, much like the 2x2 blocks.
514 // do horizontal averaging on previous raster
515 lastPixel = get4Pixel(upPtr,-1);
516 lastR = GetRed(lastPixel);
517 lastG = GetGreen(lastPixel);
518 lastB = GetBlue(lastPixel);
519 if (((fMaxErrorForTwoPixels >= 3)) && (NewDeltaE(lastR, R0, lastG, G0,lastB, B0, fMaxErrorForTwoPixels)))
525 #if kGatherStats == 1
526 blockStats[es21nw]++;
528 int didNotBuild4by1 = true;
530 AverageNRound(isOdd(pixelNum), lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0);
532 if ((pixelNum >= 3) && (flagsPtr[-3] & e21nw)) // 4,5,6,7,12,13,14,15,20...
534 ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
536 // Attempt an upper 4x1
537 lastPixel = get4Pixel(upPtr,-3);
538 R0 = GetRed(lastPixel);
539 G0 = GetGreen(lastPixel);
540 B0 = GetBlue(lastPixel);
541 if ( (maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0,lastB, B0, maxErrorForFourPixels)))
547 #if kGatherStats == 1
548 blockStats[es41ni]++;
550 didNotBuild4by1 = false;
551 AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
554 currPixel = (lastR<<16) + (lastG<<8) + lastB;
556 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
558 #if kMemWritesOptimize == 0
559 put4Pixel(upPtr, -3, currPixel);
560 put4Pixel(upPtr, -2, currPixel);
561 put4Pixel(upPtr, -1, currPixel);
562 put4Pixel(upPtr, 0, currPixel);
564 put4Pixel(upPtr, -3, currPixel);
567 ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
569 flagsPtr[-3] = (flagsPtr[-3] & ~eNorths) | e41ni;
570 flagsPtr[-2] = (flagsPtr[-2] & ~eNorths) | e41n;
571 flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e41n;
572 flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e41n;
576 if (didNotBuild4by1) // Not an upper 4x1 so output upper 2x1.
579 currPixel = (lastR<<16) + (lastG<<8) + lastB;
581 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
583 #if kMemWritesOptimize == 0
584 put4Pixel(upPtr, -1, currPixel);
585 put4Pixel(upPtr, 0, currPixel);
587 put4Pixel(upPtr, -1, currPixel);
589 ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
590 flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e21nw;
591 flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e21ne;
595 // do horizontal on current raster
596 lastPixel = get4Pixel(currPtr,-1);
597 lastR = GetRed(lastPixel);
598 lastG = GetGreen(lastPixel);
599 lastB = GetBlue(lastPixel);
600 if ((fMaxErrorForTwoPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1, lastB, B1, fMaxErrorForTwoPixels)))
606 int didNotBuild4by1 = true;
607 #if kGatherStats == 1
608 blockStats[es21sw]++;
610 AverageNRound(isOdd(pixelNum), lastR, lastR, R1, lastG, lastG, G1, lastB, lastB, B1);
611 if ((pixelNum >= 3) && (flagsPtr[-3] & e21sw)) // 4,5,6,7,12,13,14,15,20...
613 // Look for a lower 4x1
614 ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
616 lastPixel = get4Pixel(currPtr,-3);
617 R0 = GetRed(lastPixel);
618 G0 = GetGreen(lastPixel);
619 B0 = GetBlue(lastPixel);
620 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0, lastB, B0, maxErrorForFourPixels)))
626 #if kGatherStats == 1
627 blockStats[es41si]++;
629 didNotBuild4by1 = false;
630 AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
633 currPixel = (lastR<<16) + (lastG<<8) + lastB;
635 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
637 #if kMemWritesOptimize == 0
638 put4Pixel(currPtr, -3, currPixel);
639 put4Pixel(currPtr, -2, currPixel);
640 put4Pixel(currPtr, -1, currPixel);
641 put4Pixel(currPtr, 0, currPixel);
643 put4Pixel(currPtr, -3, currPixel);
645 flagsPtr[-3] = (flagsPtr[-3] & ~eSouths) | e41si;
646 flagsPtr[-2] = (flagsPtr[-2] & ~eSouths) | e41s;
647 flagsPtr[-1] = (flagsPtr[-1] & ~eSouths) | e41s;
648 flagsPtr[0] = (flagsPtr[0] & ~eSouths) | e41s;
652 if (didNotBuild4by1) // Not a lower 4x1 so output lower 2x1.
654 ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
657 currPixel = (lastR<<16) + (lastG<<8) + lastB;
659 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
661 #if kMemWritesOptimize == 0
662 put4Pixel(currPtr, -1, currPixel);
663 put4Pixel(currPtr, 0, currPixel);
665 put4Pixel(currPtr, -1, currPixel);
668 flagsPtr[-1] = (flagsPtr[-1] & ~eSouths) | e21sw;
669 flagsPtr[0] = (flagsPtr[0] & ~eSouths) | e21se;
671 } // If DeltaE... Looking for two by one
675 else // no flag bits set.
677 lastPairAveraged = false; // EGW Fixes bug on business graphics. 11/24/97
680 upPtr += eBufferedPixelWidthInBytes;
681 currPtr += eBufferedPixelWidthInBytes;
683 } // for each pixel...
686 // Filter2PairsOfFilteredRows. This routine takes 2 pairs of rows that
687 // have been through the Filter2RawRows routine and puts blocks together
688 // to make bigger blocks. It prefers taking 2 high blocks and putting
689 // them together to make four high blocks, but as a last resort it will
690 // take try to take a 1 high blocks from the second and third rasters and
691 // create 2 high blocks. The possible block sizes this routine could
692 // create are 8x4, 4x4, 2x4, and 1x4, and then with the second and third rasters
693 // 4x2, 2x2, and 1x2.
694 void TErnieFilter::Filter2PairsOfFilteredRows(unsigned char *row1Ptr, unsigned char *row2Ptr, unsigned char *row3Ptr, unsigned char *row4Ptr)
696 const unsigned int maxErrorForFourPixels = fMaxErrorForTwoPixels / 2;
697 const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
698 const unsigned int maxErrorForSixteenPixels = maxErrorForEightPixels / 2;
699 const unsigned int maxErrorForThirtyTwoPixels = maxErrorForSixteenPixels / 2;
701 for (int pixelNum = 0; pixelNum < (fRowWidthInPixels-3);) // Make sure we have four pixels to work with
703 int currPixel, upPixel;
704 int R0, G0, B0, R1, G1, B1;
706 if ((fPixelFilteredFlags[0][pixelNum] & e42i) && (fPixelFilteredFlags[1][pixelNum] & e42i))
711 - - - - We have two 4x2s.
717 ASSERT(fPixelFilteredFlags[0][pixelNum] == e42i && fPixelFilteredFlags[0][pixelNum+1] == e42 && fPixelFilteredFlags[0][pixelNum+2] == e42 && fPixelFilteredFlags[0][pixelNum+3] == e42);
718 ASSERT(fPixelFilteredFlags[1][pixelNum] == e42i && fPixelFilteredFlags[1][pixelNum+1] == e42 && fPixelFilteredFlags[1][pixelNum+2] == e42 && fPixelFilteredFlags[1][pixelNum+3] == e42);
720 upPixel = get4Pixel(row1Ptr);
721 currPixel = get4Pixel(row3Ptr);
723 R1 = GetRed(currPixel);
724 G1 = GetGreen(currPixel);
725 B1 = GetBlue(currPixel);
727 R0 = GetRed(upPixel);
728 G0 = GetGreen(upPixel);
729 B0 = GetBlue(upPixel);
731 if((maxErrorForSixteenPixels >= 3) &&(NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForSixteenPixels)))
740 #if kGatherStats == 1
741 blockStats[es44ni]++;
743 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, R0, G1, G1, G0, B1, B1, B0); // 4,5,6,7,12,13,14,15,20... Alternate between rounding up down
746 currPixel = (R1<<16) + (G1<<8) + B1;
748 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
750 #if kMemWritesOptimize == 0
751 put4Pixel(row1Ptr, 0, currPixel);
752 put4Pixel(row1Ptr, 1, currPixel);
753 put4Pixel(row1Ptr, 2, currPixel);
754 put4Pixel(row1Ptr, 3, currPixel);
755 put4Pixel(row2Ptr, 0, currPixel);
756 put4Pixel(row2Ptr, 1, currPixel);
757 put4Pixel(row2Ptr, 2, currPixel);
758 put4Pixel(row2Ptr, 3, currPixel);
759 put4Pixel(row3Ptr, 0, currPixel);
760 put4Pixel(row3Ptr, 1, currPixel);
761 put4Pixel(row3Ptr, 2, currPixel);
762 put4Pixel(row3Ptr, 3, currPixel);
763 put4Pixel(row4Ptr, 0, currPixel);
764 put4Pixel(row4Ptr, 1, currPixel);
765 put4Pixel(row4Ptr, 2, currPixel);
766 put4Pixel(row4Ptr, 3, currPixel);
768 put4Pixel(row1Ptr, 0, currPixel);
770 row1Ptr += 4*eBufferedPixelWidthInBytes;
771 row2Ptr += 4*eBufferedPixelWidthInBytes;
772 row3Ptr += 4*eBufferedPixelWidthInBytes;
773 row4Ptr += 4*eBufferedPixelWidthInBytes;
775 fPixelFilteredFlags[0][pixelNum] = e44ni;
776 fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+2] = fPixelFilteredFlags[0][pixelNum+3] = e44n;
777 fPixelFilteredFlags[1][pixelNum] = e44si;
778 fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+2] = fPixelFilteredFlags[1][pixelNum+3] = e44s;
780 if ((pixelNum >= 4) && (fPixelFilteredFlags[1][pixelNum-4] & e44si)) // 4,5,6,7,12,13,14,15,20...
784 | | | | We have two 4x4s.
789 ASSERT(fPixelFilteredFlags[0][pixelNum-4] == e44ni && fPixelFilteredFlags[0][pixelNum-3] == e44n && fPixelFilteredFlags[0][pixelNum-2] == e44n && fPixelFilteredFlags[0][pixelNum-1] == e44n);
790 ASSERT(fPixelFilteredFlags[1][pixelNum-4] == e44si && fPixelFilteredFlags[1][pixelNum-3] == e44s && fPixelFilteredFlags[1][pixelNum-2] == e44s && fPixelFilteredFlags[1][pixelNum-1] == e44s);
792 upPixel = get4Pixel(row1Ptr, -8);
794 R0 = GetRed(upPixel);
795 G0 = GetGreen(upPixel);
796 B0 = GetBlue(upPixel);
798 if( (maxErrorForThirtyTwoPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForThirtyTwoPixels)))
807 #if kGatherStats == 1
808 blockStats[es84ni]++;
810 AverageNRound((pixelNum & 0x08) == 0x08, R1, R1, R0, G1, G1, G0, B1, B1, B0);
812 currPixel = (R1<<16) + (G1<<8) + B1;
814 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
816 #if kMemWritesOptimize == 0
817 put4Pixel(row1Ptr, -8, currPixel);
818 put4Pixel(row1Ptr, -7, currPixel);
819 put4Pixel(row1Ptr, -6, currPixel);
820 put4Pixel(row1Ptr, -5, currPixel);
821 put4Pixel(row1Ptr, -4, currPixel);
822 put4Pixel(row1Ptr, -3, currPixel);
823 put4Pixel(row1Ptr, -2, currPixel);
824 put4Pixel(row1Ptr, -1, currPixel);
825 put4Pixel(row2Ptr, -8, currPixel);
826 put4Pixel(row2Ptr, -7, currPixel);
827 put4Pixel(row2Ptr, -6, currPixel);
828 put4Pixel(row2Ptr, -5, currPixel);
829 put4Pixel(row2Ptr, -4, currPixel);
830 put4Pixel(row2Ptr, -3, currPixel);
831 put4Pixel(row2Ptr, -2, currPixel);
832 put4Pixel(row2Ptr, -1, currPixel);
833 put4Pixel(row3Ptr, -8, currPixel);
834 put4Pixel(row3Ptr, -7, currPixel);
835 put4Pixel(row3Ptr, -6, currPixel);
836 put4Pixel(row3Ptr, -5, currPixel);
837 put4Pixel(row3Ptr, -4, currPixel);
838 put4Pixel(row3Ptr, -3, currPixel);
839 put4Pixel(row3Ptr, -2, currPixel);
840 put4Pixel(row3Ptr, -1, currPixel);
841 put4Pixel(row4Ptr, -8, currPixel);
842 put4Pixel(row4Ptr, -7, currPixel);
843 put4Pixel(row4Ptr, -6, currPixel);
844 put4Pixel(row4Ptr, -5, currPixel);
845 put4Pixel(row4Ptr, -4, currPixel);
846 put4Pixel(row4Ptr, -3, currPixel);
847 put4Pixel(row4Ptr, -2, currPixel);
848 put4Pixel(row4Ptr, -1, currPixel);
850 put4Pixel(row1Ptr, -8, currPixel);
852 fPixelFilteredFlags[0][pixelNum-4] = e84ni;
853 fPixelFilteredFlags[0][pixelNum-3] = fPixelFilteredFlags[0][pixelNum-2] = fPixelFilteredFlags[0][pixelNum-1] = fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+2] = fPixelFilteredFlags[0][pixelNum+3] = e84n;
854 fPixelFilteredFlags[1][pixelNum-4] = e84si;
855 fPixelFilteredFlags[1][pixelNum-3] = fPixelFilteredFlags[1][pixelNum-2] = fPixelFilteredFlags[1][pixelNum-1] = fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+2] = fPixelFilteredFlags[1][pixelNum+3] = e84s;
859 else // could not build 4x4 so move forward past the stacked 4x2s.
861 row1Ptr += 4*eBufferedPixelWidthInBytes;
862 row2Ptr += 4*eBufferedPixelWidthInBytes;
863 row3Ptr += 4*eBufferedPixelWidthInBytes;
864 row4Ptr += 4*eBufferedPixelWidthInBytes;
868 else if ((fPixelFilteredFlags[0][pixelNum] & e22w) && (fPixelFilteredFlags[1][pixelNum] & e22w))
879 ASSERT(fPixelFilteredFlags[0][pixelNum] == e22w && fPixelFilteredFlags[0][pixelNum+1] == e22e);
880 ASSERT(fPixelFilteredFlags[1][pixelNum] == e22w && fPixelFilteredFlags[1][pixelNum+1] == e22e);
882 upPixel = get4Pixel(row1Ptr);
883 currPixel = get4Pixel(row3Ptr);
885 R1 = GetRed(currPixel);
886 G1 = GetGreen(currPixel);
887 B1 = GetBlue(currPixel);
889 R0 = GetRed(upPixel);
890 G0 = GetGreen(upPixel);
891 B0 = GetBlue(upPixel);
893 if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
902 #if kGatherStats == 1
903 blockStats[es24nw]++;
905 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
908 currPixel = (R1<<16) + (G1<<8) + B1;
910 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
912 #if kMemWritesOptimize == 0
913 put4Pixel(row1Ptr, 0, currPixel);
914 put4Pixel(row1Ptr, 1, currPixel);
915 put4Pixel(row2Ptr, 0, currPixel);
916 put4Pixel(row2Ptr, 1, currPixel);
917 put4Pixel(row3Ptr, 0, currPixel);
918 put4Pixel(row3Ptr, 1, currPixel);
919 put4Pixel(row4Ptr, 0, currPixel);
920 put4Pixel(row4Ptr, 1, currPixel);
922 put4Pixel(row1Ptr, 0, currPixel);
924 row1Ptr += 2*eBufferedPixelWidthInBytes;
925 row2Ptr += 2*eBufferedPixelWidthInBytes;
926 row3Ptr += 2*eBufferedPixelWidthInBytes;
927 row4Ptr += 2*eBufferedPixelWidthInBytes;
929 fPixelFilteredFlags[0][pixelNum] = e24nw;
930 fPixelFilteredFlags[0][pixelNum+1] = e24ne;
931 fPixelFilteredFlags[1][pixelNum] = e24sw;
932 fPixelFilteredFlags[1][pixelNum+1] = e24se;
936 row1Ptr += 2*eBufferedPixelWidthInBytes;
937 row2Ptr += 2*eBufferedPixelWidthInBytes;
938 row3Ptr += 2*eBufferedPixelWidthInBytes;
939 row4Ptr += 2*eBufferedPixelWidthInBytes;
943 else if ((fPixelFilteredFlags[0][pixelNum] & e12) && (fPixelFilteredFlags[1][pixelNum] & e12))
954 ASSERT(fPixelFilteredFlags[0][pixelNum] == e12);
955 ASSERT(fPixelFilteredFlags[1][pixelNum] == e12);
957 upPixel = get4Pixel(row1Ptr);
958 currPixel = get4Pixel(row3Ptr);
960 R1 = GetRed(currPixel);
961 G1 = GetGreen(currPixel);
962 B1 = GetBlue(currPixel);
964 R0 = GetRed(upPixel);
965 G0 = GetGreen(upPixel);
966 B0 = GetBlue(upPixel);
968 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
977 #if kGatherStats == 1
980 AverageNRound((pixelNum & 0x01) == 0x01, R1, R1, R0, G1, G1, G0, B1, B1, B0);
983 currPixel = (R1<<16) + (G1<<8) + B1;
985 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
987 #if kMemWritesOptimize == 0
988 put4Pixel(row1Ptr, 0, currPixel);
989 put4Pixel(row2Ptr, 0, currPixel);
990 put4Pixel(row3Ptr, 0, currPixel);
991 put4Pixel(row4Ptr, 0, currPixel);
993 put4Pixel(row1Ptr, 0, currPixel);
995 fPixelFilteredFlags[0][pixelNum] = e14n;
996 fPixelFilteredFlags[1][pixelNum] = e14s;
999 row1Ptr += eBufferedPixelWidthInBytes;
1000 row2Ptr += eBufferedPixelWidthInBytes;
1001 row3Ptr += eBufferedPixelWidthInBytes;
1002 row4Ptr += eBufferedPixelWidthInBytes;
1006 else if ((fPixelFilteredFlags[0][pixelNum] & e41si)
1007 && (fPixelFilteredFlags[1][pixelNum] & e41ni))
1011 - - - - We have two 4x1s.
1017 upPixel = get4Pixel(row2Ptr);
1018 currPixel = get4Pixel(row3Ptr);
1020 R1 = GetRed(currPixel);
1021 G1 = GetGreen(currPixel);
1022 B1 = GetBlue(currPixel);
1024 R0 = GetRed(upPixel);
1025 G0 = GetGreen(upPixel);
1026 B0 = GetBlue(upPixel);
1029 if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
1037 #if kGatherStats == 1
1038 blockStats[es42w]++;
1040 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1043 currPixel = (R1<<16) + (G1<<8) + B1;
1045 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1047 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1048 put4Pixel(row2Ptr, 0, currPixel);
1049 put4Pixel(row2Ptr, 1, currPixel);
1050 put4Pixel(row2Ptr, 2, currPixel);
1051 put4Pixel(row2Ptr, 3, currPixel);
1052 put4Pixel(row3Ptr, 0, currPixel);
1053 put4Pixel(row3Ptr, 1, currPixel);
1054 put4Pixel(row3Ptr, 2, currPixel);
1055 put4Pixel(row3Ptr, 3, currPixel);
1057 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e41si;
1058 fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1059 fPixelFilteredFlags[0][pixelNum+2] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1060 fPixelFilteredFlags[0][pixelNum+3] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1062 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e41ni; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1063 fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1064 fPixelFilteredFlags[1][pixelNum+2] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1065 fPixelFilteredFlags[1][pixelNum+3] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1069 row1Ptr += 4*eBufferedPixelWidthInBytes;
1070 row2Ptr += 4*eBufferedPixelWidthInBytes;
1071 row3Ptr += 4*eBufferedPixelWidthInBytes;
1072 row4Ptr += 4*eBufferedPixelWidthInBytes;
1074 else if ((fPixelFilteredFlags[0][pixelNum] & e21sw)
1075 && (fPixelFilteredFlags[1][pixelNum] & e21nw))
1079 - - We have two 2x1s.
1084 ASSERT(!((fPixelFilteredFlags[0][pixelNum] & e11s) | (fPixelFilteredFlags[0][pixelNum+1] & e11s)));
1085 ASSERT(!((fPixelFilteredFlags[1][pixelNum] & e11n) | (fPixelFilteredFlags[1][pixelNum+1] & e11n)));
1087 upPixel = get4Pixel(row2Ptr);
1088 currPixel = get4Pixel(row3Ptr);
1090 R1 = GetRed(currPixel);
1091 G1 = GetGreen(currPixel);
1092 B1 = GetBlue(currPixel);
1094 R0 = GetRed(upPixel);
1095 G0 = GetGreen(upPixel);
1096 B0 = GetBlue(upPixel);
1098 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
1105 #if kGatherStats == 1
1106 blockStats[es22w]++;
1108 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1111 currPixel = (R1<<16) + (G1<<8) + B1;
1113 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1115 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1116 put4Pixel(row2Ptr, 0, currPixel);
1117 put4Pixel(row2Ptr, 1, currPixel);
1118 put4Pixel(row3Ptr, 0, currPixel);
1119 put4Pixel(row3Ptr, 1, currPixel);
1121 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e21sw;
1122 fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+1] & ~e21se;
1124 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e21nw; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1125 fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+1] & ~e21ne;
1130 row1Ptr += 2*eBufferedPixelWidthInBytes;
1131 row2Ptr += 2*eBufferedPixelWidthInBytes;
1132 row3Ptr += 2*eBufferedPixelWidthInBytes;
1133 row4Ptr += 2*eBufferedPixelWidthInBytes;
1135 else if ((fPixelFilteredFlags[0][pixelNum] & e11s)
1136 && (fPixelFilteredFlags[1][pixelNum] & e11n))
1146 upPixel = get4Pixel(row2Ptr);
1147 currPixel = get4Pixel(row3Ptr);
1149 R1 = GetRed(currPixel);
1150 G1 = GetGreen(currPixel);
1151 B1 = GetBlue(currPixel);
1153 R0 = GetRed(upPixel);
1154 G0 = GetGreen(upPixel);
1155 B0 = GetBlue(upPixel);
1157 if ((fMaxErrorForTwoPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, fMaxErrorForTwoPixels)))
1164 #if kGatherStats == 1
1165 blockStats[es12w]++;
1167 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
1170 currPixel = (R1<<16) + (G1<<8) + B1;
1172 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1174 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1175 put4Pixel(row2Ptr, 0, currPixel);
1176 put4Pixel(row3Ptr, 0, currPixel);
1178 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e11s;
1180 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e11n; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1185 row1Ptr += eBufferedPixelWidthInBytes;
1186 row2Ptr += eBufferedPixelWidthInBytes;
1187 row3Ptr += eBufferedPixelWidthInBytes;
1188 row4Ptr += eBufferedPixelWidthInBytes;
1190 else // Do no vertical filtering here.
1194 row1Ptr += eBufferedPixelWidthInBytes;
1195 row2Ptr += eBufferedPixelWidthInBytes;
1196 row3Ptr += eBufferedPixelWidthInBytes;
1197 row4Ptr += eBufferedPixelWidthInBytes;
1202 // Filter3FilteredRows. This routine only exists for the case of the odd size band
1203 // with three rasters left over. I'm not sure how much extra benifit we really
1204 // get from running this, but for now I'm leaving it in. Since Ernie deals with
1205 // block sizes that are powers of two its rather difficult to filter 3 rows together,
1206 // about all I've been able to do is look for 1 high blocks in the second and third
1207 // rasters to put together into 2 high blocks. This routine will create 4x2, 2x2, and
1208 // 1x2 blocks from those second and third rasters.
1209 void TErnieFilter::Filter3FilteredRows(unsigned char *row1Ptr, unsigned char *row2Ptr, unsigned char *row3Ptr)
1211 const unsigned int maxErrorForFourPixels = fMaxErrorForTwoPixels / 2;
1212 const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
1213 // const unsigned int maxErrorForSixteenPixels = maxErrorForEightPixels / 2;
1214 // const unsigned int maxErrorForThirtyTwoPixels = maxErrorForSixteenPixels / 2;
1216 for (int pixelNum = 0; pixelNum < (fRowWidthInPixels-3);) // Make sure we have four pixels to work with
1218 // int currPixel, upPixel;
1219 uint32_t currPixel, upPixel;
1220 int R0, G0, B0, R1, G1, B1;
1222 if ((fPixelFilteredFlags[0][pixelNum] & e41si)
1223 && (fPixelFilteredFlags[1][pixelNum] & e41ni))
1227 - - - - We have two 4x1s.
1232 ASSERT(!((fPixelFilteredFlags[0][pixelNum] & e11s) | (fPixelFilteredFlags[0][pixelNum+1] & e11s)));
1233 ASSERT(!((fPixelFilteredFlags[1][pixelNum] & e11n) | (fPixelFilteredFlags[1][pixelNum+1] & e11n)));
1235 upPixel = get4Pixel(row2Ptr);
1236 currPixel = get4Pixel(row3Ptr);
1238 R1 = GetRed(currPixel);
1239 G1 = GetGreen(currPixel);
1240 B1 = GetBlue(currPixel);
1242 R0 = GetRed(upPixel);
1243 G0 = GetGreen(upPixel);
1244 B0 = GetBlue(upPixel);
1247 if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
1255 #if kGatherStats == 1
1256 blockStats[es42w]++;
1258 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1261 currPixel = (R1<<16) + (G1<<8) + B1;
1263 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1265 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1266 put4Pixel(row2Ptr, 0, currPixel);
1267 put4Pixel(row2Ptr, 1, currPixel);
1268 put4Pixel(row2Ptr, 2, currPixel);
1269 put4Pixel(row2Ptr, 3, currPixel);
1270 put4Pixel(row3Ptr, 0, currPixel);
1271 put4Pixel(row3Ptr, 1, currPixel);
1272 put4Pixel(row3Ptr, 2, currPixel);
1273 put4Pixel(row3Ptr, 3, currPixel);
1275 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e41si;
1276 fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1277 fPixelFilteredFlags[0][pixelNum+2] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1278 fPixelFilteredFlags[0][pixelNum+3] = fPixelFilteredFlags[0][pixelNum+1] & ~e41s;
1280 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e41ni; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1281 fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1282 fPixelFilteredFlags[1][pixelNum+2] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1283 fPixelFilteredFlags[1][pixelNum+3] = fPixelFilteredFlags[1][pixelNum+1] & ~e41n;
1287 row1Ptr += 4*eBufferedPixelWidthInBytes;
1288 row2Ptr += 4*eBufferedPixelWidthInBytes;
1289 row3Ptr += 4*eBufferedPixelWidthInBytes;
1291 else if ((fPixelFilteredFlags[0][pixelNum] & e21sw)
1292 && (fPixelFilteredFlags[1][pixelNum] & e21nw))
1296 - - We have two 2x1s.
1301 ASSERT(!((fPixelFilteredFlags[0][pixelNum] & e11s) | (fPixelFilteredFlags[0][pixelNum+1] & e11s)));
1302 ASSERT(!((fPixelFilteredFlags[1][pixelNum] & e11n) | (fPixelFilteredFlags[1][pixelNum+1] & e11n)));
1304 upPixel = get4Pixel(row2Ptr);
1305 currPixel = get4Pixel(row3Ptr);
1307 R1 = GetRed(currPixel);
1308 G1 = GetGreen(currPixel);
1309 B1 = GetBlue(currPixel);
1311 R0 = GetRed(upPixel);
1312 G0 = GetGreen(upPixel);
1313 B0 = GetBlue(upPixel);
1315 if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
1322 #if kGatherStats == 1
1323 blockStats[es22w]++;
1325 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1328 currPixel = (R1<<16) + (G1<<8) + B1;
1330 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1332 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1333 put4Pixel(row2Ptr, 0, currPixel);
1334 put4Pixel(row2Ptr, 1, currPixel);
1335 put4Pixel(row3Ptr, 0, currPixel);
1336 put4Pixel(row3Ptr, 1, currPixel);
1338 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e21sw;
1339 fPixelFilteredFlags[0][pixelNum+1] = fPixelFilteredFlags[0][pixelNum+1] & ~e21se;
1341 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e21nw; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1342 fPixelFilteredFlags[1][pixelNum+1] = fPixelFilteredFlags[1][pixelNum+1] & ~e21ne;
1347 row1Ptr += 2*eBufferedPixelWidthInBytes;
1348 row2Ptr += 2*eBufferedPixelWidthInBytes;
1349 row3Ptr += 2*eBufferedPixelWidthInBytes;
1351 else if ((fPixelFilteredFlags[0][pixelNum] & e11s)
1352 && (fPixelFilteredFlags[1][pixelNum] & e11n))
1362 upPixel = get4Pixel(row2Ptr);
1363 currPixel = get4Pixel(row3Ptr);
1365 R1 = GetRed(currPixel);
1366 G1 = GetGreen(currPixel);
1367 B1 = GetBlue(currPixel);
1369 R0 = GetRed(upPixel);
1370 G0 = GetGreen(upPixel);
1371 B0 = GetBlue(upPixel);
1373 if ((fMaxErrorForTwoPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, fMaxErrorForTwoPixels)))
1380 #if kGatherStats == 1
1381 blockStats[es12w]++;
1383 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
1386 currPixel = (R1<<16) + (G1<<8) + B1;
1388 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1390 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1391 put4Pixel(row2Ptr, 0, currPixel);
1392 put4Pixel(row3Ptr, 0, currPixel);
1394 fPixelFilteredFlags[0][pixelNum] = fPixelFilteredFlags[0][pixelNum] & ~e11s;
1396 fPixelFilteredFlags[1][pixelNum] = fPixelFilteredFlags[1][pixelNum] & ~e11n; // Note that we just formed a 2x2 in the middle of the filtered sets of rows (and do not remember it). We then remove the 2x1s that the 2x2 eliminated.
1401 row1Ptr += eBufferedPixelWidthInBytes;
1402 row2Ptr += eBufferedPixelWidthInBytes;
1403 row3Ptr += eBufferedPixelWidthInBytes;
1405 else // Do no vertical filtering here.
1409 row1Ptr += eBufferedPixelWidthInBytes;
1410 row2Ptr += eBufferedPixelWidthInBytes;
1411 row3Ptr += eBufferedPixelWidthInBytes;
1416 #define NEWTEST true
1418 inline bool TErnieFilter::NewDeltaE(int dr0, int dr1, int dg0, int dg1, int db0, int db1, int tolerance)
1420 int Y0, Y1, dY, Cr0, Cr1, Cb0, Cb1, dCr, dCb;
1422 // new Delta E stuff from Jay
1424 Y0 = 5*dr0 + 9*dg0 + 2*db0;
1425 Y1 = 5*dr1 + 9*dg1 + 2*db1;
1427 dY = ABS(Y0 - Y1) >> 4;
1429 if(dY > tolerance) {
1434 Cr0 = (dr0 << 4) - Y0;
1435 Cr1 = (dr1 << 4) - Y1;
1436 dCr = ABS(Cr0 - Cr1) >> 5;
1443 Cb0 = (db0 << 4) - Y0;
1444 Cb1 = (db1 << 4) - Y1;
1445 dCb = ABS(Cb0 - Cb1) >> 6;
1455 TErnieFilter::TErnieFilter(int rowWidthInPixels, pixelTypes pixelType, unsigned int maxErrorForTwoPixels, int bytesPerPixel)
1456 : fOriginalPixelSize(bytesPerPixel)
1459 ASSERT((fOriginalPixelSize == 3) || (fOriginalPixelSize == 4));
1460 ASSERT(rowWidthInPixels > 0);
1461 ASSERT(pixelType == eBGRPixelData);
1463 fInternalBufferPixelSize = 4;
1465 fPixelOffsetIndex = 0;
1466 fRowWidthInPixels = rowWidthInPixels;
1467 fRowWidthInBytes = fRowWidthInPixels*fInternalBufferPixelSize;
1468 fMaxErrorForTwoPixels = maxErrorForTwoPixels;
1470 for (index = 0; index < 4; index++)
1472 fRowBuf[index] = new uint32_t[rowWidthInPixels];
1473 ASSERT(fRowBuf[index]);
1475 fRowPtr[index] = new unsigned char[rowWidthInPixels*fOriginalPixelSize];
1476 ASSERT(fRowPtr[index]);
1478 fBlackRowPtr[index] = new BYTE[rowWidthInPixels*fOriginalPixelSize];
1479 ASSERT(fBlackRowPtr[index]);
1481 BlackRasterSize[index] = 0;
1484 for (index = 0; index < 2; index++)
1486 fPixelFilteredFlags[index] = new unsigned int[rowWidthInPixels];
1487 ASSERT(fPixelFilteredFlags[index]);
1490 // The least compressible image will be all raw pixels. Maximum compressed size is:
1491 // full size + a bloat of Cmd byte + 1 VLI byte per 255 pixels rounded up to nearest integer.
1493 int maxCompressionBufSize = fRowWidthInBytes + 1 + ((int)ceil((float)MAX((rowWidthInPixels-2)/255, 0)));
1495 fCompressionOutBuf = new unsigned char[maxCompressionBufSize];
1496 ASSERT(fCompressionOutBuf);
1498 fNumberOfBufferedRows = 0;
1500 fPixelOffset[0] = 0;
1501 fPixelOffset[1] = 5;
1502 fPixelOffset[2] = 2;
1503 fPixelOffset[3] = 7;
1504 fPixelOffset[4] = 1;
1505 fPixelOffset[5] = 4;
1506 fPixelOffset[6] = 6;
1507 fPixelOffset[7] = 3;
1513 TErnieFilter::~TErnieFilter()
1515 // Deallocate memory next.
1518 for (index = 0; index < 4; index++)
1520 delete [] fRowBuf[index];
1521 delete [] fRowPtr[index];
1522 delete [] fBlackRowPtr[index];
1525 for (index = 0; index < 2; index++)
1527 delete [] fPixelFilteredFlags[index];
1530 delete [] fCompressionOutBuf;
1533 void TErnieFilter::writeBufferedRows()
1537 // We just have one lonely raster left. Nothing
1538 // we can do but filter it horizontally.
1539 if( 1 == fNumberOfBufferedRows)
1542 int offset2 = fPixelOffset[fPixelOffsetIndex];
1544 Filter1RawRow( (unsigned char*)(fRowBuf[0] + offset2),
1545 fRowWidthInPixels - fPixelOffset[fPixelOffsetIndex],
1546 fPixelFilteredFlags[0] + fPixelOffset[fPixelOffsetIndex]);
1549 unsigned char *rowPtr = fRowPtr[0];
1554 memcpy(rowPtr, &fRowBuf[0][pixelIndex], 3);
1556 } while (++pixelIndex < fRowWidthInPixels);
1559 // If we've got a pair of rasters in the buffer, that pair
1560 // has already been filtered somewhat. So lets just write them
1561 // out, some filtering is better than none.
1562 else if (2 == fNumberOfBufferedRows)
1564 // Write the two rows back out.
1566 for (k = 0; k < 2; k++)
1568 unsigned char *rowPtr = fRowPtr[k];
1573 memcpy(rowPtr, &fRowBuf[k][pixelIndex], 3);
1575 } while (++pixelIndex < fRowWidthInPixels);
1578 // Okay, if we had three rasters in the buffer, the pair
1579 // should have already been written out above, so lets
1580 // just run the odd raster through Ernie with to
1581 // get the horizontal filtering. [Need to look to see
1582 // if there's something more we can do with filtering
1583 // all three together.]
1584 else if (3 == fNumberOfBufferedRows)
1587 int offset2 = fPixelOffset[fPixelOffsetIndex];
1589 Filter1RawRow( (unsigned char*)(fRowBuf[2] + offset2),
1590 fRowWidthInPixels - fPixelOffset[fPixelOffsetIndex],
1591 fPixelFilteredFlags[1] + fPixelOffset[fPixelOffsetIndex]);
1594 Filter3FilteredRows( (unsigned char*)fRowBuf[0],
1595 (unsigned char*)fRowBuf[1],
1596 (unsigned char*)fRowBuf[2]);
1599 for (k = 0; k < 3; k++)
1601 unsigned char *rowPtr = fRowPtr[k];
1606 memcpy(rowPtr, &fRowBuf[k][pixelIndex], 3);
1608 } while (++pixelIndex < fRowWidthInPixels);
1613 void TErnieFilter::submitRowToFilter(unsigned char *rowPtr)
1615 memcpy(fRowPtr[fNumberOfBufferedRows], rowPtr, fRowWidthInPixels*3);
1617 // Now reformat the pixel data from 24 bit to 32 bit pixels
1619 uint32_t *RowPtrDest = fRowBuf[fNumberOfBufferedRows];
1629 RowPtrDest[pixelIndex] = ((byte3 << 16) | (byte2 << 8) | (byte1)) & 0x00FFFFFF;
1631 RowPtrDest[pixelIndex] = ((byte1 << 24) | (byte2 << 16) | (byte3 << 8)) & 0xFFFFFF00;
1632 } while (++pixelIndex < fRowWidthInPixels);
1634 fNumberOfBufferedRows++;
1637 iRastersDelivered=0;
1639 // Next see about filtering & compression.
1640 // NOTE 1: as an optimization only do subsections of the raster at a time to stay in cache.
1641 // NOTE 2: Could filter the pixels left of the offset.
1642 if (2 == fNumberOfBufferedRows)
1644 int offset2 = fPixelOffset[fPixelOffsetIndex];
1646 Filter2RawRows( (unsigned char*)(fRowBuf[1] + offset2),
1647 (unsigned char*)(fRowBuf[0] + offset2),
1648 fRowWidthInPixels - fPixelOffset[fPixelOffsetIndex],
1649 fPixelFilteredFlags[0] + fPixelOffset[fPixelOffsetIndex]);
1652 if (4 == fNumberOfBufferedRows)
1654 int offset4 = fPixelOffset[fPixelOffsetIndex];
1655 Filter2RawRows( (unsigned char*)(fRowBuf[3] + offset4),
1656 (unsigned char*)(fRowBuf[2] + offset4),
1657 fRowWidthInPixels - fPixelOffset[fPixelOffsetIndex],
1658 fPixelFilteredFlags[1] + fPixelOffset[fPixelOffsetIndex]);
1660 Filter2PairsOfFilteredRows( (unsigned char*)fRowBuf[0],
1661 (unsigned char*)fRowBuf[1],
1662 (unsigned char*)fRowBuf[2],
1663 (unsigned char*)fRowBuf[3]);
1665 #if kMemWritesOptimize == 1
1666 // Writing the blocks out on a post processing step in this manner could leave the last 3 rows
1667 // unfiltered. This is a trade off we make for simplicity. The resulting loss in compression is small.
1671 fPixelOffsetIndex = (fPixelOffsetIndex + 1) % 8; // cycle the offset index.
1674 for (k = 0; k < fPixelOffset[fPixelOffsetIndex]; k++) // Clear out the flags that we're offsetting past for this next iteration.
1676 fPixelFilteredFlags[0][k] = eDone;
1677 fPixelFilteredFlags[1][k] = eDone;
1680 // Write the four rows back out.
1681 for (k = 0; k < 4; k++)
1683 unsigned char *rowPtr = fRowPtr[k];
1688 memcpy(rowPtr, &fRowBuf[k][pixelIndex], fOriginalPixelSize);
1689 rowPtr += fOriginalPixelSize;
1690 } while (++pixelIndex < fRowWidthInPixels);
1693 fNumberOfBufferedRows = 0;
1699 #if kMemWritesOptimize == 1
1703 At this point the color for the entire block is stored in the top left
1704 corner of the block. This routine takes that pixel and smears it into the
1707 void TErnieFilter::WriteBlockPixels(void)
1709 unsigned char *row1Ptr = (unsigned char*)fRowBuf[0];
1710 unsigned char *row2Ptr = (unsigned char*)fRowBuf[1];
1711 unsigned char *row3Ptr = (unsigned char*)fRowBuf[2];
1712 unsigned char *row4Ptr = (unsigned char*)fRowBuf[3];
1714 for (int flagSet = 0; flagSet <= 1; flagSet++)
1716 unsigned int *flagsPtr = fPixelFilteredFlags[0];
1717 unsigned char *rowA = (unsigned char*)fRowBuf[0];
1718 unsigned char *rowB = (unsigned char*)fRowBuf[1];
1722 flagsPtr = fPixelFilteredFlags[1];
1723 rowA = (unsigned char*)fRowBuf[2];
1724 rowB = (unsigned char*)fRowBuf[3];
1727 for (int rowIndex = 0; rowIndex < fRowWidthInPixels;)
1729 unsigned int currentFlags = flagsPtr[rowIndex];
1731 #ifndef NDEBUG /* only done for debug builds */
1732 int numberOfBitsSet = 0;
1733 unsigned int currentFlagsCopy = currentFlags & eTopLeftOfBlocks;
1734 while (currentFlagsCopy)
1736 if (currentFlagsCopy & 1) numberOfBitsSet++;
1737 currentFlagsCopy >>= 1;
1739 ASSERT( (numberOfBitsSet <= 1) ||
1740 ((numberOfBitsSet == 2) &&
1741 (((currentFlags & eTopLeftOfBlocks) & ~(e21nw|e21sw|e41ni|e41si))==0)));
1744 if (currentFlags & eTopLeftOfBlocks) // Avoids doing a lot of checks if nothing is set.
1746 // unsigned int pixel;
1748 // The three possible scenerios are:
1749 // 1: No top left of block bits are set.
1750 // 2: 1 top left block bit is set.
1751 // 3: 2 top left block bits are set. They are 21nw and 21sw.
1753 // Note: Due to possibly having two groups tracked by this flag we require the north checks to occur before the south checks.
1754 if (currentFlags & e22w)
1756 pixel = get4Pixel(rowA, rowIndex);
1758 put4Pixel(rowB, rowIndex, pixel);
1760 put4Pixel(rowA, rowIndex, pixel);
1761 put4Pixel(rowB, rowIndex, pixel);
1767 if (currentFlags & e12)
1769 put4Pixel(rowB, rowIndex, get4Pixel(rowA, rowIndex));
1775 if (currentFlags & e42i)
1777 pixel = get4Pixel(rowA, rowIndex);
1779 put4Pixel(rowB, rowIndex, pixel);
1782 put4Pixel(rowA, rowIndex, pixel);
1783 put4Pixel(rowB, rowIndex, pixel);
1786 put4Pixel(rowB, rowIndex, pixel);
1787 put4Pixel(rowA, rowIndex, pixel);
1790 put4Pixel(rowA, rowIndex, pixel);
1791 put4Pixel(rowB, rowIndex, pixel);
1797 if (currentFlags & e84ni)
1799 pixel = get4Pixel(rowA, rowIndex);
1801 put4Pixel(row2Ptr, rowIndex, pixel);
1802 put4Pixel(row3Ptr, rowIndex, pixel);
1803 put4Pixel(row4Ptr, rowIndex, pixel);
1806 put4Pixel(row1Ptr, rowIndex, pixel);
1807 put4Pixel(row2Ptr, rowIndex, pixel);
1808 put4Pixel(row3Ptr, rowIndex, pixel);
1809 put4Pixel(row4Ptr, rowIndex, pixel);
1812 put4Pixel(row1Ptr, rowIndex, pixel);
1813 put4Pixel(row2Ptr, rowIndex, pixel);
1814 put4Pixel(row3Ptr, rowIndex, pixel);
1815 put4Pixel(row4Ptr, rowIndex, pixel);
1818 put4Pixel(row1Ptr, rowIndex, pixel);
1819 put4Pixel(row2Ptr, rowIndex, pixel);
1820 put4Pixel(row3Ptr, rowIndex, pixel);
1821 put4Pixel(row4Ptr, rowIndex, pixel);
1824 put4Pixel(row1Ptr, rowIndex, pixel);
1825 put4Pixel(row2Ptr, rowIndex, pixel);
1826 put4Pixel(row3Ptr, rowIndex, pixel);
1827 put4Pixel(row4Ptr, rowIndex, pixel);
1830 put4Pixel(row1Ptr, rowIndex, pixel);
1831 put4Pixel(row2Ptr, rowIndex, pixel);
1832 put4Pixel(row3Ptr, rowIndex, pixel);
1833 put4Pixel(row4Ptr, rowIndex, pixel);
1836 put4Pixel(row1Ptr, rowIndex, pixel);
1837 put4Pixel(row2Ptr, rowIndex, pixel);
1838 put4Pixel(row3Ptr, rowIndex, pixel);
1839 put4Pixel(row4Ptr, rowIndex, pixel);
1842 put4Pixel(row1Ptr, rowIndex, pixel);
1843 put4Pixel(row2Ptr, rowIndex, pixel);
1844 put4Pixel(row3Ptr, rowIndex, pixel);
1845 put4Pixel(row4Ptr, rowIndex, pixel);
1852 if (currentFlags & e24nw)
1854 pixel = get4Pixel(row1Ptr, rowIndex);
1856 put4Pixel(row2Ptr, rowIndex, pixel);
1857 put4Pixel(row3Ptr, rowIndex, pixel);
1858 put4Pixel(row4Ptr, rowIndex, pixel);
1861 put4Pixel(row1Ptr, rowIndex, pixel);
1862 put4Pixel(row2Ptr, rowIndex, pixel);
1863 put4Pixel(row3Ptr, rowIndex, pixel);
1864 put4Pixel(row4Ptr, rowIndex, pixel);
1870 if (currentFlags & e44ni)
1872 pixel = get4Pixel(row1Ptr, rowIndex);
1874 put4Pixel(row2Ptr, rowIndex, pixel);
1875 put4Pixel(row3Ptr, rowIndex, pixel);
1876 put4Pixel(row4Ptr, rowIndex, pixel);
1879 put4Pixel(row1Ptr, rowIndex, pixel);
1880 put4Pixel(row2Ptr, rowIndex, pixel);
1881 put4Pixel(row3Ptr, rowIndex, pixel);
1882 put4Pixel(row4Ptr, rowIndex, pixel);
1885 put4Pixel(row1Ptr, rowIndex, pixel);
1886 put4Pixel(row2Ptr, rowIndex, pixel);
1887 put4Pixel(row3Ptr, rowIndex, pixel);
1888 put4Pixel(row4Ptr, rowIndex, pixel);
1891 put4Pixel(row1Ptr, rowIndex, pixel);
1892 put4Pixel(row2Ptr, rowIndex, pixel);
1893 put4Pixel(row3Ptr, rowIndex, pixel);
1894 put4Pixel(row4Ptr, rowIndex, pixel);
1900 if (currentFlags & e14n)
1902 pixel = get4Pixel(row1Ptr, rowIndex);
1904 put4Pixel(row2Ptr, rowIndex, pixel);
1905 put4Pixel(row3Ptr, rowIndex, pixel);
1906 put4Pixel(row4Ptr, rowIndex, pixel);
1912 if (currentFlags & e21nw)
1914 put4Pixel(rowA, rowIndex+1, get4Pixel(rowA, rowIndex));
1916 if (!(currentFlags & (e21sw|e41si))) // if no south groups
1923 if (currentFlags & e41ni)
1925 pixel = get4Pixel(rowA, rowIndex);
1927 put4Pixel(rowA, rowIndex+1, pixel);
1928 put4Pixel(rowA, rowIndex+2, pixel);
1929 put4Pixel(rowA, rowIndex+3, pixel);
1931 if (!(currentFlags & (e21sw|e41si))) // if no south groups.
1938 if (currentFlags & e21sw)
1940 put4Pixel(rowB, rowIndex+1, get4Pixel(rowB, rowIndex));
1946 if (currentFlags & e41si)
1948 pixel = get4Pixel(rowB, rowIndex);
1950 put4Pixel(rowB, rowIndex+1, pixel);
1951 put4Pixel(rowB, rowIndex+2, pixel);
1952 put4Pixel(rowB, rowIndex+3, pixel);
1963 #endif // kMemWritesOptimize
1967 #endif // APDK_DJ9xxVIP && APDK_VIP_COLORFILTERING