Windows Mobile上實作圖檔任意角度旋轉
作者:金海建
目的:在Windows Mobile上,微軟的API和庫不支援圖檔的任意角度旋轉,隻支援90,180,270度旋轉。既然它不支援我們隻能自力更生了。
簡介:通過介紹和實作旋轉PNG圖檔,來說明實作圖檔旋轉的方法。過程大概如下,先用Imaging讀取并解碼png圖檔,使之轉成ARGB格式的位圖。然後利用頂點旋轉的公式,對位圖矩陣進行旋轉,旋轉完成後,利用Imaging庫,轉換成IImage接口。最後利用IImage接口來畫圖。
先來看下平面直角坐标變換的旋轉坐标變換,其定義是
定義:若二坐标系{O;i,j}和{O′;i′,j′}滿足O≡O′,另∠(i,j′)=θ
則坐标系{O′;i′,j′}可看成是由坐标系{O;i,j}繞O旋轉θ角得到的,稱由{O;i,j}到{O′;i′,j′}的變換為旋轉坐标變換。
旋轉公式為:
X' = X cosθ - Y sinθ
Y' = X sinθ + Y cosθ
由于我們是用數組的下表來表示坐标的,是以最小的坐标是為(0,0)。我們需要先做坐标旋轉,然後平移坐标。如下圖所示:
平移的動作,是把是以的負坐标變成正坐标。用MinX和MinY來表示最小的X坐标和Y坐标。
X' = X cosθ - Y sinθ - MinX;
Y' = X sinθ + Y cosθ - MinY;
根據上面的公式我們推出
x = (x'+MinX)cosθ+ (y'+MinY)sinθ
y = (y'+MinY)conθ- (x'+MinX)sinθ
圖檔旋轉後,會出現失真現象,需要用雙線性内插值進行優化
雙線性内插值:對于一個目的像素,設定坐标通過反向變換得到的浮點坐标為(i+u,j+v),其中i、j均為非負整數,u、v為[0,1)區間的浮點數,則這個像素得值 f(i+u,j+v) 可由原圖像中坐标為 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應的周圍四個像素的值決定,即:
f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1) |
其中f(i,j)表示源圖像(i,j)處的的像素值,以此類推。
這就是雙線性内插值法。雙線性内插值法計算量大,但縮放後圖像品質高,不會出現像素值不連續的的情況。由于雙線性插值具有低通濾波器的性質,使高頻分量受損,是以可能會使圖像輪廓在一定程度上變得模糊。
旋轉代碼
- void RotateImageBuf(const BYTE* src, int srcWidth, int srcHeight, BYTE*& dst, int& dstWidth, int& dstHeight, double angle)
- {
- double cosA = cos(angle);
- double sinA = sin(angle);
- double x1 = srcWidth * cosA;
- double y1 = srcWidth * sinA;
- double x2 = srcWidth * cosA - srcHeight * sinA;
- double y2 = srcWidth * sinA + srcHeight * cosA;
- double x3 = - srcHeight * sinA;
- double y3 = srcHeight * cosA;
- double minWidth = min(0,min(x3,min(x1,x2)));
- double minHeight = min(0,min(y3,min(y1,y2)));
- double maxWidth = max(0,max(x3,max(x1,x2)));
- double maxHeight = max(0,max(y3,max(y1,y2)));
- dstWidth = abs(maxWidth - minWidth) + 1;
- dstHeight = abs(maxHeight - minHeight) + 1;
- double ndstX = 0;
- double ndstY = 0;
- dst = new BYTE[dstWidth * dstHeight * 4];
- UINT32 *pDst = (UINT32 *)dst;
- UINT32 *pSrc = (UINT32 *)src;
- UINT32 crDefault = ((UINT32 *)src)[0];
- UINT32 color [2][2];
- BYTE alpha[2][2];
- BYTE rColor[2][2];
- BYTE gColor[2][2];
- BYTE bColor[2][2];
- BYTE afinal,rfinal,gfinal,bfinal;
- double ox,oy;
- for(int y=0; y < dstHeight; y++)
- {
- for(int x=0; x < dstWidth; x++)
- {
- ndstX = (x + minWidth) * cosA + (y + minHeight) * sinA;
- ndstY = (y + minHeight) * cosA - (x +minWidth) * sinA;
- if((ndstX >= 0) && (ndstX < srcWidth) && (ndstY >= 0) && (ndstY < srcHeight))
- {
- color[0][0] = pSrc[ (int)((int)ndstY*srcWidth + ndstX) ];
- if((int)ndstX + 1 >= srcWidth)
- color[1][0] = crDefault;
- else
- color[1][0] = pSrc[ (int)((int)ndstY*srcWidth + ndstX +1) ];
- if((int)ndstY + 1 >= srcHeight)
- color[0][1] = crDefault;
- color[0][1] = pSrc[ (int)(((int)ndstY+1)*srcWidth + ndstX) ];
- if((int)ndstY + 1 >= srcHeight || (int)ndstX + 1 >= srcWidth)
- color[1][1] = crDefault;
- color[1][1] = pSrc[ (int)(((int)ndstY+1)*srcWidth + ndstX+1) ];
- alpha[0][0] = (color[0][0] & 0XFF000000) >> 24;
- bColor[0][0] = (color[0][0] & 0XFF0000) >> 16;
- gColor[0][0] = (color[0][0] & 0X00FF00) >> 8;
- rColor[0][0] = color[0][0] & 0X0000FF;
- alpha[1][0] = (color[1][0] & 0XFF000000) >> 24;
- bColor[1][0] = (color[1][0] & 0XFF0000) >> 16;
- gColor[1][0] = (color[1][0] & 0X00FF00) >> 8;
- rColor[1][0] = color[1][0] & 0X0000FF;
- alpha[0][1] = (color[0][1] & 0XFF000000) >> 24;
- bColor[0][1] = (color[0][1] & 0XFF0000) >> 16;
- gColor[0][1] = (color[0][1] & 0X00FF00) >> 8;
- rColor[0][1] = color[0][1] & 0X0000FF;
- alpha[1][1] = (color[1][1] & 0XFF000000) >> 24;
- bColor[1][1] = (color[1][1] & 0XFF0000) >> 16;
- gColor[1][1] = (color[1][1] & 0X00FF00) >> 8;
- rColor[1][1] = color[1][1] & 0X0000FF;
- //f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
- ox = (ndstX - (int)ndstX);
- oy = (ndstY - (int)ndstY);
- rfinal = (1 - ox)*(1 - oy)*rColor[0][0] + ox*(1 - oy)*rColor[1][0] + (1-ox)*oy*rColor[0][1] + ox*oy*rColor[1][1];
- gfinal = (1 - ox)*(1 - oy)*gColor[0][0] + ox*(1 - oy)*gColor[1][0] + (1-ox)*oy*gColor[0][1] + ox*oy*gColor[1][1];
- bfinal = (1 - ox)*(1 - oy)*bColor[0][0] + ox*(1 - oy)*bColor[1][0] + (1-ox)*oy*bColor[0][1] + ox*oy*bColor[1][1];
- afinal = (1 - ox)*(1 - oy)*alpha[0][0] + ox*(1 - oy)*alpha[1][0] + (1-ox)*oy*alpha[0][1] + ox*oy*alpha[1][1];
- pDst[y * (int)dstWidth + x] = RGBA(rfinal,gfinal,bfinal,afinal);
- }
- else
- pDst[y * (int)dstWidth + x] = crDefault;
- }
- }
- }
需要對浮點型運算,改為整形預算。在模拟上測試過,旋轉一張圖檔,整形預算要比浮點型預算快20倍。
改為整形運算代碼。
- void RotateImageZhenxingBuf(const BYTE* src, int srcWidth, int srcHeight, BYTE*& dst, int& dstWidth, int& dstHeight, double angle)
- double minWidthD = min(0,min(x3,min(x1,x2)));
- double minHeightD = min(0,min(y3,min(y1,y2)));
- double maxWidthD = max(0,max(x3,max(x1,x2)));
- double maxHeightD = max(0,max(y3,max(y1,y2)));
- dstWidth = abs(minWidthD - maxWidthD) + 1;
- dstHeight = abs(maxHeightD - minHeightD) + 1;
- int minWidth= minWidthD;
- int minHeight= minHeightD;
- int maxWidth = maxWidthD;
- int maxHeight = maxHeightD;
- int ndstX = 0;
- int ndstY = 0;
- int afinal,rfinal,gfinal,bfinal;
- long icosA = cosA * 256 * 256;
- long isinA = sinA * 256 * 256;
- int x;
- int y;
- int kx;
- int ky;
- for(int j=0; j < dstHeight; j++)
- for(int i=0; i < dstWidth; i++)
- ndstX = (i + minWidth) * icosA + (j + minHeight) * isinA;
- ndstY = (j + minHeight) * icosA - (i +minWidth) * isinA;
- ndstX = ndstX >> 8;
- ndstY = ndstY >> 8;
- if ( (ndstX >> 8) < srcWidth && (ndstX >> 8) >=0 && (ndstY >> 8) < srcHeight && (ndstY >> 8) >= 0)
- {
- kx = ndstX >> 8;
- ky = ndstY >> 8;
- x = ndstX & 0xFF;
- y = ndstY & 0xFF;
- color[0][0] = pSrc[ ky*srcWidth + kx ];
- if(kx + 1 >= srcWidth)
- color[1][0] = pSrc[ ky*srcWidth + kx +1 ];
- if(ky + 1 >= srcHeight)
- color[0][1] = pSrc[ (ky+1)*srcWidth + kx ];
- if(ky + 1 >= srcHeight || kx + 1 >= srcWidth)
- color[1][1] = pSrc[ (ky+1)*srcWidth + kx+1 ];
- rColor[0][0] = color[0][0] & 0XFF;
- rColor[1][0] = color[1][0] & 0XFF;
- rColor[0][1] = color[0][1] & 0XFF;
- rColor[1][1] = color[1][1] & 0XFF;
- afinal = (0x100 - x)*(0x100 - y)*alpha[0][0] + x*(0x100 - y)*alpha[1][0] + (0x100-x)*y*alpha[0][1] + x*y*alpha[1][1];
- rfinal = (0x100 - x)*(0x100 - y)*rColor[0][0] + x*(0x100 - y)*rColor[1][0] + (0x100-x)*y*rColor[0][1] + x*y*rColor[1][1];
- gfinal = (0x100 - x)*(0x100 - y)*gColor[0][0] + x*(0x100 - y)*gColor[1][0] + (0x100-x)*y*gColor[0][1] + x*y*gColor[1][1];
- bfinal = (0x100 - x)*(0x100 - y)*bColor[0][0] + x*(0x100 - y)*bColor[1][0] + (0x100-x)*y*bColor[0][1] + x*y*bColor[1][1];
- afinal = afinal >> 16;
- if (afinal>255)
- afinal = 255;
- if (afinal<0)
- afinal = 0;
- rfinal = rfinal >> 16;
- if (rfinal>255)
- rfinal = 255;
- if (rfinal<0)
- rfinal = 0;
- gfinal = gfinal >> 16;
- if (gfinal>255)
- gfinal = 255;
- if (gfinal<0)
- gfinal = 0;
- bfinal = bfinal >> 16;
- if (bfinal>255)
- bfinal = 255;
- if (bfinal<0)
- bfinal = 0;
- pDst[j * dstWidth + i] = RGBA(rfinal,gfinal,bfinal,afinal);
- pDst[j * dstWidth + i] = crDefault;
用Imaging接口加載png圖檔,并調用RotateImageBuf進行旋轉,最後傳回IImage接口。
- // need to delete pImagebuf;
- HRESULT RotateImage(IImagingFactory *pImgFactory, IImage *pImage, IImage * &pImageOut, LPBYTE &pImageBuf, double angle)
- {
- HRESULT hr = S_OK;
- IBitmapImage *pIBitmap = NULL;
- ImageInfo ImInfo;
- pImage->GetImageInfo(&ImInfo);
- if((ImInfo.PixelFormat & 0xFF00)>>8 != 32)
- hr = S_FALSE;
- goto Error;
- if(S_OK != pImgFactory->CreateBitmapFromImage(
- pImage, ImInfo.Width, ImInfo.Height, ImInfo.PixelFormat, InterpolationHintDefault, &pIBitmap))
- RECT rcLocked ={0,0,ImInfo.Width,ImInfo.Height};
- BitmapData bmpData;
- if(S_OK != pIBitmap->LockBits(&rcLocked, ImageLockModeRead | ImageLockModeWrite, ImInfo.PixelFormat, &bmpData))
- BYTE *pDst = NULL;
- int nDstWidth=0,nDstHeight=0;
- RotateImageZhenxingBuf((BYTE *)bmpData.Scan0, ImInfo.Width, ImInfo.Height, pDst, nDstWidth, nDstHeight, angle);
- pIBitmap->UnlockBits(&bmpData);
- BitmapData bmpDataNew;
- bmpDataNew.Height = nDstHeight;
- bmpDataNew.Width = nDstWidth;
- bmpDataNew.PixelFormat = ImInfo.PixelFormat;
- bmpDataNew.Stride = nDstWidth * 4;
- bmpDataNew.Reserved = 0;
- bmpDataNew.Scan0 = (void *)pDst;
- IBitmapImage *pBmpImageNew = NULL;
- hr = pImgFactory->CreateBitmapFromBuffer(&bmpDataNew, &pBmpImageNew);
- IImage * pImageNew = NULL;
- pBmpImageNew->QueryInterface(IID_IImage, (void **)&pImageNew);
- pImageOut = pImageNew;
- pImageBuf = pDst;
- Error:
- if(pBmpImageNew)
- pBmpImageNew->Release();
- if(pIBitmap)
- pIBitmap->Release();
- return hr;