Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / hpcups / ErnieFilter.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2 //    Copyright (c) 1996 - 2008, Hewlett-Packard Development Company, L.P.
3 //    All rights reserved.
4 //    
5 //    This software is licensed solely for use with HP products.  Redistribution
6 //    and use with HP products in source and binary forms, with or without
7 //    modification, are permitted provided that the following conditions are met:
8 //    
9 //    -   Redistributions of source code must retain the above copyright notice,
10 //        this list of conditions and the following disclaimer.
11 //    -   Redistributions in binary form must reproduce the above copyright
12 //        notice, this list of conditions and the following disclaimer in the
13 //        documentation and/or other materials provided with the distribution.
14 //    -   Neither the name of Hewlett-Packard nor the names of its contributors
15 //        may be used to endorse or promote products derived from this software
16 //        without specific prior written permission.
17 //    -   Redistributors making defect corrections to source code grant to
18 //        Hewlett-Packard the right to use and redistribute such defect
19 //        corrections.
20 //    
21 //    This software contains technology licensed from third parties; use with
22 //    non-HP products is at your own risk and may require a royalty.
23 //    
24 //    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 //    'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 //    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 //    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL HEWLETT-PACKARD OR ITS
28 //    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 //    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 //    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31 //    OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 //    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33 //    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 //    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 ////////////////////////////////////////////////////////////////////////////////
36
37
38
39 // copied from vob \di_research on 10/31/00
40 // MODIFICATIONS BY GE:
41 // 0. remove Windows header references
42 // 1. define assert
43 // 2. set iRastersReady, iRastersDelivered in submitrowtofilter
44 // 3. (constructor) allocate (and delete in destructor) buffers for m_row_ptrs
45 //      (instead of setting it to input buffers, since we reuse input buffers)
46 // 4. copy data into m_row_ptrs in submitrowtofilter
47
48 //#define assert ASSERT
49
50 #include "ErnieFilter.h"
51
52
53 #if defined(__APPLE__) || defined(__linux)
54 #include <math.h>
55 #endif
56
57
58 #if kGatherStats == 1
59 extern int blockStats[];
60 #endif
61
62 #if ((kMemWritesOptimize != 1) && (kMemWritesOptimize != 0))
63 #error "kMemWritesOptimize must be 0 or 1"
64 #endif
65
66 inline void AverageNRound(bool roundGreenDown, int &rFinal, int &r0, int &r1, int &gFinal, int &g0, int &g1, int &bFinal, int &b0, int &b1);
67 inline void AverageNRound(bool roundGreenDown, int &rFinal, int &r0, int &r1, int &gFinal, int &g0, int &g1, int &bFinal, int &b0, int &b1)
68 {
69     // 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.
70     if (roundGreenDown)
71     {
72         rFinal = (r0 + r1 + 1) / 2;
73         gFinal = (g0 + g1) / 2;
74         bFinal = (b0 + b1 + 1) / 2;
75     }
76     else
77     {
78         rFinal = (r0 + r1) / 2;
79         gFinal = (g0 + g1 + 1) / 2;
80         bFinal = (b0 + b1) / 2;
81     }
82 }
83
84
85 // Filter1RawRow.  To be used to filter an odd row for which we don't have a pair,
86 // found at the bottom of bands that aren't divisable by 2.  This routine
87 // filters its row horizontally forming 4x1 and 2x1 blocks.
88 void ErnieFilter::Filter1RawRow(unsigned char *currPtr, int rowWidthInPixels, unsigned int *flagsPtr)
89 {
90     ASSERT(currPtr);
91     ASSERT(rowWidthInPixels > 0);
92
93     int R0, G0, B0, R1, G1, B1, lastR, lastG, lastB;
94     const unsigned int maxErrorForFourPixels = m_max_error_for_two_pixels / 2;
95 //    const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
96
97 //    int currPixel, lastPixel;
98     uint32_t currPixel, lastPixel;
99     bool lastPairAveraged = false;
100     bool last2by2Averaged = false;
101
102
103     for (int pixelNum = 0; pixelNum < rowWidthInPixels; pixelNum++)
104     {
105         if ((pixelNum & 0x03) == 0x00) // 0,4,8...
106         {
107             last2by2Averaged = false; // Reinitialize every four columns;
108         }
109
110         currPixel = get4Pixel(currPtr);
111
112         flagsPtr[0] = (e11n|e11s);  // Initialize in case nothing is found for this column
113
114         if (isWhite(currPixel))
115         {
116             flagsPtr[0] = eDone;
117 #if kGatherStats == 1
118             blockStats[esWhiteFound]++;
119 #endif
120         }
121
122         // Currently we bail entirely if there is white. Later we may still do RLE on the non white pixel if one is present.
123         if (flagsPtr[0] == (e11n|e11s))
124         {
125             R1 = GetRed(currPixel);
126             G1 = GetGreen(currPixel);
127             B1 = GetBlue(currPixel);
128
129             // Can only horizontally average every other pixel, much like the 2x2 blocks.
130             if (isOdd(pixelNum))
131             {
132                 // do horizontal on current raster
133                 lastPixel = get4Pixel(currPtr,-1);
134                 lastR = GetRed(lastPixel);
135                 lastG = GetGreen(lastPixel);
136                 lastB = GetBlue(lastPixel);
137                 if ((m_max_error_for_two_pixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1, lastB, B1, m_max_error_for_two_pixels)))
138                 {
139                     /*   - -
140                         |   | build 2x1
141                          - -
142                     */
143                     int didNotBuild4by1 = true;
144 #if kGatherStats == 1
145                     blockStats[es21nw]++;
146 #endif
147                     AverageNRound(isOdd(pixelNum), lastR, lastR, R1, lastG, lastG, G1, lastB, lastB, B1);
148                     if ((pixelNum >= 3) && (flagsPtr[-3] & e21nw)) // 4,5,6,7,12,13,14,15,20...
149                     {
150                         // Look for a 4x1
151                         ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
152
153                         lastPixel = get4Pixel(currPtr,-3);
154                         R0 = GetRed(lastPixel);
155                         G0 = GetGreen(lastPixel);
156                         B0 = GetBlue(lastPixel);
157                         if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0, lastB, B0, maxErrorForFourPixels)))
158                         {
159                             /*   - - - -
160                                 |       | build 4x1
161                                  - - - -
162                             */
163 #if kGatherStats == 1
164                             blockStats[es41ni]++;
165 #endif
166                             didNotBuild4by1 = false;
167                             AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
168
169                             if(m_eEndian == LITTLEENDIAN)
170                                 currPixel = (lastR<<16) + (lastG<<8) + lastB;
171                             else if(m_eEndian == BIGENDIAN)
172                                 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
173
174 #if kMemWritesOptimize == 0
175                             put4Pixel(currPtr, -3, currPixel);
176                             put4Pixel(currPtr, -2, currPixel);
177                             put4Pixel(currPtr, -1, currPixel);
178                             put4Pixel(currPtr, 0, currPixel);
179 #else
180                             put4Pixel(currPtr, -3, currPixel);
181 #endif
182                             flagsPtr[-3] = (flagsPtr[-3] & ~eNorths) | e41ni;
183                             flagsPtr[-2] = (flagsPtr[-2] & ~eNorths) | e41n;
184                             flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e41n;
185                             flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e41n;
186                         }
187                     }
188
189                     if (didNotBuild4by1) // Not a 4x1 so output 2x1.
190                     {
191                         ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
192
193                         if(m_eEndian == LITTLEENDIAN)
194                             currPixel = (lastR<<16) + (lastG<<8) + lastB;
195                         else if(m_eEndian == BIGENDIAN)
196                             currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
197
198 #if kMemWritesOptimize == 0
199                         put4Pixel(currPtr, -1, currPixel);
200                         put4Pixel(currPtr, 0, currPixel);
201 #else
202                         put4Pixel(currPtr, -1, currPixel);
203 #endif
204                         flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e21nw;
205                         flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e21ne;
206                     }
207                 }  // If DeltaE... Looking for two by one
208             }  // IsOdd(pixelNum)
209         }
210         else // no flag bits set.
211         {
212             lastPairAveraged = false; // EGW Fixes bug on business graphics. 11/24/97
213         }
214
215         currPtr += eBufferedPixelWidthInBytes;
216         flagsPtr++;
217     }      // for each pixel...
218 }
219
220 // Filter2RawRows:  Looks filter two raw rows together to form blocks.  Vertical
221 // blocks are prefered over horizontal ones.  The routine will create 1x2 blocks
222 // before it will create 4x1's.  In total this routine will create 1x2, 2x2, 4x2,
223 // 4x1, and 2x1 blocks sizes, with the potential for two seperate 4x1's or 2x1's
224 // in the upper and lower rasters.
225 void ErnieFilter::Filter2RawRows(unsigned char *currPtr, unsigned char *upPtr, int rowWidthInPixels, unsigned int *flagsPtr)
226 {
227     ASSERT(currPtr);
228     ASSERT(upPtr);
229     ASSERT(rowWidthInPixels > 0);
230
231     int R0, G0, B0, R1, G1, B1, lastR, lastG, lastB;
232     const unsigned int maxErrorForFourPixels = m_max_error_for_two_pixels / 2;
233     const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
234
235 //    int currPixel, upPixel, lastPixel;
236     uint32_t currPixel, upPixel, lastPixel;
237     bool lastPairAveraged = false;
238     bool last2by2Averaged = false;
239
240     for (int pixelNum = 0; pixelNum < rowWidthInPixels; pixelNum++)
241     {
242         if ((pixelNum & 0x03) == 0x00) // 0,4,8...
243         {
244             last2by2Averaged = false; // Reinitialize every four columns;
245         }
246
247         upPixel = get4Pixel(upPtr);
248         currPixel = get4Pixel(currPtr);
249
250         flagsPtr[0] = (e11n|e11s);  // Initialize in case nothing is found for this column
251
252         if (isWhite(upPixel) && isWhite(currPixel)) // both white?
253         {
254             flagsPtr[0] = eDone;
255 #if kGatherStats == 1
256             blockStats[esWhiteFound]++;
257 #endif
258         }
259
260         // Do vertical average on the current 2 pixel high column
261
262         // Currently we bail entirely if there is white. Later we may still do RLE on the non white pixel if one is present.
263         if (flagsPtr[0] == (e11n|e11s))
264         {
265             R1 = GetRed(currPixel);
266             G1 = GetGreen(currPixel);
267             B1 = GetBlue(currPixel);
268
269             R0 = GetRed(upPixel);
270             G0 = GetGreen(upPixel);
271             B0 = GetBlue(upPixel);
272
273             if ((m_max_error_for_two_pixels >= 3) && (NewDeltaE(R0, R1, G0, G1, B0, B1, m_max_error_for_two_pixels)))
274             {
275                 /*   _
276                     | | build 1x2
277                     | |
278                      -
279                 */
280                 ASSERT(flagsPtr[0] == (e11n|e11s));
281                 flagsPtr[0] = e12;
282 #if kGatherStats == 1
283                 blockStats[es12]++;
284 #endif
285                 R1 = GetRed(currPixel);
286                 G1 = GetGreen(currPixel);
287                 B1 = GetBlue(currPixel);
288
289                 R0 = GetRed(upPixel);
290                 G0 = GetGreen(upPixel);
291                 B0 = GetBlue(upPixel);
292
293                 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
294
295                 // look for a 2x2 block average on every other column
296                 if (isOdd(pixelNum))
297                 {   // It looks like we are at the end of a 2x2 block
298                     if (lastPairAveraged)
299                     {
300                         // Last pair was averaged so it's ok to try to make a 2x2 block
301                         if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1,lastB, B1, maxErrorForFourPixels)))
302                         {
303                             /* - -
304                               |   | build 2x2
305                               |   |
306                                - -
307                             */
308                             ASSERT(flagsPtr[-1] == e12);
309                             int didNotBuild4by2 = true;
310 #if kGatherStats == 1
311                             blockStats[es22w]++;
312 #endif
313                             flagsPtr[-1] = e22w;
314                             flagsPtr[0] = e22e;
315
316                             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
317
318                             if ((pixelNum & 0x03) == 0x03)  // 3,7,11,15... Looking for a 4x2 block to average
319                             {
320                                 if (last2by2Averaged)
321                                 {
322                                     /*   - -   - -
323                                         |   | |   | We have two 2x2s.
324                                         |   | |   |
325                                          - -   - -
326                                     */
327
328                                     lastPixel = get4Pixel(upPtr, -3); // Go back to previous 2x2 block and get the pixel
329                                     lastR = GetRed(lastPixel);
330                                     lastG = GetGreen(lastPixel);
331                                     lastB = GetBlue(lastPixel);
332                                     if ((maxErrorForEightPixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1,lastB, B1, maxErrorForEightPixels)))
333                                     {
334
335
336                                         /* - - - -
337                                           |       | build 4x2.
338                                           |       |
339                                            - - - -
340                                         */
341 #if kGatherStats == 1
342                                         blockStats[es42i]++;
343 #endif
344                                         didNotBuild4by2 = false;
345
346                                         flagsPtr[-3] = e42i;
347                                         flagsPtr[-2] = flagsPtr[-1] = flagsPtr[0] = e42;
348
349                                         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
350
351                                         if(m_eEndian == LITTLEENDIAN)
352                                             currPixel = (R1<<16) + (G1<<8) + B1;
353                                         else if(m_eEndian == BIGENDIAN)
354                                             currPixel = (R1<<24) + (G1<<16) + (B1<<8);
355
356 #if kMemWritesOptimize == 0
357                                         put4Pixel(upPtr, -3, currPixel);
358                                         put4Pixel(upPtr, -2, currPixel);
359                                         put4Pixel(upPtr, -1, currPixel);
360                                         put4Pixel(upPtr,  0, currPixel);
361                                         put4Pixel(currPtr, -3, currPixel);
362                                         put4Pixel(currPtr, -2, currPixel);
363                                         put4Pixel(currPtr, -1, currPixel);
364                                         put4Pixel(currPtr, 0, currPixel);
365 #else
366                                         put4Pixel(upPtr, -3, currPixel);
367 #endif
368                                     }
369                                 }
370
371                                 if (didNotBuild4by2)
372                                 {   // The first 2x2 block of this pair of 2x2 blocks wasn't averaged.
373                                     /*    - -    - -
374                                          |X X|  |   | not averaged block and averaged 2x2.
375                                          |X X|  |   |
376                                           - -    - -
377                                     */
378
379                                     last2by2Averaged = true;
380
381                                     if(m_eEndian == LITTLEENDIAN)
382                                         currPixel = (R1<<16) + (G1<<8) + B1;
383                                     else if(m_eEndian == BIGENDIAN)
384                                         currPixel = (R1<<24) + (G1<<16) + (B1<<8);
385
386 #if kMemWritesOptimize == 0
387                                     put4Pixel(upPtr, -1, currPixel);
388                                     put4Pixel(upPtr, 0, currPixel);
389                                     put4Pixel(currPtr, -1, currPixel);
390                                     put4Pixel(currPtr, 0, currPixel);
391 #else
392                                     put4Pixel(upPtr, -1, currPixel);
393 #endif
394                                 }
395                             }
396                             else  // Not looking for a 4x2 block yet so just output this 2x2 block for now.
397                             {
398                                 /*    - -    - -
399                                      |   |  |? ?| 1st 2x2 and maybe another later.
400                                      |   |  |? ?|
401                                       - -    - -
402                                 */
403
404                                 last2by2Averaged = true;
405
406                                 if(m_eEndian == LITTLEENDIAN)
407                                     currPixel = (R1<<16) + (G1<<8) + B1;
408                                 else if(m_eEndian == BIGENDIAN)
409                                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
410
411 #if kMemWritesOptimize == 0
412                                 put4Pixel(upPtr, -1, currPixel);
413                                 put4Pixel(upPtr, 0, currPixel);
414                                 put4Pixel(currPtr, -1, currPixel);
415                                 put4Pixel(currPtr, 0, currPixel);
416 #else
417                                 put4Pixel(upPtr, -1, currPixel);
418 #endif
419                             }
420                         }
421                         else  // The two averaged columns are not close enough in Delta E
422                         {
423                             /*  -    _
424                                | |  | | 2 1x2 blocks
425                                | |  | |
426                                 -    -
427                             */
428
429                             last2by2Averaged = false;
430
431                             if(m_eEndian == LITTLEENDIAN)
432                                 currPixel = (R1<<16) + (G1<<8) + B1;
433                             else if(m_eEndian == BIGENDIAN)
434                                 currPixel = (R1<<24) + (G1<<16) + (B1<<8);
435
436 #if kMemWritesOptimize == 0
437                             put4Pixel(upPtr, 0, currPixel);
438                             put4Pixel(currPtr, 0, currPixel);
439 #else
440                             put4Pixel(upPtr,0, currPixel);
441 #endif
442                         }
443                         lastR = R1;
444                         lastG = G1;
445                         lastB = B1;
446                         lastPairAveraged = true;
447                     }
448                     else  // This is the right place for 2x2 averaging but the previous column wasn't averaged
449                     {
450                         /*     -
451                             X | | Two non averaged pixels and a 1x2.
452                             X | |
453                                -
454                         */
455                         last2by2Averaged = false;
456                         lastPairAveraged = true;
457                         lastR = R1;
458                         lastG = G1;
459                         lastB = B1;
460
461                         if(m_eEndian == LITTLEENDIAN)
462                             currPixel = (R1<<16) + (G1<<8) + B1;
463                         else if(m_eEndian == BIGENDIAN)
464                             currPixel = (R1<<24) + (G1<<16) + (B1<<8);
465
466 #if kMemWritesOptimize == 0
467                         put4Pixel(upPtr, 0, currPixel);
468                         put4Pixel(currPtr, 0, currPixel);
469 #else
470                         put4Pixel(upPtr, 0, currPixel);
471 #endif
472                     }
473                 }
474                 else  // Not on the boundary for a 2x2 block, so just output current averaged 1x2 column
475                 {
476                     /*    -
477                          | | ?  1x2
478                          | | ?
479                           -
480                     */
481
482                     lastPairAveraged = true;
483                     lastR = R1;
484                     lastG = G1;
485                     lastB = B1;
486
487                     if(m_eEndian == LITTLEENDIAN)
488                         currPixel = (R1<<16) + (G1<<8) + B1;
489                     else if(m_eEndian == BIGENDIAN)
490                         currPixel = (R1<<24) + (G1<<16) + (B1<<8);
491
492 #if kMemWritesOptimize == 0
493                     put4Pixel(upPtr, 0, currPixel);
494                     put4Pixel(currPtr, 0, currPixel);
495 #else
496                     put4Pixel(upPtr, 0, currPixel);
497 #endif
498                 }
499             }
500             else if (lastPairAveraged)
501             {   // This is the case where we can't average current column and the last column was averaged.
502                 // Don't do anything if last pair was averaged and this one can't be
503
504                 /*    -
505                      | | X 1x2 averaged block and two non averaged pixels.
506                      | | X
507                       -
508                 */
509
510                 lastPairAveraged = false;
511             }
512             else
513              // can't vertically average current column so look for some horizontal averaging as a fallback
514              // Only do it if the last pair wasn't averaged either because we don't want to mess up a vertical averaging
515              // just to create a possible horizontal averaging.
516             {
517                 // Can only horizontally average every other pixel, much like the 2x2 blocks.
518                 if (isOdd(pixelNum))
519                 {
520                     // do horizontal averaging on previous raster
521                     lastPixel = get4Pixel(upPtr,-1);
522                     lastR = GetRed(lastPixel);
523                     lastG = GetGreen(lastPixel);
524                     lastB = GetBlue(lastPixel);
525                     if (((m_max_error_for_two_pixels >= 3)) && (NewDeltaE(lastR, R0, lastG, G0,lastB, B0, m_max_error_for_two_pixels)))
526                     {
527                         /*   - -
528                             |   | build upper 2x1
529                              - -
530                         */
531 #if kGatherStats == 1
532                         blockStats[es21nw]++;
533 #endif
534                         int didNotBuild4by1 = true;
535
536                         AverageNRound(isOdd(pixelNum), lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0);
537
538                         if ((pixelNum >= 3) && (flagsPtr[-3] & e21nw)) // 4,5,6,7,12,13,14,15,20...
539                         {
540                             ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
541
542                             // Attempt an upper 4x1
543                             lastPixel = get4Pixel(upPtr,-3);
544                             R0 = GetRed(lastPixel);
545                             G0 = GetGreen(lastPixel);
546                             B0 = GetBlue(lastPixel);
547                             if ( (maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0,lastB, B0, maxErrorForFourPixels)))
548                             {
549                                 /*   - - - -
550                                     |       | build upper 4x1
551                                      - - - -
552                                 */
553 #if kGatherStats == 1
554                                 blockStats[es41ni]++;
555 #endif
556                                 didNotBuild4by1 = false;
557                                 AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
558
559                                 if(m_eEndian == LITTLEENDIAN)
560                                     currPixel = (lastR<<16) + (lastG<<8) + lastB;
561                                 else if(m_eEndian == BIGENDIAN)
562                                     currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
563
564 #if kMemWritesOptimize == 0
565                                 put4Pixel(upPtr, -3, currPixel);
566                                 put4Pixel(upPtr, -2, currPixel);
567                                 put4Pixel(upPtr, -1, currPixel);
568                                 put4Pixel(upPtr, 0, currPixel);
569 #else
570                                 put4Pixel(upPtr, -3, currPixel);
571 #endif
572
573                                 ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
574
575                                 flagsPtr[-3] = (flagsPtr[-3] & ~eNorths) | e41ni;
576                                 flagsPtr[-2] = (flagsPtr[-2] & ~eNorths) | e41n;
577                                 flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e41n;
578                                 flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e41n;
579                             }
580                         }
581
582                         if (didNotBuild4by1) // Not an upper 4x1 so output upper 2x1.
583                         {
584                             if(m_eEndian == LITTLEENDIAN)
585                                 currPixel = (lastR<<16) + (lastG<<8) + lastB;
586                             else if(m_eEndian == BIGENDIAN)
587                                 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
588
589 #if kMemWritesOptimize == 0
590                             put4Pixel(upPtr, -1, currPixel);
591                             put4Pixel(upPtr, 0, currPixel);
592 #else
593                             put4Pixel(upPtr, -1, currPixel);
594 #endif
595                             ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
596                             flagsPtr[-1] = (flagsPtr[-1] & ~eNorths) | e21nw;
597                             flagsPtr[0] = (flagsPtr[0] & ~eNorths) | e21ne;
598                         }
599                     }
600
601                     // do horizontal on current raster
602                     lastPixel = get4Pixel(currPtr,-1);
603                     lastR = GetRed(lastPixel);
604                     lastG = GetGreen(lastPixel);
605                     lastB = GetBlue(lastPixel);
606                     if ((m_max_error_for_two_pixels >= 3) && (NewDeltaE(lastR, R1, lastG, G1, lastB, B1, m_max_error_for_two_pixels)))
607                     {
608                         /*   - -
609                             |   | build lower 2x1
610                              - -
611                         */
612                         int didNotBuild4by1 = true;
613 #if kGatherStats == 1
614                         blockStats[es21sw]++;
615 #endif
616                         AverageNRound(isOdd(pixelNum), lastR, lastR, R1, lastG, lastG, G1, lastB, lastB, B1);
617                         if ((pixelNum >= 3) && (flagsPtr[-3] & e21sw)) // 4,5,6,7,12,13,14,15,20...
618                         {
619                             // Look for a lower 4x1
620                             ASSERT(!((flagsPtr[-3] | flagsPtr[-2] | flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
621
622                             lastPixel = get4Pixel(currPtr,-3);
623                             R0 = GetRed(lastPixel);
624                             G0 = GetGreen(lastPixel);
625                             B0 = GetBlue(lastPixel);
626                             if ((maxErrorForFourPixels >= 3) && (NewDeltaE(lastR, R0, lastG, G0, lastB, B0, maxErrorForFourPixels)))
627                             {
628                                 /*   - - - -
629                                     |       | build lower 4x1
630                                      - - - -
631                                 */
632 #if kGatherStats == 1
633                                 blockStats[es41si]++;
634 #endif
635                                 didNotBuild4by1 = false;
636                                 AverageNRound((pixelNum & 0x04)== 0x04, lastR, lastR, R0, lastG, lastG, G0, lastB, lastB, B0); // 4,5,6,7,12,13,14,15,20...
637
638                                 if(m_eEndian == LITTLEENDIAN)
639                                     currPixel = (lastR<<16) + (lastG<<8) + lastB;
640                                 else if(m_eEndian == BIGENDIAN)
641                                     currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
642
643 #if kMemWritesOptimize == 0
644                                 put4Pixel(currPtr, -3, currPixel);
645                                 put4Pixel(currPtr, -2, currPixel);
646                                 put4Pixel(currPtr, -1, currPixel);
647                                 put4Pixel(currPtr, 0, currPixel);
648 #else
649                                 put4Pixel(currPtr, -3, currPixel);
650 #endif
651                                 flagsPtr[-3] = (flagsPtr[-3] & ~eSouths) | e41si;
652                                 flagsPtr[-2] = (flagsPtr[-2] & ~eSouths) | e41s;
653                                 flagsPtr[-1] = (flagsPtr[-1] & ~eSouths) | e41s;
654                                 flagsPtr[0] = (flagsPtr[0] & ~eSouths) | e41s;
655                             }
656                         }
657
658                         if (didNotBuild4by1) // Not a lower 4x1 so output lower 2x1.
659                         {
660                             ASSERT(!((flagsPtr[-1] | flagsPtr[0]) & eTheRest)); // no vertical blocks
661
662                             if(m_eEndian == LITTLEENDIAN)
663                                 currPixel = (lastR<<16) + (lastG<<8) + lastB;
664                             else if(m_eEndian == BIGENDIAN)
665                                 currPixel = (lastR<<24) + (lastG<<16) + (lastB<<8);
666
667 #if kMemWritesOptimize == 0
668                             put4Pixel(currPtr, -1, currPixel);
669                             put4Pixel(currPtr, 0, currPixel);
670 #else
671                             put4Pixel(currPtr, -1, currPixel);
672 #endif
673
674                             flagsPtr[-1] = (flagsPtr[-1] & ~eSouths) | e21sw;
675                             flagsPtr[0] = (flagsPtr[0] & ~eSouths) | e21se;
676                         }
677                     }  // If DeltaE... Looking for two by one
678                 }  // IsOdd(pixelNum)
679             }
680         }
681         else // no flag bits set.
682         {
683             lastPairAveraged = false; // EGW Fixes bug on business graphics. 11/24/97
684         }
685
686         upPtr += eBufferedPixelWidthInBytes;
687         currPtr += eBufferedPixelWidthInBytes;
688         flagsPtr++;
689     }  // for each pixel...
690 }
691
692 // Filter2PairsOfFilteredRows.  This routine takes 2 pairs of rows that
693 // have been through the Filter2RawRows routine and puts blocks together
694 // to make bigger blocks.  It prefers taking 2 high blocks and putting
695 // them together to make four high blocks, but as a last resort it will
696 // take try to take a 1 high blocks from the second and third rasters and
697 // create 2 high blocks.  The possible block sizes this routine could
698 // create are 8x4, 4x4, 2x4, and 1x4, and then with the second and third rasters
699 // 4x2, 2x2, and 1x2.
700 void ErnieFilter::Filter2PairsOfFilteredRows(unsigned char *row1Ptr, unsigned char *row2Ptr, unsigned char *row3Ptr, unsigned char *row4Ptr)
701 {
702     const unsigned int maxErrorForFourPixels = m_max_error_for_two_pixels / 2;
703     const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
704     const unsigned int maxErrorForSixteenPixels = maxErrorForEightPixels / 2;
705     const unsigned int maxErrorForThirtyTwoPixels = maxErrorForSixteenPixels / 2;
706
707     for (int pixelNum = 0; pixelNum < (m_row_width_in_pixels-3);)  // Make sure we have four pixels to work with
708     {
709         int currPixel, upPixel;
710         int R0, G0, B0, R1, G1, B1;
711
712         if ((m_pixel_filtered_flags[0][pixelNum] & e42i) && (m_pixel_filtered_flags[1][pixelNum] & e42i))
713         {
714             /*  - - - -
715                |       |
716                |       |
717                 - - - -     We have two 4x2s.
718                 - - - -
719                |       |
720                |       |
721                 - - - -
722             */
723             ASSERT(m_pixel_filtered_flags[0][pixelNum] == e42i && m_pixel_filtered_flags[0][pixelNum+1] == e42 && m_pixel_filtered_flags[0][pixelNum+2] == e42 && m_pixel_filtered_flags[0][pixelNum+3] == e42);
724             ASSERT(m_pixel_filtered_flags[1][pixelNum] == e42i && m_pixel_filtered_flags[1][pixelNum+1] == e42 && m_pixel_filtered_flags[1][pixelNum+2] == e42 && m_pixel_filtered_flags[1][pixelNum+3] == e42);
725
726             upPixel = get4Pixel(row1Ptr);
727             currPixel = get4Pixel(row3Ptr);
728
729             R1 = GetRed(currPixel);
730             G1 = GetGreen(currPixel);
731             B1 = GetBlue(currPixel);
732
733             R0 = GetRed(upPixel);
734             G0 = GetGreen(upPixel);
735             B0 = GetBlue(upPixel);
736
737             if((maxErrorForSixteenPixels >= 3) &&(NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForSixteenPixels)))
738             {
739                 /*   - - - -
740                     |       |
741                     |       | build 4x4
742                     |       |
743                     |       |
744                      - - - -
745                 */
746 #if kGatherStats == 1
747                 blockStats[es44ni]++;
748 #endif
749                 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
750
751                 if(m_eEndian == LITTLEENDIAN)
752                     currPixel = (R1<<16) + (G1<<8) + B1;
753                 else if(m_eEndian == BIGENDIAN)
754                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
755
756 #if kMemWritesOptimize == 0
757                 put4Pixel(row1Ptr, 0, currPixel);
758                 put4Pixel(row1Ptr, 1, currPixel);
759                 put4Pixel(row1Ptr, 2, currPixel);
760                 put4Pixel(row1Ptr, 3, currPixel);
761                 put4Pixel(row2Ptr, 0, currPixel);
762                 put4Pixel(row2Ptr, 1, currPixel);
763                 put4Pixel(row2Ptr, 2, currPixel);
764                 put4Pixel(row2Ptr, 3, currPixel);
765                 put4Pixel(row3Ptr, 0, currPixel);
766                 put4Pixel(row3Ptr, 1, currPixel);
767                 put4Pixel(row3Ptr, 2, currPixel);
768                 put4Pixel(row3Ptr, 3, currPixel);
769                 put4Pixel(row4Ptr, 0, currPixel);
770                 put4Pixel(row4Ptr, 1, currPixel);
771                 put4Pixel(row4Ptr, 2, currPixel);
772                 put4Pixel(row4Ptr, 3, currPixel);
773 #else
774                 put4Pixel(row1Ptr, 0, currPixel);
775 #endif
776                 row1Ptr += 4*eBufferedPixelWidthInBytes;
777                 row2Ptr += 4*eBufferedPixelWidthInBytes;
778                 row3Ptr += 4*eBufferedPixelWidthInBytes;
779                 row4Ptr += 4*eBufferedPixelWidthInBytes;
780
781                 m_pixel_filtered_flags[0][pixelNum] = e44ni;
782                 m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+2] = m_pixel_filtered_flags[0][pixelNum+3] = e44n;
783                 m_pixel_filtered_flags[1][pixelNum] = e44si;
784                 m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+2] = m_pixel_filtered_flags[1][pixelNum+3] = e44s;
785
786                 if ((pixelNum >= 4) && (m_pixel_filtered_flags[1][pixelNum-4] & e44si)) // 4,5,6,7,12,13,14,15,20...
787                 {
788                     /*   - - - -     - - - -
789                         |       |   |       |
790                         |       |   |       | We have two 4x4s.
791                         |       |   |       |
792                         |       |   |       |
793                          - - - -     - - - -
794                     */
795                     ASSERT(m_pixel_filtered_flags[0][pixelNum-4] == e44ni && m_pixel_filtered_flags[0][pixelNum-3] == e44n && m_pixel_filtered_flags[0][pixelNum-2] == e44n && m_pixel_filtered_flags[0][pixelNum-1] == e44n);
796                     ASSERT(m_pixel_filtered_flags[1][pixelNum-4] == e44si && m_pixel_filtered_flags[1][pixelNum-3] == e44s && m_pixel_filtered_flags[1][pixelNum-2] == e44s && m_pixel_filtered_flags[1][pixelNum-1] == e44s);
797
798                     upPixel = get4Pixel(row1Ptr, -8);
799
800                     R0 = GetRed(upPixel);
801                     G0 = GetGreen(upPixel);
802                     B0 = GetBlue(upPixel);
803
804                     if( (maxErrorForThirtyTwoPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForThirtyTwoPixels)))
805                     {
806                         /*   - - - - - - - -
807                             |               |
808                             |               | build 8x4
809                             |               |
810                             |               |
811                              - - - - - - - -
812                         */
813 #if kGatherStats == 1
814                         blockStats[es84ni]++;
815 #endif
816                         AverageNRound((pixelNum & 0x08) == 0x08, R1, R1, R0, G1, G1, G0, B1, B1, B0);
817                         if(m_eEndian == LITTLEENDIAN)
818                             currPixel = (R1<<16) + (G1<<8) + B1;
819                         else if(m_eEndian == BIGENDIAN)
820                             currPixel = (R1<<24) + (G1<<16) + (B1<<8);
821
822 #if kMemWritesOptimize == 0
823                         put4Pixel(row1Ptr, -8, currPixel);
824                         put4Pixel(row1Ptr, -7, currPixel);
825                         put4Pixel(row1Ptr, -6, currPixel);
826                         put4Pixel(row1Ptr, -5, currPixel);
827                         put4Pixel(row1Ptr, -4, currPixel);
828                         put4Pixel(row1Ptr, -3, currPixel);
829                         put4Pixel(row1Ptr, -2, currPixel);
830                         put4Pixel(row1Ptr, -1, currPixel);
831                         put4Pixel(row2Ptr, -8, currPixel);
832                         put4Pixel(row2Ptr, -7, currPixel);
833                         put4Pixel(row2Ptr, -6, currPixel);
834                         put4Pixel(row2Ptr, -5, currPixel);
835                         put4Pixel(row2Ptr, -4, currPixel);
836                         put4Pixel(row2Ptr, -3, currPixel);
837                         put4Pixel(row2Ptr, -2, currPixel);
838                         put4Pixel(row2Ptr, -1, currPixel);
839                         put4Pixel(row3Ptr, -8, currPixel);
840                         put4Pixel(row3Ptr, -7, currPixel);
841                         put4Pixel(row3Ptr, -6, currPixel);
842                         put4Pixel(row3Ptr, -5, currPixel);
843                         put4Pixel(row3Ptr, -4, currPixel);
844                         put4Pixel(row3Ptr, -3, currPixel);
845                         put4Pixel(row3Ptr, -2, currPixel);
846                         put4Pixel(row3Ptr, -1, currPixel);
847                         put4Pixel(row4Ptr, -8, currPixel);
848                         put4Pixel(row4Ptr, -7, currPixel);
849                         put4Pixel(row4Ptr, -6, currPixel);
850                         put4Pixel(row4Ptr, -5, currPixel);
851                         put4Pixel(row4Ptr, -4, currPixel);
852                         put4Pixel(row4Ptr, -3, currPixel);
853                         put4Pixel(row4Ptr, -2, currPixel);
854                         put4Pixel(row4Ptr, -1, currPixel);
855 #else
856                         put4Pixel(row1Ptr, -8, currPixel);
857 #endif
858                         m_pixel_filtered_flags[0][pixelNum-4] = e84ni;
859                         m_pixel_filtered_flags[0][pixelNum-3] = m_pixel_filtered_flags[0][pixelNum-2] = m_pixel_filtered_flags[0][pixelNum-1] = m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+2] = m_pixel_filtered_flags[0][pixelNum+3] = e84n;
860                         m_pixel_filtered_flags[1][pixelNum-4] = e84si;
861                         m_pixel_filtered_flags[1][pixelNum-3] = m_pixel_filtered_flags[1][pixelNum-2] = m_pixel_filtered_flags[1][pixelNum-1] = m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+2] = m_pixel_filtered_flags[1][pixelNum+3] = e84s;
862                     }
863                 }
864             }
865             else // could not build 4x4 so move forward past the stacked 4x2s.
866             {
867                 row1Ptr += 4*eBufferedPixelWidthInBytes;
868                 row2Ptr += 4*eBufferedPixelWidthInBytes;
869                 row3Ptr += 4*eBufferedPixelWidthInBytes;
870                 row4Ptr += 4*eBufferedPixelWidthInBytes;
871             }
872             pixelNum += 4;
873         }
874         else if ((m_pixel_filtered_flags[0][pixelNum] & e22w) && (m_pixel_filtered_flags[1][pixelNum] & e22w))
875         {
876             /*   - -
877                 |   |
878                 |   |
879                  - -   we have 2 2x2s.
880                  - -
881                 |   |
882                 |   |
883                  - -
884             */
885             ASSERT(m_pixel_filtered_flags[0][pixelNum] == e22w && m_pixel_filtered_flags[0][pixelNum+1] == e22e);
886             ASSERT(m_pixel_filtered_flags[1][pixelNum] == e22w && m_pixel_filtered_flags[1][pixelNum+1] == e22e);
887
888             upPixel = get4Pixel(row1Ptr);
889             currPixel = get4Pixel(row3Ptr);
890
891             R1 = GetRed(currPixel);
892             G1 = GetGreen(currPixel);
893             B1 = GetBlue(currPixel);
894
895             R0 = GetRed(upPixel);
896             G0 = GetGreen(upPixel);
897             B0 = GetBlue(upPixel);
898
899             if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
900             {
901                 /*   - -
902                     |   |
903                     |   | build 2x4
904                     |   |
905                     |   |
906                      - -
907                 */
908 #if kGatherStats == 1
909                 blockStats[es24nw]++;
910 #endif
911                 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
912
913                 if(m_eEndian == LITTLEENDIAN)
914                     currPixel = (R1<<16) + (G1<<8) + B1;
915                 else if(m_eEndian == BIGENDIAN)
916                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
917
918 #if kMemWritesOptimize == 0
919                 put4Pixel(row1Ptr, 0, currPixel);
920                 put4Pixel(row1Ptr, 1, currPixel);
921                 put4Pixel(row2Ptr, 0, currPixel);
922                 put4Pixel(row2Ptr, 1, currPixel);
923                 put4Pixel(row3Ptr, 0, currPixel);
924                 put4Pixel(row3Ptr, 1, currPixel);
925                 put4Pixel(row4Ptr, 0, currPixel);
926                 put4Pixel(row4Ptr, 1, currPixel);
927 #else
928                 put4Pixel(row1Ptr, 0, currPixel);
929 #endif
930                 row1Ptr += 2*eBufferedPixelWidthInBytes;
931                 row2Ptr += 2*eBufferedPixelWidthInBytes;
932                 row3Ptr += 2*eBufferedPixelWidthInBytes;
933                 row4Ptr += 2*eBufferedPixelWidthInBytes;
934
935                 m_pixel_filtered_flags[0][pixelNum] = e24nw;
936                 m_pixel_filtered_flags[0][pixelNum+1] = e24ne;
937                 m_pixel_filtered_flags[1][pixelNum] = e24sw;
938                 m_pixel_filtered_flags[1][pixelNum+1] = e24se;
939             }
940             else
941             {
942                 row1Ptr += 2*eBufferedPixelWidthInBytes;
943                 row2Ptr += 2*eBufferedPixelWidthInBytes;
944                 row3Ptr += 2*eBufferedPixelWidthInBytes;
945                 row4Ptr += 2*eBufferedPixelWidthInBytes;
946             }
947             pixelNum += 2;
948         }
949         else if ((m_pixel_filtered_flags[0][pixelNum] & e12) && (m_pixel_filtered_flags[1][pixelNum] & e12))
950         {
951             /*   -
952                 | |
953                 | |
954                  -  we have two 1x2s.
955                  -
956                 | |
957                 | |
958                  -
959             */
960             ASSERT(m_pixel_filtered_flags[0][pixelNum] == e12);
961             ASSERT(m_pixel_filtered_flags[1][pixelNum] == e12);
962
963             upPixel = get4Pixel(row1Ptr);
964             currPixel = get4Pixel(row3Ptr);
965
966             R1 = GetRed(currPixel);
967             G1 = GetGreen(currPixel);
968             B1 = GetBlue(currPixel);
969
970             R0 = GetRed(upPixel);
971             G0 = GetGreen(upPixel);
972             B0 = GetBlue(upPixel);
973
974             if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
975             {
976                 /*   -
977                     | |
978                     | | build 1x4
979                     | |
980                     | |
981                      -
982                 */
983 #if kGatherStats == 1
984                 blockStats[es14n]++;
985 #endif
986                 AverageNRound((pixelNum & 0x01) == 0x01, R1, R1, R0, G1, G1, G0, B1, B1, B0);
987
988                 if(m_eEndian == LITTLEENDIAN)
989                     currPixel = (R1<<16) + (G1<<8) + B1;
990                 else if(m_eEndian == BIGENDIAN)
991                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
992
993 #if kMemWritesOptimize == 0
994                 put4Pixel(row1Ptr, 0, currPixel);
995                 put4Pixel(row2Ptr, 0, currPixel);
996                 put4Pixel(row3Ptr, 0, currPixel);
997                 put4Pixel(row4Ptr, 0, currPixel);
998 #else
999                 put4Pixel(row1Ptr, 0, currPixel);
1000 #endif
1001                 m_pixel_filtered_flags[0][pixelNum] = e14n;
1002                 m_pixel_filtered_flags[1][pixelNum] = e14s;
1003             }
1004
1005             row1Ptr += eBufferedPixelWidthInBytes;
1006             row2Ptr += eBufferedPixelWidthInBytes;
1007             row3Ptr += eBufferedPixelWidthInBytes;
1008             row4Ptr += eBufferedPixelWidthInBytes;
1009
1010             pixelNum++;
1011         }
1012         else if ((m_pixel_filtered_flags[0][pixelNum] & e41si)
1013             && (m_pixel_filtered_flags[1][pixelNum] & e41ni))
1014         {
1015             /*    - - - -
1016                  |       |
1017                   - - - -   We have two 4x1s.
1018                   - - - -
1019                  |       |
1020                   - - - -
1021             */
1022
1023             upPixel = get4Pixel(row2Ptr);
1024             currPixel = get4Pixel(row3Ptr);
1025
1026             R1 = GetRed(currPixel);
1027             G1 = GetGreen(currPixel);
1028             B1 = GetBlue(currPixel);
1029
1030             R0 = GetRed(upPixel);
1031             G0 = GetGreen(upPixel);
1032             B0 = GetBlue(upPixel);
1033
1034
1035             if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
1036             {
1037
1038                 /*    - - - -
1039                      |       |  build 4x2.
1040                      |       |
1041                       - - - -
1042                 */
1043 #if kGatherStats == 1
1044                 blockStats[es42w]++;
1045 #endif
1046                 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1047
1048                 if(m_eEndian == LITTLEENDIAN)
1049                     currPixel = (R1<<16) + (G1<<8) + B1;
1050                 else if(m_eEndian == BIGENDIAN)
1051                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1052
1053                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1054                 put4Pixel(row2Ptr, 0, currPixel);
1055                 put4Pixel(row2Ptr, 1, currPixel);
1056                 put4Pixel(row2Ptr, 2, currPixel);
1057                 put4Pixel(row2Ptr, 3, currPixel);
1058                 put4Pixel(row3Ptr, 0, currPixel);
1059                 put4Pixel(row3Ptr, 1, currPixel);
1060                 put4Pixel(row3Ptr, 2, currPixel);
1061                 put4Pixel(row3Ptr, 3, currPixel);
1062
1063                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e41si;
1064                 m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1065                 m_pixel_filtered_flags[0][pixelNum+2] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1066                 m_pixel_filtered_flags[0][pixelNum+3] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1067
1068                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1069                 m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1070                 m_pixel_filtered_flags[1][pixelNum+2] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1071                 m_pixel_filtered_flags[1][pixelNum+3] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1072             }
1073             pixelNum += 4;
1074
1075             row1Ptr += 4*eBufferedPixelWidthInBytes;
1076             row2Ptr += 4*eBufferedPixelWidthInBytes;
1077             row3Ptr += 4*eBufferedPixelWidthInBytes;
1078             row4Ptr += 4*eBufferedPixelWidthInBytes;
1079         }
1080         else if ((m_pixel_filtered_flags[0][pixelNum] & e21sw)
1081                 && (m_pixel_filtered_flags[1][pixelNum] & e21nw))
1082         {
1083             /*    - -
1084                  |   |
1085                   - -  We have two 2x1s.
1086                   - -
1087                  |   |
1088                   - -
1089             */
1090             ASSERT(!((m_pixel_filtered_flags[0][pixelNum] & e11s) | (m_pixel_filtered_flags[0][pixelNum+1] & e11s)));
1091             ASSERT(!((m_pixel_filtered_flags[1][pixelNum] & e11n) | (m_pixel_filtered_flags[1][pixelNum+1] & e11n)));
1092
1093             upPixel = get4Pixel(row2Ptr);
1094             currPixel = get4Pixel(row3Ptr);
1095
1096             R1 = GetRed(currPixel);
1097             G1 = GetGreen(currPixel);
1098             B1 = GetBlue(currPixel);
1099
1100             R0 = GetRed(upPixel);
1101             G0 = GetGreen(upPixel);
1102             B0 = GetBlue(upPixel);
1103
1104             if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
1105             {
1106                 /*    - -
1107                      |   |  build 2x2.
1108                      |   |
1109                       - -
1110                 */
1111 #if kGatherStats == 1
1112                 blockStats[es22w]++;
1113 #endif
1114                 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1115
1116                 if(m_eEndian == LITTLEENDIAN)
1117                     currPixel = (R1<<16) + (G1<<8) + B1;
1118                 else if(m_eEndian == BIGENDIAN)
1119                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1120
1121                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1122                 put4Pixel(row2Ptr, 0, currPixel);
1123                 put4Pixel(row2Ptr, 1, currPixel);
1124                 put4Pixel(row3Ptr, 0, currPixel);
1125                 put4Pixel(row3Ptr, 1, currPixel);
1126
1127                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e21sw;
1128                 m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+1] & ~e21se;
1129
1130                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1131                 m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+1] & ~e21ne;
1132             }
1133
1134             pixelNum += 2;
1135
1136             row1Ptr += 2*eBufferedPixelWidthInBytes;
1137             row2Ptr += 2*eBufferedPixelWidthInBytes;
1138             row3Ptr += 2*eBufferedPixelWidthInBytes;
1139             row4Ptr += 2*eBufferedPixelWidthInBytes;
1140         }
1141         else if ((m_pixel_filtered_flags[0][pixelNum] & e11s)
1142                 && (m_pixel_filtered_flags[1][pixelNum] & e11n))
1143         {
1144             /*    -
1145                  | |
1146                   -   We have two 1x1s.
1147                   -
1148                  | |
1149                   -
1150             */
1151
1152             upPixel = get4Pixel(row2Ptr);
1153             currPixel = get4Pixel(row3Ptr);
1154
1155             R1 = GetRed(currPixel);
1156             G1 = GetGreen(currPixel);
1157             B1 = GetBlue(currPixel);
1158
1159             R0 = GetRed(upPixel);
1160             G0 = GetGreen(upPixel);
1161             B0 = GetBlue(upPixel);
1162
1163             if ((m_max_error_for_two_pixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, m_max_error_for_two_pixels)))
1164             {
1165                 /*    -
1166                      | |  build 1x2.
1167                      | |
1168                       -
1169                 */
1170 #if kGatherStats == 1
1171                 blockStats[es12w]++;
1172 #endif
1173                 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
1174
1175                 if(m_eEndian == LITTLEENDIAN)
1176                     currPixel = (R1<<16) + (G1<<8) + B1;
1177                 else if(m_eEndian == BIGENDIAN)
1178                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1179
1180                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1181                 put4Pixel(row2Ptr, 0, currPixel);
1182                 put4Pixel(row3Ptr, 0, currPixel);
1183
1184                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e11s;
1185
1186                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1187             }
1188
1189             pixelNum += 1;
1190
1191             row1Ptr += eBufferedPixelWidthInBytes;
1192             row2Ptr += eBufferedPixelWidthInBytes;
1193             row3Ptr += eBufferedPixelWidthInBytes;
1194             row4Ptr += eBufferedPixelWidthInBytes;
1195         }
1196         else // Do no vertical filtering here.
1197         {
1198             pixelNum += 1;
1199
1200             row1Ptr += eBufferedPixelWidthInBytes;
1201             row2Ptr += eBufferedPixelWidthInBytes;
1202             row3Ptr += eBufferedPixelWidthInBytes;
1203             row4Ptr += eBufferedPixelWidthInBytes;
1204         }
1205     }
1206 }
1207
1208 // Filter3FilteredRows.  This routine only exists for the case of the odd size band
1209 // with three rasters left over.  I'm not sure how much extra benifit we really
1210 // get from running this, but for now I'm leaving it in.  Since Ernie deals with
1211 // block sizes that are powers of two its rather difficult to filter 3 rows together,
1212 // about all I've been able to do is look for 1 high blocks in the second and third
1213 // rasters to put together into 2 high blocks.  This routine will create 4x2, 2x2, and
1214 // 1x2 blocks from those second and third rasters.
1215 void ErnieFilter::Filter3FilteredRows(unsigned char *row1Ptr, unsigned char *row2Ptr, unsigned char *row3Ptr)
1216 {
1217     const unsigned int maxErrorForFourPixels = m_max_error_for_two_pixels / 2;
1218     const unsigned int maxErrorForEightPixels = maxErrorForFourPixels / 2;
1219 //    const unsigned int maxErrorForSixteenPixels = maxErrorForEightPixels / 2;
1220 //    const unsigned int maxErrorForThirtyTwoPixels = maxErrorForSixteenPixels / 2;
1221
1222     for (int pixelNum = 0; pixelNum < (m_row_width_in_pixels-3);)  // Make sure we have four pixels to work with
1223     {
1224 //        int currPixel, upPixel;
1225         uint32_t currPixel, upPixel;
1226         int R0, G0, B0, R1, G1, B1;
1227
1228         if ((m_pixel_filtered_flags[0][pixelNum] & e41si)
1229                 && (m_pixel_filtered_flags[1][pixelNum] & e41ni))
1230         {
1231             /*    - - - -
1232                  |       |
1233                   - - - -   We have two 4x1s.
1234                   - - - -
1235                  |       |
1236                   - - - -
1237             */
1238             ASSERT(!((m_pixel_filtered_flags[0][pixelNum] & e11s) | (m_pixel_filtered_flags[0][pixelNum+1] & e11s)));
1239             ASSERT(!((m_pixel_filtered_flags[1][pixelNum] & e11n) | (m_pixel_filtered_flags[1][pixelNum+1] & e11n)));
1240
1241             upPixel = get4Pixel(row2Ptr);
1242             currPixel = get4Pixel(row3Ptr);
1243
1244             R1 = GetRed(currPixel);
1245             G1 = GetGreen(currPixel);
1246             B1 = GetBlue(currPixel);
1247
1248             R0 = GetRed(upPixel);
1249             G0 = GetGreen(upPixel);
1250             B0 = GetBlue(upPixel);
1251
1252
1253             if ((maxErrorForEightPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForEightPixels)))
1254             {
1255
1256                 /*    - - - -
1257                      |       |  build 4x2.
1258                      |       |
1259                       - - - -
1260                 */
1261 #if kGatherStats == 1
1262                 blockStats[es42w]++;
1263 #endif
1264                 AverageNRound((pixelNum & 0x04) == 0x04, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1265
1266                 if(m_eEndian == LITTLEENDIAN)
1267                     currPixel = (R1<<16) + (G1<<8) + B1;
1268                 else if(m_eEndian == BIGENDIAN)
1269                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1270
1271                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1272                 put4Pixel(row2Ptr, 0, currPixel);
1273                 put4Pixel(row2Ptr, 1, currPixel);
1274                 put4Pixel(row2Ptr, 2, currPixel);
1275                 put4Pixel(row2Ptr, 3, currPixel);
1276                 put4Pixel(row3Ptr, 0, currPixel);
1277                 put4Pixel(row3Ptr, 1, currPixel);
1278                 put4Pixel(row3Ptr, 2, currPixel);
1279                 put4Pixel(row3Ptr, 3, currPixel);
1280
1281                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e41si;
1282                 m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1283                 m_pixel_filtered_flags[0][pixelNum+2] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1284                 m_pixel_filtered_flags[0][pixelNum+3] = m_pixel_filtered_flags[0][pixelNum+1] & ~e41s;
1285
1286                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1287                 m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1288                 m_pixel_filtered_flags[1][pixelNum+2] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1289                 m_pixel_filtered_flags[1][pixelNum+3] = m_pixel_filtered_flags[1][pixelNum+1] & ~e41n;
1290             }
1291             pixelNum += 4;
1292
1293             row1Ptr += 4*eBufferedPixelWidthInBytes;
1294             row2Ptr += 4*eBufferedPixelWidthInBytes;
1295             row3Ptr += 4*eBufferedPixelWidthInBytes;
1296         }
1297         else if ((m_pixel_filtered_flags[0][pixelNum] & e21sw)
1298                 && (m_pixel_filtered_flags[1][pixelNum] & e21nw))
1299         {
1300             /*    - -
1301                  |   |
1302                   - -  We have two 2x1s.
1303                   - -
1304                  |   |
1305                   - -
1306             */
1307             ASSERT(!((m_pixel_filtered_flags[0][pixelNum] & e11s) | (m_pixel_filtered_flags[0][pixelNum+1] & e11s)));
1308             ASSERT(!((m_pixel_filtered_flags[1][pixelNum] & e11n) | (m_pixel_filtered_flags[1][pixelNum+1] & e11n)));
1309
1310             upPixel = get4Pixel(row2Ptr);
1311             currPixel = get4Pixel(row3Ptr);
1312
1313             R1 = GetRed(currPixel);
1314             G1 = GetGreen(currPixel);
1315             B1 = GetBlue(currPixel);
1316
1317             R0 = GetRed(upPixel);
1318             G0 = GetGreen(upPixel);
1319             B0 = GetBlue(upPixel);
1320
1321             if ((maxErrorForFourPixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, maxErrorForFourPixels)))
1322             {
1323                 /*    - -
1324                      |   |  build 2x2.
1325                      |   |
1326                       - -
1327                 */
1328 #if kGatherStats == 1
1329                 blockStats[es22w]++;
1330 #endif
1331                 AverageNRound((pixelNum & 0x02) == 0x02, R1, R1, R0, G1, G1, G0, B1, B1, B0);
1332
1333                 if(m_eEndian == LITTLEENDIAN)
1334                     currPixel = (R1<<16) + (G1<<8) + B1;
1335                 else if(m_eEndian == BIGENDIAN)
1336                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1337
1338                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1339                 put4Pixel(row2Ptr, 0, currPixel);
1340                 put4Pixel(row2Ptr, 1, currPixel);
1341                 put4Pixel(row3Ptr, 0, currPixel);
1342                 put4Pixel(row3Ptr, 1, currPixel);
1343
1344                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e21sw;
1345                 m_pixel_filtered_flags[0][pixelNum+1] = m_pixel_filtered_flags[0][pixelNum+1] & ~e21se;
1346
1347                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1348                 m_pixel_filtered_flags[1][pixelNum+1] = m_pixel_filtered_flags[1][pixelNum+1] & ~e21ne;
1349             }
1350
1351             pixelNum += 2;
1352
1353             row1Ptr += 2*eBufferedPixelWidthInBytes;
1354             row2Ptr += 2*eBufferedPixelWidthInBytes;
1355             row3Ptr += 2*eBufferedPixelWidthInBytes;
1356         }
1357         else if ((m_pixel_filtered_flags[0][pixelNum] & e11s)
1358                 && (m_pixel_filtered_flags[1][pixelNum] & e11n))
1359         {
1360             /*    -
1361                  | |
1362                   -   We have two 1x1s.
1363                   -
1364                  | |
1365                   -
1366             */
1367
1368             upPixel = get4Pixel(row2Ptr);
1369             currPixel = get4Pixel(row3Ptr);
1370
1371             R1 = GetRed(currPixel);
1372             G1 = GetGreen(currPixel);
1373             B1 = GetBlue(currPixel);
1374
1375             R0 = GetRed(upPixel);
1376             G0 = GetGreen(upPixel);
1377             B0 = GetBlue(upPixel);
1378
1379             if ((m_max_error_for_two_pixels >= 3) && (NewDeltaE(R1, R0, G1, G0, B1, B0, m_max_error_for_two_pixels)))
1380             {
1381                 /*    -
1382                      | |  build 1x2.
1383                      | |
1384                       -
1385                 */
1386 #if kGatherStats == 1
1387                 blockStats[es12w]++;
1388 #endif
1389                 AverageNRound(isOdd(pixelNum), R1, R1, R0, G1, G1, G0, B1, B1, B0);
1390
1391                 if(m_eEndian == LITTLEENDIAN)
1392                     currPixel = (R1<<16) + (G1<<8) + B1;
1393                 else if(m_eEndian == BIGENDIAN)
1394                     currPixel = (R1<<24) + (G1<<16) + (B1<<8);
1395
1396                 // Note we write this block out now and do not delay the writes for the postprocessing step since we do not track this block.
1397                 put4Pixel(row2Ptr, 0, currPixel);
1398                 put4Pixel(row3Ptr, 0, currPixel);
1399
1400                 m_pixel_filtered_flags[0][pixelNum] = m_pixel_filtered_flags[0][pixelNum] & ~e11s;
1401
1402                 m_pixel_filtered_flags[1][pixelNum] = m_pixel_filtered_flags[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.
1403             }
1404
1405             pixelNum += 1;
1406
1407             row1Ptr += eBufferedPixelWidthInBytes;
1408             row2Ptr += eBufferedPixelWidthInBytes;
1409             row3Ptr += eBufferedPixelWidthInBytes;
1410         }
1411         else // Do no vertical filtering here.
1412         {
1413             pixelNum += 1;
1414
1415             row1Ptr += eBufferedPixelWidthInBytes;
1416             row2Ptr += eBufferedPixelWidthInBytes;
1417             row3Ptr += eBufferedPixelWidthInBytes;
1418         }
1419     }
1420 }
1421
1422 #define NEWTEST true
1423
1424 inline bool ErnieFilter::NewDeltaE(int dr0, int dr1, int dg0, int dg1, int db0, int db1, int tolerance)
1425 {
1426     int Y0, Y1, dY, Cr0, Cr1, Cb0, Cb1, dCr, dCb;
1427
1428     // new Delta E stuff from Jay
1429
1430     Y0 = 5*dr0 + 9*dg0 + 2*db0;
1431     Y1 = 5*dr1 + 9*dg1 + 2*db1;
1432
1433     dY = ABS(Y0 - Y1) >> 4;
1434
1435     if(dY > tolerance) {
1436         return false;
1437     }
1438     else
1439     {
1440         Cr0 = (dr0 << 4) - Y0;
1441         Cr1 = (dr1 << 4) - Y1;
1442         dCr = ABS(Cr0 - Cr1) >> 5;
1443         if(dCr > tolerance)
1444         {
1445             return false;
1446         }
1447         else
1448         {
1449             Cb0 = (db0 << 4) - Y0;
1450             Cb1 = (db1 << 4) - Y1;
1451             dCb = ABS(Cb0 - Cb1) >> 6;
1452             if(dCb > tolerance)
1453             {
1454                 return false;
1455             }
1456         }
1457     }
1458     return true;
1459 }
1460
1461 ErnieFilter::ErnieFilter(int rowWidthInPixels, pixelTypes pixelType, unsigned int maxErrorForTwoPixels)
1462 {
1463     int index;
1464     m_input_bytes_per_pixel = 3;
1465     ASSERT(rowWidthInPixels > 0);
1466     ASSERT(pixelType == eBGRPixelData);
1467
1468     union
1469     {
1470         short    s;
1471         char     c[2];
1472     } uEndian;
1473     uEndian.s = 0x0A0B;
1474     m_eEndian = LITTLEENDIAN;
1475     if (uEndian.c[0] == 0x0A)
1476         m_eEndian = BIGENDIAN;
1477
1478     m_internal_bytes_per_pixel = 4;
1479
1480     m_pixel_offsets_index = 0;
1481     m_row_width_in_pixels = rowWidthInPixels;
1482     m_row_width_in_bytes = m_row_width_in_pixels*m_internal_bytes_per_pixel;
1483     m_max_error_for_two_pixels = maxErrorForTwoPixels;
1484
1485     for (index = 0; index < 4; index++)
1486     {
1487         m_row_bufs[index] = new uint32_t[rowWidthInPixels];
1488         ASSERT(m_row_bufs[index]);
1489
1490         m_row_ptrs[index] = new unsigned char[rowWidthInPixels*m_input_bytes_per_pixel];
1491         ASSERT(m_row_ptrs[index]);
1492
1493         m_black_row_ptrs[index] = new BYTE[rowWidthInPixels*m_input_bytes_per_pixel];
1494         ASSERT(m_black_row_ptrs[index]);
1495
1496         m_black_raster_sizes[index] = 0;
1497     }
1498
1499     for (index = 0; index < 2; index++)
1500     {
1501         m_pixel_filtered_flags[index] = new unsigned int[rowWidthInPixels];
1502         ASSERT(m_pixel_filtered_flags[index]);
1503     }
1504
1505     // The least compressible image will be all raw pixels. Maximum compressed size is:
1506     // full size + a bloat of Cmd byte + 1 VLI byte per 255 pixels rounded up to nearest integer.
1507
1508     int maxCompressionBufSize = m_row_width_in_bytes + 1 + ((int)ceil((double) MAX((rowWidthInPixels-2)/255, 0)));
1509
1510     m_compression_out_buf = new unsigned char[maxCompressionBufSize];
1511     ASSERT(m_compression_out_buf);
1512
1513     m_buffered_row_count = 0;
1514
1515     m_pixel_offsets[0] = 0;
1516     m_pixel_offsets[1] = 5;
1517     m_pixel_offsets[2] = 2;
1518     m_pixel_offsets[3] = 7;
1519     m_pixel_offsets[4] = 1;
1520     m_pixel_offsets[5] = 4;
1521     m_pixel_offsets[6] = 6;
1522     m_pixel_offsets[7] = 3;
1523
1524     m_row_index = 0;
1525 }
1526
1527
1528 ErnieFilter::~ErnieFilter()
1529 {
1530     // Deallocate memory next.
1531     int index;
1532
1533     for (index = 0; index < 4; index++)
1534     {
1535         delete [] m_row_bufs[index];
1536         delete [] m_row_ptrs[index];
1537         delete [] m_black_row_ptrs[index];
1538     }
1539
1540     for (index = 0; index < 2; index++)
1541     {
1542         delete [] m_pixel_filtered_flags[index];
1543     }
1544
1545     delete [] m_compression_out_buf;
1546 }
1547
1548 void ErnieFilter::writeBufferedRows()
1549 {
1550     int pixelIndex = 0;
1551
1552     // We just have one lonely raster left.  Nothing
1553     // we can do but filter it horizontally.
1554     if( 1 == m_buffered_row_count)
1555     {
1556
1557         int offset2 = m_pixel_offsets[m_pixel_offsets_index];
1558
1559         Filter1RawRow( (unsigned char*)(m_row_bufs[0] + offset2),
1560                        m_row_width_in_pixels - m_pixel_offsets[m_pixel_offsets_index],
1561                        m_pixel_filtered_flags[0] + m_pixel_offsets[m_pixel_offsets_index]);
1562
1563
1564         unsigned char *rowPtr = m_row_ptrs[0];
1565         ASSERT(rowPtr);
1566         pixelIndex = 0;
1567         do
1568         {
1569             memcpy(rowPtr, &m_row_bufs[0][pixelIndex], 3);
1570             rowPtr += 3;
1571         } while (++pixelIndex < m_row_width_in_pixels);
1572
1573     }
1574     // If we've got a pair of rasters in the buffer, that pair
1575     // has already been filtered somewhat.  So lets just write them
1576     // out, some filtering is better than none.
1577     else if (2 == m_buffered_row_count)
1578     {
1579         // Write the two rows back out.
1580         int k;
1581         for (k = 0; k < 2; k++)
1582         {
1583             unsigned char *rowPtr = m_row_ptrs[k];
1584             ASSERT(rowPtr);
1585             pixelIndex = 0;
1586             do
1587             {
1588                 memcpy(rowPtr, &m_row_bufs[k][pixelIndex], 3);
1589                 rowPtr += 3;
1590             } while (++pixelIndex < m_row_width_in_pixels);
1591         }
1592     }
1593     // Okay, if we had three rasters in the buffer, the pair
1594     // should have already been written out above, so lets
1595     // just run the odd raster through Ernie with to
1596     // get the horizontal filtering.  [Need to look to see
1597     // if there's something more we can do with filtering
1598     // all three together.]
1599     else if (3 == m_buffered_row_count)
1600     {
1601
1602         int offset2 = m_pixel_offsets[m_pixel_offsets_index];
1603
1604         Filter1RawRow( (unsigned char*)(m_row_bufs[2] + offset2),
1605                        m_row_width_in_pixels - m_pixel_offsets[m_pixel_offsets_index],
1606                        m_pixel_filtered_flags[1] + m_pixel_offsets[m_pixel_offsets_index]);
1607
1608
1609         Filter3FilteredRows( (unsigned char*)m_row_bufs[0],
1610                              (unsigned char*)m_row_bufs[1],
1611                              (unsigned char*)m_row_bufs[2]);
1612
1613         int k;
1614         for (k = 0; k < 3; k++)
1615         {
1616             unsigned char *rowPtr = m_row_ptrs[k];
1617             ASSERT(rowPtr);
1618             pixelIndex = 0;
1619             do
1620             {
1621                 memcpy(rowPtr, &m_row_bufs[k][pixelIndex], 3);
1622                 rowPtr += 3;
1623             } while (++pixelIndex < m_row_width_in_pixels);
1624         }
1625     }
1626 }
1627
1628 void ErnieFilter::submitRowToFilter(unsigned char *rowPtr)
1629 {
1630     memcpy(m_row_ptrs[m_buffered_row_count], rowPtr, m_row_width_in_pixels*m_input_bytes_per_pixel);
1631
1632     int pixelIndex = 0;
1633     uint32_t *RowPtrDest = m_row_bufs[m_buffered_row_count];
1634     BYTE byte1 = 0;
1635     BYTE byte2 = 0;
1636     BYTE byte3 = 0;
1637     do
1638     {
1639         byte1 = *rowPtr++;
1640         byte2 = *rowPtr++;
1641         byte3 = *rowPtr++;
1642         if(m_eEndian == LITTLEENDIAN)
1643             RowPtrDest[pixelIndex] = ((byte3 << 16) | (byte2 << 8) | (byte1)) & 0x00FFFFFF;
1644         else if(m_eEndian == BIGENDIAN)
1645             RowPtrDest[pixelIndex] = ((byte1 << 24) | (byte2 << 16) | (byte3 << 8)) & 0xFFFFFF00;
1646     } while (++pixelIndex < m_row_width_in_pixels);
1647
1648     m_buffered_row_count++;
1649
1650     iRastersReady=0;
1651     iRastersDelivered=0;
1652
1653     // Next see about filtering & compression.
1654     // NOTE 1: as an optimization only do subsections of the raster at a time to stay in cache.
1655     // NOTE 2: Could filter the pixels left of the offset.
1656     if (2 == m_buffered_row_count)
1657     {
1658         int offset2 = m_pixel_offsets[m_pixel_offsets_index];
1659
1660         Filter2RawRows( (unsigned char*)(m_row_bufs[1] + offset2),
1661                         (unsigned char*)(m_row_bufs[0] + offset2),
1662                         m_row_width_in_pixels - m_pixel_offsets[m_pixel_offsets_index],
1663                         m_pixel_filtered_flags[0] + m_pixel_offsets[m_pixel_offsets_index]);
1664     }
1665
1666     if (4 == m_buffered_row_count)
1667     {
1668         int offset4 = m_pixel_offsets[m_pixel_offsets_index];
1669         Filter2RawRows( (unsigned char*)(m_row_bufs[3] + offset4),
1670                         (unsigned char*)(m_row_bufs[2] + offset4),
1671                         m_row_width_in_pixels - m_pixel_offsets[m_pixel_offsets_index],
1672                         m_pixel_filtered_flags[1] + m_pixel_offsets[m_pixel_offsets_index]);
1673
1674         Filter2PairsOfFilteredRows( (unsigned char*)m_row_bufs[0],
1675                                     (unsigned char*)m_row_bufs[1],
1676                                     (unsigned char*)m_row_bufs[2],
1677                                     (unsigned char*)m_row_bufs[3]);
1678
1679 #if kMemWritesOptimize == 1
1680         // Writing the blocks out on a post processing step in this manner could leave the last 3 rows
1681         // unfiltered. This is a trade off we make for simplicity. The resulting loss in compression is small.
1682         WriteBlockPixels();
1683 #endif
1684
1685         m_pixel_offsets_index = (m_pixel_offsets_index + 1) % 8; // cycle the offset index.
1686
1687         int k;
1688         for (k = 0; k < m_pixel_offsets[m_pixel_offsets_index]; k++) // Clear out the flags that we're offsetting past for this next iteration.
1689         {
1690             m_pixel_filtered_flags[0][k] = eDone;
1691             m_pixel_filtered_flags[1][k] = eDone;
1692         }
1693
1694         // Write the four rows back out.
1695         for (k = 0; k < 4; k++)
1696         {
1697             unsigned char *rowPtr = m_row_ptrs[k];
1698             ASSERT(rowPtr);
1699             pixelIndex = 0;
1700             do
1701             {
1702                 memcpy(rowPtr, &m_row_bufs[k][pixelIndex], m_input_bytes_per_pixel);
1703                 rowPtr += m_input_bytes_per_pixel;
1704             } while (++pixelIndex < m_row_width_in_pixels);
1705         }
1706
1707         m_buffered_row_count = 0;
1708         iRastersReady = 4;
1709     }
1710 }
1711
1712 #if kMemWritesOptimize == 1
1713 /*
1714 At this point the color for the entire block is stored in the top left
1715 corner of the block. This routine takes that pixel and smears it into the
1716 rest of the block.
1717 */
1718 void ErnieFilter::WriteBlockPixels(void)
1719 {
1720     unsigned char *row1Ptr = (unsigned char*)m_row_bufs[0];
1721     unsigned char *row2Ptr = (unsigned char*)m_row_bufs[1];
1722     unsigned char *row3Ptr = (unsigned char*)m_row_bufs[2];
1723     unsigned char *row4Ptr = (unsigned char*)m_row_bufs[3];
1724
1725     for (int flagSet = 0; flagSet <= 1; flagSet++)
1726     {
1727         unsigned int *flagsPtr = m_pixel_filtered_flags[0];
1728         unsigned char *rowA = (unsigned char*)m_row_bufs[0];
1729         unsigned char *rowB = (unsigned char*)m_row_bufs[1];
1730
1731         if (flagSet == 1)
1732         {
1733             flagsPtr = m_pixel_filtered_flags[1];
1734             rowA = (unsigned char*)m_row_bufs[2];
1735             rowB = (unsigned char*)m_row_bufs[3];
1736         }
1737
1738         for (int rowIndex = 0; rowIndex < m_row_width_in_pixels;)
1739         {
1740             unsigned int currentFlags = flagsPtr[rowIndex];
1741
1742 #ifndef NDEBUG /* only done for debug builds */
1743             int numberOfBitsSet = 0;
1744             unsigned int currentFlagsCopy = currentFlags & eTopLeftOfBlocks;
1745             while (currentFlagsCopy)
1746             {
1747                 if (currentFlagsCopy & 1) numberOfBitsSet++;
1748                 currentFlagsCopy >>= 1;
1749             }
1750             ASSERT( (numberOfBitsSet <= 1) ||
1751                     ((numberOfBitsSet == 2) &&
1752                     (((currentFlags & eTopLeftOfBlocks) & ~(e21nw|e21sw|e41ni|e41si))==0)));
1753 #endif
1754
1755             if (currentFlags & eTopLeftOfBlocks) // Avoids doing a lot of checks if nothing is set.
1756             {
1757 //                unsigned int pixel;
1758                 uint32_t pixel;
1759                 //  The three possible scenerios are:
1760                 //  1: No top left of block bits are set.
1761                 //  2: 1 top left block bit is set.
1762                 //  3: 2 top left block bits are set. They are 21nw and 21sw.
1763
1764                 // Note: Due to possibly having two groups tracked by this flag we require the north checks to occur before the south checks.
1765                 if (currentFlags & e22w)
1766                 {
1767                     pixel = get4Pixel(rowA, rowIndex);
1768
1769                     put4Pixel(rowB, rowIndex, pixel);
1770                     rowIndex += 1;
1771                     put4Pixel(rowA, rowIndex, pixel);
1772                     put4Pixel(rowB, rowIndex, pixel);
1773
1774                     rowIndex += 1;
1775                     continue;
1776                 }
1777
1778                 if (currentFlags & e12)
1779                 {
1780                     put4Pixel(rowB, rowIndex, get4Pixel(rowA, rowIndex));
1781
1782                     rowIndex += 1;
1783                     continue;
1784                 }
1785
1786                 if (currentFlags & e42i)
1787                 {
1788                     pixel = get4Pixel(rowA, rowIndex);
1789
1790                     put4Pixel(rowB, rowIndex, pixel);
1791
1792                     rowIndex += 1;
1793                     put4Pixel(rowA, rowIndex, pixel);
1794                     put4Pixel(rowB, rowIndex, pixel);
1795
1796                     rowIndex += 1;
1797                     put4Pixel(rowB, rowIndex, pixel);
1798                     put4Pixel(rowA, rowIndex, pixel);
1799
1800                     rowIndex += 1;
1801                     put4Pixel(rowA, rowIndex, pixel);
1802                     put4Pixel(rowB, rowIndex, pixel);
1803
1804                     rowIndex += 1;
1805                     continue;
1806                 }
1807
1808                 if (currentFlags & e84ni)
1809                 {
1810                     pixel = get4Pixel(rowA, rowIndex);
1811
1812                     put4Pixel(row2Ptr, rowIndex, pixel);
1813                     put4Pixel(row3Ptr, rowIndex, pixel);
1814                     put4Pixel(row4Ptr, rowIndex, pixel);
1815
1816                     rowIndex += 1;
1817                     put4Pixel(row1Ptr, rowIndex, pixel);
1818                     put4Pixel(row2Ptr, rowIndex, pixel);
1819                     put4Pixel(row3Ptr, rowIndex, pixel);
1820                     put4Pixel(row4Ptr, rowIndex, pixel);
1821
1822                     rowIndex += 1;
1823                     put4Pixel(row1Ptr, rowIndex, pixel);
1824                     put4Pixel(row2Ptr, rowIndex, pixel);
1825                     put4Pixel(row3Ptr, rowIndex, pixel);
1826                     put4Pixel(row4Ptr, rowIndex, pixel);
1827
1828                     rowIndex += 1;
1829                     put4Pixel(row1Ptr, rowIndex, pixel);
1830                     put4Pixel(row2Ptr, rowIndex, pixel);
1831                     put4Pixel(row3Ptr, rowIndex, pixel);
1832                     put4Pixel(row4Ptr, rowIndex, pixel);
1833
1834                     rowIndex += 1;
1835                     put4Pixel(row1Ptr, rowIndex, pixel);
1836                     put4Pixel(row2Ptr, rowIndex, pixel);
1837                     put4Pixel(row3Ptr, rowIndex, pixel);
1838                     put4Pixel(row4Ptr, rowIndex, pixel);
1839
1840                     rowIndex += 1;
1841                     put4Pixel(row1Ptr, rowIndex, pixel);
1842                     put4Pixel(row2Ptr, rowIndex, pixel);
1843                     put4Pixel(row3Ptr, rowIndex, pixel);
1844                     put4Pixel(row4Ptr, rowIndex, pixel);
1845
1846                     rowIndex += 1;
1847                     put4Pixel(row1Ptr, rowIndex, pixel);
1848                     put4Pixel(row2Ptr, rowIndex, pixel);
1849                     put4Pixel(row3Ptr, rowIndex, pixel);
1850                     put4Pixel(row4Ptr, rowIndex, pixel);
1851
1852                     rowIndex += 1;
1853                     put4Pixel(row1Ptr, rowIndex, pixel);
1854                     put4Pixel(row2Ptr, rowIndex, pixel);
1855                     put4Pixel(row3Ptr, rowIndex, pixel);
1856                     put4Pixel(row4Ptr, rowIndex, pixel);
1857
1858                     rowIndex += 1;
1859
1860                     continue;
1861                 }
1862
1863                 if (currentFlags & e24nw)
1864                 {
1865                     pixel = get4Pixel(row1Ptr, rowIndex);
1866
1867                     put4Pixel(row2Ptr, rowIndex, pixel);
1868                     put4Pixel(row3Ptr, rowIndex, pixel);
1869                     put4Pixel(row4Ptr, rowIndex, pixel);
1870
1871                     rowIndex += 1;
1872                     put4Pixel(row1Ptr, rowIndex, pixel);
1873                     put4Pixel(row2Ptr, rowIndex, pixel);
1874                     put4Pixel(row3Ptr, rowIndex, pixel);
1875                     put4Pixel(row4Ptr, rowIndex, pixel);
1876
1877                     rowIndex += 1;
1878                     continue;
1879                 }
1880
1881                 if (currentFlags & e44ni)
1882                 {
1883                     pixel = get4Pixel(row1Ptr, rowIndex);
1884
1885                     put4Pixel(row2Ptr, rowIndex, pixel);
1886                     put4Pixel(row3Ptr, rowIndex, pixel);
1887                     put4Pixel(row4Ptr, rowIndex, pixel);
1888
1889                     rowIndex += 1;
1890                     put4Pixel(row1Ptr, rowIndex, pixel);
1891                     put4Pixel(row2Ptr, rowIndex, pixel);
1892                     put4Pixel(row3Ptr, rowIndex, pixel);
1893                     put4Pixel(row4Ptr, rowIndex, pixel);
1894
1895                     rowIndex += 1;
1896                     put4Pixel(row1Ptr, rowIndex, pixel);
1897                     put4Pixel(row2Ptr, rowIndex, pixel);
1898                     put4Pixel(row3Ptr, rowIndex, pixel);
1899                     put4Pixel(row4Ptr, rowIndex, pixel);
1900
1901                     rowIndex += 1;
1902                     put4Pixel(row1Ptr, rowIndex, pixel);
1903                     put4Pixel(row2Ptr, rowIndex, pixel);
1904                     put4Pixel(row3Ptr, rowIndex, pixel);
1905                     put4Pixel(row4Ptr, rowIndex, pixel);
1906
1907                     rowIndex += 1;
1908                     continue;
1909                 }
1910
1911                 if (currentFlags & e14n)
1912                 {
1913                     pixel = get4Pixel(row1Ptr, rowIndex);
1914
1915                     put4Pixel(row2Ptr, rowIndex, pixel);
1916                     put4Pixel(row3Ptr, rowIndex, pixel);
1917                     put4Pixel(row4Ptr, rowIndex, pixel);
1918
1919                     rowIndex += 1;
1920                     continue;
1921                 }
1922
1923                 if (currentFlags & e21nw)
1924                 {
1925                     put4Pixel(rowA, rowIndex+1, get4Pixel(rowA, rowIndex));
1926
1927                     if (!(currentFlags & (e21sw|e41si))) // if no south groups
1928                     {
1929                         rowIndex += 2;
1930                         continue;
1931                     }
1932                 }
1933
1934                 if (currentFlags & e41ni)
1935                 {
1936                     pixel = get4Pixel(rowA, rowIndex);
1937
1938                     put4Pixel(rowA, rowIndex+1, pixel);
1939                     put4Pixel(rowA, rowIndex+2, pixel);
1940                     put4Pixel(rowA, rowIndex+3, pixel);
1941
1942                     if (!(currentFlags & (e21sw|e41si))) // if no south groups.
1943                     {
1944                         rowIndex += 2;
1945                         continue;
1946                     }
1947                 }
1948
1949                 if (currentFlags & e21sw)
1950                 {
1951                     put4Pixel(rowB, rowIndex+1, get4Pixel(rowB, rowIndex));
1952
1953                     rowIndex += 2;
1954                     continue;
1955                 }
1956
1957                 if (currentFlags & e41si)
1958                 {
1959                     pixel = get4Pixel(rowB, rowIndex);
1960
1961                     put4Pixel(rowB, rowIndex+1, pixel);
1962                     put4Pixel(rowB, rowIndex+2, pixel);
1963                     put4Pixel(rowB, rowIndex+3, pixel);
1964
1965                     rowIndex += 2;
1966                     continue;
1967                 }
1968             }
1969             rowIndex += 1;
1970         }
1971     }
1972 }
1973
1974 #endif // kMemWritesOptimize
1975
1976 bool ErnieFilter::Process (RASTERDATA* ImageData)
1977 {
1978     if ( ImageData == NULL || 
1979          (ImageData->rasterdata[COLORTYPE_COLOR] == NULL && ImageData->rasterdata[COLORTYPE_BLACK] == NULL))
1980     {
1981         return false;
1982     }
1983     if (ImageData->rasterdata[COLORTYPE_BLACK])
1984     {
1985         memcpy(m_black_row_ptrs[m_row_index], ImageData->rasterdata[COLORTYPE_BLACK],
1986                (ImageData->rastersize[COLORTYPE_BLACK] + 7) / 8);
1987     }
1988     m_black_raster_sizes[m_row_index++] = ImageData->rastersize[COLORTYPE_BLACK];
1989
1990     if (m_row_index == 4)
1991         m_row_index = 0;
1992     if (ImageData->rasterdata[COLORTYPE_COLOR])
1993     {
1994         submitRowToFilter(ImageData->rasterdata[COLORTYPE_COLOR]);
1995
1996         // something ready after 4th time only
1997         return (m_buffered_row_count == 0);
1998     }
1999     iRastersReady = 1;
2000     return true;
2001 } //Process
2002
2003 bool ErnieFilter::NextOutputRaster(RASTERDATA& next_raster)
2004 {
2005     if (iRastersReady == 0){
2006         return false;
2007     }
2008
2009     next_raster.rastersize[COLORTYPE_COLOR] = m_row_width_in_pixels * m_input_bytes_per_pixel;
2010     next_raster.rasterdata[COLORTYPE_COLOR] = m_row_ptrs[iRastersDelivered];
2011     next_raster.rastersize[COLORTYPE_BLACK] = m_black_raster_sizes[iRastersDelivered];
2012     if ( m_black_raster_sizes[iRastersDelivered] > 0 ){
2013         next_raster.rasterdata[COLORTYPE_BLACK] = m_black_row_ptrs[iRastersDelivered];
2014     } else {
2015         next_raster.rasterdata[COLORTYPE_BLACK] = NULL;
2016     }
2017     iRastersReady--;
2018     iRastersDelivered++;
2019     if (iRastersDelivered == 4) iRastersDelivered = 0;
2020     return true;
2021 } //NextOutputRaster
2022
2023 unsigned int ErnieFilter::GetMaxOutputWidth()
2024 {
2025     return m_row_width_in_pixels * m_input_bytes_per_pixel;
2026 } //GetMaxOutputWidth
2027
2028 void ErnieFilter::Flush()
2029 {
2030     writeBufferedRows();
2031     iRastersDelivered=0;
2032     m_pixel_offsets_index = 0;
2033     iRastersReady = m_buffered_row_count;
2034     m_buffered_row_count = 0;
2035     m_row_index = 0;
2036 } //Flush
2037