
/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file
 *
 * For Intel x86 CPU and Microsoft Visual C++ compiler
 *
 * Last changed in libpng 1.2.6 - August 15, 2004
 * For conditions of distribution and use, see copyright notice in png.h
 * Copyright (c) 1998-2004 Glenn Randers-Pehrson
 * Copyright (c) 1998, Intel Corporation
 *
 * Contributed by Nirav Chhatrapati, Intel Corporation, 1998
 * Interface to libpng contributed by Gilles Vollant, 1999
 *
 *
 * In png_do_read_interlace() in libpng versions 1.0.3a through 1.0.4d,
 * a sign error in the post-MMX cleanup code for each pixel_depth resulted
 * in bad pixels at the beginning of some rows of some images, and also
 * (due to out-of-range memory reads and writes) caused heap corruption
 * when compiled with MSVC 6.0.  The error was fixed in version 1.0.4e.
 *
 * [png_read_filter_row_mmx_avg() bpp == 2 bugfix, GRR 20000916]
 *
 * [runtime MMX configuration, GRR 20010102]
 *
 */

#define PNG_INTERNAL
#include "png.h"

#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)

static int mmx_supported=2;


int PNGAPI
png_mmx_support(void)
{
  int mmx_supported_local = 0;
  _asm {
    push ebx          //CPUID will trash these
    push ecx
    push edx

    pushfd            //Save Eflag to stack
    pop eax           //Get Eflag from stack into eax
    mov ecx, eax      //Make another copy of Eflag in ecx
    xor eax, 0x200000 //Toggle ID bit in Eflag [i.e. bit(21)]
    push eax          //Save modified Eflag back to stack

    popfd             //Restored modified value back to Eflag reg
    pushfd            //Save Eflag to stack
    pop eax           //Get Eflag from stack
    push ecx          // save original Eflag to stack
    popfd             // restore original Eflag
    xor eax, ecx      //Compare the new Eflag with the original Eflag
    jz NOT_SUPPORTED  //If the same, CPUID instruction is not supported,
                      //skip following instructions and jump to
                      //NOT_SUPPORTED label

    xor eax, eax      //Set eax to zero

    _asm _emit 0x0f   //CPUID instruction  (two bytes opcode)
    _asm _emit 0xa2

    cmp eax, 1        //make sure eax return non-zero value
    jl NOT_SUPPORTED  //If eax is zero, mmx not supported

    xor eax, eax      //set eax to zero
    inc eax           //Now increment eax to 1.  This instruction is
                      //faster than the instruction "mov eax, 1"

    _asm _emit 0x0f   //CPUID instruction
    _asm _emit 0xa2

    and edx, 0x00800000  //mask out all bits but mmx bit(24)
    cmp edx, 0        // 0 = mmx not supported
    jz  NOT_SUPPORTED // non-zero = Yes, mmx IS supported

    mov  mmx_supported_local, 1  //set return value to 1

NOT_SUPPORTED:
    mov  eax, mmx_supported_local  //move return value to eax
    pop edx          //CPUID trashed these
    pop ecx
    pop ebx
  }

  //mmx_supported_local=0; // test code for force don't support MMX
  //printf("MMX : %u (1=MMX supported)\n",mmx_supported_local);

  mmx_supported = mmx_supported_local;
  return mmx_supported_local;
}

/* Combines the row recently read in with the previous row.
   This routine takes care of alpha and transparency if requested.
   This routine also handles the two methods of progressive display
   of interlaced images, depending on the mask value.
   The mask value describes which pixels are to be combined with
   the row.  The pattern always repeats every 8 pixels, so just 8
   bits are needed.  A one indicates the pixel is to be combined; a
   zero indicates the pixel is to be skipped.  This is in addition
   to any alpha or transparency value associated with the pixel.  If
   you want all pixels to be combined, pass 0xff (255) in mask.  */

/* Use this routine for x86 platform - uses faster MMX routine if machine
   supports MMX */

