图形图像处理-之-高质量的快速的图像缩放 中篇 二次线性插值和三次卷积插值

news/2024/11/22 14:47:25/

from:http://blog.csdn.net/housisong/article/details/1452249


图形图像处理-之-高质量的快速的图像缩放 中篇 二次线性插值和三次卷积插值
                     HouSisong@GMail.com   2006255.12.13

(2015.08.15  PicZoom_ftBilinear_Common更精确的边界公式推导)

(2009.03.07  可以到这里下载缩放算法的完整的可以编译的项目源代码: http://blog.csdn.net/housisong/archive/2009/03/07/3967270.aspx  )

(2007.11.12 替换了二次线性插值的实现(以前偷懒使用了一个近似公式),改进后在图片边缘的插值效果更好(包括三次卷积插值的边界也更精确);
(2007.09.14 修正三次卷积的MMX版本中表的精度太低(7bit),造成卷积结果误差较大的问题,该版本提高了插值质量,并且速度加快12-25%)
(2007.09.07 PicZoom_ThreeOrder2和PicZoom_ThreeOrder_MMX在缩放的图片宽或高
小于3个像素的时候有一个Bug(边界计算错误);将unsigned long xrIntFloat_16,
yrIntFloat_16的定义改成long xrIntFloat_16,yrIntFloat_16就可以了)
(2007.07.02 ThreeOrder2_Fast一点小的改进,加快14%)
(2007.06.18 优化PicZoom_BilInear_MMX的实现(由138.5fps提高到147.9fps),
            并添加更快的两路展开的实现版本BilInear_MMX_expand2函数;
            补充新的SSE2的实现PicZoom_BilInear_SSE2函数)
(2007.06.06 更新测试数据,编译器由vc6改为vc2005,CPU由赛扬2G改为AMD64x2 4200+(2.1G) )
(2007.03.06 更新)


tag:图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,三次线性插值,
   MipMap链,三次卷积插值,MMX,SSE,SSE2,CPU缓存优化

摘要:首先给出一个基本的图像缩放算法,然后一步一步的优化其速度和缩放质量;

高质量的快速的图像缩放 全文 分为:
     上篇 近邻取样插值和其速度优化
     中篇 二次线性插值和三次卷积插值
     下篇 三次线性插值和MipMap链

     补充 使用SSE2优化

正文:
  为了便于讨论,这里只处理32bit的ARGB颜色;
  代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
  为了代码的可读性,没有加入异常处理代码;
   测试使用的CPU为AMD64x2 4200+(2.37G)  和 Intel Core2 4400(2.00G);


速度测试说明:
  只测试内存数据到内存数据的缩放
  测试图片都是800*600缩放到1024*768; fps表示每秒钟的帧数,值越大表示函数越快


A:近邻取样插值、二次线性插值、三次卷积插值 缩放效果对比

                                  
       原图         近邻取样缩放到0.6倍     近邻取样缩放到1.6倍

                                       
                二次线性插值缩放到0.6倍   二次线性插值缩放到1.6倍

                                      
               三次卷积插值缩放到0.6倍   三次卷积插值缩放到1.6倍

     
 原图 近邻取样缩放到8倍 二次线性插值缩放到8倍 三次卷积插值缩放到8倍 二次线性插值(近似公式)


     近邻取样插值缩放简单、速度快,但很多时候缩放出的图片质量比较差(特别是对于人物、景色等),
图片的缩放有比较明显的锯齿;使用二次或更高次插值有利于改善缩放效果;


B: 首先定义图像数据结构:

#define asm __asm

typedef unsigned 
char TUInt8; // [0..255]
struct TARGB32      //32 bit color
{
    TUInt8  b,g,r,a;          
//a is alpha
};

struct TPicRegion  //一块颜色数据区的描述,便于参数传递
{
    TARGB32
*    pdata;         //颜色数据首地址
    long        byte_width;    //一行数据的物理宽度(字节宽度);
                
//abs(byte_width)有可能大于等于width*sizeof(TARGB32);
    long        width;         //像素宽度
    long        height;        //像素高度
};

//那么访问一个点的函数可以写为:
inline TARGB32& Pixels(const TPicRegion& pic,const long x,const long y)
{
    
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}



二次线性插值缩放:

C: 二次线性插值缩放原理和公式图示:

        

             缩放后图片                 原图片
            (宽DW,高DH)              (宽SW,高SH)

  缩放映射原理:
  (Sx-0)/(SW-0)=(Dx-0)/(DW-0)   (Sy-0)/(SH-0)=(Dy-0)/(DH-0)
 =>   Sx=Dx*SW/DW                    Sy=Dy*SH/DH

  聚焦看看(Sx,Sy)坐标点(Sx,Sy为浮点数)附近的情况;


        


  对于近邻取样插值的缩放算法,直接取Color0颜色作为缩放后点的颜色;
二次线性插值需要考虑(Sx,Sy)坐标点周围的4个颜色值Color0/Color1/Color2/Color3,
把(Sx,Sy)到A/B/C/D坐标点的距离作为系数来把4个颜色混合出缩放后点的颜色;
( u=Sx-floor(Sx); v=Sy-floor(Sy); 说明:floor函数的返回值为小于等于参数的最大整数 ) 
  二次线性插值公式为:
 tmpColor0=Color0*(1-u) + Color2*u;
 tmpColor1=Color1*(1-u) + Color3*u;
        DstColor =tmpColor0*(1-v) + tmpColor2*v;

  展开公式为:
        pm0=(1-u)*(1-v);
        pm1=v*(1-u);
        pm2=u*(1-v);
        pm3=u*v;
  则颜色混合公式为:
        DstColor = Color0*pm0 + Color1*pm1 + Color2*pm2 + Color3*pm3;

参数函数图示:

     

                                              二次线性插值函数图示

对于上面的公式,它将图片向右下各移动了半个像素,需要对此做一个修正;
  =>   Sx=(Dx+0.5)*SW/DW-0.5; Sy=(Dy+0.5)*SH/DH-0.5;
而实际的程序,还需要考虑到边界(访问源图片可能超界)对于算法的影响,边界的处理可能有各种
方案(不处理边界或边界回绕或边界饱和或边界映射或用背景颜色混合等;文章中默认使用边界饱和来处理超界);
比如:边界饱和函数: 

//访问一个点的函数,(x,y)坐标可能超出图片边界; //边界处理模式:边界饱和
inline TARGB32 Pixels_Bound(const TPicRegion& pic,long x,long y)
{
    
//assert((pic.width>0)&&(pic.height>0));
    bool IsInPic=true;
    
if (x<0) {x=0; IsInPic=false; } else if (x>=pic.width ) {x=pic.width -1; IsInPic=false; }
    
if (y<0) {y=0; IsInPic=false; } else if (y>=pic.height) {y=pic.height-1; IsInPic=false; }
    TARGB32 result
=Pixels(pic,x,y);
    
if (!IsInPic) result.a=0;
    
return result;
}


D: 二次线性插值缩放算法的一个参考实现:PicZoom_BilInear0
  该函数并没有做什么优化,只是一个简单的浮点实现版本;


    inline void Bilinear0(const TPicRegion& pic,float fx,float fy,TARGB32* result)
    {
        
long x=(long)fx; if (x>fx) --x; //x=floor(fx);    
        long y=(long)fy; if (y>fy) --y; //y=floor(fy);
        
        TARGB32 Color0
=Pixels_Bound(pic,x,y);
        TARGB32 Color2
=Pixels_Bound(pic,x+1,y);
        TARGB32 Color1
=Pixels_Bound(pic,x,y+1);
        TARGB32 Color3
=Pixels_Bound(pic,x+1,y+1);

        
float u=fx-x;
        
float v=fy-y;
        
float pm3=u*v;
        
float pm2=u*(1-v);
        
float pm1=v*(1-u);
        
float pm0=(1-u)*(1-v);

        result
->a=(pm0*Color0.a+pm1*Color1.a+pm2*Color2.a+pm3*Color3.a);
        result
->r=(pm0*Color0.r+pm1*Color1.r+pm2*Color2.r+pm3*Color3.r);
        result
->g=(pm0*Color0.g+pm1*Color1.g+pm2*Color2.g+pm3*Color3.g);
        result
->b=(pm0*Color0.b+pm1*Color1.b+pm2*Color2.b+pm3*Color3.b);
    }

void PicZoom_Bilinear0(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    unsigned 
long dst_width=Dst.width;
    TARGB32
* pDstLine=Dst.pdata;
    
for (unsigned long y=0;y<Dst.height;++y)
    {
        
float srcy=(y+0.4999999)*Src.height/Dst.height-0.5;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            
float srcx=(x+0.4999999)*Src.width/Dst.width-0.5;
            Bilinear0(Src,srcx,srcy,
&pDstLine[x]);
        }
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
}


//速度测试:
//==============================================================================
// PicZoom_BilInear0      8.3 fps

E: 把PicZoom_BilInear0的浮点计算改写为定点数实现:PicZoom_BilInear1

    inline void Bilinear1(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        
long x=x_16>>16;
        
long y=y_16>>16;
        TARGB32 Color0
=Pixels_Bound(pic,x,y);
        TARGB32 Color2
=Pixels_Bound(pic,x+1,y);
        TARGB32 Color1
=Pixels_Bound(pic,x,y+1);
        TARGB32 Color3
=Pixels_Bound(pic,x+1,y+1);

        unsigned 
long u_8=(x_16 & 0xFFFF)>>8;
        unsigned 
long v_8=(y_16 & 0xFFFF)>>8;
        unsigned 
long pm3_16=(u_8*v_8);
        unsigned 
long pm2_16=(u_8*(unsigned long)(256-v_8));
        unsigned 
long pm1_16=(v_8*(unsigned long)(256-u_8));
        unsigned 
long pm0_16=((256-u_8)*(256-v_8));

        result
->a=((pm0_16*Color0.a+pm1_16*Color1.a+pm2_16*Color2.a+pm3_16*Color3.a)>>16);
        result
->r=((pm0_16*Color0.r+pm1_16*Color1.r+pm2_16*Color2.r+pm3_16*Color3.r)>>16);
        result
->g=((pm0_16*Color0.g+pm1_16*Color1.g+pm2_16*Color2.g+pm3_16*Color3.g)>>16);
        result
->b=((pm0_16*Color0.b+pm1_16*Color1.b+pm2_16*Color2.b+pm3_16*Color3.b)>>16);
    }

void PicZoom_Bilinear1(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    TARGB32
* pDstLine=Dst.pdata;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear1(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }



//速度测试:
//==============================================================================
// PicZoom_BilInear1     17.7 fps

F: 二次线性插值需要考略边界访问超界的问题,我们可以将边界区域和内部区域分开处理,这样就可以优化内部的插值实现函数了:比如不需要判断访问超界、减少颜色数据复制、减少一些不必要的重复坐标计算等等



    inline void Bilinear2_Fast(TARGB32* PColor0,TARGB32* PColor1,unsigned long u_8,unsigned long v_8,TARGB32* result)
    {
        unsigned 
long pm3_16=u_8*v_8;
        unsigned 
long pm2_16=(u_8<<8)-pm3_16;
        unsigned 
long pm1_16=(v_8<<8)-pm3_16;
        unsigned 
long pm0_16=(1<<16)-pm1_16-pm2_16-pm3_16;
   
        result
->a=((pm0_16*PColor0[0].a+pm2_16*PColor0[1].a+pm1_16*PColor1[0].a+pm3_16*PColor1[1].a)>>16);
        result
->r=((pm0_16*PColor0[0].r+pm2_16*PColor0[1].r+pm1_16*PColor1[0].r+pm3_16*PColor1[1].r)>>16);
        result
->g=((pm0_16*PColor0[0].g+pm2_16*PColor0[1].g+pm1_16*PColor1[0].g+pm3_16*PColor1[1].g)>>16);
        result
->b=((pm0_16*PColor0[0].b+pm2_16*PColor0[1].b+pm1_16*PColor1[0].b+pm3_16*PColor1[1].b)>>16);
    }

    inline 
void Bilinear2_Border(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        
long x=(x_16>>16);
        
long y=(y_16>>16);
        unsigned 
long u_16=((unsigned short)(x_16));
        unsigned 
long v_16=((unsigned short)(y_16));

        TARGB32 pixel[
4];
        pixel[
0]=Pixels_Bound(pic,x,y);
        pixel[
1]=Pixels_Bound(pic,x+1,y);
        pixel[
2]=Pixels_Bound(pic,x,y+1);
        pixel[
3]=Pixels_Bound(pic,x+1,y+1);
        
        Bilinear2_Fast(
&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
    }

void PicZoom_Bilinear2(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=-csDErrorY/yrIntFloat_16+1;              //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=-csDErrorX/xrIntFloat_16+1;     
    
if (border_x0>=Dst.width ) border_x0=Dst.width; 
    
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long Src_byte_width=Src.byte_width;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear2_Border(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            Bilinear2_Border(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }

        {
            unsigned 
long v_8=(srcy_16 & 0xFFFF)>>8;
            TARGB32
* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
            
for (unsigned long x=border_x0;x<border_x1;++x)
            {
                TARGB32
* PColor0=&PSrcLineColor[srcx_16>>16];
                TARGB32
* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
                Bilinear2_Fast(PColor0,PColor1,(srcx_16 
& 0xFFFF)>>8,v_8,&pDstLine[x]);
                srcx_16
+=xrIntFloat_16;
            }
        }

        
for (x=border_x1;x<dst_width;++x)
        {
            Bilinear2_Border(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear2_Border(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
}


//速度测试:
//==============================================================================
// PicZoom_BilInear2     43.4 fps

(F'补充:
  如果不想处理边界访问超界问题,可以考虑扩大源图片的尺寸,加一个边框 (“哨兵”优化);
这样插值算法就不用考虑边界问题了,程序写起来也简单很多! 
  如果对缩放结果的边界像素级精度要求不是太高,我还有一个方案,一个稍微改变的缩放公式:
  Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;  (源图片宽和高:SW>=2;SH>=2)
  证明这个公式不会造成内存访问超界: 


    //fit的基本想法,不超界情况下尽量增大inc的值

    //fit算法推导:
    //寻址不超界要求 (inc*(D-1)>>16)+1 <= S-1
    //即:  inc*(D-1)/(1<<16) <= (S-2)
    //     inc*(D-1) <= (S-2)*(1<<16) +((1<<16)-1)
    //     inc*(D-1) <=  ((S-1)<<16) -1
    //     inc <= (((S-1)<<16) -1)/(D-1)
  比如,按这个公式的一个简单实现: (缩放效果见前面的"二次线性插值(近似公式)"图示)
void PicZoom_ftBilinear_Common(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(2>Src.width)||(2>Src.height)) return;

    
long xrIntFloat_16=((Src.width-1)<<16)/Dst.width; 
    
long yrIntFloat_16=((Src.height-1)<<16)/Dst.height;

    unsigned 
long dst_width=Dst.width;
    
long Src_byte_width=Src.byte_width;
    TARGB32
* pDstLine=Dst.pdata;
    
long srcy_16=0;
    
for (unsigned long y=0;y<Dst.height;++y)
    {
        unsigned 
long v_8=(srcy_16 & 0xFFFF)>>8;
        TARGB32
* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
        
long srcx_16=0;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            TARGB32
* PColor0=&PSrcLineColor[srcx_16>>16];
            Bilinear_Fast_Common(PColor0,(TARGB32
*)((TUInt8*)(PColor0)+Src_byte_width),(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
            srcx_16
+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }


)

G:利用单指令多数据处理的MMX指令一般都可以加快颜色的运算;在使用MMX改写之前,利用
32bit寄存器(或变量)来模拟单指令多数据处理;
数据储存原理:一个颜色数据分量只有一个字节,用2个字节来储存单个颜色分量的计算结果,
对于很多颜色计算来说精度就够了;那么一个32bit寄存器(或变量)就可以储存2个计算出的
临时颜色分量;从而达到了单个指令两路数据处理的目的;
单个指令两路数据处理的计算:
  乘法: ((0x00AA*a)<<16) | (0x00BB*a) = 0x00AA00BB * a
    可见只要保证0x00AA*a和0x00BB*a都小于(1<<16)那么乘法可以直接使用无符号数乘法了
  加法: ((0x00AA+0x00CC)<<16) | (0x00BB+0x00DD) = 0x00AA00BB + 0x00CC00DD
    可见只要0x00AA+0x00CC和0x00BB+0x00DD小于(1<<16)那么加法可以直接使用无符号数加法了
  (移位、减法等稍微复杂一点,因为这里没有用到就不推倒运算公式了)


    inline void Bilinear_Fast_Common(TARGB32* PColor0,TARGB32* PColor1,unsigned long u_8,unsigned long v_8,TARGB32* result)
    {
        unsigned 
long pm3_8=(u_8*v_8)>>8;
        unsigned 
long pm2_8=u_8-pm3_8;
        unsigned 
long pm1_8=v_8-pm3_8;
        unsigned 
long pm0_8=256-pm1_8-pm2_8-pm3_8;

        unsigned 
long Color=*(unsigned long*)(PColor0);
        unsigned 
long BR=(Color & 0x00FF00FF)*pm0_8;
        unsigned 
long GA=((Color & 0xFF00FF00)>>8)*pm0_8;
                      Color
=((unsigned long*)(PColor0))[1];
                      GA
+=((Color & 0xFF00FF00)>>8)*pm2_8;
                      BR
+=(Color & 0x00FF00FF)*pm2_8;
                      Color
=*(unsigned long*)(PColor1);
                      GA
+=((Color & 0xFF00FF00)>>8)*pm1_8;
                      BR
+=(Color & 0x00FF00FF)*pm1_8;
                      Color
=((unsigned long*)(PColor1))[1];
                      GA
+=((Color & 0xFF00FF00)>>8)*pm3_8;
                      BR
+=(Color & 0x00FF00FF)*pm3_8;

        
*(unsigned long*)(result)=(GA & 0xFF00FF00)|((BR & 0xFF00FF00)>>8);
    }

    inline 
void Bilinear_Border_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        
long x=(x_16>>16);
        
long y=(y_16>>16);
        unsigned 
long u_16=((unsigned short)(x_16));
        unsigned 
long v_16=((unsigned short)(y_16));

        TARGB32 pixel[
4];
        pixel[
0]=Pixels_Bound(pic,x,y);
        pixel[
1]=Pixels_Bound(pic,x+1,y);
        pixel[
2]=Pixels_Bound(pic,x,y+1);
        pixel[
3]=Pixels_Bound(pic,x+1,y+1);
        
        Bilinear_Fast_Common(
&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
    }

void PicZoom_Bilinear_Common(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=-csDErrorY/yrIntFloat_16+1;              //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=-csDErrorX/xrIntFloat_16+1;     
    
if (border_x0>=Dst.width ) border_x0=Dst.width; 
    
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long Src_byte_width=Src.byte_width;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            Bilinear_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }

        {
            unsigned 
long v_8=(srcy_16 & 0xFFFF)>>8;
            TARGB32
* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
            
for (unsigned long x=border_x0;x<border_x1;++x)
            {
                TARGB32
* PColor0=&PSrcLineColor[srcx_16>>16];
                TARGB32
* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
                Bilinear_Fast_Common(PColor0,PColor1,(srcx_16 
& 0xFFFF)>>8,v_8,&pDstLine[x]);
                srcx_16
+=xrIntFloat_16;
            }
        }

        
for (x=border_x1;x<dst_width;++x)
        {
            Bilinear_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
}


//速度测试:
//==============================================================================
// PicZoom_BilInear_Common   65.3 fps

H:使用MMX指令改写:PicZoom_Bilinear_MMX

    inline void  Bilinear_Fast_MMX(TARGB32* PColor0,TARGB32* PColor1,unsigned long u_8,unsigned long v_8,TARGB32* result)
    {
        asm
        {    
              MOVD      MM6,v_8
              MOVD      MM5,u_8
              mov       edx,PColor0
              mov       eax,PColor1
              PXOR      mm7,mm7

              MOVD         MM2,dword ptr [eax]  
              MOVD         MM0,dword ptr [eax
+4]
              PUNPCKLWD    MM5,MM5
              PUNPCKLWD    MM6,MM6
              MOVD         MM3,dword ptr [edx]  
              MOVD         MM1,dword ptr [edx
+4]
              PUNPCKLDQ    MM5,MM5 
              PUNPCKLBW    MM0,MM7
              PUNPCKLBW    MM1,MM7
              PUNPCKLBW    MM2,MM7
              PUNPCKLBW    MM3,MM7
              PSUBw        MM0,MM2
              PSUBw        MM1,MM3
              PSLLw        MM2,
8
              PSLLw        MM3,
8
              PMULlw       MM0,MM5
              PMULlw       MM1,MM5
              PUNPCKLDQ    MM6,MM6 
              PADDw        MM0,MM2
              PADDw        MM1,MM3

              PSRLw        MM0,
8
              PSRLw        MM1,
8
              PSUBw        MM0,MM1
              PSLLw        MM1,
8
              PMULlw       MM0,MM6
              mov       eax,result
              PADDw        MM0,MM1

              PSRLw        MM0,
8
              PACKUSwb     MM0,MM7
              movd      [eax],MM0 
              
//emms
        }
    }

    
void Bilinear_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        
long x=(x_16>>16);
        
long y=(y_16>>16);
        unsigned 
long u_16=((unsigned short)(x_16));
        unsigned 
long v_16=((unsigned short)(y_16));

        TARGB32 pixel[
4];
        pixel[
0]=Pixels_Bound(pic,x,y);
        pixel[
1]=Pixels_Bound(pic,x+1,y);
        pixel[
2]=Pixels_Bound(pic,x,y+1);
        pixel[
3]=Pixels_Bound(pic,x+1,y+1);
        
        Bilinear_Fast_MMX(
&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
    }

void PicZoom_Bilinear_MMX(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=-csDErrorY/yrIntFloat_16+1;              //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=-csDErrorX/xrIntFloat_16+1;     
    
if (border_x0>=Dst.width ) border_x0=Dst.width; 
    
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long Src_byte_width=Src.byte_width;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }

        {
            unsigned 
long v_8=(srcy_16 & 0xFFFF)>>8;
            TARGB32
* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
            
for (unsigned long x=border_x0;x<border_x1;++x)
            {
                TARGB32
* PColor0=&PSrcLineColor[srcx_16>>16];
                TARGB32
* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
                Bilinear_Fast_MMX(PColor0,PColor1,(srcx_16 
& 0xFFFF)>>8,v_8,&pDstLine[x]);
                srcx_16
+=xrIntFloat_16;
            }
        }

        
for (x=border_x1;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    asm emms
}


//速度测试:
//==============================================================================
// PicZoom_BilInear_MMX 132.9 fps

 

H' 对BilInear_MMX简单改进:PicZoom_Bilinear_MMX_Ex


void PicZoom_Bilinear_MMX_Ex(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=-csDErrorY/yrIntFloat_16+1;              //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=-csDErrorX/xrIntFloat_16+1;     
    
if (border_x0>=Dst.width ) border_x0=Dst.width; 
    
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long Src_byte_width=Src.byte_width;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }

    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }

        {
            
long dst_width_fast=border_x1-border_x0;
            
if (dst_width_fast>0)
            {
                unsigned 
long v_8=(srcy_16 & 0xFFFF)>>8;
                TARGB32
* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
                TARGB32
* PSrcLineColorNext= (TARGB32*)((TUInt8*)(PSrcLineColor)+Src_byte_width) ;
                TARGB32
* pDstLine_Fast=&pDstLine[border_x0];
                asm
                {
                      movd         mm6,v_8
                      pxor         mm7,mm7 
//mm7=0
                      PUNPCKLWD    MM6,MM6
                      PUNPCKLDQ    MM6,MM6
//mm6=v_8
                    
                      mov       esi,PSrcLineColor
                      mov       ecx,PSrcLineColorNext
                      mov       edx,srcx_16
                      mov       ebx,dst_width_fast
                      mov       edi,pDstLine_Fast
                      lea       edi,[edi
+ebx*4]
                      push      ebp
                      mov       ebp,xrIntFloat_16
                      neg       ebx

                loop_start:

                          mov       eax,edx
                          shl       eax,
16
                          shr       eax,
24
                          
//== movzx       eax,dh  //eax=u_8
                          MOVD      MM5,eax
                          mov       eax,edx
                          shr       eax,
16     //srcx_16>>16

                          MOVD         MM2,dword ptr [ecx
+eax*4]  
                          MOVD         MM0,dword ptr [ecx
+eax*4+4]
                          PUNPCKLWD    MM5,MM5
                          MOVD         MM3,dword ptr [esi
+eax*4]  
                          MOVD         MM1,dword ptr [esi
+eax*4+4]
                          PUNPCKLDQ    MM5,MM5 
//mm5=u_8
                          PUNPCKLBW    MM0,MM7
                          PUNPCKLBW    MM1,MM7
                          PUNPCKLBW    MM2,MM7
                          PUNPCKLBW    MM3,MM7
                          PSUBw        MM0,MM2
                          PSUBw        MM1,MM3
                          PSLLw        MM2,
8
                          PSLLw        MM3,
8
                          PMULlw       MM0,MM5
                          PMULlw       MM1,MM5
                          PADDw        MM0,MM2
                          PADDw        MM1,MM3

                          PSRLw        MM0,
8
                          PSRLw        MM1,
8
                          PSUBw        MM0,MM1
                          PSLLw        MM1,
8
                          PMULlw       MM0,MM6
                          PADDw        MM0,MM1

                          PSRLw     MM0,
8
                          PACKUSwb  MM0,MM7
                          MOVd   dword ptr    [edi
+ebx*4],MM0 //write DstColor
                                      
                          add       edx,ebp 
//srcx_16+=xrIntFloat_16
                          inc       ebx
                          jnz       loop_start

                      pop       ebp
                      mov       srcx_16,edx
                }
            }
        }

        
for (x=border_x1;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            Bilinear_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    asm emms
}


//速度测试:
//==============================================================================
// PicZoom_Bilinear_MMX_Ex 157.0 fps

I: 把测试成绩放在一起:


//CPU: AMD64x2 4200+(2.37G)  zoom 800*600 to 1024*768
//==============================================================================
// StretchBlt                   232.7 fps  
// PicZoom3_SSE                 711.7 fps
//
// PicZoom_BilInear0              8.3 fps
// PicZoom_BilInear1             17.7 fps
// PicZoom_BilInear2             43.4 fps
// PicZoom_BilInear_Common       65.3 fps
// PicZoom_BilInear_MMX         132.9 fps
// PicZoom_BilInear_MMX_Ex      157.0 fps

补充Intel Core2 4400上的测试成绩:


//CPU: Intel Core2 4400(2.00G)  zoom 800*600 to 1024*768
//==============================================================================
// PicZoom3_SSE                1099.7 fps  
//
// PicZoom_BilInear0             10.7 fps
// PicZoom_BilInear1             24.2 fps
// PicZoom_BilInear2             54.3 fps
// PicZoom_BilInear_Common       59.8 fps
// PicZoom_BilInear_MMX         118.4 fps
// PicZoom_BilInear_MMX_Ex      142.9 fps

 

 

三次卷积插值:

 


J: 二次线性插值缩放出的图片很多时候让人感觉变得模糊(术语叫低通滤波),特别是在放大
的时候;使用三次卷积插值来改善插值结果;三次卷积插值考虑映射点周围16个点(4x4)的颜色来
计算最终的混合颜色,如图;

         
         P(0,0)所在像素为映射的点,加上它周围的15个点,按一定系数混合得到最终输出结果;

         混合公式参见PicZoom_ThreeOrder0的实现;

    插值曲线公式sin(x*PI)/(x*PI),如图:


                             三次卷积插值曲线sin(x*PI)/(x*PI) (其中PI=3.1415926...)

 

K:三次卷积插值缩放算法的一个参考实现:PicZoom_ThreeOrder0
  该函数并没有做过多的优化,只是一个简单的浮点实现版本;



        inline 
double SinXDivX(double x) 
        {
            
//该函数计算插值曲线sin(x*PI)/(x*PI)的值 //PI=3.1415926535897932385; 
            
//下面是它的近似拟合表达式
            const float a = -1//a还可以取 a=-2,-1,-0.75,-0.5等等,起到调节锐化或模糊程度的作用

            
if (x<0) x=-x; //x=abs(x);
            double x2=x*x;
            
double x3=x2*x;
            
if (x<=1)
              
return (a+2)*x3 - (a+3)*x2 + 1;
            
else if (x<=2
              
return a*x3 - (5*a)*x2 + (8*a)*- (4*a);
            
else
              
return 0;
        } 

        inline TUInt8 border_color(
long Color)
        {
            
if (Color<=0)
                
return 0;
            
else if (Color>=255)
                
return 255;
            
else
                
return Color;
        }
        
    
void ThreeOrder0(const TPicRegion& pic,const float fx,const float fy,TARGB32* result)
    {
        
long x0=(long)fx; if (x0>fx) --x0; //x0=floor(fx);    
        long y0=(long)fy; if (y0>fy) --y0; //y0=floor(fy);
        float fu=fx-x0;
        
float fv=fy-y0;

        TARGB32 pixel[
16];
        
long i,j;

        
for (i=0;i<4;++i)
        {
            
for (j=0;j<4;++j)
            {
                
long x=x0-1+j;
                
long y=y0-1+i;
                pixel[i
*4+j]=Pixels_Bound(pic,x,y);
            }
        }

        
float afu[4],afv[4];
        
//
        afu[0]=SinXDivX(1+fu);
        afu[
1]=SinXDivX(fu);
        afu[
2]=SinXDivX(1-fu);
        afu[
3]=SinXDivX(2-fu);
        afv[
0]=SinXDivX(1+fv);
        afv[
1]=SinXDivX(fv);
        afv[
2]=SinXDivX(1-fv);
        afv[
3]=SinXDivX(2-fv);

        
float sR=0,sG=0,sB=0,sA=0;
        
for (i=0;i<4;++i)
        {
            
float aR=0,aG=0,aB=0,aA=0;
            
for (long j=0;j<4;++j)
            {
                aA
+=afu[j]*pixel[i*4+j].a;
                aR
+=afu[j]*pixel[i*4+j].r;
                aG
+=afu[j]*pixel[i*4+j].g;
                aB
+=afu[j]*pixel[i*4+j].b;
            }
            sA
+=aA*afv[i];
            sR
+=aR*afv[i];
            sG
+=aG*afv[i];
            sB
+=aB*afv[i];
        }

        result
->a=border_color((long)(sA+0.5));
        result
->r=border_color((long)(sR+0.5));
        result
->g=border_color((long)(sG+0.5));
        result
->b=border_color((long)(sB+0.5));
    }

void PicZoom_ThreeOrder0(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;


    unsigned 
long dst_width=Dst.width;
    TARGB32
* pDstLine=Dst.pdata;
    
for (unsigned long y=0;y<Dst.height;++y)
    {
        
float srcy=(y+0.4999999)*Src.height/Dst.height-0.5;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            
float srcx=(x+0.4999999)*Src.width/Dst.width-0.5;
            ThreeOrder0(Src,srcx,srcy,
&pDstLine[x]);
        }
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
}


//速度测试:
//==============================================================================
// PicZoom_ThreeOrder0    3.6 fps

L: 使用定点数来优化缩放函数;边界和内部分开处理;对SinXDivX做一个查找表;对border_color做一个查找表;


    static long SinXDivX_Table_8[(2<<8)+1];
    
class _CAutoInti_SinXDivX_Table {
    
private
        
void _Inti_SinXDivX_Table()
        {
            
for (long i=0;i<=(2<<8);++i)
                SinXDivX_Table_8[i]
=long(0.5+256*SinXDivX(i*(1.0/(256))))*1;
        };
    
public:
        _CAutoInti_SinXDivX_Table() { _Inti_SinXDivX_Table(); }
    };
    
static _CAutoInti_SinXDivX_Table __tmp_CAutoInti_SinXDivX_Table;


    
//颜色查表
    static TUInt8 _color_table[256*3];
    
static const TUInt8* color_table=&_color_table[256];
    
class _CAuto_inti_color_table
    {
    
public:
        _CAuto_inti_color_table() {
            
for (int i=0;i<256*3;++i)
                _color_table[i]
=border_color(i-256);
        }
    };
    
static _CAuto_inti_color_table _Auto_inti_color_table;

    
void ThreeOrder_Fast_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        unsigned long u_8=(unsigned char)((x_16)>>8);
        unsigned 
long v_8=(unsigned char)((y_16)>>8);
        
const TARGB32* pixel=&Pixels(pic,(x_16>>16)-1,(y_16>>16)-1);
        
long pic_byte_width=pic.byte_width;

        
long au_8[4],av_8[4];
        
//
        au_8[0]=SinXDivX_Table_8[(1<<8)+u_8];
        au_8[
1]=SinXDivX_Table_8[u_8];
        au_8[
2]=SinXDivX_Table_8[(1<<8)-u_8];
        au_8[
3]=SinXDivX_Table_8[(2<<8)-u_8];
        av_8[
0]=SinXDivX_Table_8[(1<<8)+v_8];
        av_8[
1]=SinXDivX_Table_8[v_8];
        av_8[
2]=SinXDivX_Table_8[(1<<8)-v_8];
        av_8[
3]=SinXDivX_Table_8[(2<<8)-v_8];

        
long sR=0,sG=0,sB=0,sA=0;
        
for (long i=0;i<4;++i)
        {
            
long aA=au_8[0]*pixel[0].a + au_8[1]*pixel[1].a + au_8[2]*pixel[2].a + au_8[3]*pixel[3].a;
            
long aR=au_8[0]*pixel[0].r + au_8[1]*pixel[1].r + au_8[2]*pixel[2].r + au_8[3]*pixel[3].r;
            
long aG=au_8[0]*pixel[0].g + au_8[1]*pixel[1].g + au_8[2]*pixel[2].g + au_8[3]*pixel[3].g;
            
long aB=au_8[0]*pixel[0].b + au_8[1]*pixel[1].b + au_8[2]*pixel[2].b + au_8[3]*pixel[3].b;
            sA
+=aA*av_8[i];
            sR
+=aR*av_8[i];
            sG
+=aG*av_8[i];
            sB
+=aB*av_8[i];
            ((TUInt8
*&)pixel)+=pic_byte_width;
        }

        result
->a=color_table[sA>>16];
        result
->r=color_table[sR>>16];
        result
->g=color_table[sG>>16];
        result
->b=color_table[sB>>16];
    }

    
void ThreeOrder_Border_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        
long x0_sub1=(x_16>>16)-1;
        
long y0_sub1=(y_16>>16)-1;
        unsigned 
long u_16_add1=((unsigned short)(x_16))+(1<<16);
        unsigned 
long v_16_add1=((unsigned short)(y_16))+(1<<16);

        TARGB32 pixel[
16];
        
long i;

        
for (i=0;i<4;++i)
        {
            
long y=y0_sub1+i;
            pixel[i
*4+0]=Pixels_Bound(pic,x0_sub1+0,y);
            pixel[i
*4+1]=Pixels_Bound(pic,x0_sub1+1,y);
            pixel[i
*4+2]=Pixels_Bound(pic,x0_sub1+2,y);
            pixel[i
*4+3]=Pixels_Bound(pic,x0_sub1+3,y);
        }
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0];
        npic.byte_width
=4*sizeof(TARGB32);
        
//npic.width     =4;
        
//npic.height    =4;
        ThreeOrder_Fast_Common(npic,u_16_add1,v_16_add1,result);
    }

void PicZoom_ThreeOrder_Common(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y*yr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
    
if (border_x0>=Dst.width ) border_x0=Dst.width;
    
long border_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-3) => y<=(height-3-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;; 
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            ThreeOrder_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            ThreeOrder_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        
for (x=border_x0;x<border_x1;++x)
        {
            ThreeOrder_Fast_Common(Src,srcx_16,srcy_16,
&pDstLine[x]);//fast  !
            srcx_16+=xrIntFloat_16;
        }
        
for (x=border_x1;x<dst_width;++x)
        {
            ThreeOrder_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            ThreeOrder_Border_Common(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
}


//速度测试:
//==============================================================================
// PicZoom_ThreeOrder_Common    16.9 fps

 M: 用MMX来优化ThreeOrder_Common函数:ThreeOrder_MMX


    typedef   unsigned long TMMXData32;
    
static TMMXData32 SinXDivX_Table_MMX[(2<<8)+1];
    
class _CAutoInti_SinXDivX_Table_MMX {
    
private
        
void _Inti_SinXDivX_Table_MMX()
        {
            
for (long i=0;i<=(2<<8);++i)
            {
                unsigned 
short t=long(0.5+(1<<14)*SinXDivX(i*(1.0/(256))));
                unsigned 
long tl=| (((unsigned long)t)<<16);
                SinXDivX_Table_MMX[i]
=tl;
            }
        };
    
public:
        _CAutoInti_SinXDivX_Table_MMX() { _Inti_SinXDivX_Table_MMX(); }
    };
    
static _CAutoInti_SinXDivX_Table_MMX __tmp_CAutoInti_SinXDivX_Table_MMX;


    
void __declspec(naked) _private_ThreeOrder_Fast_MMX()
    {
        asm
        {
            movd        mm1,dword ptr [edx]
            movd        mm2,dword ptr [edx
+4]
            movd        mm3,dword ptr [edx
+8]
            movd        mm4,dword ptr [edx
+12]
            movd        mm5,dword ptr [(offset SinXDivX_Table_MMX)
+256*4+eax*4]
            movd        mm6,dword ptr [(offset SinXDivX_Table_MMX)
+eax*4]
            punpcklbw   mm1,mm7
            punpcklbw   mm2,mm7
            punpcklwd   mm5,mm5
            punpcklwd   mm6,mm6
            psllw       mm1,
7
            psllw       mm2,
7
            pmulhw      mm1,mm5
            pmulhw      mm2,mm6
            punpcklbw   mm3,mm7
            punpcklbw   mm4,mm7
            movd        mm5,dword ptr [(offset SinXDivX_Table_MMX)
+256*4+ecx*4]
            movd        mm6,dword ptr [(offset SinXDivX_Table_MMX)
+512*4+ecx*4]
            punpcklwd   mm5,mm5
            punpcklwd   mm6,mm6
            psllw       mm3,
7
            psllw       mm4,
7
            pmulhw      mm3,mm5
            pmulhw      mm4,mm6
            paddsw      mm1,mm2
            paddsw      mm3,mm4
            movd        mm6,dword ptr [ebx] 
//v
            paddsw      mm1,mm3
            punpcklwd   mm6,mm6

            pmulhw      mm1,mm6
            add     edx,esi  
//+pic.byte_width
            paddsw      mm0,mm1

            ret
        }
    }

    inline 
void ThreeOrder_Fast_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        asm
        {
            mov     ecx,pic
            mov     eax,y_16
            mov     ebx,x_16
            movzx   edi,ah 
//v_8
            mov     edx,[ecx+TPicRegion::pdata]
            shr     eax,
16
            mov     esi,[ecx
+TPicRegion::byte_width]
            dec     eax
            movzx   ecx,bh 
//u_8
            shr     ebx,16
            imul    eax,esi
            lea     edx,[edx
+ebx*4-4]
            add     edx,eax 
//pixel

            mov     eax,ecx
            neg     ecx

            pxor    mm7,mm7  
//0
            
//mov     edx,pixel
            pxor    mm0,mm0  //result=0
            
//lea     eax,auv_7

            lea    ebx,[(offset SinXDivX_Table_MMX)
+256*4+edi*4]
            call  _private_ThreeOrder_Fast_MMX
            lea    ebx,[(offset SinXDivX_Table_MMX)
+edi*4]
            call  _private_ThreeOrder_Fast_MMX
            neg    edi
            lea    ebx,[(offset SinXDivX_Table_MMX)
+256*4+edi*4]
            call  _private_ThreeOrder_Fast_MMX
            lea    ebx,[(offset SinXDivX_Table_MMX)
+512*4+edi*4]
            call  _private_ThreeOrder_Fast_MMX

            psraw     mm0,
3
            mov       eax,result
            packuswb  mm0,mm7
            movd      [eax],mm0
            
//emms
        }
    }

    
void ThreeOrder_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        unsigned 
long x0_sub1=(x_16>>16)-1;
        unsigned 
long y0_sub1=(y_16>>16)-1;
        
long u_16_add1=((unsigned short)(x_16))+(1<<16);
        
long v_16_add1=((unsigned short)(y_16))+(1<<16);

        TARGB32 pixel[
16];

        
for (long i=0;i<4;++i)
        {
            
long y=y0_sub1+i;
            pixel[i
*4+0]=Pixels_Bound(pic,x0_sub1  ,y);
            pixel[i
*4+1]=Pixels_Bound(pic,x0_sub1+1,y);
            pixel[i
*4+2]=Pixels_Bound(pic,x0_sub1+2,y);
            pixel[i
*4+3]=Pixels_Bound(pic,x0_sub1+3,y);
        }
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0];
        npic.byte_width
=4*sizeof(TARGB32);
        
//npic.width     =4;
        
//npic.height    =4;
        ThreeOrder_Fast_MMX(npic,u_16_add1,v_16_add1,result);
    }

void PicZoom_ThreeOrder_MMX(const TPicRegion& Dst,const TPicRegion& Src)
{
    
if (  (0==Dst.width)||(0==Dst.height)
        
||(0==Src.width)||(0==Src.height)) return;

    
long xrIntFloat_16=((Src.width)<<16)/Dst.width
    
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
    
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
    
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);

    unsigned 
long dst_width=Dst.width;

    
//计算出需要特殊处理的边界
    long border_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y*yr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
    if (border_y0>=Dst.height) border_y0=Dst.height;
    
long border_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
    
if (border_x0>=Dst.width ) border_x0=Dst.width;
    
long border_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1//y0+y*yr<=(height-3) => y<=(height-3-csDErrorY)/yr
    if (border_y1<border_y0) border_y1=border_y0;
    
long border_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;; 
    
if (border_x1<border_x0) border_x1=border_x0;

    TARGB32
* pDstLine=Dst.pdata;
    
long srcy_16=csDErrorY;
    
long y;
    
for (y=0;y<border_y0;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y0;y<border_y1;++y)
    {
        
long srcx_16=csDErrorX;
        
long x;
        
for (x=0;x<border_x0;++x)
        {
            ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        
for (x=border_x0;x<border_x1;++x)
        {
            ThreeOrder_Fast_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//fast MMX !
            srcx_16+=xrIntFloat_16;
        }
        
for (x=border_x1;x<dst_width;++x)
        {
            ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]);//border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }
    
for (y=border_y1;y<Dst.height;++y)
    {
        
long srcx_16=csDErrorX;
        
for (unsigned long x=0;x<dst_width;++x)
        {
            ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,
&pDstLine[x]); //border
            srcx_16+=xrIntFloat_16;
        }
        srcy_16
+=yrIntFloat_16;
        ((TUInt8
*&)pDstLine)+=Dst.byte_width;
    }

    asm emms
}


//速度测试:
//==============================================================================
// PicZoom_ThreeOrder_MMX   34.3 fps

 

 N:将测试结果放到一起:


//CPU: AMD64x2 4200+(2.37G)  zoom 800*600 to 1024*768
//==============================================================================
// StretchBlt                   232.7 fps  
// PicZoom3_SSE                 711.7 fps  
// PicZoom_BilInear_MMX_Ex      157.0 fps
//
// PicZoom_ThreeOrder0            3.6 fps
// PicZoom_ThreeOrder_Common     16.9 fps
// PicZoom_ThreeOrder_MMX        34.3 fps

补充Intel Core2 4400上的测试成绩:


//CPU: Intel Core2 4400(2.00G)  zoom 800*600 to 1024*768
//==============================================================================
// PicZoom3_SSE                1099.7 fps  
// PicZoom_BilInear_MMX_Ex      142.9 fps
//
// PicZoom_ThreeOrder0            4.2 fps
// PicZoom_ThreeOrder_Common     17.6 fps
// PicZoom_ThreeOrder_MMX        34.4 fps

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

0
0
主题推荐
图像处理 图形
猜你在找
有趣的算法(数据结构)
数据结构和算法
移动APP测试基础到进阶
Java经典算法讲解
Flash实战技能应用从入门到精通
查看评论
97楼 33184777 2015-01-01 11:28发表 [回复]
MMX之类的 优化 只对32位图像有效, 24位或者灰度图很难用了。 32位一个像素四个字节正好满足MMX内部的一些需求。
96楼 nancy_princess 2014-11-07 15:35发表 [回复]
对于上面的公式,它将图片向右下各移动了半个像素,需要对此做一个修正;
=> Sx=(Dx+0.5)*SW/DW-0.5; Sy=(Dy+0.5)*SH/DH-0.5;
没看出来哪向右下各移动了半个像素啊?
95楼 u013574852 2014-01-28 23:21发表 [回复]
把这个用到全景图像变换中的双向性插值,速度没有提升,可能稍微再优化一下,能到40ms每帧,自己的优化代码能到47ms每帧,不过用mmx这个快速算法,结果显示有一丁点锯齿,感觉是其中移位操作出现次序问题
94楼 yankaiyutong6 2013-09-04 14:42发表 [回复]
*****************接上一段代码************************

*result=(int16_t)((GA & 0x000007E0)|((BR & 0x0000F81F)>>8));
}
Re: housisong 2013-09-13 06:27发表 [回复]
回复yankaiyutong6:先理解我的代码如何处理各颜色分量的,再改写为你的代码,不要只是拷贝代码.
93楼 yankaiyutong6 2013-09-04 14:40发表 [回复]
void Bilinear_Fast_Common(int16_t* PColor0,int16_t* PColor1,unsigned long u_8,unsigned long v_8,int16_t* result)
{
unsigned long pm3_8=(u_8*v_8)>>8;
unsigned long pm2_8=u_8-pm3_8;
unsigned long pm1_8=v_8-pm3_8;
unsigned long pm0_8=256-pm1_8-pm2_8-pm3_8;

unsigned long Color=(unsigned long)(*PColor0);
unsigned long GA=((Color & 0x000007E0)>>8)*pm0_8;
unsigned long BR=(Color & 0x0000F81F)*pm0_8;
Color=(unsigned long)PColor0[1];
GA+=((Color & 0x000007E0)>>8)*pm2_8;
BR+=(Color & 0x0000F81F)*pm2_8;
Color=(unsigned long)*PColor1;
GA+=((Color & 0x000007E0)>>8)*pm1_8;
BR+=(Color & 0x0000F81F)*pm1_8;
Color=(unsigned long)PColor1[1];
GA+=((Color & 0x000007E0)>>8)*pm3_8;
BR+=(Color & 0x0000F81F)*pm3_8;
92楼 yankaiyutong6 2013-09-04 14:40发表 [回复]
楼主太牛了。我在将Bilinear移植中,出现的问题,博主可否帮忙看一下。多谢。
我使用的是RGB565的方式,对Bilinear_Fast_Common()函数修改如下。
这样出现的情况是颜色不正确,博主可否帮忙看一下应该如何修改,多谢!
91楼 foggy32 2013-03-31 20:14发表 [回复]
lz你好,我想问一下那个读图像的load函数里的lockbuf何意?拿来干嘛的。。
Re: housisong 2013-04-01 14:05发表 [回复]
回复foggy32:外面用来访问图像数据的内存区;这个时候数据区的拥有者也可以借此干点私活
90楼 kivi206 2013-01-26 20:08发表 [回复]
为什么我的三次卷积差值比二次线性插值的效果差?
三次卷积差值的图片放大后有麦芒样的边缘,而且好像字都变粗了,试过改a的值从-2到+2.效果都不是很好;
二次线性差值的效果放大倍数比较大的时候比较模糊,有哪个参数可以调整一下效果吗?
89楼 CAS_wycherley 2012-09-13 10:02发表 [回复]
赞一个
88楼 zhuzhubin 2011-11-14 10:09发表 [回复]
请问有没有一种算法的时候 同时清晰度能提高上去 也就是二次线性插值和三次卷积插值折中的办法 二次线性插值图片清晰度不够 三次卷积插值太耗cpu了
87楼 yjx279742048 2011-10-26 19:54发表 [回复]
你好啊,刚才向你问的那个问题已经解决了,是个小问题,我两边代码同时运行,比较一下就弄好了。
另外有个其他的问题向你请教,我现在也是在做算法方面的工作,代码写好后,也需要对代码进行优化,提高运算速度。这一方面以前没有接触过,不知道怎么下手好。请你指点下,给我介绍些资料,带我入门啊,谢谢。我的邮箱yjx_108@163.com。
Re: housisong 2011-10-27 11:15发表 [回复]
回复yjx279742048:可以看看我的blog里关于优化的一些杂文,应该有些帮助: http://blog.csdn.net/housisong/article/category/232185 ;
86楼 yjx279742048 2011-10-26 17:01发表 [回复]
Hi,你好,我刚刚把PicZoom_Bilinear2移植了一下,主要是把32位ARGB图改成8位位图。发现一个问题,边界处理时会出现像素值跳变的问题,看缩放后的图会明显看见四个边界都会有不连续的地方。检查了一下,发现这个问题:Bilinear2_Fast函数中 ,在一个点,假设PColor0和PColor1所指的四个像素值分别是102,103, 138,135,那算出来result的值可能会是168;而在相临另一个点,四个像素值变化很小,算出来的result值又会变成70。这样图像中看起来就成跳变了。
我自己对定点运算不太熟悉,希望你抽空帮我看看这个原因。如果有时间,请回复我啊,这里回也行,回我邮箱也行,yjx_108@163.com。谢谢啊。
85楼 awgin 2011-08-24 09:25发表 [回复]
数学学的不好,看起来有点吃力。
84楼 zistzh 2011-08-01 15:40发表 [回复]
楼主高人!
以前也做过一些图像算法的优化,包括MMX的,累人啊。
谢谢楼主,学到了很多
优化无止境啊
83楼 chengxuyuanit 2011-07-28 10:26发表 [回复]
有没有三次卷积缩放16位图片的啊,很期待
82楼 monokuro00 2011-06-29 23:10发表 [回复]
[e01]
81楼 ding_ 2011-06-09 16:57发表 [回复]
[e03]
80楼 gdutljg 2010-08-06 10:21发表 [回复]
我的意思是,在Bilinear_Fast_MMX里用两个字节精度来运算,可以完成那个算法吗?
79楼 gdutljg 2010-08-06 10:04发表 [回复]
楼主你好, 二次线性插值时, 在Bilinear_Fast_MMX这个函数里,你在计算完tmpColor0,tmpColor1 后,就把它右移8位,之后再计算。这样做会不会损失精度?我参照按Bilinear_Fast_MMX写了一个,图片有锯齿,不知是不是因为精度损失了?
78楼 gdutljg 2010-07-28 12:05发表 [回复]
膜拜![e01]
77楼 kuanghong 2010-05-25 11:39发表 [回复]
双线性里
pm1=v*(1-u);
pm2=u*(1-v);
pm1和pm2搞反了,需要交换一下。
Re: kuanghong 2010-05-25 12:43发表 [回复]
回复 kuanghong:搞错了,是对的。自己吧c0,c1,c2,c3顺序搞错了。
76楼 ehoo_jack 2009-08-20 15:13发表 [回复]
感谢楼主的无私,但是好多还是没有看的很明白,求此代码,bjec110@126.com 谢谢 !!!
75楼 jt_feelcool007 2009-07-27 21:35发表 [回复]
C#中好像不能直接写入汇编代码,郁闷了。。。。
74楼 jt_feelcool007 2009-07-27 20:57发表 [回复]
博主您好,看了你的文章受益匪浅,在此求代码
jt_feelcool007@sina.com
73楼 yezijian_721 2009-06-11 18:03发表 [回复]
楼主能否发份代码给我,下载不了,yezijian721@gmail.com
72楼 housisong 2009-06-05 20:31发表 [回复]
你仔细看看代码; 那里传的是指针!
71楼 im_ym 2009-06-03 18:00发表 [回复]
void PicZoom_Bilinear2(const TPicRegion&amp; Dst,const TPicRegion&amp; Src) 和以后的函数中为什么都只用了color0和color1进行计算了? color2和color3都不需要用了吗?
70楼 gaoxuankang 2009-05-20 15:14发表 [回复]
您好,您的代码我主要做了三次卷积这部分的,将32位改为16位的我做出来了,但是将32位的改成16位的MMX代码一直有问题,您能帮我指出问题在哪么?谢谢了。
我的邮箱是:gaoxuankang@gmail.com
能告诉我您的邮箱么?把我做的MMX三次卷积代码给您发过去。
69楼 neugls 2009-05-02 18:28发表 [回复]
太强大了
68楼 housisong 2009-04-23 12:46发表 [回复]
en 用256来减更好些 我会更新一下文章; 你的代码不全而且格式丢失,可以用邮件联系;delphi的MMX版本你可以用Bilinear_Fast_MMX来改写,少量修改就可以了
67楼 阿发伯 2009-04-22 20:06发表 [回复]
说明,上面过程中的[ebx].TImageOperate.FStride等是对我定义的类数据的引用,通过字面应该能明白意思;x,y扩大256倍;因原数据已经作了边界扩展,所以过程中没有边界检测代码。你的mmx比我用得好,看看可不可以继续改进?如果有疑问可发邮件给我(在你的好友名单中查)
Re: housisong 2009-04-22 23:13发表 [回复]
你的代码不全,而且回车和格式也没有了,可以给我发邮件
我的MMX代码你可以借鉴Bilinear_Fast_MMX,改成delphi版的代码只需要很少的改动
66楼 阿发伯 2009-04-22 19:56发表 [回复]
认识我吧!我按你的二次线形插值定点数算法写了几个版本,基本不错,但后来发现无论哪个版本,x,y无论是扩大65536还是256,计算出的ARGB都偏小,如Alpha原为255,返回值却为253,我仔细查了原因,与扩大倍数无关,问题在于v,u,255-v,255-u等都是以x,y余数的255倍,v,u相乘可能的最大值是65025,而最后计算结果时却用65536去除,所以结果偏小了。但如果改用65025,又会大大影响速度,所以我做了一点对结果影响不大的小改进,即255-v,255-u改为256-v,256-u,最后达到了要求,下面是我写的一个Delphi类内部使用的过程(其实是个函数,不过没标明参数和返回值):// in ebx = TImageOperate object, edx = x, ecx = y// mm7 = 00 00 00 00 00 00 00 00// out eax = ARGB colorprocedure GetBilinearColor;asmpushesi push edx push ecx// pxor mm7, mm7 mov esi, ecx mov eax, edx sar esi, 8 // y0 = y &gt;&gt; 8 sar eax, 8 // x0 = x &gt;&gt; 8 imul esi, [ebx].TImageOperate.FStride add esi, [ebx].TImageOperate.FScan0 shl eax, 2 add esi, eax // esi = Scan0 y0 * Stride x0 * 4 movq mm0, [esi] // mm0 = C2 (x0 1, y0) C0(x0, y0) movq mm1, mm0 add esi, [ebx].TImageOperate.FStride // [esi] = C3(x0 1, y0 1) C1(x0, y0 1) punpcklbw mm0, [esi] // mm0 = A1 A0 R1 R0 G1 G0 B1 B0 punpckhbw mm1, [esi] // mm1 = A3 A2 R3 R2 G3 G2 B3 B2 movq mm2, mm0 movq mm3, mm1 punpcklbw mm0, mm7 // mm0 = 00 G1 00 G0 00 B1 00 B0 punpcklbw mm1, mm7 // mm1 = 00 G3 00 G2 00 B3 00 B2 punpckhbw mm2, mm7 // mm2 = 00 A1 00 A0 00 R1 00 R0 punpckhbw mm3, mm7 // mm3 = 00 A3 00 A2 00 R3 00 R2 mov eax, 100hand edx, 255 // u = x
65楼 housisong 2008-12-01 11:33发表 [回复]
to: whdqpi
这里就是源代码了
关于定点数优化/查表优化/MMX优化等 要搞明白 建议看看我的其他优化文章
64楼 whdqpi 2008-11-30 19:34发表 [回复]
您的这个网页我已经看过很多次了,对于三次插值算法,浮点的那个已经看懂了,但是对于后面两次优化的程序还是不太明白,希望您能将完整的三次插值的程序源码发给我一份,最好是能多写点注释的。谢谢!
whdqpi@126.com
63楼 hdjhdj 2008-09-28 11:19发表 [回复]
有没有DELPHI的代码呢?能不能帮我发一个二线插值delphi MMX的算法到hdj5_cn@163.com
62楼 journ 2008-07-01 18:29发表 [回复]
牛就一个字
61楼 housisong 2008-06-20 14:25发表 [回复]
to: wanruirui
你使用不是汇编优化的版本不就可以了吗:(
60楼 红烧大肠 2008-06-20 11:04发表 [回复]
我想把你的代码用在CE上.不知道汇编那段代码会不会有影响????
59楼 红烧大肠 2008-06-20 11:02发表 [回复]
代码啊代码。难度这么大留个代码啊,demo就更好了。想自己拷你代码却还有汇编。要命啊。dulton@gmail.com。谢谢。
58楼 xlkj 2008-06-07 19:22发表 [回复]
谢谢,C to delphi的代码我已经调试好了。
57楼 xlkjsoft 2008-06-06 19:03发表 [回复]
有没有DELPHI的代码呢?C的看不懂,能不能帮我发一个二线插值delphi MMX的算法到vod8844@163.com。看了文章,更深一步了解了二线插值算法,感谢了。
56楼 ivy 2008-06-06 16:56发表 [回复]
楼主真厉害,刚开始学习图像编程很困惑,看见你对缩放算法的研究很深入很钦佩。获益匪浅,慢慢学习吧,以后在向您请教!
55楼 savior00 2008-06-05 16:29发表 [回复]
太强了
学习一下
。。
为什么总是不对呢。
不知道能把源码发一分给我么..
zhaofenghai@gmail.com
54楼 iBug168 2008-06-03 13:42发表 [回复]
主要是我自己写了个缩放算法...好像有内存越界的问题..
痛苦ing....
53楼 iBug168 2008-06-03 13:41发表 [回复]
无语咯...
学习学习...
不知道能不能给份算法源码....
iidioter@gmail.com
52楼 zz 2008-05-29 08:54发表 [回复]
楼主,你好,我不太明白CSDerrorx,您说是一个预设的偏移值,我还是不太懂它的作用,为什么要用这个呢?
51楼 micvenus 2008-05-26 00:10发表 [回复]
高手,您好,您好厉害哈,能把3次卷积插值的源代码发给我看下噢?我现在也在写一个图像缩放的程序,用的是图像缩放的分片连续算法,希望你发3次卷积插值的代码,我想比较2个的效果,十分感激
我的邮箱是 micvenus@163.com
50楼 ericjwh 2008-05-24 01:34发表 [回复]
MMX的内存读写我还是不懂,头大阿,现在有缩放MMX的问题急于请教,已发到你邮箱,希望你帮我看看,需不需要用SSE(SSE2),万谢!
49楼 ericjwh 2008-05-21 21:46发表 [回复]
谢谢 资料收到了,很有用。 我先好好看看,再向你请教。
48楼 housisong 2008-05-21 10:37发表 [回复]
to:ericjwh
1. 我也不知道为什么出错了,思路应该没有错; 你调试跟踪一下吧,看哪一步没有达到预想的功能(VC调试时可以看到MMX寄存器的值)
2.你看看可以利用hadd*指令; 另外,有时也不一定非要水平加(一个累加器),某些时候可以用多个累加器而避免水平加
3.我自己写MMX的时候,很少去细调指令位置的,一般都是适当调整,保持一定的结构和可读性也比较重要;
关于MMX/SSE等的更多资料,建议直接到intel和amd的官方主页去下载手册;
(收邮件)
47楼 ericjwh 2008-05-20 22:11发表 [回复]
to:housisong

谢谢你的耐心指导,几句话点醒梦中人。

第一个问题真惭愧,我基本的概念都搞错了
第二个问题你说的也是我看错的地方,pmulhw相当于右移16位,我当成8位了。

还想再问两个问题

1 一个16位(32位)的数在mmx寄存器里怎么符号扩展到32位(64位)。 我先pslld(mm0,16) 然后psrad(mm0,16) 结果出错了

2 一个mmx寄存器里4个16位的数的和怎么算方便(0-15,16-31,32-47,48-63的和),能比移出到内存中算快吗

由于新学,我现在编写mmx代码在微码阻塞,指令配对上存在很大问题,速度提不起来,有时改写还会发生数据的溢出。 你能发点这方面的资料到我邮箱吗,要是方便的话,提供几份你写的非商业性的mmx,sse代码让我多熟悉一下。 非常感谢
46楼 housisong 2008-05-19 23:52发表 [回复]
to:ericjwh
1. eax寄存器是32bit,ax是其低位的16bit,ah是ax的高位的8bit,al是ax的低8位bit;
movzx edi,ah 这句相当于 edi=(eax &amp;0x0000FF00)&gt;&gt;8;

2.你应该计算错了:) 可能你要注意一下pmulhw的使用; 代码的效果我测试过的;

3.你说的SSE2,应该是指其提供的128bit的整数运算支持吧;在我的多次测试中,当前的CPU(包括intel/amd)的实现都不尽如人意,很多时候没有MMX的实现快;
45楼 ericjwh 2008-05-19 22:29发表 [回复]
只恨没有早点拜读你的文章阿,受益很大,佩服阿
有几个问题,是关于三次卷积插值的MMX运算的

1 inline void ThreeOrder_Fast_MMX 的注释
movzx edi,ah //v_8
movzx ecx,bh //u_8

这里大小头赋值 我感觉是al和bl才对应 v_8 和u_8,一般是小头。

2 inline void ThreeOrder_Fast_MMX 这个函数中最后 psraw mm0,3 是不是应该改成
psraw mm0,13 前面左移了14位,7位,又右移了8位,这里是不是应该是13

3 希望楼主能提供sse2的三次卷积插值算法 或者二次线性的,非常期望

我是新人,希望楼主能够指点一下,万分感激。我也在深圳,邮箱ericjwh@163.com
44楼 jacky22 2008-03-21 17:04发表 [回复]
你太牛了,我以前从来没会没事留言,不过,真的觉得你是个真正的技术人员,相对于你,我只是个枪手,惭愧。
43楼 housisong 2008-03-21 12:58发表 [回复]
to:nazio
CSDerrorx是一个预设的偏移值(X轴平移量);
"需要特殊处理边界"是通过离散数学计算得到的;
比如类似这样的缩放公式; s=(d*A+B)&gt;&gt;16; 由于 s&gt;=0并且s&lt;sW,那么可以有: 求d的最小值公式 ((d*A+B)&gt;&gt;16)&gt;=0; 最大值值公式((d*A+B)&gt;&gt;16)&lt;sW ......
42楼 nazio 2008-03-21 10:34发表 [回复]
lz我很困惑那个边界问题的CSDerrorx的物理意义是什么?
他是怎么样找到需要特殊处理边界的呢?
41楼 Beaning 2007-12-19 15:49发表 [回复]
to housisong

颜色现在改对了,是typedef struct TARGB16 //16 bit color
{
U8 b:4;
U8 g:4;
U8 r:4;
U8 a:4; //a is alpha
}TARGB16;
的问题,谢谢您的提醒:)
真是太感谢了^_^
还是想知道您的mail,您真的很强,很想认识您
40楼 Beaning 2007-12-19 15:22发表 [回复]
我只知道BMP的文件格式,然后是每个像素是RGB,还是您告诉我吧,没看到您的mail地址,告诉您我的mail地址吧,是fean800910@163.com,您给我发个mail,我就知道您的mail了:)
39楼 housisong 2007-12-19 13:08发表 [回复]
你知道16bit高彩色的颜色编码方式吗?

给我发邮件吧 blog上的谈论不适合贴代码
38楼 Beaning 2007-12-19 11:54发表 [回复]
for (x=border_x1;x&lt;dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&amp;pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst-&gt;byte_width;
}
for (y=border_y1;y&lt;(Dst-&gt;height)/2;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x&lt;dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&amp;pDstLine[x]); //border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst-&gt;byte_width;
}
}
//=============================================================
37楼 Beaning 2007-12-19 11:53发表 [回复]
TARGB16* pDstLine=Dst-&gt;pdata;
long Src_byte_width=Src-&gt;byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y&lt;border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x&lt;dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&amp;pDstLine[x]); //border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst-&gt;byte_width;
}
for (y=border_y0;y&lt;border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x&lt;border_x0;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&amp;pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}

