如何按一定角度显示bmp图片!!!!!!!!!!!!!!!!!!!!!

月光莫利亚 2003-11-25 04:52:34
加精
请问高手在view上显示bmp图片,如何使其旋转一定的角度?
...全文
178 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jasonlu 2003-11-28
  • 打赏
  • 举报
回复

在你视图类里面调用RotateDIB函数

// 更新视图
pDoc->UpdateAllViews(NULL);
另外,注意更新调色板
HGLOBAL WINAPI RotateDIB(LPSTR lpDIB, int iRotateAngle)
{

// 源图像的宽度和高度
LONG lWidth;
LONG lHeight;

// 旋转后图像的宽度和高度
LONG lNewWidth;
LONG lNewHeight;

// 图像每行的字节数
LONG lLineBytes;

// 旋转后图像的宽度(lNewWidth',必须是4的倍数)
LONG lNewLineBytes;

// 指向源图像的指针
LPSTR lpDIBBits;

// 指向源象素的指针
LPSTR lpSrc;

// 旋转后新DIB句柄
HDIB hDIB;

// 指向旋转图像对应象素的指针
LPSTR lpDst;

// 指向旋转图像的指针
LPSTR lpNewDIB;
LPSTR lpNewDIBBits;

// 指向BITMAPINFO结构的指针(Win3.0)
LPBITMAPINFOHEADER lpbmi;

// 指向BITMAPCOREINFO结构的指针
LPBITMAPCOREHEADER lpbmc;

// 循环变量(象素在新DIB中的坐标)
LONG i;
LONG j;

// 象素在源DIB中的坐标
LONG i0;
LONG j0;

// 旋转角度(弧度)
float fRotateAngle;

// 旋转角度的正弦和余弦
float fSina, fCosa;

// 源图四个角的坐标(以图像中心为坐标系原点)
float fSrcX1,fSrcY1,fSrcX2,fSrcY2,fSrcX3,fSrcY3,fSrcX4,fSrcY4;

// 旋转后四个角的坐标(以图像中心为坐标系原点)
float fDstX1,fDstY1,fDstX2,fDstY2,fDstX3,fDstY3,fDstX4,fDstY4;

// 两个中间常量
float f1,f2;

// 找到源DIB图像象素起始位置
lpDIBBits = ::FindDIBBits(lpDIB);

// 获取图像的"宽度"(4的倍数)
lWidth = ::DIBWidth(lpDIB);

// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);

// 获取图像的高度
lHeight = ::DIBHeight(lpDIB);

// 将旋转角度从度转换到弧度
fRotateAngle = (float) RADIAN(iRotateAngle);

// 计算旋转角度的正弦
fSina = (float) sin((double)fRotateAngle);

// 计算旋转角度的余弦
fCosa = (float) cos((double)fRotateAngle);

// 计算原图的四个角的坐标(以图像中心为坐标系原点)
fSrcX1 = (float) (- (lWidth - 1) / 2);
fSrcY1 = (float) ( (lHeight - 1) / 2);
fSrcX2 = (float) ( (lWidth - 1) / 2);
fSrcY2 = (float) ( (lHeight - 1) / 2);
fSrcX3 = (float) (- (lWidth - 1) / 2);
fSrcY3 = (float) (- (lHeight - 1) / 2);
fSrcX4 = (float) ( (lWidth - 1) / 2);
fSrcY4 = (float) (- (lHeight - 1) / 2);

// 计算新图四个角的坐标(以图像中心为坐标系原点)
fDstX1 = fCosa * fSrcX1 + fSina * fSrcY1;
fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1;
fDstX2 = fCosa * fSrcX2 + fSina * fSrcY2;
fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2;
fDstX3 = fCosa * fSrcX3 + fSina * fSrcY3;
fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3;
fDstX4 = fCosa * fSrcX4 + fSina * fSrcY4;
fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4;

// 计算旋转后的图像实际宽度
lNewWidth = (LONG) ( max( fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2) ) + 0.5);