void /* PRIVATE */
png_combine_row(png_structp png_ptr, png_bytep row, int mask)
{
#ifdef PNG_USE_LOCAL_ARRAYS
   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
#endif

   png_debug(1,"in png_combine_row_asm\n");

   if (mmx_supported == 2) {
#if !defined(PNG_1_0_X)
       /* this should have happened in png_init_mmx_flags() already */
       png_warning(png_ptr, "asm_flags may not have been initialized");
#endif
       png_mmx_support();
   }

   if (mask == 0xff)
   {
      png_memcpy(row, png_ptr->row_buf + 1,
       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
       png_ptr->width));
   }
   /* GRR:  add "else if (mask == 0)" case?
    *       or does png_combine_row() not even get called in that case? */
   else
   {
      switch (png_ptr->row_info.pixel_depth)
      {
         case 1:
         {
            png_bytep sp;
            png_bytep dp;
            int s_inc, s_start, s_end;
            int m;
            int shift;
            png_uint_32 i;

            sp = png_ptr->row_buf + 1;
            dp = row;
            m = 0x80;
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (png_ptr->transformations & PNG_PACKSWAP)
            {
                s_start = 0;
                s_end = 7;
                s_inc = 1;
            }
            else
#endif
            {
                s_start = 7;
                s_end = 0;
                s_inc = -1;
            }

            shift = s_start;

            for (i = 0; i < png_ptr->width; i++)
            {
               if (m & mask)
               {
                  int value;

                  value = (*sp >> shift) & 0x1;
                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
                  *dp |= (png_byte)(value << shift);
               }

               if (shift == s_end)
               {
                  shift = s_start;
                  sp++;
                  dp++;
               }
               else
                  shift += s_inc;

               if (m == 1)
                  m = 0x80;
               else
                  m >>= 1;
            }
            break;
         }

         case 2:
         {
            png_bytep sp;
            png_bytep dp;
            int s_start, s_end, s_inc;
            int m;
            int shift;
            png_uint_32 i;
            int value;

            sp = png_ptr->row_buf + 1;
            dp = row;
            m = 0x80;
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (png_ptr->transformations & PNG_PACKSWAP)
            {
               s_start = 0;
               s_end = 6;
               s_inc = 2;
            }
            else
#endif
            {
               s_start = 6;
               s_end = 0;
               s_inc = -2;
            }

            shift = s_start;

            for (i = 0; i < png_ptr->width; i++)
            {
               if (m & mask)
               {
                  value = (*sp >> shift) & 0x3;
                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
                  *dp |= (png_byte)(value << shift);
               }

               if (shift == s_end)
               {
                  shift = s_start;
                  sp++;
                  dp++;
               }
               else
                  shift += s_inc;
               if (m == 1)
                  m = 0x80;
               else
                  m >>= 1;
            }
            break;
         }

         case 4:
         {
            png_bytep sp;
            png_bytep dp;
            int s_start, s_end, s_inc;
            int m;
            int shift;
            png_uint_32 i;
            int value;

            sp = png_ptr->row_buf + 1;
            dp = row;
            m = 0x80;
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (png_ptr->transformations & PNG_PACKSWAP)
            {
               s_start = 0;
               s_end = 4;
               s_inc = 4;
            }
            else
#endif
            {
               s_start = 4;
               s_end = 0;
               s_inc = -4;
            }
            shift = s_start;

            for (i = 0; i < png_ptr->width; i++)
            {
               if (m & mask)
               {
                  value = (*sp >> shift) & 0xf;
                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
                  *dp |= (png_byte)(value << shift);
               }

               if (shift == s_end)
               {
                  shift = s_start;
                  sp++;
                  dp++;
               }
               else
                  shift += s_inc;
               if (m == 1)
                  m = 0x80;
               else
                  m >>= 1;
            }
            break;
         }

         case 8:
         {
            png_bytep srcptr;
            png_bytep dstptr;
            png_uint_32 len;
            int m;
            int diff, unmask;

            __int64 mask0=0x0102040810204080;

#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               srcptr = png_ptr->row_buf + 1;
               dstptr = row;
               m = 0x80;
               unmask = ~mask;
               len  = png_ptr->width &~7;  //reduce to multiple of 8
               diff = png_ptr->width & 7;  //amount lost

               _asm
               {
                  movd       mm7, unmask   //load bit pattern
                  psubb      mm6,mm6       //zero mm6
                  punpcklbw  mm7,mm7
                  punpcklwd  mm7,mm7
                  punpckldq  mm7,mm7       //fill register with 8 masks

                  movq       mm0,mask0

                  pand       mm0,mm7       //nonzero if keep byte
                  pcmpeqb    mm0,mm6       //zeros->1s, v versa

                  mov        ecx,len       //load length of line (pixels)
                  mov        esi,srcptr    //load source
                  mov        ebx,dstptr    //load dest
                  cmp        ecx,0         //lcr
                  je         mainloop8end

mainloop8:
                  movq       mm4,[esi]
                  pand       mm4,mm0
                  movq       mm6,mm0
                  pandn      mm6,[ebx]
                  por        mm4,mm6
                  movq       [ebx],mm4

                  add        esi,8         //inc by 8 bytes processed
                  add        ebx,8
                  sub        ecx,8         //dec by 8 pixels processed

                  ja         mainloop8
mainloop8end:

                  mov        ecx,diff
                  cmp        ecx,0
                  jz         end8

                  mov        edx,mask
                  sal        edx,24        //make low byte the high byte

secondloop8:
                  sal        edx,1         //move high bit to CF
                  jnc        skip8         //if CF = 0
                  mov        al,[esi]
                  mov        [ebx],al
skip8:
                  inc        esi
                  inc        ebx

                  dec        ecx
                  jnz        secondloop8
end8:
                  emms
               }
            }
            else /* mmx not supported - use modified C routine */
            {
               register unsigned int incr1, initial_val, final_val;
               png_size_t pixel_bytes;
               png_uint_32 i;
               register int disp = png_pass_inc[png_ptr->pass];
               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};

               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
                  pixel_bytes;
               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
               final_val = png_ptr->width*pixel_bytes;
               incr1 = (disp)*pixel_bytes;
               for (i = initial_val; i < final_val; i += incr1)
               {
                  png_memcpy(dstptr, srcptr, pixel_bytes);
                  srcptr += incr1;
                  dstptr += incr1;
               }
            } /* end of else */

            break;
         }       // end 8 bpp

         case 16:
         {
            png_bytep srcptr;
            png_bytep dstptr;
            png_uint_32 len;
            int unmask, diff;
            __int64 mask1=0x0101020204040808,
                    mask0=0x1010202040408080;

#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               srcptr = png_ptr->row_buf + 1;
               dstptr = row;

               unmask = ~mask;
               len     = (png_ptr->width)&~7;
               diff = (png_ptr->width)&7;
               _asm
               {
                  movd       mm7, unmask       //load bit pattern
                  psubb      mm6,mm6           //zero mm6
                  punpcklbw  mm7,mm7
                  punpcklwd  mm7,mm7
                  punpckldq  mm7,mm7           //fill register with 8 masks

                  movq       mm0,mask0
                  movq       mm1,mask1

                  pand       mm0,mm7
                  pand       mm1,mm7

                  pcmpeqb    mm0,mm6
                  pcmpeqb    mm1,mm6

                  mov        ecx,len           //load length of line
                  mov        esi,srcptr        //load source
                  mov        ebx,dstptr        //load dest
                  cmp        ecx,0             //lcr
                  jz         mainloop16end

mainloop16:
                  movq       mm4,[esi]
                  pand       mm4,mm0
                  movq       mm6,mm0
                  movq       mm7,[ebx]
                  pandn      mm6,mm7
                  por        mm4,mm6
                  movq       [ebx],mm4

                  movq       mm5,[esi+8]
                  pand       mm5,mm1
                  movq       mm7,mm1
                  movq       mm6,[ebx+8]
                  pandn      mm7,mm6
                  por        mm5,mm7
                  movq       [ebx+8],mm5

                  add        esi,16            //inc by 16 bytes processed
                  add        ebx,16
                  sub        ecx,8             //dec by 8 pixels processed

                  ja         mainloop16

mainloop16end:
                  mov        ecx,diff
                  cmp        ecx,0
                  jz         end16

                  mov        edx,mask
                  sal        edx,24            //make low byte the high byte
secondloop16:
                  sal        edx,1             //move high bit to CF
                  jnc        skip16            //if CF = 0
                  mov        ax,[esi]
                  mov        [ebx],ax
skip16:
                  add        esi,2
                  add        ebx,2

                  dec        ecx
                  jnz        secondloop16
end16:
                  emms
               }
            }
            else /* mmx not supported - use modified C routine */
            {
               register unsigned int incr1, initial_val, final_val;
               png_size_t pixel_bytes;
               png_uint_32 i;
               register int disp = png_pass_inc[png_ptr->pass];
               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};

               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
                  pixel_bytes;
               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
               final_val = png_ptr->width*pixel_bytes;
               incr1 = (disp)*pixel_bytes;
               for (i = initial_val; i < final_val; i += incr1)
               {
                  png_memcpy(dstptr, srcptr, pixel_bytes);
                  srcptr += incr1;
                  dstptr += incr1;
               }
            } /* end of else */

            break;
         }       // end 16 bpp

         case 24:
         {
            png_bytep srcptr;
            png_bytep dstptr;
            png_uint_32 len;
            int unmask, diff;

            __int64 mask2=0x0101010202020404,  //24bpp
                    mask1=0x0408080810101020,
                    mask0=0x2020404040808080;

            srcptr = png_ptr->row_buf + 1;
            dstptr = row;

            unmask = ~mask;
            len     = (png_ptr->width)&~7;
            diff = (png_ptr->width)&7;

#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               _asm
               {
                  movd       mm7, unmask       //load bit pattern
                  psubb      mm6,mm6           //zero mm6
                  punpcklbw  mm7,mm7
                  punpcklwd  mm7,mm7
                  punpckldq  mm7,mm7           //fill register with 8 masks

                  movq       mm0,mask0
                  movq       mm1,mask1
                  movq       mm2,mask2

                  pand       mm0,mm7
                  pand       mm1,mm7
                  pand       mm2,mm7

                  pcmpeqb    mm0,mm6
                  pcmpeqb    mm1,mm6
                  pcmpeqb    mm2,mm6

                  mov        ecx,len           //load length of line
                  mov        esi,srcptr        //load source
                  mov        ebx,dstptr        //load dest
                  cmp        ecx,0
                  jz         mainloop24end

mainloop24:
                  movq       mm4,[esi]
                  pand       mm4,mm0
                  movq       mm6,mm0
                  movq       mm7,[ebx]
                  pandn      mm6,mm7
                  por        mm4,mm6
                  movq       [ebx],mm4


                  movq       mm5,[esi+8]
                  pand       mm5,mm1
                  movq       mm7,mm1
                  movq       mm6,[ebx+8]
                  pandn      mm7,mm6
                  por        mm5,mm7
                  movq       [ebx+8],mm5

                  movq       mm6,[esi+16]
                  pand       mm6,mm2
                  movq       mm4,mm2
                  movq       mm7,[ebx+16]
                  pandn      mm4,mm7
                  por        mm6,mm4
                  movq       [ebx+16],mm6

                  add        esi,24            //inc by 24 bytes processed
                  add        ebx,24
                  sub        ecx,8             //dec by 8 pixels processed

                  ja         mainloop24

mainloop24end:
                  mov        ecx,diff
                  cmp        ecx,0
                  jz         end24

                  mov        edx,mask
                  sal        edx,24            //make low byte the high byte
secondloop24:
                  sal        edx,1             //move high bit to CF
                  jnc        skip24            //if CF = 0
                  mov        ax,[esi]
                  mov        [ebx],ax
                  xor        eax,eax
                  mov        al,[esi+2]
                  mov        [ebx+2],al
skip24:
                  add        esi,3
                  add        ebx,3

                  dec        ecx
                  jnz        secondloop24

end24:
                  emms
               }
            }
            else /* mmx not supported - use modified C routine */
            {
               register unsigned int incr1, initial_val, final_val;
               png_size_t pixel_bytes;
               png_uint_32 i;
               register int disp = png_pass_inc[png_ptr->pass];
               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};

               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
                  pixel_bytes;
               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
               final_val = png_ptr->width*pixel_bytes;
               incr1 = (disp)*pixel_bytes;
               for (i = initial_val; i < final_val; i += incr1)
               {
                  png_memcpy(dstptr, srcptr, pixel_bytes);
                  srcptr += incr1;
                  dstptr += incr1;
               }
            } /* end of else */

            break;
         }       // end 24 bpp

         case 32:
         {
            png_bytep srcptr;
            png_bytep dstptr;
            png_uint_32 len;
            int unmask, diff;

            __int64 mask3=0x0101010102020202,  //32bpp
                    mask2=0x0404040408080808,
                    mask1=0x1010101020202020,
                    mask0=0x4040404080808080;

            srcptr = png_ptr->row_buf + 1;
            dstptr = row;

            unmask = ~mask;
            len     = (png_ptr->width)&~7;
            diff = (png_ptr->width)&7;

#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               _asm
               {
                  movd       mm7, unmask       //load bit pattern
                  psubb      mm6,mm6           //zero mm6
                  punpcklbw  mm7,mm7
                  punpcklwd  mm7,mm7
                  punpckldq  mm7,mm7           //fill register with 8 masks

                  movq       mm0,mask0
                  movq       mm1,mask1
                  movq       mm2,mask2
                  movq       mm3,mask3

                  pand       mm0,mm7
                  pand       mm1,mm7
                  pand       mm2,mm7
                  pand       mm3,mm7

                  pcmpeqb    mm0,mm6
                  pcmpeqb    mm1,mm6
                  pcmpeqb    mm2,mm6
                  pcmpeqb    mm3,mm6

                  mov        ecx,len           //load length of line
                  mov        esi,srcptr        //load source
                  mov        ebx,dstptr        //load dest

                  cmp        ecx,0             //lcr
                  jz         mainloop32end

mainloop32:
                  movq       mm4,[esi]
                  pand       mm4,mm0
                  movq       mm6,mm0
                  movq       mm7,[ebx]
                  pandn      mm6,mm7
                  por        mm4,mm6
                  movq       [ebx],mm4

                  movq       mm5,[esi+8]
                  pand       mm5,mm1
                  movq       mm7,mm1
                  movq       mm6,[ebx+8]
                  pandn      mm7,mm6
                  por        mm5,mm7
                  movq       [ebx+8],mm5

                  movq       mm6,[esi+16]
                  pand       mm6,mm2
                  movq       mm4,mm2
                  movq       mm7,[ebx+16]
                  pandn      mm4,mm7
                  por        mm6,mm4
                  movq       [ebx+16],mm6

                  movq       mm7,[esi+24]
                  pand       mm7,mm3
                  movq       mm5,mm3
                  movq       mm4,[ebx+24]
                  pandn      mm5,mm4
                  por        mm7,mm5
                  movq       [ebx+24],mm7

                  add        esi,32            //inc by 32 bytes processed
                  add        ebx,32
                  sub        ecx,8             //dec by 8 pixels processed

                  ja         mainloop32

mainloop32end:
                  mov        ecx,diff
                  cmp        ecx,0
                  jz         end32

                  mov        edx,mask
                  sal        edx,24            //make low byte the high byte
secondloop32:
                  sal        edx,1             //move high bit to CF
                  jnc        skip32            //if CF = 0
                  mov        eax,[esi]
                  mov        [ebx],eax
skip32:
                  add        esi,4
                  add        ebx,4

                  dec        ecx
                  jnz        secondloop32

end32:
                  emms
               }
            }
            else /* mmx _not supported - Use modified C routine */
            {
               register unsigned int incr1, initial_val, final_val;
               png_size_t pixel_bytes;
               png_uint_32 i;
               register int disp = png_pass_inc[png_ptr->pass];
               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};

               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
                  pixel_bytes;
               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
               final_val = png_ptr->width*pixel_bytes;
               incr1 = (disp)*pixel_bytes;
               for (i = initial_val; i < final_val; i += incr1)
               {
                  png_memcpy(dstptr, srcptr, pixel_bytes);
                  srcptr += incr1;
                  dstptr += incr1;
               }
            } /* end of else */

            break;
         }       // end 32 bpp

         case 48:
         {
            png_bytep srcptr;
            png_bytep dstptr;
            png_uint_32 len;
            int unmask, diff;

            __int64 mask5=0x0101010101010202,
                    mask4=0x0202020204040404,
                    mask3=0x0404080808080808,
                    mask2=0x1010101010102020,
                    mask1=0x2020202040404040,
                    mask0=0x4040808080808080;

#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               srcptr = png_ptr->row_buf + 1;
               dstptr = row;

               unmask = ~mask;
               len     = (png_ptr->width)&~7;
               diff = (png_ptr->width)&7;
               _asm
               {
                  movd       mm7, unmask       //load bit pattern
                  psubb      mm6,mm6           //zero mm6
                  punpcklbw  mm7,mm7
                  punpcklwd  mm7,mm7
                  punpckldq  mm7,mm7           //fill register with 8 masks

                  movq       mm0,mask0
                  movq       mm1,mask1
                  movq       mm2,mask2
                  movq       mm3,mask3
                  movq       mm4,mask4
                  movq       mm5,mask5

                  pand       mm0,mm7
                  pand       mm1,mm7
                  pand       mm2,mm7
                  pand       mm3,mm7
                  pand       mm4,mm7
                  pand       mm5,mm7

                  pcmpeqb    mm0,mm6
                  pcmpeqb    mm1,mm6
                  pcmpeqb    mm2,mm6
                  pcmpeqb    mm3,mm6
                  pcmpeqb    mm4,mm6
                  pcmpeqb    mm5,mm6

                  mov        ecx,len           //load length of line
                  mov        esi,srcptr        //load source
                  mov        ebx,dstptr        //load dest

                  cmp        ecx,0
                  jz         mainloop48end

mainloop48:
                  movq       mm7,[esi]
                  pand       mm7,mm0
                  movq       mm6,mm0
                  pandn      mm6,[ebx]
                  por        mm7,mm6
                  movq       [ebx],mm7

                  movq       mm6,[esi+8]
                  pand       mm6,mm1
                  movq       mm7,mm1
                  pandn      mm7,[ebx+8]
                  por        mm6,mm7
                  movq       [ebx+8],mm6

                  movq       mm6,[esi+16]
                  pand       mm6,mm2
                  movq       mm7,mm2
                  pandn      mm7,[ebx+16]
                  por        mm6,mm7
                  movq       [ebx+16],mm6

                  movq       mm7,[esi+24]
                  pand       mm7,mm3
                  movq       mm6,mm3
                  pandn      mm6,[ebx+24]
                  por        mm7,mm6
                  movq       [ebx+24],mm7

                  movq       mm6,[esi+32]
                  pand       mm6,mm4
                  movq       mm7,mm4
                  pandn      mm7,[ebx+32]
                  por        mm6,mm7
                  movq       [ebx+32],mm6

                  movq       mm7,[esi+40]
                  pand       mm7,mm5
                  movq       mm6,mm5
                  pandn      mm6,[ebx+40]
                  por        mm7,mm6
                  movq       [ebx+40],mm7

                  add        esi,48            //inc by 32 bytes processed
                  add        ebx,48
                  sub        ecx,8             //dec by 8 pixels processed

                  ja         mainloop48
mainloop48end:

                  mov        ecx,diff
                  cmp        ecx,0
                  jz         end48

                  mov        edx,mask
                  sal        edx,24            //make low byte the high byte

secondloop48:
                  sal        edx,1             //move high bit to CF
                  jnc        skip48            //if CF = 0
                  mov        eax,[esi]
                  mov        [ebx],eax
skip48:
                  add        esi,4
                  add        ebx,4

                  dec        ecx
                  jnz        secondloop48

end48:
                  emms
               }
            }
            else /* mmx _not supported - Use modified C routine */
            {
               register unsigned int incr1, initial_val, final_val;
               png_size_t pixel_bytes;
               png_uint_32 i;
               register int disp = png_pass_inc[png_ptr->pass];
               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};

               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
                  pixel_bytes;
               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
               final_val = png_ptr->width*pixel_bytes;
               incr1 = (disp)*pixel_bytes;
               for (i = initial_val; i < final_val; i += incr1)
               {
                  png_memcpy(dstptr, srcptr, pixel_bytes);
                  srcptr += incr1;
                  dstptr += incr1;
               }
            } /* end of else */

            break;
         }       // end 48 bpp

         default:
         {
            png_bytep sptr;
            png_bytep dp;
            png_size_t pixel_bytes;
            int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
            unsigned int i;
            register int disp = png_pass_inc[png_ptr->pass];  // get the offset
            register unsigned int incr1, initial_val, final_val;

            pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
            sptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
               pixel_bytes;
            dp = row + offset_table[png_ptr->pass]*pixel_bytes;
            initial_val = offset_table[png_ptr->pass]*pixel_bytes;
            final_val = png_ptr->width*pixel_bytes;
            incr1 = (disp)*pixel_bytes;
            for (i = initial_val; i < final_val; i += incr1)
            {
               png_memcpy(dp, sptr, pixel_bytes);
               sptr += incr1;
               dp += incr1;
            }
            break;
         }
      } /* end switch (png_ptr->row_info.pixel_depth) */
   } /* end if (non-trivial mask) */

} /* end png_combine_row() */


#if defined(PNG_READ_INTERLACING_SUPPORTED)

