您的位置:首页 > 其它

.net如何根据不规则png图形裁剪图片

2012-01-12 21:04 411 查看

需要表述

项目中遇到一些不规则的图片作品需要合成,给一张不规则的png图片(比如圆形图片,中间是白色的圆形区域,外面是透明区域),需要将用白色区域裁剪用户图片并且要求效率不低于1s

解决方法

现采用.net中的Region 多边形区域采集和裁剪,并用到了win32的api:gdi32.dll

示意代码

//区域采集函数

public static Region ImageToRegion(Image AImage, Color ATransparent)
{
if (AImage == null) return null;
Bitmap vBitmap = new Bitmap(AImage);
BitmapData vBitmapData = vBitmap.LockBits(new Rectangle(0, 0, vBitmap.Width, vBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
int vAddress = (int)vBitmapData.Scan0;
int vOffset = vBitmapData.Stride - vBitmap.Width * 4; // 每行多出的字节数
int h = vBitmap.Height, w = vBitmap.Width;
int vTransparent = ColorTranslator.ToWin32(ATransparent); // 透明色
int vAllocRect = (0x1000 - sizeof(uint) * 8) / sizeof(int); // 预分配的矩形数
if (h * w < vAllocRect) vAllocRect = h * w;

Byte[] vBuffer = new byte[sizeof(uint) * 8 + sizeof(int) * 4 * vAllocRect];
//头信息dwSize\iType\nCount\nRegSize
uint vCount = 0;
vBuffer[0] = sizeof(uint) * 8; //dwSize//头信息大小
vBuffer[4] = 1; //iType//int RDH_RECTANGLES = 1;//数据类型
IntPtr vResult = IntPtr.Zero;

uint vPointer = sizeof(uint) * 8;
bool vWriteRect = false;
bool vWriteAlways = false;

for (int y = 0; y < h; y++)
{
int vBlockStart = 0;
bool vLastMaskBit = false;
for (int x = 0; x < w; x++)
{
int i = Marshal.ReadInt32((IntPtr)vAddress) & 0x00FFFFFF;
if (i == 0) // 透明色 (黑色区域也会被识别成透明色)
{
if (vLastMaskBit)
vWriteRect = true;
}
else
{
if (!vLastMaskBit)
{
vBlockStart = x;
vLastMaskBit = true;
}
}

if (x == w - 1)
{
if (y == h - 1)
{
vWriteRect = true;
vWriteAlways = true;
}
else if (vLastMaskBit)
{
vWriteRect = true;
}
x++;
}

if (vWriteRect)
{
if (vLastMaskBit)
{
vCount++;
WriteRect(vBuffer, ref vPointer, new Rectangle(vBlockStart, y, x - vBlockStart, 1));
}

if (vCount == vAllocRect || vWriteAlways)
{
vBuffer[8] = (byte)vCount;
vBuffer[9] = (byte)(vCount >> 8);
vBuffer[10] = (byte)(vCount >> 16);
vBuffer[11] = (byte)(vCount >> 24);
IntPtr hTemp = ExtCreateRegion(IntPtr.Zero,
sizeof(uint) * 8 + sizeof(int) * 4 * vCount,
ref vBuffer[0]);
if (vResult == IntPtr.Zero)
vResult = hTemp;
else
{
CombineRgn(vResult, vResult, hTemp, RGN_OR);
DeleteObject(hTemp);
}
vCount = 0;
vPointer = sizeof(uint) * 4;
vWriteAlways = false;
}
vWriteRect = false;
vLastMaskBit = false;
}
vAddress += 4;
}
vAddress += vOffset;
}

vBitmap.UnlockBits(vBitmapData);
return Region.FromHrgn(vResult);
}

//按指定的裁剪区域对图像进行裁剪
public static Bitmap CutRegionImage(Bitmap b, Region region)
{
Graphics g = System.Drawing.Graphics.FromImage(b);

// 获取区域边界
RectangleF validRect = region.GetBounds(g);

int x = (int)validRect.X;
int y = (int)validRect.Y;
int width = (int)validRect.Width;
int height = (int)validRect.Height;

// 对区域进行平移
Region validRegion = region;
validRegion.Translate(-x, -y);

Bitmap dstImage = new Bitmap(width, height);
Graphics dstGraphics = System.Drawing.Graphics.FromImage(dstImage);

// 设置剪辑区域
dstGraphics.SetClip(validRegion, CombineMode.Replace);

// 绘图
dstGraphics.DrawImage(b, new Rectangle(0, 0, width, height),
validRect, GraphicsUnit.Pixel);

g.Dispose();
dstGraphics.Dispose();
b.Dispose();

return dstImage;
}

//调用win32 api的函数定义

[DllImport("gdi32.dll")]
public static extern IntPtr ExtCreateRegion(IntPtr lpXform, uint nCount,
ref byte lpRgnData);

[DllImport("gdi32.dll")]
public static extern int CombineRgn(IntPtr hrgnDest, IntPtr hrgnSrc1, IntPtr hrgnSrc2,
int fnCombineMode);

[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

结果示意图





完整代码

/Files/ruimingde/PngImage.txt
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: