c/c++实现图像旋转任意角度

旋转原理和旋转公式:

文章出处
https://blog.csdn.net/wonengguwozai/article/details/52049092
推导旋转公式:
在这里插入图片描述
旋转示意图
有: tg(b)=y/x ----(1)
tg(a+b)=y’/x’ ----(2)
xx + yy = x’x’ + y’y’ ----(3)
有公式:tg(a+b) = ( tg(a)+tg(b) ) / ( 1-tg(a)tg(b) ) ----(4)
把(1)代入(4)从而消除参数b;
tg(a)+y/x = y’/x’
( 1-tg(a)y/x ) ----(5)
由(5)可以得x’=y’
(x-y
tg(a))/( x
tg(a)+y ) ----(6)
把(6)代入(3)从而消除参数x’,化简后求得:
y’=xsin(a)+ycos(a); ----(7)
把(7)代入(6),有:
x’=xcos(a)-ysin(a); ----(8)

OK,旋转公式有了,那么来看看在图片旋转中的应用;
假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x’, y’),有公式:
(x平移rx0,y平移ry0,角度a对应-RotaryAngle , 带入方程(7)、(8)后有: )
x’= (x - rx0)*cos(RotaryAngle) + (y - ry0)*sin(RotaryAngle) + rx0 ;
y’=-(x - rx0)*sin(RotaryAngle) + (y - ry0)*cos(RotaryAngle) + ry0 ;

那么,根据新的坐标点求源坐标点的公式为:
x=(x’- rx0)*cos(RotaryAngle) - (y’- ry0)*sin(RotaryAngle) + rx0 ;
y=(x’- rx0)*sin(RotaryAngle) + (y’- ry0)*cos(RotaryAngle) + ry0 ;

旋转的时候还可以顺便加入x轴和y轴的缩放和平移,而不影响速度,那么完整的公式为:
x=(x’- move_x-rx0)/ZoomXcos(RotaryAngle) - (y’- move_y-ry0)/ZoomYsin(RotaryAngle) + rx0 ;
y=(x’- move_x-rx0)/ZoomXsin(RotaryAngle) + (y’- move_y-ry0)/ZoomYcos(RotaryAngle) + ry0 ;
其中: RotaryAngle为逆时针旋转的角度;
ZoomX,ZoomY为x轴y轴的缩放系数(支持负的系数,相当于图像翻转);
move_x,move_y为x轴y轴的平移量;

公式推导

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一些颜色和图片的数据定义:

#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];
}
//判断一个点是否在图片中
inline bool PixelsIsInPic(const TPicRegion& pic,const long x,const long  y)
{
    return ( (x>=0)&&(x<pic.width) && (y>=0)&&(y< pic.height) );
}

B:一个简单的浮点实现版本

//函数假设以原图片的中心点坐标为旋转和缩放的中心
void PicRotary0(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double  move_y)
{
    if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见
    double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 
    double ry0=Src.height*0.5 ; 
    for (long y=0;y<Dst.height;++ y)
    {
        for (long x=0;x<Dst.width;++ x)
        {
            long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y- move_y-ry0)/ZoomY*sin(RotaryAngle) +  rx0) ;
            long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y- move_y-ry0)/ZoomY*cos(RotaryAngle) +  ry0) ;
            if  (PixelsIsInPic(Src,srcx,srcy))
                Pixels(Dst,x,y)= Pixels(Src,srcx,srcy);
        }
    }
}

(调用方法比如:
PicRotary0(ppicDst,ppicSrc,PI/6,0.9,0.9,(dst_wh-ppicSrc.width)*0.5,(dst_wh-ppicSrc.height)*0.5);
//作用:将图片ppicSrc按0.9的缩放比例旋转PI/6幅度后绘制到图片ppicDst的中心
)

//注:测试图片都是800600的图片旋转到10041004的图片中心 测试成绩取各个旋转角度的平均速度值

速度测试:

