|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|