// 计算新图像每行的字节数
lNewLineBytes = WIDTHBYTES(lNewWidth * 8);

// 计算旋转后的图像高度
lNewHeight = (LONG) ( max( fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2) ) + 0.5);

// 两个常数,这样不用以后每次都计算了
f1 = (float) (-0.5 * (lNewWidth - 1) * fCosa - 0.5 * (lNewHeight - 1) * fSina
+ 0.5 * (lWidth - 1));
f2 = (float) ( 0.5 * (lNewWidth - 1) * fSina - 0.5 * (lNewHeight - 1) * fCosa
+ 0.5 * (lHeight - 1));

// 分配内存,以保存新DIB
hDIB = (HDIB) ::GlobalAlloc(GHND, lNewLineBytes * lNewHeight + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB));

// 判断是否内存分配失败
if (hDIB == NULL)
{
// 分配内存失败
return NULL;
}

// 锁定内存
lpNewDIB = (char * )::GlobalLock((HGLOBAL) hDIB);

// 复制DIB信息头和调色板
memcpy(lpNewDIB, lpDIB, *(LPDWORD)lpDIB + ::PaletteSize(lpDIB));

// 找到新DIB象素起始位置
lpNewDIBBits = ::FindDIBBits(lpNewDIB);

// 获取指针
lpbmi = (LPBITMAPINFOHEADER)lpNewDIB;
lpbmc = (LPBITMAPCOREHEADER)lpNewDIB;

// 更新DIB中图像的高度和宽度
if (IS_WIN30_DIB(lpNewDIB))
{
// 对于Windows 3.0 DIB
lpbmi->biWidth = lNewWidth;
lpbmi->biHeight = lNewHeight;
}
else
{
// 对于其它格式的DIB
lpbmc->bcWidth = (unsigned short) lNewWidth;
lpbmc->bcHeight = (unsigned short) lNewHeight;
}

// 针对图像每行进行操作
for(i = 0; i < lNewHeight; i++)
{
// 针对图像每列进行操作
for(j = 0; j < lNewWidth; j++)
{
// 指向新DIB第i行,第j个象素的指针
// 注意此处宽度和高度是新DIB的宽度和高度
lpDst = (char *)lpNewDIBBits + lNewLineBytes * (lNewHeight - 1 - i) + j;

// 计算该象素在源DIB中的坐标
i0 = (LONG) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5);
j0 = (LONG) ( ((float) j) * fCosa + ((float) i) * fSina + f1 + 0.5);

// 判断是否在源图范围内
if( (j0 >= 0) && (j0 < lWidth) && (i0 >= 0) && (i0 < lHeight))
{
// 指向源DIB第i0行,第j0个象素的指针
lpSrc = (char *)lpDIBBits + lLineBytes * (lHeight - 1 - i0) + j0;

// 复制象素
*lpDst = *lpSrc;
}
else
{
// 对于源图中没有的象素,直接赋值为255
* ((unsigned char*)lpDst) = 255;
}

}

}

// 返回
return hDIB;
}

hujun4u 2003-11-27
  • 打赏
  • 举报
回复
用OpenCv
或intelipp库,
简单易用
zjpixyniannian 2003-11-27
  • 打赏
  • 举报
回复
直接对象素进行操作,看看《VC++数字图像处理》
  • 打赏
  • 举报
回复
提醒你:
旋转后的结果 nx,ny 是浮点型的,绘制时要进行反走样
否则会惨不忍睹的。
很艰巨的工作呀,加油呀。
  • 打赏
  • 举报
回复
假使
旋转中心 cx ,cy
角度 a
x,y处的象素旋转后的位置 nx,ny
nx=x+cosa*(x-cx)-sina*(y-cy)
ny=y+sina*(x-cx)+cosa*(y-cy)
我到现在还没得过分 可怜可怜我吧。
月光莫利亚 2003-11-27
  • 打赏
  • 举报
回复
to肖天:可否讲的详细点,不太明白。
meler 2003-11-25
  • 打赏
  • 举报