优化循环内部,化简系数
1.sin和cos函数是很慢的计算函数,可以在循环前预先计算好sin(RotaryAngle)和cos(RotaryAngle)的值:
double sinA=sin(RotaryAngle);
double cosA=cos(RotaryAngle);
2.可以将除以ZoomX、ZoomY改成乘法,预先计算出倒数:
double rZoomX=1.0/ZoomX;
double rZoomY=1.0/ZoomY;
3.优化内部的旋转公式,将能够预先计算的部分提到循环外(即:拆解公式):
原: long srcx=(long)((x- move_x-rx0)/ZoomXcos(RotaryAngle) - (y- move_y-ry0)/ZoomYsin(RotaryAngle) + rx0) ;
long srcy=(long)((x- move_x-rx0)/ZoomXsin(RotaryAngle) + (y- move_y-ry0)/ZoomYcos(RotaryAngle) + ry0) ;
变形为:
long srcx=(long)( Axx + Bxy +Cx ) ;
long srcy=(long)( Ayx + Byy +Cy ) ;
其中: Ax=(rZoomXcosA); Bx=(-rZoomYsinA); Cx=(-(rx0+move_x)rZoomXcosA+(ry0+move_y)rZoomYsinA+rx0);
Ay=(rZoomXsinA); By=(rZoomYcosA); Cy=(-(rx0+move_x)rZoomXsinA-(ry0+move_y)rZoomYcosA+ry0);
(提示: Ax,Bx,Cx,Ay,By,Cy都可以在旋转之前预先计算出来)

改进后的函数为:

void PicRotary1(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double  move_y)
{
    if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见
    double rZoomX=1.0/ ZoomX;
    double rZoomY=1.0/ ZoomY;
    double sinA= sin(RotaryAngle);
    double cosA= cos(RotaryAngle);
    double Ax=(rZoomX* cosA); 
    double Ay=(rZoomX* sinA); 
    double Bx=(-rZoomY* sinA); 
    double By=(rZoomY* cosA); 
    double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 
    double ry0=Src.height*0.5 ; 
    double Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+ rx0);
    double Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ ry0); 

    TARGB32* pDstLine= Dst.pdata;
    double srcx0_f= (Cx);
    double srcy0_f= (Cy);
    for (long y=0;y<Dst.height;++ y)
    {
        double srcx_f= srcx0_f;
        double srcy_f= srcy0_f;
        for (long x=0;x<Dst.width;++ x)
        {
            long srcx=(long )(srcx_f);
            long srcy=(long )(srcy_f);
            if  (PixelsIsInPic(Src,srcx,srcy))
                pDstLine[x]= Pixels(Src,srcx,srcy);
            srcx_f+= Ax;
            srcy_f+= Ay;
        }
        srcx0_f+= Bx;
        srcy0_f+= By;
        ((TUInt8*&)pDstLine)+= Dst.byte_width;
    }
}

//速度测试:
//==============================================================================
// PicRotary1 62.0 fps

( 在AMD64x2 4200+和VC2005编译下PicRotary1(51.8fps)比PicRotary0(27.1fps)快90%;
在AMD64x2 4200+和VC6编译下PicRotary1(20.3fps)比PicRotary0(16.1fps)快26%;
以前在赛扬2G和VC6编译下PicRotary1(8.4fps)反而比PicRotary0(12.7fps)慢50%! 😦 )

D:更深入的优化、定点数优化
(浮点数到整数的转化也是应该优化的一个地方,这里不再处理,可以参见
<图形图像处理-之-高质量的快速的图像缩放 上篇 近邻取样插值和其速度优化>中的PicZoom3_float函数)
1.优化除法:
原: double rZoomX=1.0/ZoomX;
double rZoomY=1.0/ZoomY;
改写为(优化掉了一次除法):
double tmprZoomXY=1.0/(ZoomXZoomY);
double rZoomX=tmprZoomXY
ZoomY;
double rZoomY=tmprZoomXY*ZoomX;
2.x86的浮点计算单元(FPU)有一条指令"fsincos"可以同时计算出sin和cos值
//定义SinCos函数: 同时计算sin(Angle)和cos(Angle)的内嵌x86汇编函数
void __declspec(naked) __stdcall SinCos(const double Angle,double& sina,double& cosa)
{
asm
{
fld qword ptr [esp+4]//Angle
mov eax,[esp+12]//&sina
mov edx,[esp+16]//&cosa
fsincos
fstp qword ptr [edx]
fstp qword ptr [eax]
ret 16
}
}

用定点数代替浮点计算

