/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file * * For Intel x86 CPU and Microsoft Visual C++ compiler * * libpng version 1.2.5 - October 3, 2002 * For conditions of distribution and use, see copyright notice in png.h * Copyright (c) 1998-2002 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) { /* this should have happened in png_init_mmx_flags() already */ png_warning(png_ptr, "asm_flags may not have been initialized"); png_mmx_support(); } if (mask == 0xff) { png_memcpy(row, png_ptr->row_buf + 1, (png_size_t)((png_ptr->width * png_ptr->row_info.pixel_depth + 7) >> 3)); } /* 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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) /* && mmx_supported */ ) { 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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) /* && mmx_supported */ ) { 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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) /* && mmx_supported */ ) { _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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) /* && mmx_supported */ ) { _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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) /* && mmx_supported */ ) { 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) { /* this should have happened in png_init_mmx_flags() already */ png_warning(png_ptr, "asm_flags may not have been initialized"); 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 ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE) /* && mmx_supported */ ) { 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 = ((final_width * (png_uint_32)row_info->pixel_depth + 7) >> 3); } } #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@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) { /* this should have happened in png_init_mmx_flags() already */ png_warning(png_ptr, "asm_flags may not have been initialized"); png_mmx_support(); } #ifdef PNG_DEBUG png_debug(1, "in png_read_filter_row\n"); switch (filter) { case 0: sprintf(filnm, "none"); break; 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; 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 ((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)) { 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 ((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)) { 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 ((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)) { 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 ((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)) { 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 */