回复
用GDI+
月光莫利亚 2003-11-25
  • 打赏
  • 举报
回复
谢谢踏雪有痕,不过看起来好像很困难,不知道你的方法能不能实现图片的任意角度旋转?努力研究先。
robothn 2003-11-25
  • 打赏
  • 举报
回复
leadtools作 旋转/放缩 很快,失真也小,不过要花钱
yintongshun 2003-11-25
  • 打赏
  • 举报
回复
http://www.tech521.com/techData/data/1974.asp
yintongshun 2003-11-25
  • 打赏
  • 举报
回复
假如你想拷贝你的destDC的rectDC的内容到新建的设备描述表,设为TRUE。假如
内存设备描述表剪贴回了它的位图,你就不想重绘已有的任何未旋转的位图,这时这
个参数就有用了。请注意CDC::PlgBlt不是一个最快的函数,因此你可以首先创建旋转
的数据,把它们剪贴过去然后创建别的要旋转的内容
假如
--destDC用于打印(我没有一个能够打印旋转后图像的打印机)
--rectDC 与你的destDC的剪贴区域不相交,或者你没有提供rectDC--而你的destDC的
剪贴又是空的
函数就会返回false
bool IsCreated() const;
设备描述表被成功创建,返回true,否则返回false。
const CRect & GetRotRect() const;
operator const CRect & () const { return GetRotRect(); }
  用于获得与目的(“输出”)设备描述表相配合的旋转后的内存设备描述表的
矩形区域假如你指一个图像,对于目的矩形(10,20,250,120),这个函数返回
(-120,10,-20,250) (rotate() 能用于传送别的2维对象)
假如设备描述表没有成功建立,该函数返回(0,0,0,0)
bool Finish();
  通知cdxCRot90DC 你已完成工作--该函数拷贝位图回目的设备描述表,假如
Create()成功返回,该函数由析构器自动调用
你不必调用Invalidate()而且也不用亲自调用Finish() 注意再次调用Finish()
不会再次拷贝位图
void Invalidate();
作废cdxCRot90DC设备描述表--这样Finish()就不会把位图绘制到目的设备描述
表(因此析构器也不会)而且,这个函数会把当前的剪贴区域设为一个空矩形--所
有更多的绘制操作就不会影响内存设备描述表了假如你发现你旋转后的图形是
空的,就可以使用这个函数来避免内存设备描述表把它拷贝回目的设备描述表
(这样做更快速)
CRect rotate(const CRect & r) const;
CPoint rotate(const CPoint & p) const;
CSize rotate(const CSize & s) const;
void rotate(POINT *pPnts, UINT nCnt) const;
转换目的2维对象为旋转后的2维对象. 例如:假如你向Create()传递"r",
GetRotRect()会返回rotate(r).
CRect rotateBack(const CRect & r) const;
CPoint rotateBack(const CPoint & p) const;
CSize rotateBack(const CSize & s) const;
void rotateBack(POINT *pPnts, UINT nCnt) const;
转换旋转后的2维对象为目的2维对象.
CRect operator()(const CRect & r, bool bFwd = true) const;
CPoint operator()(const CPoint & p, bool bFwd = true) const;
CSize operator()(const CSize & sz, bool bFwd = true) const;
void operator()(POINT *pPnts, UINT nCnt, bool bFwd = true) const;
上述函数操作的快捷操作.假如bFwd是true, 使用rotate(),否则使用rotateBack() .
static CRect rotate(const CRect & r, int iAngle);
static CPoint rotate(const CPoint & p, int iAngle);
static CSize rotate(const CSize & s, int iAngle);
static void rotate(POINT *pPnts, UINT nCnt, int iAngle);
  所有从一个目的2维对象到旋转后的2维对象的转化,这种旋转使用"方角"