void /* PRIVATE */
png_do_read_interlace(png_structp png_ptr)
{
   png_row_infop row_info = &(png_ptr->row_info);
   png_bytep row = png_ptr->row_buf + 1;
   int pass = png_ptr->pass;
   png_uint_32 transformations = png_ptr->transformations;
#ifdef PNG_USE_LOCAL_ARRAYS
   const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
#endif

   png_debug(1,"in png_do_read_interlace\n");

   if (mmx_supported == 2) {
#if !defined(PNG_1_0_X)
       /* this should have happened in png_init_mmx_flags() already */
       png_warning(png_ptr, "asm_flags may not have been initialized");
#endif
       png_mmx_support();
   }

   if (row != NULL && row_info != NULL)
   {
      png_uint_32 final_width;

      final_width = row_info->width * png_pass_inc[pass];

      switch (row_info->pixel_depth)
      {
         case 1:
         {
            png_bytep sp, dp;
            int sshift, dshift;
            int s_start, s_end, s_inc;
            png_byte v;
            png_uint_32 i;
            int j;

            sp = row + (png_size_t)((row_info->width - 1) >> 3);
            dp = row + (png_size_t)((final_width - 1) >> 3);
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (transformations & PNG_PACKSWAP)
            {
               sshift = (int)((row_info->width + 7) & 7);
               dshift = (int)((final_width + 7) & 7);
               s_start = 7;
               s_end = 0;
               s_inc = -1;
            }
            else
#endif
            {
               sshift = 7 - (int)((row_info->width + 7) & 7);
               dshift = 7 - (int)((final_width + 7) & 7);
               s_start = 0;
               s_end = 7;
               s_inc = 1;
            }

            for (i = row_info->width; i; i--)
            {
               v = (png_byte)((*sp >> sshift) & 0x1);
               for (j = 0; j < png_pass_inc[pass]; j++)
               {
                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
                  *dp |= (png_byte)(v << dshift);
                  if (dshift == s_end)
                  {
                     dshift = s_start;
                     dp--;
                  }
                  else
                     dshift += s_inc;
               }
               if (sshift == s_end)
               {
                  sshift = s_start;
                  sp--;
               }
               else
                  sshift += s_inc;
            }
            break;
         }

         case 2:
         {
            png_bytep sp, dp;
            int sshift, dshift;
            int s_start, s_end, s_inc;
            png_uint_32 i;

            sp = row + (png_size_t)((row_info->width - 1) >> 2);
            dp = row + (png_size_t)((final_width - 1) >> 2);
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (transformations & PNG_PACKSWAP)
            {
               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
               s_start = 6;
               s_end = 0;
               s_inc = -2;
            }
            else
#endif
            {
               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
               s_start = 0;
               s_end = 6;
               s_inc = 2;
            }

            for (i = row_info->width; i; i--)
            {
               png_byte v;
               int j;

               v = (png_byte)((*sp >> sshift) & 0x3);
               for (j = 0; j < png_pass_inc[pass]; j++)
               {
                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
                  *dp |= (png_byte)(v << dshift);
                  if (dshift == s_end)
                  {
                     dshift = s_start;
                     dp--;
                  }
                  else
                     dshift += s_inc;
               }
               if (sshift == s_end)
               {
                  sshift = s_start;
                  sp--;
               }
               else
                  sshift += s_inc;
            }
            break;
         }

         case 4:
         {
            png_bytep sp, dp;
            int sshift, dshift;
            int s_start, s_end, s_inc;
            png_uint_32 i;

            sp = row + (png_size_t)((row_info->width - 1) >> 1);
            dp = row + (png_size_t)((final_width - 1) >> 1);
#if defined(PNG_READ_PACKSWAP_SUPPORTED)
            if (transformations & PNG_PACKSWAP)
            {
               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
               s_start = 4;
               s_end = 0;
               s_inc = -4;
            }
            else
#endif
            {
               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
               s_start = 0;
               s_end = 4;
               s_inc = 4;
            }

            for (i = row_info->width; i; i--)
            {
               png_byte v;
               int j;

               v = (png_byte)((*sp >> sshift) & 0xf);
               for (j = 0; j < png_pass_inc[pass]; j++)
               {
                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
                  *dp |= (png_byte)(v << dshift);
                  if (dshift == s_end)
                  {
                     dshift = s_start;
                     dp--;
                  }
                  else
                     dshift += s_inc;
               }
               if (sshift == s_end)
               {
                  sshift = s_start;
                  sp--;
               }
               else
                  sshift += s_inc;
            }
            break;
         }

         default:         // This is the place where the routine is modified
         {
            __int64 const4 = 0x0000000000FFFFFF;
            // __int64 const5 = 0x000000FFFFFF0000;  // unused...
            __int64 const6 = 0x00000000000000FF;
            png_bytep sptr, dp;
            png_uint_32 i;
            png_size_t pixel_bytes;
            int width = row_info->width;

            pixel_bytes = (row_info->pixel_depth >> 3);

            sptr = row + (width - 1) * pixel_bytes;
            dp = row + (final_width - 1) * pixel_bytes;
            // New code by Nirav Chhatrapati - Intel Corporation
            // sign fix by GRR
            // NOTE:  there is NO MMX code for 48-bit and 64-bit images

            // use MMX routine if machine supports it
#if !defined(PNG_1_0_X)
            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)
                /* && mmx_supported */ )
#else
            if (mmx_supported)
#endif
            {
               if (pixel_bytes == 3)
               {
                  if (((pass == 0) || (pass == 1)) && width)
                  {
                     _asm
                     {
                        mov esi, sptr
                        mov edi, dp
                        mov ecx, width
                        sub edi, 21   // (png_pass_inc[pass] - 1)*pixel_bytes
loop_pass0:
                        movd mm0, [esi]     ; X X X X X v2 v1 v0
                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
                        movq mm3, mm0       ; v2 v1 v0 v2 v1 v0 v2 v1
                        psllq mm0, 16       ; v0 v2 v1 v0 v2 v1 0 0
                        movq mm4, mm3       ; v2 v1 v0 v2 v1 v0 v2 v1
                        punpckhdq mm3, mm0  ; v0 v2 v1 v0 v2 v1 v0 v2
                        movq [edi+16] , mm4
                        psrlq mm0, 32       ; 0 0 0 0 v0 v2 v1 v0
                        movq [edi+8] , mm3
                        punpckldq mm0, mm4  ; v1 v0 v2 v1 v0 v2 v1 v0
                        sub esi, 3
                        movq [edi], mm0
                        sub edi, 24
                        //sub esi, 3
                        dec ecx
                        jnz loop_pass0
                        EMMS
                     }
                  }
                  else if (((pass == 2) || (pass == 3)) && width)
                  {
                     _asm
                     {
                        mov esi, sptr
                        mov edi, dp
                        mov ecx, width
                        sub edi, 9   // (png_pass_inc[pass] - 1)*pixel_bytes
loop_pass2:
                        movd mm0, [esi]     ; X X X X X v2 v1 v0
                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
                        movq [edi+4], mm0   ; move to memory
                        psrlq mm0, 16       ; 0 0 v2 v1 v0 v2 v1 v0
                        movd [edi], mm0     ; move to memory
                        sub esi, 3
                        sub edi, 12
                        dec ecx
                        jnz loop_pass2
                        EMMS
                     }
                  }
                  else if (width) /* && ((pass == 4) || (pass == 5)) */
                  {
                     int width_mmx = ((width >> 1) << 1) - 8;
                     if (width_mmx < 0)
                         width_mmx = 0;
                     width -= width_mmx;        // 8 or 9 pix, 24 or 27 bytes
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 3
                           sub edi, 9
loop_pass4:
                           movq mm0, [esi]     ; X X v2 v1 v0 v5 v4 v3
                           movq mm7, mm0       ; X X v2 v1 v0 v5 v4 v3
                           movq mm6, mm0       ; X X v2 v1 v0 v5 v4 v3
                           psllq mm0, 24       ; v1 v0 v5 v4 v3 0 0 0
                           pand mm7, const4    ; 0 0 0 0 0 v5 v4 v3
                           psrlq mm6, 24       ; 0 0 0 X X v2 v1 v0
                           por mm0, mm7        ; v1 v0 v5 v4 v3 v5 v4 v3
                           movq mm5, mm6       ; 0 0 0 X X v2 v1 v0
                           psllq mm6, 8        ; 0 0 X X v2 v1 v0 0
                           movq [edi], mm0     ; move quad to memory
                           psrlq mm5, 16       ; 0 0 0 0 0 X X v2
                           pand mm5, const6    ; 0 0 0 0 0 0 0 v2
                           por mm6, mm5        ; 0 0 X X v2 v1 v0 v2
                           movd [edi+8], mm6   ; move double to memory
                           sub esi, 6
                           sub edi, 12
                           sub ecx, 2
                           jnz loop_pass4
                           EMMS
                        }
                     }

                     sptr -= width_mmx*3;
                     dp -= width_mmx*6;
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;

                        png_memcpy(v, sptr, 3);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           png_memcpy(dp, v, 3);
                           dp -= 3;
                        }
                        sptr -= 3;
                     }
                  }
               } /* end of pixel_bytes == 3 */

               else if (pixel_bytes == 1)
               {
                  if (((pass == 0) || (pass == 1)) && width)
                  {
                     int width_mmx = ((width >> 2) << 2);
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub edi, 31
                           sub esi, 3
loop1_pass0:
                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
                           movq mm1, mm0       ; X X X X v0 v1 v2 v3
                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
                           movq mm2, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
                           movq mm3, mm0       ; v2 v2 v2 v2 v3 v3 v3 v3
                           punpckldq mm0, mm0  ; v3 v3 v3 v3 v3 v3 v3 v3
                           punpckhdq mm3, mm3  ; v2 v2 v2 v2 v2 v2 v2 v2
                           movq [edi], mm0     ; move to memory v3
                           punpckhwd mm2, mm2  ; v0 v0 v0 v0 v1 v1 v1 v1
                           movq [edi+8], mm3   ; move to memory v2
                           movq mm4, mm2       ; v0 v0 v0 v0 v1 v1 v1 v1
                           punpckldq mm2, mm2  ; v1 v1 v1 v1 v1 v1 v1 v1
                           punpckhdq mm4, mm4  ; v0 v0 v0 v0 v0 v0 v0 v0
                           movq [edi+16], mm2  ; move to memory v1
                           movq [edi+24], mm4  ; move to memory v0
                           sub esi, 4
                           sub edi, 32
                           sub ecx, 4
                           jnz loop1_pass0
                           EMMS
                        }
                     }

                     sptr -= width_mmx;
                     dp -= width_mmx*8;
                     for (i = width; i; i--)
                     {
                        int j;

                       /* I simplified this part in version 1.0.4e
                        * here and in several other instances where
                        * pixel_bytes == 1  -- GR-P
                        *
                        * Original code:
                        *
                        * png_byte v[8];
                        * png_memcpy(v, sptr, pixel_bytes);
                        * for (j = 0; j < png_pass_inc[pass]; j++)
                        * {
                        *    png_memcpy(dp, v, pixel_bytes);
                        *    dp -= pixel_bytes;
                        * }
                        * sptr -= pixel_bytes;
                        *
                        * Replacement code is in the next three lines:
                        */

                        for (j = 0; j < png_pass_inc[pass]; j++)
                           *dp-- = *sptr;
                        sptr--;
                     }
                  }
                  else if (((pass == 2) || (pass == 3)) && width)
                  {
                     int width_mmx = ((width >> 2) << 2);
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub edi, 15
                           sub esi, 3
loop1_pass2:
                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
                           movq mm1, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
                           punpckhwd mm1, mm1  ; v0 v0 v0 v0 v1 v1 v1 v1
                           movq [edi], mm0     ; move to memory v2 and v3
                           sub esi, 4
                           movq [edi+8], mm1   ; move to memory v1     and v0
                           sub edi, 16
                           sub ecx, 4
                           jnz loop1_pass2
                           EMMS
                        }
                     }

                     sptr -= width_mmx;
                     dp -= width_mmx*4;
                     for (i = width; i; i--)
                     {
                        int j;

                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           *dp-- = *sptr;
                        }
                        sptr --;
                     }
                  }
                  else if (width) /* && ((pass == 4) || (pass == 5))) */
                  {
                     int width_mmx = ((width >> 3) << 3);
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub edi, 15
                           sub esi, 7
loop1_pass4:
                           movq mm0, [esi]     ; v0 v1 v2 v3 v4 v5 v6 v7
                           movq mm1, mm0       ; v0 v1 v2 v3 v4 v5 v6 v7
                           punpcklbw mm0, mm0  ; v4 v4 v5 v5 v6 v6 v7 v7
                           //movq mm1, mm0     ; v0 v0 v1 v1 v2 v2 v3 v3
                           punpckhbw mm1, mm1  ;v0 v0 v1 v1 v2 v2 v3 v3
                           movq [edi+8], mm1   ; move to memory v0 v1 v2 and v3
                           sub esi, 8
                           movq [edi], mm0     ; move to memory v4 v5 v6 and v7
                           //sub esi, 4
                           sub edi, 16
                           sub ecx, 8
                           jnz loop1_pass4
                           EMMS
                        }
                     }

                     sptr -= width_mmx;
                     dp -= width_mmx*2;
                     for (i = width; i; i--)
                     {
                        int j;

                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           *dp-- = *sptr;
                        }
                        sptr --;
                     }
                  }
               } /* end of pixel_bytes == 1 */

               else if (pixel_bytes == 2)
               {
                  if (((pass == 0) || (pass == 1)) && width)
                  {
                     int width_mmx = ((width >> 1) << 1);
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 2
                           sub edi, 30
loop2_pass0:
                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
                           movq [edi], mm0
                           movq [edi + 8], mm0
                           movq [edi + 16], mm1
                           movq [edi + 24], mm1
                           sub esi, 4
                           sub edi, 32
                           sub ecx, 2
                           jnz loop2_pass0
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*2 - 2);            // sign fixed
                     dp -= (width_mmx*16 - 2);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 2;
                        png_memcpy(v, sptr, 2);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 2;
                           png_memcpy(dp, v, 2);
                        }
                     }
                  }
                  else if (((pass == 2) || (pass == 3)) && width)
                  {
                     int width_mmx = ((width >> 1) << 1) ;
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 2
                           sub edi, 14
loop2_pass2:
                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
                           movq [edi], mm0
                           sub esi, 4
                           movq [edi + 8], mm1
                           //sub esi, 4
                           sub edi, 16
                           sub ecx, 2
                           jnz loop2_pass2
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*2 - 2);            // sign fixed
                     dp -= (width_mmx*8 - 2);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 2;
                        png_memcpy(v, sptr, 2);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 2;
                           png_memcpy(dp, v, 2);
                        }
                     }
                  }
                  else if (width)  // pass == 4 or 5
                  {
                     int width_mmx = ((width >> 1) << 1) ;
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 2
                           sub edi, 6
loop2_pass4:
                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
                           sub esi, 4
                           movq [edi], mm0
                           sub edi, 8
                           sub ecx, 2
                           jnz loop2_pass4
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*2 - 2);            // sign fixed
                     dp -= (width_mmx*4 - 2);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 2;
                        png_memcpy(v, sptr, 2);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 2;
                           png_memcpy(dp, v, 2);
                        }
                     }
                  }
               } /* end of pixel_bytes == 2 */

               else if (pixel_bytes == 4)
               {
                  if (((pass == 0) || (pass == 1)) && width)
                  {
                     int width_mmx = ((width >> 1) << 1) ;
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 4
                           sub edi, 60
loop4_pass0:
                           movq mm0, [esi]        ; v3 v2 v1 v0 v7 v6 v5 v4
                           movq mm1, mm0          ; v3 v2 v1 v0 v7 v6 v5 v4
                           punpckldq mm0, mm0     ; v7 v6 v5 v4 v7 v6 v5 v4
                           punpckhdq mm1, mm1     ; v3 v2 v1 v0 v3 v2 v1 v0
                           movq [edi], mm0
                           movq [edi + 8], mm0
                           movq [edi + 16], mm0
                           movq [edi + 24], mm0
                           movq [edi+32], mm1
                           movq [edi + 40], mm1
                           movq [edi+ 48], mm1
                           sub esi, 8
                           movq [edi + 56], mm1
                           sub edi, 64
                           sub ecx, 2
                           jnz loop4_pass0
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*4 - 4);            // sign fixed
                     dp -= (width_mmx*32 - 4);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 4;
                        png_memcpy(v, sptr, 4);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 4;
                           png_memcpy(dp, v, 4);
                        }
                     }
                  }
                  else if (((pass == 2) || (pass == 3)) && width)
                  {
                     int width_mmx = ((width >> 1) << 1) ;
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 4
                           sub edi, 28
loop4_pass2:
                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
                           movq [edi], mm0
                           movq [edi + 8], mm0
                           movq [edi+16], mm1
                           movq [edi + 24], mm1
                           sub esi, 8
                           sub edi, 32
                           sub ecx, 2
                           jnz loop4_pass2
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*4 - 4);            // sign fixed
                     dp -= (width_mmx*16 - 4);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 4;
                        png_memcpy(v, sptr, 4);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 4;
                           png_memcpy(dp, v, 4);
                        }
                     }
                  }
                  else if (width)  // pass == 4 or 5
                  {
                     int width_mmx = ((width >> 1) << 1) ;
                     width -= width_mmx;
                     if (width_mmx)
                     {
                        _asm
                        {
                           mov esi, sptr
                           mov edi, dp
                           mov ecx, width_mmx
                           sub esi, 4
                           sub edi, 12
loop4_pass4:
                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
                           movq [edi], mm0
                           sub esi, 8
                           movq [edi + 8], mm1
                           sub edi, 16
                           sub ecx, 2
                           jnz loop4_pass4
                           EMMS
                        }
                     }

                     sptr -= (width_mmx*4 - 4);          // sign fixed
                     dp -= (width_mmx*8 - 4);            // sign fixed
                     for (i = width; i; i--)
                     {
                        png_byte v[8];
                        int j;
                        sptr -= 4;
                        png_memcpy(v, sptr, 4);
                        for (j = 0; j < png_pass_inc[pass]; j++)
                        {
                           dp -= 4;
                           png_memcpy(dp, v, 4);
                        }
                     }
                  }

               } /* end of pixel_bytes == 4 */

               else if (pixel_bytes == 6)
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, 6);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, 6);
                        dp -= 6;
                     }
                     sptr -= 6;
                  }
               } /* end of pixel_bytes == 6 */

               else
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr-= pixel_bytes;
                  }
               }
            } /* end of mmx_supported */

            else /* MMX not supported:  use modified C code - takes advantage
                  * of inlining of memcpy for a constant */
            {
               if (pixel_bytes == 1)
               {
                  for (i = width; i; i--)
                  {
                     int j;
                     for (j = 0; j < png_pass_inc[pass]; j++)
                        *dp-- = *sptr;
                     sptr--;
                  }
               }
               else if (pixel_bytes == 3)
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr -= pixel_bytes;
                  }
               }
               else if (pixel_bytes == 2)
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr -= pixel_bytes;
                  }
               }
               else if (pixel_bytes == 4)
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr -= pixel_bytes;
                  }
               }
               else if (pixel_bytes == 6)
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr -= pixel_bytes;
                  }
               }
               else
               {
                  for (i = width; i; i--)
                  {
                     png_byte v[8];
                     int j;
                     png_memcpy(v, sptr, pixel_bytes);
                     for (j = 0; j < png_pass_inc[pass]; j++)
                     {
                        png_memcpy(dp, v, pixel_bytes);
                        dp -= pixel_bytes;
                     }
                     sptr -= pixel_bytes;
                  }
               }

            } /* end of MMX not supported */
            break;
         }
      } /* end switch (row_info->pixel_depth) */

      row_info->width = final_width;

      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
   }

}

