You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
8.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace POSV.Utils
{
/// <summary>
///
/// NinePatch ninePatch = new NinePatch(image); // 9-patch
/// Image newImage = ninePatch.ImageSizeOf(500, 500);
///
/// </summary>
public class NinePatch
{
private Image image;
public Bitmap OriginalImage
{
get
{
using (Bitmap baseBitmap = new Bitmap(this.image))
{
Rectangle rect = new Rectangle(1 , 1 , baseBitmap.Width - 2 , baseBitmap.Height - 2);
Bitmap result = baseBitmap.Clone(rect , baseBitmap.PixelFormat);
return result;
}
}
}
private Dictionary<string , Image> cache;
private List<int> topPatches;
private List<int> leftPatches;
private List<int> bottomPatches;
private List<int> rightPatches;
private const int BYTES_PER_PIXEL = 4;
public NinePatch(Image image)
{
this.image = image;
cache = new Dictionary<string , Image>();
FindPatchRegion();
}
public void ClearCache()
{
foreach (KeyValuePair<string , Image> pair in cache)
{
pair.Value.Dispose();
}
cache.Clear();
}
public Image ImageSizeOf(int w , int h)
{
if (cache.ContainsKey(String.Format("{0}x{1}" , w , h)))
{
return cache[String.Format("{0}x{1}" , w , h)];
}
using (Bitmap src = this.OriginalImage)
{
int sourceWidth = src.Width;
int sourceHeight = src.Height;
int targetWidth = w;
int targetHeight = h;
targetWidth = System.Math.Max(sourceWidth , targetWidth);
targetHeight = System.Math.Max(sourceHeight , targetHeight);
if (sourceWidth == targetWidth && sourceHeight == targetHeight)
{
return src;
}
BitmapData srcData = src.LockBits(
new Rectangle(0 , 0 , sourceWidth , sourceHeight) ,
ImageLockMode.ReadOnly ,
src.PixelFormat
);
byte[] srcBuf = new byte[sourceWidth * sourceHeight * BYTES_PER_PIXEL];
Marshal.Copy(srcData.Scan0 , srcBuf , 0 , srcBuf.Length);
Bitmap dst = new Bitmap(targetWidth , targetHeight);
byte[] dstBuf = new byte[dst.Width * dst.Height * BYTES_PER_PIXEL];
List<int> xMapping = XMapping(targetWidth - sourceWidth , targetWidth);
List<int> yMapping = YMapping(targetHeight - sourceHeight , targetHeight);
for (int y = 0; y < targetHeight; y++)
{
int sourceY = yMapping[y];
for (int x = 0; x < targetWidth; x++)
{
int sourceX = xMapping[x];
for (int z = 0; z < BYTES_PER_PIXEL; z++)
{
dstBuf[y * targetWidth * BYTES_PER_PIXEL + x * BYTES_PER_PIXEL + z] =
srcBuf[sourceY * sourceWidth * BYTES_PER_PIXEL + sourceX * BYTES_PER_PIXEL + z];
}
}
}
BitmapData dstData = dst.LockBits(
new Rectangle(0 , 0 , dst.Width , dst.Height) ,
ImageLockMode.WriteOnly ,
src.PixelFormat
);
IntPtr dstScan0 = dstData.Scan0;
Marshal.Copy(dstBuf , 0 , dstScan0 , dstBuf.Length);
src.UnlockBits(srcData);
dst.UnlockBits(dstData);
cache.Add(String.Format("{0}x{1}" , w , h) , dst);
return dst;
}
}
private void FindPatchRegion()
{
topPatches = new List<int>();
leftPatches = new List<int>();
bottomPatches = new List<int>();
rightPatches = new List<int>();
using (Bitmap src = new Bitmap(image))
{
BitmapData srcData = src.LockBits(
new Rectangle(0 , 0 , src.Width , src.Height) ,
ImageLockMode.ReadOnly ,
src.PixelFormat
);
byte[] srcBuf = new byte[src.Width * src.Height * BYTES_PER_PIXEL];
Marshal.Copy(srcData.Scan0 , srcBuf , 0 , srcBuf.Length);
// top
for (int x = 1; x < srcData.Width - 1; x++)
{
int index = x * BYTES_PER_PIXEL;
byte b = srcBuf[index];
byte g = srcBuf[index + 1];
byte r = srcBuf[index + 2];
byte alpha = srcBuf[index + 3];
if (r == 0 && g == 0 && b == 0 && alpha == 255)
{
topPatches.Add(x - 1);
}
}
// left
for (int y = 1; y < srcData.Height - 1; y++)
{
int index = y * BYTES_PER_PIXEL * srcData.Width;
byte b = srcBuf[index];
byte g = srcBuf[index + 1];
byte r = srcBuf[index + 2];
byte alpha = srcBuf[index + 3];
if (r == 0 && g == 0 && b == 0 && alpha == 255)
{
leftPatches.Add(y - 1);
}
}
// bottom
for (int x = 1; x < srcData.Width - 1; x++)
{
int index = (srcData.Height - 1) * BYTES_PER_PIXEL * srcData.Width + x * BYTES_PER_PIXEL;
byte b = srcBuf[index];
byte g = srcBuf[index + 1];
byte r = srcBuf[index + 2];
byte alpha = srcBuf[index + 3];
if (r == 0 && g == 0 && b == 0 && alpha == 255)
{
bottomPatches.Add(x - 1);
}
}
// right
for (int y = 1; y < srcData.Height - 1; y++)
{
int index = y * BYTES_PER_PIXEL * srcData.Width + (srcData.Width - 1) * BYTES_PER_PIXEL;
byte b = srcBuf[index];
byte g = srcBuf[index + 1];
byte r = srcBuf[index + 2];
byte alpha = srcBuf[index + 3];
if (r == 0 && g == 0 && b == 0 && alpha == 255)
{
rightPatches.Add(y - 1);
}
}
}
}
private List<int> XMapping(int diffWidth , int targetWidth)
{
List<int> result = new List<int>(targetWidth);
int src = 0;
int dst = 0;
while (dst < targetWidth)
{
int foundIndex = topPatches.IndexOf(src);
if (foundIndex != -1)
{
int repeatCount = (diffWidth / topPatches.Count) + 1;
if (foundIndex < diffWidth % topPatches.Count)
{
repeatCount++;
}
for (int j = 0; j < repeatCount; j++)
{
result.Insert(dst++ , src);
}
}
else
{
result.Insert(dst++ , src);
}
src++;
}
return result;
}
private List<int> YMapping(int diffHeight , int targetHeight)
{
List<int> result = new List<int>(targetHeight);
int src = 0;
int dst = 0;
while (dst < targetHeight)
{
int foundIndex = leftPatches.IndexOf(src);
if (foundIndex != -1)
{
int repeatCount = (diffHeight / leftPatches.Count) + 1;
if (foundIndex < diffHeight % leftPatches.Count)
{
repeatCount++;
}
for (int j = 0; j < repeatCount; j++)
{
result.Insert(dst++ , src);
}
}
else
{
result.Insert(dst++ , src);
}
src++;
}
return result;
}
}
}