iAngle你可以使用这些函数来进行计算而不需要创建一个设备描述表
operator cdxCRot90DC * ();
cdxCRot90DC *operator->();
const cdxCRot90DC *operator->() const;
用指针使用类cdxCRot90DC的对象这在使用CDC的要求有一个CDC对象的指针为
参数的函数时有用
一些技巧
  在旋转后的设备描述表中绘制3维文字需要你知道: 在旋转后的设备描述
表中,目的设备描述表的右下角在哪个方向 例如: 假如你想绘制一些变灰的
文字(就如在变灰的CStatic中看到的一样),通常如下使用:
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
dc.TextOut(pnt + CPoint(1,1),strText);
dc.SetTextColor(::GetSysColor(COLOR_3DSHADOW));
dc.TextOut(pnt,strText);
...
  但假如你在旋转后的设备描述表中这样做会怎样呢?以前,你的"阴影"应在
左上角,但假如设备描述表旋转你的输出180°,它就会在右下角--难看!解决方
法是使用rotate()把向量(1,1)转换为旋转后的设备描述表中的相应向量
...
rotDC.SetBkMode(TRANSPARENT);
rotDC.SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
rotDC.TextOut(pnt + rotDC.rotate( CPoint(1,1) ),strText);
rotDC.SetTextColor(::GetSysColor(COLOR_3DSHADOW));
rotDC.TextOut(pnt,strText);
...
...一切都正常了:)
注意成员函数DrawControl() (此处未介绍),它也许会解决你的许多问题
CDC::DrawEdge() 和CDC::Draw3dRect() 也许会有同样的问题!我建议使用如例2
中开头所示的代码,建立需要这些函数的对象(尽管为它们写一个快捷操作会更容
易一些)以支持打印
- cdxCRotButton: A CButton with rotated text & icon. 带有旋转文字和图标的CButton
- cdxCRotStatic: A CStatic with rotated text & icon. 带有旋转文字和图标的CStatic
- cdxCRotBevelLine: A bevelline with rotated text.  带有旋转文字的斜线
这些类已经完成,但我没有时间为codeguru写相应的文章.希望不久就能有时间使cdxCRotDC 能以任何角度旋转.
yintongshun 2003-11-25
  • 打赏
  • 举报
回复
http://www.codeguru.com/bitmap/RotateByShear.shtml
http://www.codeguru.com/bitmap/rotate_bitmap.shtml
http://www.ittide.com/document/article/graphic/12.html
旋转你的图形-高级内存设备描述表
一个内存设备描述表类(CMemDC)
  我最近下载了由Keith Rule发布的CMemDC类(见他在codeguru的文章)它面向
下列问题:假如你在一个设备描述表上执行一个相当大的操作(例如在OnPaint()中)
,你的显示器闪烁相当严重。围绕这个问题,Keith设计了CMemDC,它可以创建一个
兼容的“内存”设备描述表用于画图。在你向这个设备描述表完成绘制后(在内存中),
就可以向可视的原始文本中粘贴了--只有一个blit操作发生。因此输出的闪烁几乎就再
也不会发生了。然而,它的实现假定你总是画整个粘贴区域--甚至在它已经被
WM_ERASEBKGND或某些材料填满的情况下
为什么旋转文本不是很容易
  此外,最近我设计了一个显示旋转文本的按钮。现在,你能创建一个输出旋转文本
的CFont(使用一个LOGFONT并设置lfOrientation和lfEscapement),但它只能在AFAIK的
TrueType类型字体下工作--不能使用MS Sans Serif。至少,假如你设定这个字体,CDC
文本函数看起来会变得混淆不清--会产生“随机”结果....如果不信可以试一试
  因此我看了一下Keith的 CMemDC类和CDC::PlgBlt()函数,决定把CMemDC扩展成一