#endif /* PNG_READ_INTERLACING_SUPPORTED */


// These variables are utilized in the functions below.  They are declared
// globally here to ensure alignment on 8-byte boundaries.

union uAll {
   __int64 use;
   double  align;
} LBCarryMask = {0x0101010101010101},
  HBClearMask = {0x7f7f7f7f7f7f7f7f},
  ActiveMask, ActiveMask2, ActiveMaskEnd, ShiftBpp, ShiftRem;


// Optimized code for PNG Average filter decoder
void /* PRIVATE */
png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row
                            , png_bytep prev_row)
{
   int bpp;
   png_uint_32 FullLength;
   png_uint_32 MMXLength;
   //png_uint_32 len;
   int diff;

   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
   FullLength  = row_info->rowbytes; // # of bytes to filter
   _asm {
         // Init address pointers and offset
         mov edi, row          // edi ==> Avg(x)
         xor ebx, ebx          // ebx ==> x
         mov edx, edi
         mov esi, prev_row           // esi ==> Prior(x)
         sub edx, bpp          // edx ==> Raw(x-bpp)

         xor eax, eax
         // Compute the Raw value for the first bpp bytes
         //    Raw(x) = Avg(x) + (Prior(x)/2)
davgrlp:
         mov al, [esi + ebx]   // Load al with Prior(x)
         inc ebx
         shr al, 1             // divide by 2
         add al, [edi+ebx-1]   // Add Avg(x); -1 to offset inc ebx
         cmp ebx, bpp
         mov [edi+ebx-1], al    // Write back Raw(x);
                            // mov does not affect flags; -1 to offset inc ebx
         jb davgrlp
         // get # of bytes to alignment
         mov diff, edi         // take start of row
         add diff, ebx         // add bpp
         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
         and diff, 0xfffffff8  // mask to alignment boundary
         sub diff, edi         // subtract from start ==> value ebx at alignment
         jz davggo
         // fix alignment
         // Compute the Raw value for the bytes upto the alignment boundary
         //    Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
         xor ecx, ecx
davglp1:
         xor eax, eax
         mov cl, [esi + ebx]        // load cl with Prior(x)
         mov al, [edx + ebx]  // load al with Raw(x-bpp)
         add ax, cx
         inc ebx
         shr ax, 1            // divide by 2
         add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
         cmp ebx, diff              // Check if at alignment boundary
         mov [edi+ebx-1], al        // Write back Raw(x);
                            // mov does not affect flags; -1 to offset inc ebx
         jb davglp1               // Repeat until at alignment boundary
davggo:
         mov eax, FullLength
         mov ecx, eax
         sub eax, ebx          // subtract alignment fix
         and eax, 0x00000007   // calc bytes over mult of 8
         sub ecx, eax          // drop over bytes from original length
         mov MMXLength, ecx
   } // end _asm block
   // Now do the math for the rest of the row
   switch ( bpp )
   {
      case 3:
      {
         ActiveMask.use  = 0x0000000000ffffff;
         ShiftBpp.use = 24;    // == 3 * 8
         ShiftRem.use = 40;    // == 64 - 24
         _asm {
            // Re-init address pointers and offset
            movq mm7, ActiveMask
            mov ebx, diff      // ebx ==> x = offset to alignment boundary
            movq mm5, LBCarryMask
            mov edi, row       // edi ==> Avg(x)
            movq mm4, HBClearMask
            mov esi, prev_row        // esi ==> Prior(x)
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
                               // (we correct position in loop below)
davg3lp:
            movq mm0, [edi + ebx]      // Load mm0 with Avg(x)
            // Add (Prev_row/2) to Average
            movq mm3, mm5
            psrlq mm2, ShiftRem      // Correct position Raw(x-bpp) data
            movq mm1, [esi + ebx]    // Load mm1 with Prior(x)
            movq mm6, mm7
            pand mm3, mm1      // get lsb for each prev_row byte
            psrlq mm1, 1       // divide prev_row bytes by 2
            pand  mm1, mm4     // clear invalid bit 7 of each byte
            paddb mm0, mm1     // add (Prev_row/2) to Avg for each byte
            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
            movq mm1, mm3      // now use mm1 for getting LBCarrys
            pand mm1, mm2      // get LBCarrys for each byte where both
                               // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1       // divide raw bytes by 2
            pand  mm2, mm4     // clear invalid bit 7 of each byte
            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6      // Leave only Active Group 1 bytes to add to Avg
            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
                               //  byte
            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 3-5
            movq mm2, mm0        // mov updated Raws to mm2
            psllq mm2, ShiftBpp  // shift data to position correctly
            movq mm1, mm3        // now use mm1 for getting LBCarrys
            pand mm1, mm2      // get LBCarrys for each byte where both
                               // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1       // divide raw bytes by 2
            pand  mm2, mm4     // clear invalid bit 7 of each byte
            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6      // Leave only Active Group 2 bytes to add to Avg
            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
                               //  byte

            // Add 3rd active group (Raw(x-bpp)/2) to Average with LBCarry
            psllq mm6, ShiftBpp  // shift the mm6 mask to cover the last two
                                 // bytes
            movq mm2, mm0        // mov updated Raws to mm2
            psllq mm2, ShiftBpp  // shift data to position correctly
                              // Data only needs to be shifted once here to
                              // get the correct x-bpp offset.
            movq mm1, mm3     // now use mm1 for getting LBCarrys
            pand mm1, mm2     // get LBCarrys for each byte where both
                              // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1      // divide raw bytes by 2
            pand  mm2, mm4    // clear invalid bit 7 of each byte
            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
            add ebx, 8
            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
                              // byte

            // Now ready to write back to memory
            movq [edi + ebx - 8], mm0
            // Move updated Raw(x) to use as Raw(x-bpp) for next loop
            cmp ebx, MMXLength
            movq mm2, mm0     // mov updated Raw(x) to mm2
            jb davg3lp
         } // end _asm block
      }
      break;

      case 6:
      case 4:
      case 7:
      case 5:
      {
         ActiveMask.use  = 0xffffffffffffffff;  // use shift below to clear
                                                // appropriate inactive bytes
         ShiftBpp.use = bpp << 3;
         ShiftRem.use = 64 - ShiftBpp.use;
         _asm {
            movq mm4, HBClearMask
            // Re-init address pointers and offset
            mov ebx, diff       // ebx ==> x = offset to alignment boundary
            // Load ActiveMask and clear all bytes except for 1st active group
            movq mm7, ActiveMask
            mov edi, row         // edi ==> Avg(x)
            psrlq mm7, ShiftRem
            mov esi, prev_row    // esi ==> Prior(x)
            movq mm6, mm7
            movq mm5, LBCarryMask
            psllq mm6, ShiftBpp  // Create mask for 2nd active group
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
                                 // (we correct position in loop below)
davg4lp:
            movq mm0, [edi + ebx]
            psrlq mm2, ShiftRem  // shift data to position correctly
            movq mm1, [esi + ebx]
            // Add (Prev_row/2) to Average
            movq mm3, mm5
            pand mm3, mm1     // get lsb for each prev_row byte
            psrlq mm1, 1      // divide prev_row bytes by 2
            pand  mm1, mm4    // clear invalid bit 7 of each byte
            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
            movq mm1, mm3     // now use mm1 for getting LBCarrys
            pand mm1, mm2     // get LBCarrys for each byte where both
                              // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1      // divide raw bytes by 2
            pand  mm2, mm4    // clear invalid bit 7 of each byte
            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm7     // Leave only Active Group 1 bytes to add to Avg
            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
                              // byte
            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
            movq mm2, mm0     // mov updated Raws to mm2
            psllq mm2, ShiftBpp // shift data to position correctly
            add ebx, 8
            movq mm1, mm3     // now use mm1 for getting LBCarrys
            pand mm1, mm2     // get LBCarrys for each byte where both
                              // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1      // divide raw bytes by 2
            pand  mm2, mm4    // clear invalid bit 7 of each byte
            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
                              // byte
            cmp ebx, MMXLength
            // Now ready to write back to memory
            movq [edi + ebx - 8], mm0
            // Prep Raw(x-bpp) for next loop
            movq mm2, mm0     // mov updated Raws to mm2
            jb davg4lp
         } // end _asm block
      }
      break;
      case 2:
      {
         ActiveMask.use  = 0x000000000000ffff;
         ShiftBpp.use = 16;   // == 2 * 8     [BUGFIX]
         ShiftRem.use = 48;   // == 64 - 16   [BUGFIX]
         _asm {
            // Load ActiveMask
            movq mm7, ActiveMask
            // Re-init address pointers and offset
            mov ebx, diff     // ebx ==> x = offset to alignment boundary
            movq mm5, LBCarryMask
            mov edi, row      // edi ==> Avg(x)
            movq mm4, HBClearMask
            mov esi, prev_row  // esi ==> Prior(x)
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
                              // (we correct position in loop below)
davg2lp:
            movq mm0, [edi + ebx]
            psrlq mm2, ShiftRem  // shift data to position correctly   [BUGFIX]
            movq mm1, [esi + ebx]
            // Add (Prev_row/2) to Average
            movq mm3, mm5
            pand mm3, mm1     // get lsb for each prev_row byte
            psrlq mm1, 1      // divide prev_row bytes by 2
            pand  mm1, mm4    // clear invalid bit 7 of each byte
            movq mm6, mm7
            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
            movq mm1, mm3     // now use mm1 for getting LBCarrys
            pand mm1, mm2     // get LBCarrys for each byte where both
                              // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1      // divide raw bytes by 2
            pand  mm2, mm4    // clear invalid bit 7 of each byte
            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6     // Leave only Active Group 1 bytes to add to Avg
            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 2 & 3
            movq mm2, mm0       // mov updated Raws to mm2
            psllq mm2, ShiftBpp // shift data to position correctly
            movq mm1, mm3       // now use mm1 for getting LBCarrys
            pand mm1, mm2       // get LBCarrys for each byte where both
                                // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1        // divide raw bytes by 2
            pand  mm2, mm4      // clear invalid bit 7 of each byte
            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte

            // Add rdd active group (Raw(x-bpp)/2) to Average with LBCarry
            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 4 & 5
            movq mm2, mm0       // mov updated Raws to mm2
            psllq mm2, ShiftBpp // shift data to position correctly
                                // Data only needs to be shifted once here to
                                // get the correct x-bpp offset.
            movq mm1, mm3       // now use mm1 for getting LBCarrys
            pand mm1, mm2       // get LBCarrys for each byte where both
                                // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1        // divide raw bytes by 2
            pand  mm2, mm4      // clear invalid bit 7 of each byte
            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte

            // Add 4th active group (Raw(x-bpp)/2) to Average with LBCarry
            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 6 & 7
            movq mm2, mm0        // mov updated Raws to mm2
            psllq mm2, ShiftBpp  // shift data to position correctly
                                 // Data only needs to be shifted once here to
                                 // get the correct x-bpp offset.
            add ebx, 8
            movq mm1, mm3    // now use mm1 for getting LBCarrys
            pand mm1, mm2    // get LBCarrys for each byte where both
                             // lsb's were == 1 (Only valid for active group)
            psrlq mm2, 1     // divide raw bytes by 2
            pand  mm2, mm4   // clear invalid bit 7 of each byte
            paddb mm2, mm1   // add LBCarrys to (Raw(x-bpp)/2) for each byte
            pand mm2, mm6    // Leave only Active Group 2 bytes to add to Avg
            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte

            cmp ebx, MMXLength
            // Now ready to write back to memory
            movq [edi + ebx - 8], mm0
            // Prep Raw(x-bpp) for next loop
            movq mm2, mm0    // mov updated Raws to mm2
            jb davg2lp
        } // end _asm block
      }
      break;

      case 1:                 // bpp == 1
      {
         _asm {
            // Re-init address pointers and offset
            mov ebx, diff     // ebx ==> x = offset to alignment boundary
            mov edi, row      // edi ==> Avg(x)
            cmp ebx, FullLength  // Test if offset at end of array
            jnb davg1end
            // Do Paeth decode for remaining bytes
            mov esi, prev_row    // esi ==> Prior(x)
            mov edx, edi
            xor ecx, ecx         // zero ecx before using cl & cx in loop below
            sub edx, bpp         // edx ==> Raw(x-bpp)
davg1lp:
            // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
            xor eax, eax
            mov cl, [esi + ebx]  // load cl with Prior(x)
            mov al, [edx + ebx]  // load al with Raw(x-bpp)
            add ax, cx
            inc ebx
            shr ax, 1            // divide by 2
            add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
            cmp ebx, FullLength  // Check if at end of array
            mov [edi+ebx-1], al  // Write back Raw(x);
                         // mov does not affect flags; -1 to offset inc ebx
            jb davg1lp
davg1end:
         } // end _asm block
      }
      return;

      case 8:             // bpp == 8
      {
         _asm {
            // Re-init address pointers and offset
            mov ebx, diff           // ebx ==> x = offset to alignment boundary
            movq mm5, LBCarryMask
            mov edi, row            // edi ==> Avg(x)
            movq mm4, HBClearMask
            mov esi, prev_row       // esi ==> Prior(x)
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
                                // (NO NEED to correct position in loop below)
davg8lp:
            movq mm0, [edi + ebx]
            movq mm3, mm5
            movq mm1, [esi + ebx]
            add ebx, 8
            pand mm3, mm1       // get lsb for each prev_row byte
            psrlq mm1, 1        // divide prev_row bytes by 2
            pand mm3, mm2       // get LBCarrys for each byte where both
                                // lsb's were == 1
            psrlq mm2, 1        // divide raw bytes by 2
            pand  mm1, mm4      // clear invalid bit 7 of each byte
            paddb mm0, mm3      // add LBCarrys to Avg for each byte
            pand  mm2, mm4      // clear invalid bit 7 of each byte
            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
            cmp ebx, MMXLength
            movq [edi + ebx - 8], mm0
            movq mm2, mm0       // reuse as Raw(x-bpp)
            jb davg8lp
        } // end _asm block
      }
      break;
      default:                  // bpp greater than 8
      {
        _asm {
            movq mm5, LBCarryMask
            // Re-init address pointers and offset
            mov ebx, diff       // ebx ==> x = offset to alignment boundary
            mov edi, row        // edi ==> Avg(x)
            movq mm4, HBClearMask
            mov edx, edi
            mov esi, prev_row   // esi ==> Prior(x)
            sub edx, bpp        // edx ==> Raw(x-bpp)
davgAlp:
            movq mm0, [edi + ebx]
            movq mm3, mm5
            movq mm1, [esi + ebx]
            pand mm3, mm1       // get lsb for each prev_row byte
            movq mm2, [edx + ebx]
            psrlq mm1, 1        // divide prev_row bytes by 2
            pand mm3, mm2       // get LBCarrys for each byte where both
                                // lsb's were == 1
            psrlq mm2, 1        // divide raw bytes by 2
            pand  mm1, mm4      // clear invalid bit 7 of each byte
            paddb mm0, mm3      // add LBCarrys to Avg for each byte
            pand  mm2, mm4      // clear invalid bit 7 of each byte
            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
            add ebx, 8
            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
            cmp ebx, MMXLength
            movq [edi + ebx - 8], mm0
            jb davgAlp
        } // end _asm block
      }
      break;
   }                         // end switch ( bpp )

   _asm {
         // MMX acceleration complete now do clean-up
         // Check if any remaining bytes left to decode
         mov ebx, MMXLength    // ebx ==> x = offset bytes remaining after MMX
         mov edi, row          // edi ==> Avg(x)
         cmp ebx, FullLength   // Test if offset at end of array
         jnb davgend
         // Do Paeth decode for remaining bytes
         mov esi, prev_row     // esi ==> Prior(x)
         mov edx, edi
         xor ecx, ecx          // zero ecx before using cl & cx in loop below
         sub edx, bpp          // edx ==> Raw(x-bpp)
davglp2:
         // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
         xor eax, eax
         mov cl, [esi + ebx]   // load cl with Prior(x)
         mov al, [edx + ebx]   // load al with Raw(x-bpp)
         add ax, cx
         inc ebx
         shr ax, 1              // divide by 2
         add al, [edi+ebx-1]    // Add Avg(x); -1 to offset inc ebx
         cmp ebx, FullLength    // Check if at end of array
         mov [edi+ebx-1], al    // Write back Raw(x);
                          // mov does not affect flags; -1 to offset inc ebx
         jb davglp2
davgend:
         emms             // End MMX instructions; prep for possible FP instrs.
   } // end _asm block
}