void PicRotary2(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double  move_y)
{
    if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见
    double tmprZoomXY=1.0/(ZoomX* ZoomY);  
    double rZoomX=tmprZoomXY* ZoomY;
    double rZoomY=tmprZoomXY* ZoomX;
    double  sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    long Ax_16=(long)(rZoomX*cosA*(1<<16 )); 
    long Ay_16=(long)(rZoomX*sinA*(1<<16 )); 
    long Bx_16=(long)(-rZoomY*sinA*(1<<16 )); 
    long By_16=(long)(rZoomY*cosA*(1<<16 )); 
    double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 
    double ry0=Src.height*0.5 ; 
    long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16 ));
    long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16 )); 

    TARGB32* pDstLine= Dst.pdata;
    long srcx0_16= (Cx_16);
    long srcy0_16= (Cy_16);
    for (long y=0;y<Dst.height;++ y)
    {
        long srcx_16= srcx0_16;
        long srcy_16= srcy0_16;
        for (long x=0;x<Dst.width;++ x)
        {
            long srcx=(srcx_16>>16 );
            long srcy=(srcy_16>>16 );
            if  (PixelsIsInPic(Src,srcx,srcy))
                pDstLine[x]= Pixels(Src,srcx,srcy);
            srcx_16+= Ax_16;
            srcy_16+= Ay_16;
        }
        srcx0_16+= Bx_16;
        srcy0_16+= By_16;
        ((TUInt8*&)pDstLine)+= Dst.byte_width;
    }
}

//速度测试:
//==============================================================================
// PicRotary2 134.2 fps

E:优化内部循环的判断函数PixelsIsInPic,将判断展开到内部循环之外,跳过不需要处理的目标像素;
TRotaryClipData类用于寻找旋转需要处理的边界范围;算法思路是首先寻找原图片中心点对应的;
那条扫描线,然后依次向上和向下寻找边界; 如果想要更快速的边界寻找算法,可以考虑利用像素的直线
绘制原理来自动生成边界(有机会的时候再来实现它);
在这里插入图片描述
边界寻找算法图示

struct  TRotaryClipData{
public :
    long  src_width;
    long  src_height;
    long  dst_width;
    long  dst_height;
    long  Ax_16; 
    long  Ay_16; 
    long  Bx_16; 
    long  By_16; 
    long  Cx_16;
    long  Cy_16; 
public :
    long  out_dst_up_y;
    long  out_dst_down_y;

    long  out_src_x0_16;
    long  out_src_y0_16;
private :
    long  out_dst_up_x0;
    long  out_dst_up_x1;
    long  out_dst_down_x0;
    long  out_dst_down_x1;
public :
    inline long get_up_x0(){ if (out_dst_up_x0<0) return 0;  else return  out_dst_up_x0; }
    inline long get_up_x1(){ if (out_dst_up_x1>=dst_width) return dst_width;  else return  out_dst_up_x1; }
    inline long get_down_x0(){ if (out_dst_down_x0<0) return 0;  else return  out_dst_down_x0; }
    inline long get_down_x1(){ if (out_dst_down_x1>=dst_width) return dst_width;  else return  out_dst_down_x1; }

    inline bool is_in_src(long src_x_16,long  src_y_16)
    {
         return ( ( (src_x_16>=0)&&((src_x_16>>16)< src_width) )
               && ( (src_y_16>=0)&&((src_y_16>>16)< src_height) ) );
    }
    void find_begin_in(long dst_y,long& out_dst_x,long& src_x_16,long&  src_y_16)
    {
        src_x_16-= Ax_16;
        src_y_16-= Ay_16;
        while  (is_in_src(src_x_16,src_y_16))
        {
            -- out_dst_x;
            src_x_16-= Ax_16;
            src_y_16-= Ay_16;
        }
        src_x_16+= Ax_16;
        src_y_16+= Ay_16;
    }
    bool find_begin(long dst_y,long& out_dst_x0,long  dst_x1)
    {
        long test_dst_x0=out_dst_x0-1 ;
        long src_x_16=Ax_16*test_dst_x0 + Bx_16*dst_y +  Cx_16;
        long src_y_16=Ay_16*test_dst_x0 + By_16*dst_y +  Cy_16;
        for (long i=test_dst_x0;i<=dst_x1;++ i)
        {
            if  (is_in_src(src_x_16,src_y_16))
            {
                out_dst_x0= i;
                if (i== test_dst_x0)
                    find_begin_in(dst_y,out_dst_x0,src_x_16,src_y_16);
                if (out_dst_x0<0 )
                {
                    src_x_16-=(Ax_16* out_dst_x0);
                    src_y_16-=(Ay_16* out_dst_x0);
                }
                out_src_x0_16= src_x_16;
                out_src_y0_16= src_y_16;
                return true ;
            }
            else
            {
                src_x_16 += Ax_16;
                src_y_16+= Ay_16;
            }
        }
        return false ;
    }
    void find_end(long dst_y,long dst_x0,long&  out_dst_x1)
    {
        long test_dst_x1= out_dst_x1;
        if (test_dst_x1<dst_x0) test_dst_x1= dst_x0;

        long src_x_16=Ax_16*test_dst_x1 + Bx_16*dst_y +  Cx_16;
        long src_y_16=Ay_16*test_dst_x1 + By_16*dst_y +  Cy_16;
        if  (is_in_src(src_x_16,src_y_16))
        {
            ++ test_dst_x1;
            src_x_16+= Ax_16;
            src_y_16+= Ay_16;
            while  (is_in_src(src_x_16,src_y_16))
            {
                ++ test_dst_x1;
                src_x_16+= Ax_16;
                src_y_16+= Ay_16;
            }
            out_dst_x1= test_dst_x1;
        }
        else
        {
            src_x_16 -= Ax_16;
            src_y_16-= Ay_16;
            while (! is_in_src(src_x_16,src_y_16))
            {
                -- test_dst_x1;
                src_x_16-= Ax_16;
                src_y_16-= Ay_16;
            }
            out_dst_x1= test_dst_x1;
        }
    }