个“可旋转的” 内存文本,例如:它不仅可以向输出设备描述表剪贴内存位图,如果
你愿意,甚至可以旋转内存位图
cdxCRot90DC--一个可以旋转的内存设备描述表
  cdxCRot90DC 可以解决以下问题。先指定一个“目的设备描述表”(一般是你的输
出设备),它甚至允许你提供当前输出设备描述表之外的一个矩形(而CMemDC总是使用
当前输出设备描述表的剪贴板)以及一个“方角”注意:“方角”是指一个能被90°整
除的角,这就是为什么它叫cdxCRot90DC 而不是cdxCRotDC 。并且注意,我指出的角度x
同样适用于x+n*360 和 x-n*360, n是自然数
一个例子可以说明这个设备描述表做什么(使用一个90°角的cdxCRot90DC )
  你可能认为这种旋转对文本没有限制--你能随心所欲向内存设备描述表绘图,而在
旋转后的结果会放入你的原始设备描述表--很遗憾,你不能使用这样,不能使用你的原
始矩形向cdxCRot90DC绘制--你需要使用自动传送给你的矩形(用GetRotRect()来得到)
假如你选择一个0°的“方角”,cdxCRot90DC 基本上如CMemDC一样具有以下优点:
  你能随意定义想要操作的的目的设备描述表的矩形区域[见constructor/Create()]
  你能最先拷贝前一个设备描述表的内容到你的内存位图(这样可以阻止任何由
WM_ERASEBKGND完成的绘制或由一个自绘控件的DrawItem() 函数完成的绘制)
[见constructor/Create()]
你可以放弃所做出的改变
  CMemDC 总是把它的位图拷贝回你的原始设备描述表--这也许不合适(例如:你发
现一段文本是空的而位图中什么也没有)[function Invalidate()]
对于旋转输出的特性:
透明的角度使用
  你的代码不必为不同的角度而改变(通过使用GetRotRect())。自动克隆原始
设备描述表的字体,背景色,文本模式和文本颜色(而且使用后恢复它们)。对象
能被另一个目的设备描述表矩形重用(没有新的设备描述表对象被创建,旧的位图
只要可能就会被重用)[见Create()]
请注意:
  这个设备描述表不支持打印(与CMemDC相反)。假如谁愿意帮助我实现它,我
会很高兴的。因为我看来没有一个支持旋转输出的打印机:以下是一个简单的例子
OnPaint(), 可以向设备描述表中绘制旋转的文本:[这个例子使用构造器、
GetRotRect(), IsCreated() 和调用Finish()的析构器]
void MyWnd::OnPaint()
  // create "destDC"
  //创建“destDC”
  CPaintDC  destDC(this);
  // get client rect
  //获得客户区矩形
  CRect    rectClient;
  GetClientRect(rectClient);
  {
    cdxCRot90DC rotDC(destDC,rectClient,90 /*90 degress*/ );
    if(!rotDC.IsCreated())
      return;
    // get client rect in rotated coordinates
    //获得在旋转后的坐标系中的客户区矩形
    CRect rectRot = dc.GetRotRect();
    // example: print programmer's most loved text into the
    // the center of the dest DC - rotated and centered
    //例子:打印程序员最爱使用的文本到目的设备描述表的中心--旋转并居中
    CString s = _T("Hello world");
    CSize sz = rotDC.GetTextExtent(s);
    rotDC.TextOut(rectRot.left + (rectRot.Width() - sz.cx) / 2,
      rectRot.top  + (rectRot.Height() - sz.cy) / 2,
      s);
  }
  // destructor of rotDC calls rotDC.Finish()
  //rotDC的析构器调用 rotDC.Finish()