// Optimized code for PNG Paeth filter decoder
void /* PRIVATE */
png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row,
                              png_bytep prev_row)
{
   png_uint_32 FullLength;
   png_uint_32 MMXLength;
   //png_uint_32 len;
   int bpp;
   int diff;
   //int ptemp;
   int patemp, pbtemp, pctemp;

   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
   FullLength  = row_info->rowbytes; // # of bytes to filter
   _asm
   {
         xor ebx, ebx        // ebx ==> x offset
         mov edi, row
         xor edx, edx        // edx ==> x-bpp offset
         mov esi, prev_row
         xor eax, eax

         // Compute the Raw value for the first bpp bytes
         // Note: the formula works out to be always
         //   Paeth(x) = Raw(x) + Prior(x)      where x < bpp
dpthrlp:
         mov al, [edi + ebx]
         add al, [esi + ebx]
         inc ebx
         cmp ebx, bpp
         mov [edi + ebx - 1], al
         jb dpthrlp
         // get # of bytes to alignment
         mov diff, edi         // take start of row
         add diff, ebx         // add bpp
         xor ecx, ecx
         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
         and diff, 0xfffffff8  // mask to alignment boundary
         sub diff, edi         // subtract from start ==> value ebx at alignment
         jz dpthgo
         // fix alignment
dpthlp1:
         xor eax, eax
         // pav = p - a = (a + b - c) - a = b - c
         mov al, [esi + ebx]   // load Prior(x) into al
         mov cl, [esi + edx]   // load Prior(x-bpp) into cl
         sub eax, ecx          // subtract Prior(x-bpp)
         mov patemp, eax       // Save pav for later use
         xor eax, eax
         // pbv = p - b = (a + b - c) - b = a - c
         mov al, [edi + edx]   // load Raw(x-bpp) into al
         sub eax, ecx          // subtract Prior(x-bpp)
         mov ecx, eax
         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
         add eax, patemp       // pcv = pav + pbv
         // pc = abs(pcv)
         test eax, 0x80000000
         jz dpthpca
         neg eax               // reverse sign of neg values
dpthpca:
         mov pctemp, eax       // save pc for later use
         // pb = abs(pbv)
         test ecx, 0x80000000
         jz dpthpba
         neg ecx               // reverse sign of neg values
dpthpba:
         mov pbtemp, ecx       // save pb for later use
         // pa = abs(pav)
         mov eax, patemp
         test eax, 0x80000000
         jz dpthpaa
         neg eax               // reverse sign of neg values
dpthpaa:
         mov patemp, eax       // save pa for later use
         // test if pa <= pb
         cmp eax, ecx
         jna dpthabb
         // pa > pb; now test if pb <= pc
         cmp ecx, pctemp
         jna dpthbbc
         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
         jmp dpthpaeth
dpthbbc:
         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
         mov cl, [esi + ebx]   // load Prior(x) into cl
         jmp dpthpaeth
dpthabb:
         // pa <= pb; now test if pa <= pc
         cmp eax, pctemp
         jna dpthabc
         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
         jmp dpthpaeth
dpthabc:
         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
dpthpaeth:
         inc ebx
         inc edx
         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
         add [edi + ebx - 1], cl
         cmp ebx, diff
         jb dpthlp1
dpthgo:
         mov ecx, FullLength
         mov eax, ecx
         sub eax, ebx          // subtract alignment fix
         and eax, 0x00000007   // calc bytes over mult of 8
         sub ecx, eax          // drop over bytes from original length
         mov MMXLength, ecx
   } // end _asm block
   // Now do the math for the rest of the row
   switch ( bpp )
   {
      case 3:
      {
         ActiveMask.use = 0x0000000000ffffff;
         ActiveMaskEnd.use = 0xffff000000000000;
         ShiftBpp.use = 24;    // == bpp(3) * 8
         ShiftRem.use = 40;    // == 64 - 24
         _asm
         {
            mov ebx, diff
            mov edi, row
            mov esi, prev_row
            pxor mm0, mm0
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]
dpth3lp:
            psrlq mm1, ShiftRem     // shift last 3 bytes to 1st 3 bytes
            movq mm2, [esi + ebx]   // load b=Prior(x)
            punpcklbw mm1, mm0      // Unpack High bytes of a
            movq mm3, [esi+ebx-8]   // Prep c=Prior(x-bpp) bytes
            punpcklbw mm2, mm0      // Unpack High bytes of b
            psrlq mm3, ShiftRem     // shift last 3 bytes to 1st 3 bytes
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            punpcklbw mm3, mm0      // Unpack High bytes of c
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3

            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4       // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5       // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
            pand mm0, mm6       // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5    // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            paddw mm7, mm3
            pxor mm0, mm0
            packuswb mm7, mm1
            movq mm3, [esi + ebx]   // load c=Prior(x-bpp)
            pand mm7, ActiveMask
            movq mm2, mm3           // load b=Prior(x) step 1
            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
            punpcklbw mm3, mm0      // Unpack High bytes of c
            movq [edi + ebx], mm7   // write back updated value
            movq mm1, mm7           // Now mm1 will be used as Raw(x-bpp)
            // Now do Paeth for 2nd set of bytes (3-5)
            psrlq mm2, ShiftBpp     // load b=Prior(x) step 2
            punpcklbw mm1, mm0      // Unpack High bytes of a
            pxor mm7, mm7
            punpcklbw mm2, mm0      // Unpack High bytes of b
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            psubw mm5, mm3
            psubw mm4, mm3
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) =
            //       pav + pbv = pbv + pav
            movq mm6, mm5
            paddw mm6, mm4

            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm5       // Create mask pbv bytes < 0
            pcmpgtw mm7, mm4       // Create mask pav bytes < 0
            pand mm0, mm5          // Only pbv bytes < 0 in mm0
            pand mm7, mm4          // Only pav bytes < 0 in mm7
            psubw mm5, mm0
            psubw mm4, mm7
            psubw mm5, mm0
            psubw mm4, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            movq mm2, [esi + ebx]  // load b=Prior(x)
            pand mm3, mm7
            pandn mm7, mm0
            pxor mm1, mm1
            paddw mm7, mm3
            pxor mm0, mm0
            packuswb mm7, mm1
            movq mm3, mm2           // load c=Prior(x-bpp) step 1
            pand mm7, ActiveMask
            punpckhbw mm2, mm0      // Unpack High bytes of b
            psllq mm7, ShiftBpp     // Shift bytes to 2nd group of 3 bytes
             // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
            psllq mm3, ShiftBpp     // load c=Prior(x-bpp) step 2
            movq [edi + ebx], mm7   // write back updated value
            movq mm1, mm7
            punpckhbw mm3, mm0      // Unpack High bytes of c
            psllq mm1, ShiftBpp     // Shift bytes
                                    // Now mm1 will be used as Raw(x-bpp)
            // Now do Paeth for 3rd, and final, set of bytes (6-7)
            pxor mm7, mm7
            punpckhbw mm1, mm0      // Unpack High bytes of a
            psubw mm4, mm3
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            pxor mm0, mm0
            paddw mm6, mm5

            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
            pand mm0, mm4       // Only pav bytes < 0 in mm7
            pand mm7, mm5       // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
            pand mm0, mm6       // Only pav bytes < 0 in mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5    // pa > pb?
            movq mm0, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            pandn mm0, mm1
            pandn mm7, mm4
            paddw mm0, mm2
            paddw mm7, mm5
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6    // pab > pc?
            pand mm3, mm7
            pandn mm7, mm0
            paddw mm7, mm3
            pxor mm1, mm1
            packuswb mm1, mm7
            // Step ebx to next set of 8 bytes and repeat loop til done
            add ebx, 8
            pand mm1, ActiveMaskEnd
            paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x)

            cmp ebx, MMXLength
            pxor mm0, mm0              // pxor does not affect flags
            movq [edi + ebx - 8], mm1  // write back updated value
                                 // mm1 will be used as Raw(x-bpp) next loop
                           // mm3 ready to be used as Prior(x-bpp) next loop
            jb dpth3lp
         } // end _asm block
      }
      break;

      case 6:
      case 7:
      case 5:
      {
         ActiveMask.use  = 0x00000000ffffffff;
         ActiveMask2.use = 0xffffffff00000000;
         ShiftBpp.use = bpp << 3;    // == bpp * 8
         ShiftRem.use = 64 - ShiftBpp.use;
         _asm
         {
            mov ebx, diff
            mov edi, row
            mov esi, prev_row
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]
            pxor mm0, mm0
dpth6lp:
            // Must shift to position Raw(x-bpp) data
            psrlq mm1, ShiftRem
            // Do first set of 4 bytes
            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
            punpcklbw mm1, mm0      // Unpack Low bytes of a
            movq mm2, [esi + ebx]   // load b=Prior(x)
            punpcklbw mm2, mm0      // Unpack Low bytes of b
            // Must shift to position Prior(x-bpp) data
            psrlq mm3, ShiftRem
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            punpcklbw mm3, mm0      // Unpack Low bytes of c
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4       // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5       // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
            pand mm0, mm6       // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5    // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6    // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            paddw mm7, mm3
            pxor mm0, mm0
            packuswb mm7, mm1
            movq mm3, [esi + ebx - 8]  // load c=Prior(x-bpp)
            pand mm7, ActiveMask
            psrlq mm3, ShiftRem
            movq mm2, [esi + ebx]      // load b=Prior(x) step 1
            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
            movq mm6, mm2
            movq [edi + ebx], mm7      // write back updated value
            movq mm1, [edi+ebx-8]
            psllq mm6, ShiftBpp
            movq mm5, mm7
            psrlq mm1, ShiftRem
            por mm3, mm6
            psllq mm5, ShiftBpp
            punpckhbw mm3, mm0         // Unpack High bytes of c
            por mm1, mm5
            // Do second set of 4 bytes
            punpckhbw mm2, mm0         // Unpack High bytes of b
            punpckhbw mm1, mm0         // Unpack High bytes of a
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4          // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5          // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6           // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            pxor mm1, mm1
            paddw mm7, mm3
            pxor mm0, mm0
            // Step ex to next set of 8 bytes and repeat loop til done
            add ebx, 8
            packuswb mm1, mm7
            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
            cmp ebx, MMXLength
            movq [edi + ebx - 8], mm1      // write back updated value
                                // mm1 will be used as Raw(x-bpp) next loop
            jb dpth6lp
         } // end _asm block
      }
      break;

      case 4:
      {
         ActiveMask.use  = 0x00000000ffffffff;
         _asm {
            mov ebx, diff
            mov edi, row
            mov esi, prev_row
            pxor mm0, mm0
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]    // Only time should need to read
                                     //  a=Raw(x-bpp) bytes
dpth4lp:
            // Do first set of 4 bytes
            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
            punpckhbw mm1, mm0       // Unpack Low bytes of a
            movq mm2, [esi + ebx]    // load b=Prior(x)
            punpcklbw mm2, mm0       // Unpack High bytes of b
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            punpckhbw mm3, mm0       // Unpack High bytes of c
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4          // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5          // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            paddw mm7, mm3
            pxor mm0, mm0
            packuswb mm7, mm1
            movq mm3, [esi + ebx]      // load c=Prior(x-bpp)
            pand mm7, ActiveMask
            movq mm2, mm3              // load b=Prior(x) step 1
            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
            punpcklbw mm3, mm0         // Unpack High bytes of c
            movq [edi + ebx], mm7      // write back updated value
            movq mm1, mm7              // Now mm1 will be used as Raw(x-bpp)
            // Do second set of 4 bytes
            punpckhbw mm2, mm0         // Unpack Low bytes of b
            punpcklbw mm1, mm0         // Unpack Low bytes of a
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4          // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5          // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            pxor mm1, mm1
            paddw mm7, mm3
            pxor mm0, mm0
            // Step ex to next set of 8 bytes and repeat loop til done
            add ebx, 8
            packuswb mm1, mm7
            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
            cmp ebx, MMXLength
            movq [edi + ebx - 8], mm1      // write back updated value
                                // mm1 will be used as Raw(x-bpp) next loop
            jb dpth4lp
         } // end _asm block
      }
      break;
      case 8:                          // bpp == 8
      {
         ActiveMask.use  = 0x00000000ffffffff;
         _asm {
            mov ebx, diff
            mov edi, row
            mov esi, prev_row
            pxor mm0, mm0
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]      // Only time should need to read
                                       //  a=Raw(x-bpp) bytes
dpth8lp:
            // Do first set of 4 bytes
            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
            punpcklbw mm1, mm0         // Unpack Low bytes of a
            movq mm2, [esi + ebx]      // load b=Prior(x)
            punpcklbw mm2, mm0         // Unpack Low bytes of b
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            punpcklbw mm3, mm0         // Unpack Low bytes of c
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4          // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5          // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            paddw mm7, mm3
            pxor mm0, mm0
            packuswb mm7, mm1
            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
            pand mm7, ActiveMask
            movq mm2, [esi + ebx]    // load b=Prior(x)
            paddb mm7, [edi + ebx]   // add Paeth predictor with Raw(x)
            punpckhbw mm3, mm0       // Unpack High bytes of c
            movq [edi + ebx], mm7    // write back updated value
            movq mm1, [edi+ebx-8]    // read a=Raw(x-bpp) bytes

            // Do second set of 4 bytes
            punpckhbw mm2, mm0       // Unpack High bytes of b
            punpckhbw mm1, mm0       // Unpack High bytes of a
            // pav = p - a = (a + b - c) - a = b - c
            movq mm4, mm2
            // pbv = p - b = (a + b - c) - b = a - c
            movq mm5, mm1
            psubw mm4, mm3
            pxor mm7, mm7
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            movq mm6, mm4
            psubw mm5, mm3
            // pa = abs(p-a) = abs(pav)
            // pb = abs(p-b) = abs(pbv)
            // pc = abs(p-c) = abs(pcv)
            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
            paddw mm6, mm5
            pand mm0, mm4          // Only pav bytes < 0 in mm7
            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
            psubw mm4, mm0
            pand mm7, mm5          // Only pbv bytes < 0 in mm0
            psubw mm4, mm0
            psubw mm5, mm7
            pxor mm0, mm0
            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
            pand mm0, mm6          // Only pav bytes < 0 in mm7
            psubw mm5, mm7
            psubw mm6, mm0
            //  test pa <= pb
            movq mm7, mm4
            psubw mm6, mm0
            pcmpgtw mm7, mm5       // pa > pb?
            movq mm0, mm7
            // use mm7 mask to merge pa & pb
            pand mm5, mm7
            // use mm0 mask copy to merge a & b
            pand mm2, mm0
            pandn mm7, mm4
            pandn mm0, mm1
            paddw mm7, mm5
            paddw mm0, mm2
            //  test  ((pa <= pb)? pa:pb) <= pc
            pcmpgtw mm7, mm6       // pab > pc?
            pxor mm1, mm1
            pand mm3, mm7
            pandn mm7, mm0
            pxor mm1, mm1
            paddw mm7, mm3
            pxor mm0, mm0
            // Step ex to next set of 8 bytes and repeat loop til done
            add ebx, 8
            packuswb mm1, mm7
            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
            cmp ebx, MMXLength
            movq [edi + ebx - 8], mm1      // write back updated value
                            // mm1 will be used as Raw(x-bpp) next loop
            jb dpth8lp
         } // end _asm block
      }
      break;

      case 1:                // bpp = 1
      case 2:                // bpp = 2
      default:               // bpp > 8
      {
         _asm {
            mov ebx, diff
            cmp ebx, FullLength
            jnb dpthdend
            mov edi, row
            mov esi, prev_row
            // Do Paeth decode for remaining bytes
            mov edx, ebx
            xor ecx, ecx        // zero ecx before using cl & cx in loop below
            sub edx, bpp        // Set edx = ebx - bpp
dpthdlp:
            xor eax, eax
            // pav = p - a = (a + b - c) - a = b - c
            mov al, [esi + ebx]        // load Prior(x) into al
            mov cl, [esi + edx]        // load Prior(x-bpp) into cl
            sub eax, ecx                 // subtract Prior(x-bpp)
            mov patemp, eax                 // Save pav for later use
            xor eax, eax
            // pbv = p - b = (a + b - c) - b = a - c
            mov al, [edi + edx]        // load Raw(x-bpp) into al
            sub eax, ecx                 // subtract Prior(x-bpp)
            mov ecx, eax
            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
            add eax, patemp                 // pcv = pav + pbv
            // pc = abs(pcv)
            test eax, 0x80000000
            jz dpthdpca
            neg eax                     // reverse sign of neg values
dpthdpca:
            mov pctemp, eax             // save pc for later use
            // pb = abs(pbv)
            test ecx, 0x80000000
            jz dpthdpba
            neg ecx                     // reverse sign of neg values
dpthdpba:
            mov pbtemp, ecx             // save pb for later use
            // pa = abs(pav)
            mov eax, patemp
            test eax, 0x80000000
            jz dpthdpaa
            neg eax                     // reverse sign of neg values
dpthdpaa:
            mov patemp, eax             // save pa for later use
            // test if pa <= pb
            cmp eax, ecx
            jna dpthdabb
            // pa > pb; now test if pb <= pc
            cmp ecx, pctemp
            jna dpthdbbc
            // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
            jmp dpthdpaeth
dpthdbbc:
            // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
            mov cl, [esi + ebx]        // load Prior(x) into cl
            jmp dpthdpaeth
dpthdabb:
            // pa <= pb; now test if pa <= pc
            cmp eax, pctemp
            jna dpthdabc
            // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
            jmp dpthdpaeth
dpthdabc:
            // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
            mov cl, [edi + edx]  // load Raw(x-bpp) into cl
dpthdpaeth:
            inc ebx
            inc edx
            // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
            add [edi + ebx - 1], cl
            cmp ebx, FullLength
            jb dpthdlp
dpthdend:
         } // end _asm block
      }
      return;                   // No need to go further with this one
   }                         // end switch ( bpp )
   _asm
   {
         // MMX acceleration complete now do clean-up
         // Check if any remaining bytes left to decode
         mov ebx, MMXLength
         cmp ebx, FullLength
         jnb dpthend
         mov edi, row
         mov esi, prev_row
         // Do Paeth decode for remaining bytes
         mov edx, ebx
         xor ecx, ecx         // zero ecx before using cl & cx in loop below
         sub edx, bpp         // Set edx = ebx - bpp
dpthlp2:
         xor eax, eax
         // pav = p - a = (a + b - c) - a = b - c
         mov al, [esi + ebx]  // load Prior(x) into al
         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
         sub eax, ecx         // subtract Prior(x-bpp)
         mov patemp, eax      // Save pav for later use
         xor eax, eax
         // pbv = p - b = (a + b - c) - b = a - c
         mov al, [edi + edx]  // load Raw(x-bpp) into al
         sub eax, ecx         // subtract Prior(x-bpp)
         mov ecx, eax
         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
         add eax, patemp      // pcv = pav + pbv
         // pc = abs(pcv)
         test eax, 0x80000000
         jz dpthpca2
         neg eax              // reverse sign of neg values
dpthpca2:
         mov pctemp, eax      // save pc for later use
         // pb = abs(pbv)
         test ecx, 0x80000000
         jz dpthpba2
         neg ecx              // reverse sign of neg values
dpthpba2:
         mov pbtemp, ecx      // save pb for later use
         // pa = abs(pav)
         mov eax, patemp
         test eax, 0x80000000
         jz dpthpaa2
         neg eax              // reverse sign of neg values
dpthpaa2:
         mov patemp, eax      // save pa for later use
         // test if pa <= pb
         cmp eax, ecx
         jna dpthabb2
         // pa > pb; now test if pb <= pc
         cmp ecx, pctemp
         jna dpthbbc2
         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
         jmp dpthpaeth2
dpthbbc2:
         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
         mov cl, [esi + ebx]        // load Prior(x) into cl
         jmp dpthpaeth2
dpthabb2:
         // pa <= pb; now test if pa <= pc
         cmp eax, pctemp
         jna dpthabc2
         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
         jmp dpthpaeth2
dpthabc2:
         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
dpthpaeth2:
         inc ebx
         inc edx
         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
         add [edi + ebx - 1], cl
         cmp ebx, FullLength
         jb dpthlp2
dpthend:
         emms             // End MMX instructions; prep for possible FP instrs.
   } // end _asm block
}