{
unsigned long v_8=(srcy_16 &amp; 0xFFFF)&gt;&gt;8;
TARGB16* PSrcLineColor= (TARGB16*)((U8*)(Src-&gt;pdata)+Src_byte_width*(srcy_16&gt;&gt;16)) ;
for (unsigned long x=border_x0;x&lt;border_x1;++x)
{
TARGB16* PColor0=&amp;PSrcLineColor[srcx_16&gt;&gt;16];
TARGB16* PColor1=(TARGB16*)((U8*)(PColor0)+Src_byte_width);
Bilinear2_Fast(PColor0,PColor1,(srcx_16 &amp; 0xFFFF)&gt;&gt;8,v_8,&amp;pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
}

for (x=borde
36楼 Beaning 2007-12-19 11:53发表 [回复]
pixel[3]=Pixels_Bound(pic,x+1,y+1);

Bilinear2_Fast(&amp;pixel[0],&amp;pixel[2],u_16&gt;&gt;8,v_16&gt;&gt;8,result);
}
//=============================================================

void PicZoom_Bilinear2(const TPicRegion *Dst,const TPicRegion *Src)
{
if ( (0==Dst-&gt;width)||(0==Dst-&gt;height)
||(0==Src-&gt;width)||(0==Src-&gt;height)) return;

long xrIntFloat_16=((Src-&gt;width)&lt;&lt;16)/Dst-&gt;width+1;
long yrIntFloat_16=((Src-&gt;height)&lt;&lt;16)/Dst-&gt;height+1;
const long csDErrorX=-(1&lt;&lt;15)+(xrIntFloat_16&gt;&gt;1);
const long csDErrorY=-(1&lt;&lt;15)+(yrIntFloat_16&gt;&gt;1);

unsigned long dst_width=Dst-&gt;width;

long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr&gt;=0; y0=csDErrorY =&gt; y&gt;=-csDErrorY/yr
if (border_y0&gt;=Dst-&gt;height) border_y0=Dst-&gt;height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0&gt;=Dst-&gt;width ) border_x0=Dst-&gt;width;
long border_y1=((((Src-&gt;height-2)&lt;&lt;16)-csDErrorY)/yrIntFloat_16+1)/2; //y0+y*yr&lt;=(height-2) =&gt; y&lt;=(height-2-csDErrorY)/yr
if (border_y1&lt;border_y0) border_y1=border_y0;
long border_x1=(((Src-&gt;width-2)&lt;&lt;16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1&lt;border_x0) border_x1=border_x0;
35楼 Beaning 2007-12-19 11:52发表 [回复]
接上面:
//=============================================================

void Bilinear2_Fast(TARGB16* PColor0,TARGB16* PColor1,unsigned long u_8,unsigned long v_8,TARGB16* result)
{
unsigned long pm3_16=u_8*v_8;
unsigned long pm2_16=(u_8&lt;&lt;8)-pm3_16;
unsigned long pm1_16=(v_8&lt;&lt;8)-pm3_16;
unsigned long pm0_16=(1&lt;&lt;16)-pm1_16-pm2_16-pm3_16;

// result-&gt;a=((pm0_16*PColor0[0].a+pm2_16*PColor0[1].a+pm1_16*PColor1[0].a+pm3_16*PColor1[1].a)&gt;&gt;16);
// result-&gt;r=((pm0_16*PColor0[0].r+pm2_16*PColor0[1].r+pm1_16*PColor1[0].r+pm3_16*PColor1[1].r)&gt;&gt;16);
result-&gt;g=((pm0_16*PColor0[0].g+pm2_16*PColor0[1].g+pm1_16*PColor1[0].g+pm3_16*PColor1[1].g)&gt;&gt;16);
result-&gt;b=((pm0_16*PColor0[0].b+pm2_16*PColor0[1].b+pm1_16*PColor1[0].b+pm3_16*PColor1[1].b)&gt;&gt;16);
}
//=============================================================

void Bilinear2_Border(const TPicRegion *pic,const long x_16,const long y_16,TARGB16* result)
{
long x=(x_16&gt;&gt;16);
long y=(y_16&gt;&gt;16);
unsigned long u_16=((unsigned short)(x_16));
unsigned long v_16=((unsigned short)(y_16));

TARGB16 pixel[4];
pixel[0]=Pixels_Bound(pic,x,y);
pixel[1]=Pixels_Bound(pic,x+1,y);
pixel[2]=Pixels_Bound(pic,x,y+1);
34楼 Beaning 2007-12-19 11:49发表 [回复]
to housisong,您好,以下是我从您的32bit的二次线性插值做的图像缩放的code 改成的16bit,但显示的颜色不太对,而且图像有亮暗的闪烁,请问我哪里改的不对呢,谢谢您




typedef struct TARGB16 //32 bit color
{
U8 b,g; //a is alpha
}TARGB16;

typedef struct TPicRegion
{
TARGB16* pdata;
long byte_width;
long width;
long height;
}TPicRegion;









TPicRegion *dst=NULL,*src=NULL;



TARGB16 Pixels(const TPicRegion *pic,const long x,const long y)
{
return ( (TARGB16*)((U8*)pic-&gt;pdata+pic-&gt;byte_width*y) )[x];
}
//============================================================
TARGB16 Pixels_Bound(const TPicRegion *pic,long x,long y)
{
//assert((pic.width&gt;0)&amp;&amp;(pic.height&gt;0));
FBOOL IsInPic=FTRUE;
if (x&lt;0) {x=0; IsInPic=FFALSE; } else if (x&gt;=pic-&gt;width ) {x=pic-&gt;width -1; IsInPic=FFALSE; }
if (y&lt;0) {y=0; IsInPic=FFALSE; } else if (y&gt;=pic-&gt;height) {y=pic-&gt;height-1; IsInPic=FFALSE; }
TARGB16 result=Pixels(pic,x,y);
if (!IsInPic) result.g=0;
return result;
}


//=============================================================

void Bilinear2_Fast(TARGB16* PColor0,TARGB16* PColor1,unsigned long u_8,unsigned long v_8,TARGB16* result)
{
unsigne
33楼 Beaning 2007-12-19 09:25发表 [回复]
专家,您说的转换成16bit定点数,还是不清楚,大概要改动哪,发来参考呢,谢谢您了:)
32楼 housisong 2007-12-19 09:21发表 [回复]
to:Beaning
我的文章有旋转的实现:&lt;图形图像处理-之-任意角度的高质量的快速的图像旋转&gt;,分三篇; 难道你没有看到?!
关于不使用sin和cos的旋转,你可以建立一个表,预先保存好各个角度的sin值(cos的计算也可以用这个表);表的大小看你要求的精度,也可以考虑做一下sin小角度插值;
31楼 Beaning 2007-12-18 16:11发表 [回复]
高手,请详细的指点呢,请问有不用sin,cos的处理图像旋转的代码吗
30楼 Beaning 2007-12-18 15:58发表 [回复]
您有写有关图像旋转的代码吗
29楼 Beaning 2007-12-18 14:55发表 [回复]
麻烦您,能具体指点下内存位置计算的代码和RGB颜色分量访问这两个地方吗,十分感谢
28楼 Beaning 2007-12-18 14:35发表 [回复]
您好,我现在用的就是用的PicZoom_BilInear2,想先把这个弄好了,再试MMX的。这是我现在的调用您的PicZoom_BilInear2的情况,能详细的指教吗,或者能顺手改下或者提示下就更好了,我是个初学者:)

typedef struct TARGB32 //32 bit color
{
U8 b,g,r,a; //a is alpha
}TARGB32;

typedef struct TPicRegion
{
TARGB32* pdata;
long byte_width;
long width;
long height;
}TPicRegion;


U32 start_address= NULL;
TPicRegion *dst=NULL,*src=NULL;

src-&gt;pdata = (TARGB32*)(source_address+((imageInfo-&gt;width/imageInfo-&gt;scale/imageInfo-&gt;zoomIn)*imageInfo-&gt;position_x)+(((imageInfo-&gt;height/imageInfo-&gt;scale)/imageInfo-&gt;zoomIn- 1)*((720*imageInfo-&gt;position_y))));//(TARGB32*)(0x60dee0);//
src-&gt;byte_width = 720*4;
src-&gt;height = imageInfo-&gt;height/imageInfo-&gt;scale/imageInfo-&gt;zoomIn;
src-&gt;width = imageInfo-&gt;width/imageInfo-&gt;scale/imageInfo-&gt;zoomIn;
dst-&gt;pdata = (TARGB32*)address;
dst-&gt;byte_width = 720*4;
dst-&gt;width = 720;
dst-&gt;height = 576;
a = (float)dst-&gt;height/src-&gt;height;
b = (float)dst-&gt;width/src-&gt;width;

if(a&lt;b)
dst-&gt;width = (U32)(src-&gt;width*a);
else
dst-&gt;height= (U32)(src-&gt;height*b);

FM_PrStr("zoom START TIME VALUE is ");
FM_PrInt(FD_TIMER_ReadTimerStamp());

PicZoom_Bilinear2(dst,src);
FM_PrSt
27楼 housisong 2007-12-18 12:29发表 [回复]
to:bean
需要注意内存位置计算的代码和RGB颜色分量访问这两个地方
如果你不熟悉MMX,建议用PicZoom_BilInear2作为基础来改写
26楼 bean 2007-12-18 12:17发表 [回复]
to housisong

我现在想缩放16bit的,不知道用您的函数都需要改什么呢,请指教,很急,谢谢
25楼 bean 2007-12-18 11:25发表 [回复]
你好,我现在想缩放16bit的,请问上面的二次线性插值的函数只要把32的地方改成16是不是就可以用了呢
24楼 fz 2007-11-24 10:48发表 [回复]
恩,没错,我粗心了。
23楼 housisong 2007-11-23 10:49发表 [回复]
to: fz
你在仔细算算:)
22楼 fz 2007-11-23 10:11发表 [回复]
unsigned long pm0_8=256-pm1_8-pm2_8-pm3_8;
中应该是+pm3_8。
21楼 fz 2007-11-22 16:10发表 [回复]
我也写了几年程序了,但很少看到楼主这么详尽地把过程实现地讲解一个算法,受益匪浅。十分佩服。
20楼 housisong 2007-11-15 15:50发表 [回复]
to: wazdxm1980
你自己改一下吧 我把代码都贴出来了,把它改成8位灰度的很容易了吧
我也不可能把每种颜色的实现代码都单独最为例子实现出来吧
(为了一套算法代码,支持多种颜色 建议可以用模板来改写算法,再加上特化来做高级优化 我实际使用中的代码就是)
19楼 wazdxm1980 2007-11-15 13:44发表 [回复]
谢谢。
能否有8位灰度图像缩放的实例,谢谢!
dx.zong@gmail.com
18楼 housisong 2007-11-13 00:07发表 [回复]
二次线性插值使用的公式我替换了,边界处缩放像素值更精确了
17楼 housisong 2007-11-09 13:04发表 [回复]
to:deally
1. 对于bilinear我偷懒使用的是一个近似缩放算法,计算出的U/V当然和你修正后的系数不同;
2.这个公式确实有点问题(造成bug),我会在近期替换;缩放精确度会提高; 看来我的改进版本需要早点完成了,估计下周吧
3. :( 关于越界问题,我处理的是ARGB32颜色,当然是写4字节! 你处理的是8位的吧

谢谢deally
16楼 deally 2007-11-09 11:42发表 [回复]
MOVd dword ptr [edi+ebx*4],MM0 //write DstColor
这条语句就会造成内存越界的问题,你实际只需要mm0中的一个字节,但是却写了4个字节,考虑一下整个压缩后的图像的最后一个像素。
我觉得Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
可能会存在3问题,能解释下为什么这么处理么。
图片我发到你邮箱里面了。我这边有一些mmx优化后的固定的bilinear,bicubic算法,只能是1/2,1/4,2比例的。有机会可以帖出来。
15楼 deally 2007-11-09 11:28发表 [回复]
我这边处理一个1/2固定缩放,用你原来的算法和我校正后的算法效果很不一样。我这边有图,你可以看一下。
如果你用Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
来处理边界后在那些固定的比如1/2算法后,u,v系数就会很不一样,按理说u,v 都=0.5,不信的话你可以去查一下。
我这边也处理了bicubic算法。
14楼 housisong 2007-11-06 16:19发表 [回复]
to: deally
你没有仔细看文章;
1. 从PicZoom_BilInear1开始,为了不处理边界访问超界的问题
我使用了一个变形了的公式:Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
所以后面的函数不需要修正0.5的问题,也没有内存访问超界的问题

2.你如果要使用原来的公式,就必须考虑边界处理问题,边界处理方法可以 参考PicZoom_ThreeOrderX的边界处理方式

3.关于使用MMX的问题,二次线性插值缩放的速度
从59.9fps提高到了136.1fps,你能说MMX优化没有作用?!

4.我会考虑提供一个按照原始缩放算法实现的二次线性插值缩放,可能要过一段时间吧
13楼 deally 2007-11-06 14:57发表 [回复]
回帖怎么只能这么短,我在用楼主的算法时,发现两个问题,void PicZoom_BilInear3(const TPicRegion&amp; Dst,const TPicRegion&amp; Src)
没有校正0.5个像素的的偏移:unsigned long v_8=((srcy_16 &amp; 0xFFFF)+0x8000)&gt;&gt;8;
pDstLine[x]=BilInear3(PColor0,(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width),((srcx_16 &amp; 0xFFFF)+0x8000)&gt;&gt;8,v_8);
同样相应的mmx也存在这样的问题。
还有更致命的就是如果在没有说明的情况下,调用bilinear_mmx算法,会造成内存溢出的问题。所以最好要求dst能多分配4个字节。
希望在文章中改进下。
个人感觉mmx优化在这个算法中作用不是很大,完全没有体现出单指令多数据的特点。
12楼 deally 2007-11-06 14:46发表 [回复]
H:使用MMX指令改写;(速度的瓶颈在于计算,内存读写的优化意义不大)

//BilInear_MMX(out [edi+ebx*4];mm6=v_8,mm7=0,edx=srcx_16,esi=PSrcLineColor,ecx=PSrcLineColorNext)
void __declspec(naked) BilInear_MMX()
{
asm
{
mov eax,edx
shl eax,16
add eax,0x80000000;//校正0.5个像素
shr eax,24
//== movzx eax,dh //eax=u_8
MOVD MM5,eax
mov eax,edx
shr eax,16 //srcx_16&gt;&gt;16

MOVD MM0,dword ptr [ecx+eax*4+4]//MM0=Color0
MOVD MM2,dword ptr [ecx+eax*4] //MM2=Color2
PUNPCKLWD MM5,MM5
MOVD MM1,dword ptr [esi+eax*4+4]//MM1=Color1
MOVD MM3,dword ptr [esi+eax*4] //MM3=Color3
PUNPCKLDQ MM5,MM5 //mm5=u_8
PUNPCKLBW MM0,MM7
PUNPCKLBW MM1,MM7
PUNPCKLBW MM2,MM7
PUNPCKLBW MM3,MM7
PSUBw MM0,MM2
PSUBw MM1,MM3
PSLLw MM2,8
PSLLw MM3,8
PMULlw MM0,MM5
PMULlw MM1,MM5
PADDw MM0,MM2
PADDw MM1,MM3

PSRLw MM0,8
11楼 zww 2007-10-10 18:35发表 [回复]
你那有改写过的吗?我现在急着要用,要有麻烦发到我邮箱吧:zwwll520@163.com。谢谢了。
10楼 housisong 2007-10-10 11:29发表 [回复]
对于24位RGB颜色,上面的函数你都需要修改一下,特别是内存访问的地方(3字节颜色的内存位置计算比较麻烦呵), 有人改写过的。
9楼 zww 2007-10-10 10:50发表 [回复]
谢谢,我现在要对24位bmp进行放大,是不是只要把32位的改成24位的,其他的函数都可以继续使用吧。
8楼 housisong 2007-10-09 23:20发表 [回复]
to: zww
你可以使用windows里的DIB作为数据源;
Delphi或CB中你可以用TBitmap数据作为数据源;
实在不行,你自己建一个RGB32的数组,自己加载图片数据到数组,作为数据源;
7楼 zww 2007-10-09 22:19发表 [回复]
真的谢谢你的大作,真是太强了,怎样得到颜色数据首地址?能把完整代码发给我吗。我的邮箱:zwwll520@163.com.谢谢了。对于其中的一些细节还不是很清楚。
6楼 Leaveye 2007-08-14 15:49发表 [回复]
搜索过程中发现您的文章已经被转载至此:
http://school.ogdev.net/ArticleShow.asp?id=6136&amp;categoryid=5
5楼 riverqh 2007-07-29 16:02发表 [回复]
佩服得一塌糊涂,把楼主的BLOG URL加入收藏夹方便查看。

多谢楼主分享!
4楼 housisong 2007-01-14 09:19发表 [回复]
to:51357
实际应用中, pic.byte_width很有可能&gt;=sizeof(TARGB32) * pic.width
比如该TPicRegion是一个更大的TPicRegion的一个子区域;或者TPicRegion为了每行的起始数据内存对齐;或者是DirectX的表面等
定义pic.byte_width也就是为了代码的通用性
3楼 Jackson_GZ 2007-01-13 15:59发表 [回复]
sorry, 是我看错了
pic.byte_width = sizeof(TARGB32) * pic.width
2楼 Jackson_GZ 2007-01-13 15:48发表 [回复] [引用] [举报]
//那么访问一个点的函数可以写为:
inline TARGB32&amp; Pixels(const TPicRegion&amp; pic,const long x,const long y)
{
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}

这时有误,是否漏了 pic.width ?

pic.byte_width*y 改成 pic.byte_width*y * pic.width


http://www.ppmy.cn/news/833930.html

相关文章

《程序员的呐喊》一一1.6 神秘机器的笔记

本节书摘来自异步社区出版社《程序员的呐喊》一书中的第1章&#xff0c;第1.6节&#xff0c;作者&#xff1a;【美】Steve Yegge &#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.6 神秘机器的笔记 程序员的呐喊过去8年来&#xff08;2004年6月起至今&…

有关编程的12个猜想

摘要&#xff1a;编程世界的将来如何目前仍难预料&#xff0c;但可以肯定的一点是技术一直在加速发展。本文搜罗出12个独特的编程视角猜想&#xff0c;一起来看看有哪些猜想在不久的将来就能变为现实。 编程世界的将来如何目前仍难预料&#xff0c;但可以肯定的一点是技术一直…

【财富空间】曾国藩:三分精明,七分厚道,做人最高境界

授权图片 | 滕首诗 摄 大道至简&#xff0c;最有用的道理最浅白。 大智若愚&#xff0c;最笨拙的处世最精明。 做人也如吃饭&#xff0c;要有粗有细&#xff0c;才能营养均衡。 只要精明不要厚道&#xff0c;是小聪明&#xff1b;只要厚道&#xff0c;不要精明&#xff0c;是假…

区块链光谱

虫洞社区签约作者介绍 叶露(王二) 销售人员 克莱登技术有限公司 本文根据Taylor Pearson所著区块链光谱图,从密码学、分布式系统、政治学和经济学的角度对区块链做出的全方面分析: 想象你是一位大学院长,学院正要新增一门关于区块链的课程,那么这门区块链的课程该属于哪…

latex

使用latex的错误记录 这个系列的博客对入门这来说写的不错&#xff1a;(https://www.jianshu.com/p/1d99b3c883a6)。 在使用texstudio是时遇见的错误 在config->genneral->language里选择zh_CN可以将其语言改为中文&#xff0c;在设置->编译器->默认字体编码改为U…

研究生、本科生Java开发、后台、软件工程师秋招春招经验

研究生、本科生Java开发、后台、软件工程师秋招春招经验 在2020年10月份的时候结束了自己的秋招过程。在秋招过程中&#xff0c;我也算是大厂中厂都拿过多个offer。在这个过程。在这半年的秋招过程中&#xff0c;通过自己不断的摸爬滚打&#xff0c;也踩过不少的坑&#xff0c;…

【重磅分享:一种新技术开辟了无源产品设计新思路】

2019年&#xff0c;随着苹果宣布&#xff0c;系统升级到IOS13&#xff0c;苹果7之后的设备都开放NFC的权限&#xff0c;这无疑极大的刺激了NFC市场&#xff0c;一波新的行情即将爆发。那今天给各位分享一种可以稳稳抓住这波商机的新方案&#xff1a; 该方案采用独创的TurboNFC…

量子学习手记

【声明】 内容源自网络&#xff0c;版权归原作者&#xff01; 【自序】 费曼说没人理解量子力学。 量子力学是门科学不是玄学&#xff0c;是一大批物理大师共同创立的&#xff0c;我们生活中无时无刻都在用他&#xff0c;未来科技发展方向。因此闲暇之余浅尝辄止的学习算是与…