一个OnPaint() 的例子,在你旋转后的内容上画出3维的边界(此处我们直接使用
Finish() )[这个例子使用构造器、GetRotRect(), IsCreated() 和 Finish()]
void MyWnd::OnPaint()
 // create "destDC"
 //创建“destDC”
 CPaintDC  destDC(this);
 // get entire client area
 //得到整个客户区域rectClient
 CRect    rectClient;
 GetClientRect(rectClient);
 // get rectangle you want to draw to except borders
 //得到除了边界外你想要画的矩形rectInner
 CRect rectInner = rectClient;
 rectInner.DeflateRect(::GetSystemMetrics(SM_CXEDGE),::GetSystemMetrics(SM_CYEDGE));
 // constuct rotated device context
 //构造旋转后的设备描述表rotDC
 cdxCRot90DC rotDC(origDC,rectInner,90/*90 degress*/);
 // check whether there is a non-empty visible rectangle
 //检查是否有一个非空的可视矩形
 if(rotDC.IsCreated())
 {
  // get the rectangle rectReal of rotDC that matchs rectRotated in destDC
  //得到rotDC中的用于配合目的设备描述表中旋转后的矩形的矩形rectRotClient
  CRect rectRotClient = rotDC.GetRotRect();
  // draw nice things into my device context using rectRotClient
  //使用rectRotClient向你的设备描述表中绘制
  ...
  rotDC.TextOut(rectRotClient.left,rectRotClient.top,"Left-top text"));
  ...
 }
 // now copy bitmap back to destDC
 //向目的设备描述表中拷贝绘制后的位图
 rotDC.Finish();
 // and draw a border around rectRotated
 //在rectRotated周围画边界
 destDC.DrawEdge(rectClient,EDGE_RAISED,BF_RECT);
函数参考
注意:以下只包括最重要的函数
cdxCRot90DC();
cdxCRot90DC(CDC & destDC, const CRect & rectDC, int iAngle, bool bCopy = false);
cdxCRot90DC(CDC *pdestDC, const CRect & rectDC, int iAngle, bool bCopy = false);
cdxCRot90DC(CDC & destDC, int iAngle, bool bCopy = false);
cdxCRot90DC(CDC *pdestDC, int iAngle, bool bCopy = false);
构造一个新对象
  在下面的四个构造器立即调用Create(),创建一个新的设备描述表假如使用
它们,就应该使用IsCreated()检查是否设备描述表被成功的创建了(否则,假如
Create()失败,cdxCRot90DC::m_hDC (从CDC继承)就不会被成功的建立,运行中
就会有许多ASSERTs...)。更多的信息见Create()
注意:假如设备描述表正确的建立,析构器中会自动调用Finish()
bool Create(CDC & destDC, const CRect & rectDC, int iAngle, bool bCopy = false);
bool Create(CDC * pdestDC, const CRect & rectDC, int iAngle, bool bCopy = false);
bool Create(CDC & destDC, int iAngle, bool bCopy = false);
bool Create(CDC * pdestDC, int iAngle, bool bCopy = false);
创建设备描述表
  假如这个函数成功的返回,你就可以向其中绘制了。调用Finish()把旋转后
的设备描述表中的位图拷贝回你的目的设备描述表(析构器将自动完成这件事)
详细内容见Finish()
注意1:
  因为你的矩形(见下面的参数)可能被转动了,就不能使用rectDC的坐标来绘
制你的数据(它们可能会显示在旋转后的矩形之外)。因此,使用GetRotRect()
来获得配合你的rectDC的设备描述表矩形。
注意2:
  你能调用Create()多次--每次调用都会创建一个“新的”cdxCRot90DC(假如
有了前一个调用,它就不会分配新的位图)然而,假如你想把前一个位图拷贝回它的
目的设备描述表,应该首先调用Finish()
参数:
destDC, pdestDC
“原始”设备描述表;pdestDC不能为NULL。
rectDC
  你想使用内存设备描述表绘制的原始设备描述表中的矩形区域。假如没有提供,
就使用当前设备描述表的整个剪贴区域。注意 在两种情况下,rectDC都会和原始设
备描述表剪贴区域相交,假如不相交,函数会失败。
iAngle
  你的输出的旋转角.例如:假如是“90”,你画一从左向右的的箭头显示出来是
从上倒下的(见示例的图像)假如是“0”,cdxCRot90DC 就和CMemDC 一样(图像没
有旋转)
bCopy
tonybaobao 2003-11-25
  • 打赏
  • 举报
回复
呵呵,可否考虑用photoshop做好效果后,再添加图片?

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