// Optimized code for PNG Sub filter decoder
void /* PRIVATE */
png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row)
{
   //int test;
   int bpp;
   png_uint_32 FullLength;
   png_uint_32 MMXLength;
   int diff;

   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
   FullLength  = row_info->rowbytes - bpp; // # of bytes to filter
   _asm {
        mov edi, row
        mov esi, edi               // lp = row
        add edi, bpp               // rp = row + bpp
        xor eax, eax
        // get # of bytes to alignment
        mov diff, edi               // take start of row
        add diff, 0xf               // add 7 + 8 to incr past
                                        // alignment boundary
        xor ebx, ebx
        and diff, 0xfffffff8        // mask to alignment boundary
        sub diff, edi               // subtract from start ==> value
                                        //  ebx at alignment
        jz dsubgo
        // fix alignment
dsublp1:
        mov al, [esi+ebx]
        add [edi+ebx], al
        inc ebx
        cmp ebx, diff
        jb dsublp1
dsubgo:
        mov ecx, FullLength
        mov edx, ecx
        sub edx, ebx                  // subtract alignment fix
        and edx, 0x00000007           // calc bytes over mult of 8
        sub ecx, edx                  // drop over bytes from length
        mov MMXLength, ecx
   } // end _asm block

   // Now do the math for the rest of the row
   switch ( bpp )
   {
        case 3:
        {
         ActiveMask.use  = 0x0000ffffff000000;
         ShiftBpp.use = 24;       // == 3 * 8
         ShiftRem.use  = 40;      // == 64 - 24
         _asm {
            mov edi, row
            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
            mov esi, edi              // lp = row
            add edi, bpp          // rp = row + bpp
            movq mm6, mm7
            mov ebx, diff
            psllq mm6, ShiftBpp   // Move mask in mm6 to cover 3rd active
                                  // byte group
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]
dsub3lp:
            psrlq mm1, ShiftRem   // Shift data for adding 1st bpp bytes
                          // no need for mask; shift clears inactive bytes
            // Add 1st active group
            movq mm0, [edi+ebx]
            paddb mm0, mm1
            // Add 2nd active group
            movq mm1, mm0         // mov updated Raws to mm1
            psllq mm1, ShiftBpp   // shift data to position correctly
            pand mm1, mm7         // mask to use only 2nd active group
            paddb mm0, mm1
            // Add 3rd active group
            movq mm1, mm0         // mov updated Raws to mm1
            psllq mm1, ShiftBpp   // shift data to position correctly
            pand mm1, mm6         // mask to use only 3rd active group
            add ebx, 8
            paddb mm0, mm1
            cmp ebx, MMXLength
            movq [edi+ebx-8], mm0     // Write updated Raws back to array
            // Prep for doing 1st add at top of loop
            movq mm1, mm0
            jb dsub3lp
         } // end _asm block
      }
      break;

      case 1:
      {
         // Placed here just in case this is a duplicate of the
         // non-MMX code for the SUB filter in png_read_filter_row below
         //
         //         png_bytep rp;
         //         png_bytep lp;
         //         png_uint_32 i;
         //         bpp = (row_info->pixel_depth + 7) >> 3;
         //         for (i = (png_uint_32)bpp, rp = row + bpp, lp = row;
         //            i < row_info->rowbytes; i++, rp++, lp++)
         //      {
         //            *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff);
         //      }
         _asm {
            mov ebx, diff
            mov edi, row
            cmp ebx, FullLength
            jnb dsub1end
            mov esi, edi          // lp = row
            xor eax, eax
            add edi, bpp      // rp = row + bpp
dsub1lp:
            mov al, [esi+ebx]
            add [edi+ebx], al
            inc ebx
            cmp ebx, FullLength
            jb dsub1lp
dsub1end:
         } // end _asm block
      }
      return;

      case 6:
      case 7:
      case 4:
      case 5:
      {
         ShiftBpp.use = bpp << 3;
         ShiftRem.use = 64 - ShiftBpp.use;
         _asm {
            mov edi, row
            mov ebx, diff
            mov esi, edi               // lp = row
            add edi, bpp           // rp = row + bpp
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]
dsub4lp:
            psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes
                          // no need for mask; shift clears inactive bytes
            movq mm0, [edi+ebx]
            paddb mm0, mm1
            // Add 2nd active group
            movq mm1, mm0          // mov updated Raws to mm1
            psllq mm1, ShiftBpp    // shift data to position correctly
                                   // there is no need for any mask
                                   // since shift clears inactive bits/bytes
            add ebx, 8
            paddb mm0, mm1
            cmp ebx, MMXLength
            movq [edi+ebx-8], mm0
            movq mm1, mm0          // Prep for doing 1st add at top of loop
            jb dsub4lp
         } // end _asm block
      }
      break;

      case 2:
      {
         ActiveMask.use  = 0x00000000ffff0000;
         ShiftBpp.use = 16;       // == 2 * 8
         ShiftRem.use = 48;       // == 64 - 16
         _asm {
            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
            mov ebx, diff
            movq mm6, mm7
            mov edi, row
            psllq mm6, ShiftBpp     // Move mask in mm6 to cover 3rd active
                                    //  byte group
            mov esi, edi            // lp = row
            movq mm5, mm6
            add edi, bpp            // rp = row + bpp
            psllq mm5, ShiftBpp     // Move mask in mm5 to cover 4th active
                                    //  byte group
            // PRIME the pump (load the first Raw(x-bpp) data set
            movq mm1, [edi+ebx-8]
dsub2lp:
            // Add 1st active group
            psrlq mm1, ShiftRem     // Shift data for adding 1st bpp bytes
                                    // no need for mask; shift clears inactive
                                    //  bytes
            movq mm0, [edi+ebx]
            paddb mm0, mm1
            // Add 2nd active group
            movq mm1, mm0           // mov updated Raws to mm1
            psllq mm1, ShiftBpp     // shift data to position correctly
            pand mm1, mm7           // mask to use only 2nd active group
            paddb mm0, mm1
            // Add 3rd active group
            movq mm1, mm0           // mov updated Raws to mm1
            psllq mm1, ShiftBpp     // shift data to position correctly
            pand mm1, mm6           // mask to use only 3rd active group
            paddb mm0, mm1
            // Add 4th active group
            movq mm1, mm0           // mov updated Raws to mm1
            psllq mm1, ShiftBpp     // shift data to position correctly
            pand mm1, mm5           // mask to use only 4th active group
            add ebx, 8
            paddb mm0, mm1
            cmp ebx, MMXLength
            movq [edi+ebx-8], mm0   // Write updated Raws back to array
            movq mm1, mm0           // Prep for doing 1st add at top of loop
            jb dsub2lp
         } // end _asm block
      }
      break;
      case 8:
      {
         _asm {
            mov edi, row
            mov ebx, diff
            mov esi, edi            // lp = row
            add edi, bpp            // rp = row + bpp
            mov ecx, MMXLength
            movq mm7, [edi+ebx-8]   // PRIME the pump (load the first
                                    // Raw(x-bpp) data set
            and ecx, 0x0000003f     // calc bytes over mult of 64
dsub8lp:
            movq mm0, [edi+ebx]     // Load Sub(x) for 1st 8 bytes
            paddb mm0, mm7
            movq mm1, [edi+ebx+8]   // Load Sub(x) for 2nd 8 bytes
            movq [edi+ebx], mm0    // Write Raw(x) for 1st 8 bytes
                                   // Now mm0 will be used as Raw(x-bpp) for
                                   // the 2nd group of 8 bytes.  This will be
                                   // repeated for each group of 8 bytes with
                                   // the 8th group being used as the Raw(x-bpp)
                                   // for the 1st group of the next loop.
            paddb mm1, mm0
            movq mm2, [edi+ebx+16]  // Load Sub(x) for 3rd 8 bytes
            movq [edi+ebx+8], mm1   // Write Raw(x) for 2nd 8 bytes
            paddb mm2, mm1
            movq mm3, [edi+ebx+24]  // Load Sub(x) for 4th 8 bytes
            movq [edi+ebx+16], mm2  // Write Raw(x) for 3rd 8 bytes
            paddb mm3, mm2
            movq mm4, [edi+ebx+32]  // Load Sub(x) for 5th 8 bytes
            movq [edi+ebx+24], mm3  // Write Raw(x) for 4th 8 bytes
            paddb mm4, mm3
            movq mm5, [edi+ebx+40]  // Load Sub(x) for 6th 8 bytes
            movq [edi+ebx+32], mm4  // Write Raw(x) for 5th 8 bytes
            paddb mm5, mm4
            movq mm6, [edi+ebx+48]  // Load Sub(x) for 7th 8 bytes
            movq [edi+ebx+40], mm5  // Write Raw(x) for 6th 8 bytes
            paddb mm6, mm5
            movq mm7, [edi+ebx+56]  // Load Sub(x) for 8th 8 bytes
            movq [edi+ebx+48], mm6  // Write Raw(x) for 7th 8 bytes
            add ebx, 64
            paddb mm7, mm6
            cmp ebx, ecx
            movq [edi+ebx-8], mm7   // Write Raw(x) for 8th 8 bytes
            jb dsub8lp
            cmp ebx, MMXLength
            jnb dsub8lt8
dsub8lpA:
            movq mm0, [edi+ebx]
            add ebx, 8
            paddb mm0, mm7
            cmp ebx, MMXLength
            movq [edi+ebx-8], mm0   // use -8 to offset early add to ebx
            movq mm7, mm0           // Move calculated Raw(x) data to mm1 to
                                    // be the new Raw(x-bpp) for the next loop
            jb dsub8lpA
dsub8lt8:
         } // end _asm block
      }
      break;

      default:                // bpp greater than 8 bytes
      {
         _asm {
            mov ebx, diff
            mov edi, row
            mov esi, edi           // lp = row
            add edi, bpp           // rp = row + bpp
dsubAlp:
            movq mm0, [edi+ebx]
            movq mm1, [esi+ebx]
            add ebx, 8
            paddb mm0, mm1
            cmp ebx, MMXLength
            movq [edi+ebx-8], mm0  // mov does not affect flags; -8 to offset
                                   //  add ebx
            jb dsubAlp
         } // end _asm block
      }
      break;

   } // end switch ( bpp )

   _asm {
        mov ebx, MMXLength
        mov edi, row
        cmp ebx, FullLength
        jnb dsubend
        mov esi, edi               // lp = row
        xor eax, eax
        add edi, bpp               // rp = row + bpp
dsublp2:
        mov al, [esi+ebx]
        add [edi+ebx], al
        inc ebx
        cmp ebx, FullLength
        jb dsublp2
dsubend:
        emms             // End MMX instructions; prep for possible FP instrs.
   } // end _asm block
}