    bool inti_clip(double move_x,double  move_y)
    {
        //计算src中心点映射到dst后的坐标
        out_dst_down_y=(long)(src_height*0.5+ move_y);
        out_dst_down_x0=(long)(src_width*0.5+ move_x);
        out_dst_down_x1= out_dst_down_x0;
        //得到初始out_???
        if  (find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1))
            find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);
        out_dst_up_y= out_dst_down_y;
        out_dst_up_x0= out_dst_down_x0;
        out_dst_up_x1= out_dst_down_x1;
        return (out_dst_down_x0< out_dst_down_x1);
    }
    bool  next_clip_line_down()
    {
        ++ out_dst_down_y;
        if (!find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1)) return false ;
        find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);
        return (out_dst_down_x0< out_dst_down_x1);
    }
    bool  next_clip_line_up()
    {
        -- out_dst_up_y;
        if (!find_begin(out_dst_up_y,out_dst_up_x0,out_dst_up_x1)) return false ;
        find_end(out_dst_up_y,out_dst_up_x0,out_dst_up_x1);
        return (out_dst_up_x0< out_dst_up_x1);
    }
};
void PicRotary3_CopyLine(TARGB32* pDstLine,long dstCount,long Ax_16,long  Ay_16,
                        long srcx0_16,long srcy0_16,TARGB32* pSrcLine,long  src_byte_width)
{
    for (long x=0;x<dstCount;++ x)
    {
        pDstLine[x]=Pixels(pSrcLine,src_byte_width,srcx0_16>>16,srcy0_16>>16 );
        srcx0_16+= Ax_16;
        srcy0_16+= Ay_16;
    }
}

