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