// Optimized code for PNG Up filter decoder
void /* PRIVATE */
png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row,
   png_bytep prev_row)
{
   png_uint_32 len;
   len  = row_info->rowbytes;       // # of bytes to filter
   _asm {
      mov edi, row
      // get # of bytes to alignment
      mov ecx, edi
      xor ebx, ebx
      add ecx, 0x7
      xor eax, eax
      and ecx, 0xfffffff8
      mov esi, prev_row
      sub ecx, edi
      jz dupgo
      // fix alignment
duplp1:
      mov al, [edi+ebx]
      add al, [esi+ebx]
      inc ebx
      cmp ebx, ecx
      mov [edi + ebx-1], al  // mov does not affect flags; -1 to offset inc ebx
      jb duplp1
dupgo:
      mov ecx, len
      mov edx, ecx
      sub edx, ebx                  // subtract alignment fix
      and edx, 0x0000003f           // calc bytes over mult of 64
      sub ecx, edx                  // drop over bytes from length
      // Unrolled loop - use all MMX registers and interleave to reduce
      // number of branch instructions (loops) and reduce partial stalls
duploop:
      movq mm1, [esi+ebx]
      movq mm0, [edi+ebx]
      movq mm3, [esi+ebx+8]
      paddb mm0, mm1
      movq mm2, [edi+ebx+8]
      movq [edi+ebx], mm0
      paddb mm2, mm3
      movq mm5, [esi+ebx+16]
      movq [edi+ebx+8], mm2
      movq mm4, [edi+ebx+16]
      movq mm7, [esi+ebx+24]
      paddb mm4, mm5
      movq mm6, [edi+ebx+24]
      movq [edi+ebx+16], mm4
      paddb mm6, mm7
      movq mm1, [esi+ebx+32]
      movq [edi+ebx+24], mm6
      movq mm0, [edi+ebx+32]
      movq mm3, [esi+ebx+40]
      paddb mm0, mm1
      movq mm2, [edi+ebx+40]
      movq [edi+ebx+32], mm0
      paddb mm2, mm3
      movq mm5, [esi+ebx+48]
      movq [edi+ebx+40], mm2
      movq mm4, [edi+ebx+48]
      movq mm7, [esi+ebx+56]
      paddb mm4, mm5
      movq mm6, [edi+ebx+56]
      movq [edi+ebx+48], mm4
      add ebx, 64
      paddb mm6, mm7
      cmp ebx, ecx
      movq [edi+ebx-8], mm6 // (+56)movq does not affect flags;
                                     // -8 to offset add ebx
      jb duploop

      cmp edx, 0                     // Test for bytes over mult of 64
      jz dupend


      // 2 lines added by lcreeve at netins.net
      // (mail 11 Jul 98 in png-implement list)
      cmp edx, 8 //test for less than 8 bytes
      jb duplt8


      add ecx, edx
      and edx, 0x00000007           // calc bytes over mult of 8
      sub ecx, edx                  // drop over bytes from length
      jz duplt8
      // Loop using MMX registers mm0 & mm1 to update 8 bytes simultaneously
duplpA:
      movq mm1, [esi+ebx]
      movq mm0, [edi+ebx]
      add ebx, 8
      paddb mm0, mm1
      cmp ebx, ecx
      movq [edi+ebx-8], mm0 // movq does not affect flags; -8 to offset add ebx
      jb duplpA
      cmp edx, 0            // Test for bytes over mult of 8
      jz dupend
duplt8:
      xor eax, eax
      add ecx, edx          // move over byte count into counter
      // Loop using x86 registers to update remaining bytes
duplp2:
      mov al, [edi + ebx]
      add al, [esi + ebx]
      inc ebx
      cmp ebx, ecx
      mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx
      jb duplp2
dupend:
      // Conversion of filtered row completed
      emms          // End MMX instructions; prep for possible FP instrs.
   } // end _asm block
}