void PicRotary3(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double  move_y)
{
    if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见
    double tmprZoomXY=1.0/(ZoomX* ZoomY);  
    double rZoomX=tmprZoomXY* ZoomY;
    double rZoomY=tmprZoomXY* ZoomX;
    double  sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    long Ax_16=(long)(rZoomX*cosA*(1<<16 )); 
    long Ay_16=(long)(rZoomX*sinA*(1<<16 )); 
    long Bx_16=(long)(-rZoomY*sinA*(1<<16 )); 
    long By_16=(long)(rZoomY*cosA*(1<<16 )); 
    double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 
    double ry0=Src.height*0.5 ; 
    long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16 ));
    long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16 )); 

    TRotaryClipData rcData;
    rcData.Ax_16= Ax_16;
    rcData.Bx_16= Bx_16;
    rcData.Cx_16= Cx_16;
    rcData.Ay_16= Ay_16;
    rcData.By_16= By_16;
    rcData.Cy_16= Cy_16;
    rcData.dst_width= Dst.width;
    rcData.dst_height= Dst.height;
    rcData.src_width= Src.width;
    rcData.src_height= Src.height;
    if (!rcData.inti_clip(move_x,move_y)) return ;

    TARGB32* pDstLine= Dst.pdata;
    ((TUInt8*&)pDstLine)+=(Dst.byte_width* rcData.out_dst_down_y);
    while (true) //to down
    {
        long y= rcData.out_dst_down_y;
        if (y>=Dst.height) break ;
        if (y>=0 )
        {
            long x0= rcData.get_down_x0();
            PicRotary3_CopyLine(&pDstLine[x0],rcData.get_down_x1()- x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
        if (!rcData.next_clip_line_down()) break ;
        ((TUInt8*&)pDstLine)+= Dst.byte_width;
    }
   
    pDstLine= Dst.pdata;
    ((TUInt8*&)pDstLine)+=(Dst.byte_width* rcData.out_dst_up_y);
    while (rcData.next_clip_line_up()) //to up
    {
        long y= rcData.out_dst_up_y;
        if (y<0) break ;
        ((TUInt8*&)pDstLine)-= Dst.byte_width;
        if (y< Dst.height)
        {
            long x0= rcData.get_up_x0();
            PicRotary3_CopyLine(&pDstLine[x0],rcData.get_up_x1()- x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
    }
}
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个基于C语言的图像旋转源码片段,仅支持bmp格式的图像。源码主要通过读取bmp文件的header以及像素数据进行图像旋转操作,并将旋转后的图像保存为新的bmp文件。 该源码的主要步骤包括: 1. 读取源bmp文件的header信息,获取图像的宽度、高度、色彩位数等参数。 2. 分配内存,用于存储旋转后的图像像素数据。 3. 读取源bmp文件的像素数据,并将其存储到分配的内存中。 4. 根据旋转角度,计算旋转后的图像的宽度、高度,并分配内存用于存储旋转后的像素数据。 5. 对每个像素进行旋转计算,并将其存储到相应位置的像素数据中。 6. 创建新的bmp文件,写入头文件信息,并将旋转后的像素数据写入新文件。 以下是一个简单的图像旋转源码示例: ```c #include <stdio.h> #include <stdlib.h> #pragma pack(1) //取消结构体字节对齐 typedef unsigned short WORD; typedef unsigned int DWORD; typedef unsigned char BYTE; typedef struct { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BMPFILEHEADER; typedef struct { DWORD biSize; int biWidth; int biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; int biXPelsPerMeter; int biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BMPINFOHEADER; int main() { FILE *pFile = fopen("source.bmp", "rb"); // 打开源文件 FILE *pNewFile = fopen("rotated.bmp", "wb"); // 创建旋转图像的新文件 if (pFile == NULL || pNewFile == NULL) { printf("文件打开失败!\n"); return -1; } BMPFILEHEADER fileHeader; BMPINFOHEADER infoHeader; fread(&fileHeader, sizeof(BMPFILEHEADER), 1, pFile); // 读取文件头 fread(&infoHeader, sizeof(BMPINFOHEADER), 1, pFile); // 读取信息头 int width = infoHeader.biWidth; int height = infoHeader.biHeight; int bitCount = infoHeader.biBitCount; fseek(pFile, fileHeader.bfOffBits, SEEK_SET); // 定位到像素数据 BYTE *pData = (BYTE*)malloc(infoHeader.biSizeImage); // 分配内存存储像素数据 BYTE *pRotatedData = (BYTE*)malloc(infoHeader.biSizeImage); // 分配内存存储旋转后的像素数据 fread(pData, 1, infoHeader.biSizeImage, pFile); // 读取像素数据 int newWidth = height; int newHeight = width; // 图像旋转算法 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { *(pRotatedData + (j * newWidth + (newWidth - i - 1)) * bitCount / 8) = *(pData + (i * width + j) * bitCount / 8); } } // 写入新的bmp文件 fwrite(&fileHeader, sizeof(BMPFILEHEADER), 1, pNewFile); fwrite(&infoHeader, sizeof(BMPINFOHEADER), 1, pNewFile); fseek(pNewFile, fileHeader.bfOffBits, SEEK_SET); // 定位到像素数据 fwrite(pRotatedData, 1, infoHeader.biSizeImage, pNewFile); // 释放内存 free(pData); free(pRotatedData); // 关闭文件 fclose(pFile); fclose(pNewFile); return 0; } ``` 使用此源码,你可以实现对bmp格式图像旋转操作,旋转角度可以根据需要进行调整。保证源文件名为`source.bmp`,旋转后的文件名为`rotated.bmp`。运行此代码后,将会生成一个旋转90度后的图像文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值