// Optimized png_read_filter_row routines
void /* PRIVATE */
png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
   row, png_bytep prev_row, int filter)
{
#ifdef PNG_DEBUG
   char filnm[10];
#endif

   if (mmx_supported == 2) {
#if !defined(PNG_1_0_X)
       /* this should have happened in png_init_mmx_flags() already */
       png_warning(png_ptr, "asm_flags may not have been initialized");
#endif
       png_mmx_support();
   }

#ifdef PNG_DEBUG
   png_debug(1, "in png_read_filter_row\n");
   switch (filter)
   {
      case 0: sprintf(filnm, "none");
         break;
#if !defined(PNG_1_0_X)
      case 1: sprintf(filnm, "sub-%s",
        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : "x86");
         break;
      case 2: sprintf(filnm, "up-%s",
        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : "x86");
         break;
      case 3: sprintf(filnm, "avg-%s",
        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : "x86");
         break;
      case 4: sprintf(filnm, "Paeth-%s",
        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":"x86");
         break;
#else
      case 1: sprintf(filnm, "sub");
         break;
      case 2: sprintf(filnm, "up");
         break;
      case 3: sprintf(filnm, "avg");
         break;
      case 4: sprintf(filnm, "Paeth");
         break;
#endif
      default: sprintf(filnm, "unknw");
         break;
   }
   png_debug2(0,"row=%5d, %s, ", png_ptr->row_number, filnm);
   png_debug2(0, "pd=%2d, b=%d, ", (int)row_info->pixel_depth,
      (int)((row_info->pixel_depth + 7) >> 3));
   png_debug1(0,"len=%8d, ", row_info->rowbytes);
#endif /* PNG_DEBUG */

   switch (filter)
   {
      case PNG_FILTER_VALUE_NONE:
         break;

      case PNG_FILTER_VALUE_SUB:
      {
#if !defined(PNG_1_0_X)
         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
#else
         if (mmx_supported)
#endif
         {
            png_read_filter_row_mmx_sub(row_info, row);
         }
         else
         {
            png_uint_32 i;
            png_uint_32 istop = row_info->rowbytes;
            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
            png_bytep rp = row + bpp;
            png_bytep lp = row;

            for (i = bpp; i < istop; i++)
            {
               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
               rp++;
            }
         }
         break;
      }

      case PNG_FILTER_VALUE_UP:
      {
#if !defined(PNG_1_0_X)
         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
#else
         if (mmx_supported)
#endif
         {
            png_read_filter_row_mmx_up(row_info, row, prev_row);
         }
         else
         {
            png_uint_32 i;
            png_uint_32 istop = row_info->rowbytes;
            png_bytep rp = row;
            png_bytep pp = prev_row;

            for (i = 0; i < istop; ++i)
            {
               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
               rp++;
            }
         }
         break;
      }

      case PNG_FILTER_VALUE_AVG:
      {
#if !defined(PNG_1_0_X)
         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
#else
         if (mmx_supported)
#endif
         {
            png_read_filter_row_mmx_avg(row_info, row, prev_row);
         }
         else
         {
            png_uint_32 i;
            png_bytep rp = row;
            png_bytep pp = prev_row;
            png_bytep lp = row;
            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
            png_uint_32 istop = row_info->rowbytes - bpp;

            for (i = 0; i < bpp; i++)
            {
               *rp = (png_byte)(((int)(*rp) +
                  ((int)(*pp++) >> 1)) & 0xff);
               rp++;
            }

            for (i = 0; i < istop; i++)
            {
               *rp = (png_byte)(((int)(*rp) +
                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
               rp++;
            }
         }
         break;
      }

      case PNG_FILTER_VALUE_PAETH:
      {
#if !defined(PNG_1_0_X)
         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
#else
         if (mmx_supported)
#endif
         {
            png_read_filter_row_mmx_paeth(row_info, row, prev_row);
         }
         else
         {
            png_uint_32 i;
            png_bytep rp = row;
            png_bytep pp = prev_row;
            png_bytep lp = row;
            png_bytep cp = prev_row;
            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
            png_uint_32 istop=row_info->rowbytes - bpp;

            for (i = 0; i < bpp; i++)
            {
               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
               rp++;
            }

            for (i = 0; i < istop; i++)   // use leftover rp,pp
            {
               int a, b, c, pa, pb, pc, p;

               a = *lp++;
               b = *pp++;
               c = *cp++;

               p = b - c;
               pc = a - c;

#ifdef PNG_USE_ABS
               pa = abs(p);
               pb = abs(pc);
               pc = abs(p + pc);
#else
               pa = p < 0 ? -p : p;
               pb = pc < 0 ? -pc : pc;
               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
#endif

               /*
                  if (pa <= pb && pa <= pc)
                     p = a;
                  else if (pb <= pc)
                     p = b;
                  else
                     p = c;
                */

               p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;

               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
               rp++;
            }
         }
         break;
      }

      default:
         png_warning(png_ptr, "Ignoring bad row filter type");
         *row=0;
         break;
   }
}

#endif /* PNG_ASSEMBLER_CODE_SUPPORTED && PNG_USE_PNGVCRD */
