2013-11-03 23:53:49 +13:00
|
|
|
|
#region License Information (GPL v3)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
ShareX - A program that allows you to take screenshots and share any file type
|
2021-07-29 15:22:51 +12:00
|
|
|
|
Copyright (c) 2007-2021 ShareX Team
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
|
|
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#endregion License Information (GPL v3)
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
using System.Drawing.Imaging;
|
2013-11-21 06:45:11 +13:00
|
|
|
|
using System.IO;
|
2015-09-25 22:02:31 +12:00
|
|
|
|
using System.Linq;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
using System.Reflection;
|
2013-11-06 06:58:22 +13:00
|
|
|
|
using System.Runtime.InteropServices;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows.Forms;
|
2021-07-15 03:30:26 +12:00
|
|
|
|
using Encoder = System.Drawing.Imaging.Encoder;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2014-12-11 09:25:20 +13:00
|
|
|
|
namespace ShareX.HelpersLib
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
public static class ImageHelpers
|
|
|
|
|
{
|
2017-12-06 20:05:17 +13:00
|
|
|
|
private const InterpolationMode DefaultInterpolationMode = InterpolationMode.HighQualityBicubic;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap ResizeImage(Bitmap bmp, int width, int height, InterpolationMode interpolationMode = DefaultInterpolationMode)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
if (width < 1 || height < 1 || (bmp.Width == width && bmp.Height == height))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmp;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
Bitmap bmpResult = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
|
|
|
|
bmpResult.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (bmp)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2017-12-06 20:05:17 +13:00
|
|
|
|
g.InterpolationMode = interpolationMode;
|
2013-12-25 08:19:27 +13:00
|
|
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
|
|
|
g.CompositingQuality = CompositingQuality.HighQuality;
|
|
|
|
|
g.CompositingMode = CompositingMode.SourceOver;
|
|
|
|
|
|
|
|
|
|
using (ImageAttributes ia = new ImageAttributes())
|
|
|
|
|
{
|
|
|
|
|
ia.SetWrapMode(WrapMode.TileFlipXY);
|
2020-03-22 11:07:38 +13:00
|
|
|
|
g.DrawImage(bmp, new Rectangle(0, 0, width, height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia);
|
2013-12-25 08:19:27 +13:00
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmpResult;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap ResizeImage(Bitmap bmp, Size size, InterpolationMode interpolationMode = DefaultInterpolationMode)
|
2017-12-06 20:05:17 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return ResizeImage(bmp, size.Width, size.Height, interpolationMode);
|
2017-12-06 20:05:17 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap ResizeImage(Bitmap bmp, Size size, bool allowEnlarge, bool centerImage = true)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
return ResizeImage(bmp, size.Width, size.Height, allowEnlarge, centerImage);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap ResizeImage(Bitmap bmp, int width, int height, bool allowEnlarge, bool centerImage = true)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
return ResizeImage(bmp, width, height, allowEnlarge, centerImage, Color.Transparent);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap ResizeImage(Bitmap bmp, int width, int height, bool allowEnlarge, bool centerImage, Color backColor)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
double ratio;
|
|
|
|
|
int newWidth, newHeight;
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
if (!allowEnlarge && bmp.Width <= width && bmp.Height <= height)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
ratio = 1.0;
|
2020-03-22 12:22:43 +13:00
|
|
|
|
newWidth = bmp.Width;
|
|
|
|
|
newHeight = bmp.Height;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
double ratioX = (double)width / bmp.Width;
|
|
|
|
|
double ratioY = (double)height / bmp.Height;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
ratio = ratioX < ratioY ? ratioX : ratioY;
|
2020-03-22 12:22:43 +13:00
|
|
|
|
newWidth = (int)(bmp.Width * ratio);
|
|
|
|
|
newHeight = (int)(bmp.Height * ratio);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-24 08:10:51 +13:00
|
|
|
|
int newX = 0;
|
|
|
|
|
int newY = 0;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
if (centerImage)
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
newX += (int)((width - (bmp.Width * ratio)) / 2);
|
|
|
|
|
newY += (int)((height - (bmp.Height * ratio)) / 2);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
Bitmap bmpResult = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
|
|
|
|
bmpResult.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
|
2013-12-24 08:10:51 +13:00
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-12-24 08:10:51 +13:00
|
|
|
|
{
|
2019-05-29 09:15:28 +12:00
|
|
|
|
if (backColor.A > 0)
|
|
|
|
|
{
|
|
|
|
|
g.Clear(backColor);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-24 08:10:51 +13:00
|
|
|
|
g.SetHighQuality();
|
2020-03-22 12:22:43 +13:00
|
|
|
|
g.DrawImage(bmp, newX, newY, newWidth, newHeight);
|
2013-12-24 08:10:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
return bmpResult;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap CreateThumbnail(Bitmap bmp, int width, int height)
|
2017-07-10 19:03:05 +12:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
double srcRatio = (double)bmp.Width / bmp.Height;
|
2017-07-10 19:03:05 +12:00
|
|
|
|
double dstRatio = (double)width / height;
|
|
|
|
|
int w, h;
|
|
|
|
|
|
|
|
|
|
if (srcRatio >= dstRatio)
|
|
|
|
|
{
|
|
|
|
|
if (srcRatio >= 1)
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
w = (int)(bmp.Height * dstRatio);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
w = (int)(bmp.Width / srcRatio * dstRatio);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
h = bmp.Height;
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
w = bmp.Width;
|
2017-07-10 19:03:05 +12:00
|
|
|
|
|
|
|
|
|
if (srcRatio >= 1)
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
h = (int)(bmp.Height / dstRatio * srcRatio);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
h = (int)(bmp.Height * srcRatio / dstRatio);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
int x = (bmp.Width - w) / 2;
|
|
|
|
|
int y = (bmp.Height - h) / 2;
|
2017-07-10 19:03:05 +12:00
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
Bitmap bmpResult = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
|
|
|
|
bmpResult.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2017-07-10 19:03:05 +12:00
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
2020-03-22 12:22:43 +13:00
|
|
|
|
g.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(x, y, w, h), GraphicsUnit.Pixel);
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
return bmpResult;
|
2017-07-10 19:03:05 +12:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-08 21:16:19 +13:00
|
|
|
|
/// <summary>If image size is bigger than specified size then resize it and keep aspect ratio else return image.</summary>
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap ResizeImageLimit(Bitmap bmp, int width, int height)
|
2013-12-25 07:19:01 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
if (bmp.Width <= width && bmp.Height <= height)
|
2013-12-25 07:19:01 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmp;
|
2013-12-25 07:19:01 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
double ratioX = (double)width / bmp.Width;
|
|
|
|
|
double ratioY = (double)height / bmp.Height;
|
2015-04-22 04:33:20 +12:00
|
|
|
|
|
2015-04-21 13:59:14 +12:00
|
|
|
|
if (ratioX < ratioY)
|
2015-04-22 04:33:20 +12:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
height = (int)Math.Round(bmp.Height * ratioX);
|
2015-04-22 04:33:20 +12:00
|
|
|
|
}
|
2018-02-08 21:16:19 +13:00
|
|
|
|
else if (ratioX > ratioY)
|
2015-04-22 04:33:20 +12:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
width = (int)Math.Round(bmp.Width * ratioY);
|
2015-04-22 04:33:20 +12:00
|
|
|
|
}
|
2013-12-25 07:19:01 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return ResizeImage(bmp, width, height);
|
2013-12-25 07:19:01 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap ResizeImageLimit(Bitmap bmp, Size size)
|
2015-04-21 13:59:14 +12:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return ResizeImageLimit(bmp, size.Width, size.Height);
|
2018-02-08 21:16:19 +13:00
|
|
|
|
}
|
2015-09-13 08:33:21 +12:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap ResizeImageLimit(Bitmap bmp, int size)
|
2018-02-08 21:16:19 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return ResizeImageLimit(bmp, size, size);
|
2015-04-21 13:59:14 +12:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-03 23:53:49 +13:00
|
|
|
|
public static Bitmap CropBitmap(Bitmap bmp, Rectangle rect)
|
|
|
|
|
{
|
2015-05-06 09:33:19 +12:00
|
|
|
|
if (bmp != null && rect.X >= 0 && rect.Y >= 0 && rect.Width > 0 && rect.Height > 0 && new Rectangle(0, 0, bmp.Width, bmp.Height).Contains(rect))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
return bmp.Clone(rect, bmp.PixelFormat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 08:53:58 +13:00
|
|
|
|
/// <summary>Automatically crop image to remove transparent outside area.</summary>
|
2017-11-29 08:10:45 +13:00
|
|
|
|
public static Bitmap AutoCropTransparent(Bitmap bmp)
|
2016-11-14 08:53:58 +13:00
|
|
|
|
{
|
|
|
|
|
Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
|
|
|
|
Rectangle rect = source;
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly))
|
|
|
|
|
{
|
|
|
|
|
bool leave = false;
|
|
|
|
|
|
|
|
|
|
// Find X
|
|
|
|
|
for (int x = rect.X; x < rect.Width && !leave; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = rect.Y; y < rect.Height; y++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.X = x;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 23:56:35 +12:00
|
|
|
|
// If all pixels transparent
|
|
|
|
|
if (!leave)
|
|
|
|
|
{
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 08:53:58 +13:00
|
|
|
|
leave = false;
|
|
|
|
|
|
|
|
|
|
// Find Y
|
|
|
|
|
for (int y = rect.Y; y < rect.Height && !leave; y++)
|
|
|
|
|
{
|
|
|
|
|
for (int x = rect.X; x < rect.Width; x++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Y = y;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave = false;
|
|
|
|
|
|
|
|
|
|
// Find Width
|
|
|
|
|
for (int x = rect.Width - 1; x >= rect.X && !leave; x--)
|
|
|
|
|
{
|
|
|
|
|
for (int y = rect.Y; y < rect.Height; y++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Width = x - rect.X + 1;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave = false;
|
|
|
|
|
|
|
|
|
|
// Find Height
|
|
|
|
|
for (int y = rect.Height - 1; y >= rect.Y && !leave; y--)
|
|
|
|
|
{
|
|
|
|
|
for (int x = rect.X; x < rect.Width; x++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Height = y - rect.Y + 1;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (source != rect)
|
|
|
|
|
{
|
|
|
|
|
Bitmap croppedBitmap = CropBitmap(bmp, rect);
|
|
|
|
|
|
|
|
|
|
if (croppedBitmap != null)
|
|
|
|
|
{
|
|
|
|
|
bmp.Dispose();
|
|
|
|
|
return croppedBitmap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Automatically crop image to remove transparent outside area. Only checks center pixels.</summary>
|
2017-11-29 08:10:45 +13:00
|
|
|
|
public static Bitmap QuickAutoCropTransparent(Bitmap bmp)
|
2016-11-14 08:53:58 +13:00
|
|
|
|
{
|
|
|
|
|
Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
|
|
|
|
Rectangle rect = source;
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly))
|
|
|
|
|
{
|
|
|
|
|
int middleX = rect.Width / 2;
|
|
|
|
|
int middleY = rect.Height / 2;
|
|
|
|
|
|
|
|
|
|
// Find X
|
|
|
|
|
for (int x = rect.X; x < rect.Width; x++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, middleY).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.X = x;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find Y
|
|
|
|
|
for (int y = rect.Y; y < rect.Height; y++)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(middleX, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Y = y;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find Width
|
|
|
|
|
for (int x = rect.Width - 1; x >= rect.X; x--)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(x, middleY).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Width = x - rect.X + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find Height
|
|
|
|
|
for (int y = rect.Height - 1; y >= rect.Y; y--)
|
|
|
|
|
{
|
|
|
|
|
if (unsafeBitmap.GetPixel(middleX, y).Alpha > 0)
|
|
|
|
|
{
|
|
|
|
|
rect.Height = y - rect.Y + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (source != rect)
|
|
|
|
|
{
|
|
|
|
|
Bitmap croppedBitmap = CropBitmap(bmp, rect);
|
|
|
|
|
|
|
|
|
|
if (croppedBitmap != null)
|
|
|
|
|
{
|
|
|
|
|
bmp.Dispose();
|
|
|
|
|
return croppedBitmap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
public static Bitmap AddCanvas(Image img, Padding margin)
|
2018-04-17 01:43:56 +12:00
|
|
|
|
{
|
|
|
|
|
return AddCanvas(img, margin, Color.Transparent);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
public static Bitmap AddCanvas(Image img, Padding margin, Color canvasColor)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2017-10-22 09:43:39 +13:00
|
|
|
|
if (margin.All == 0 || img.Width + margin.Horizontal < 1 || img.Height + margin.Vertical < 1)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-24 18:30:32 +12:00
|
|
|
|
Bitmap bmp = img.CreateEmptyBitmap(margin.Horizontal, margin.Vertical);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
2013-11-12 15:42:10 +13:00
|
|
|
|
g.DrawImage(img, margin.Left, margin.Top, img.Width, img.Height);
|
2018-04-17 01:43:56 +12:00
|
|
|
|
|
|
|
|
|
if (canvasColor.A > 0)
|
|
|
|
|
{
|
|
|
|
|
g.CompositingMode = CompositingMode.SourceCopy;
|
|
|
|
|
g.SmoothingMode = SmoothingMode.None;
|
|
|
|
|
|
|
|
|
|
using (Brush brush = new SolidBrush(canvasColor))
|
|
|
|
|
{
|
|
|
|
|
if (margin.Top > 0)
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, 0, 0, bmp.Width, margin.Top);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (margin.Right > 0)
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, bmp.Width - margin.Right, 0, margin.Right, bmp.Height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (margin.Bottom > 0)
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, 0, bmp.Height - margin.Bottom, bmp.Width, margin.Bottom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (margin.Left > 0)
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, 0, 0, margin.Left, bmp.Height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap RoundedCorners(Bitmap bmp, int cornerRadius)
|
2014-10-20 03:02:09 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap();
|
2014-10-20 03:02:09 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (bmp)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2014-10-20 03:02:09 +13:00
|
|
|
|
{
|
2017-08-29 19:38:35 +12:00
|
|
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
2014-10-20 03:02:09 +13:00
|
|
|
|
|
|
|
|
|
using (GraphicsPath gp = new GraphicsPath())
|
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
gp.AddRoundedRectangleProper(new RectangleF(0, 0, bmp.Width, bmp.Height), cornerRadius, 0);
|
2014-10-20 03:02:09 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (TextureBrush brush = new TextureBrush(bmp))
|
2014-10-20 03:02:09 +13:00
|
|
|
|
{
|
|
|
|
|
g.FillPath(brush, gp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmpResult;
|
2014-10-20 03:02:09 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-16 11:41:25 +12:00
|
|
|
|
public static Bitmap OutlineOld(Bitmap bmp, int borderSize, Color borderColor)
|
2014-10-21 08:37:51 +13:00
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap(borderSize * 2, borderSize * 2);
|
2014-10-21 08:37:51 +13:00
|
|
|
|
|
|
|
|
|
ColorMatrix maskMatrix = new ColorMatrix();
|
|
|
|
|
maskMatrix.Matrix00 = 0;
|
|
|
|
|
maskMatrix.Matrix11 = 0;
|
|
|
|
|
maskMatrix.Matrix22 = 0;
|
|
|
|
|
maskMatrix.Matrix33 = 1;
|
|
|
|
|
maskMatrix.Matrix40 = ((float)borderColor.R).Remap(0, 255, 0, 1);
|
|
|
|
|
maskMatrix.Matrix41 = ((float)borderColor.G).Remap(0, 255, 0, 1);
|
|
|
|
|
maskMatrix.Matrix42 = ((float)borderColor.B).Remap(0, 255, 0, 1);
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (bmp)
|
|
|
|
|
using (Image shadow = maskMatrix.Apply(bmp))
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2014-10-21 08:37:51 +13:00
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i <= borderSize * 2; i++)
|
|
|
|
|
{
|
|
|
|
|
g.DrawImage(shadow, new Rectangle(i, 0, shadow.Width, shadow.Height));
|
|
|
|
|
g.DrawImage(shadow, new Rectangle(i, borderSize * 2, shadow.Width, shadow.Height));
|
|
|
|
|
g.DrawImage(shadow, new Rectangle(0, i, shadow.Width, shadow.Height));
|
|
|
|
|
g.DrawImage(shadow, new Rectangle(borderSize * 2, i, shadow.Width, shadow.Height));
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
g.DrawImage(bmp, new Rectangle(borderSize, borderSize, bmp.Width, bmp.Height));
|
2014-10-21 08:37:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return bmpResult;
|
2014-10-21 08:37:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-16 11:41:25 +12:00
|
|
|
|
public static Bitmap Outline(Bitmap bmp, int borderSize, Color borderColor, int padding = 0, bool outlineOnly = false)
|
|
|
|
|
{
|
|
|
|
|
Bitmap outline = MakeOutline(bmp, padding, padding + borderSize + 1, borderColor);
|
|
|
|
|
|
|
|
|
|
if (outlineOnly)
|
|
|
|
|
{
|
|
|
|
|
bmp.Dispose();
|
|
|
|
|
return outline;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
using (outline)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
|
|
|
|
g.DrawImage(outline, 0, 0, outline.Width, outline.Height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 02:30:21 +12:00
|
|
|
|
public static Bitmap MakeOutline(Bitmap bmp, int minRadius, int maxRadius, Color color)
|
2020-07-16 11:41:25 +12:00
|
|
|
|
{
|
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap();
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap source = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly))
|
|
|
|
|
using (UnsafeBitmap dest = new UnsafeBitmap(bmpResult, true, ImageLockMode.WriteOnly))
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < source.Width; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < source.Height; y++)
|
|
|
|
|
{
|
2020-07-20 02:30:21 +12:00
|
|
|
|
float dist = DistanceToThreshold(source, x, y, maxRadius, 255);
|
2020-07-16 11:41:25 +12:00
|
|
|
|
|
2020-07-20 02:30:21 +12:00
|
|
|
|
if (dist > minRadius && dist < maxRadius)
|
2020-07-16 11:41:25 +12:00
|
|
|
|
{
|
|
|
|
|
byte alpha = 255;
|
|
|
|
|
|
2020-07-20 02:30:21 +12:00
|
|
|
|
if (dist - minRadius < 1)
|
2020-07-16 11:41:25 +12:00
|
|
|
|
{
|
2020-07-20 02:30:21 +12:00
|
|
|
|
alpha = (byte)(255 * (dist - minRadius));
|
2020-07-16 11:41:25 +12:00
|
|
|
|
}
|
2020-07-20 02:30:21 +12:00
|
|
|
|
else if (maxRadius - dist < 1)
|
2020-07-16 11:41:25 +12:00
|
|
|
|
{
|
2020-07-20 02:30:21 +12:00
|
|
|
|
alpha = (byte)(255 * (maxRadius - dist));
|
2020-07-16 11:41:25 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColorBgra bgra = new ColorBgra(color.B, color.G, color.R, alpha);
|
|
|
|
|
dest.SetPixel(x, y, bgra);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmpResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static float DistanceToThreshold(UnsafeBitmap unsafeBitmap, int x, int y, int radius, int threshold)
|
|
|
|
|
{
|
|
|
|
|
int minx = Math.Max(x - radius, 0);
|
|
|
|
|
int maxx = Math.Min(x + radius, unsafeBitmap.Width - 1);
|
|
|
|
|
int miny = Math.Max(y - radius, 0);
|
|
|
|
|
int maxy = Math.Min(y + radius, unsafeBitmap.Height - 1);
|
2020-08-31 21:53:04 +12:00
|
|
|
|
int dist2 = (radius * radius) + 1;
|
2020-07-16 11:41:25 +12:00
|
|
|
|
|
|
|
|
|
for (int tx = minx; tx <= maxx; tx++)
|
|
|
|
|
{
|
|
|
|
|
for (int ty = miny; ty <= maxy; ty++)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(tx, ty);
|
|
|
|
|
|
|
|
|
|
if (color.Alpha >= threshold)
|
|
|
|
|
{
|
|
|
|
|
int dx = tx - x;
|
|
|
|
|
int dy = ty - y;
|
2020-08-31 21:53:04 +12:00
|
|
|
|
int test_dist2 = (dx * dx) + (dy * dy);
|
2020-07-16 11:41:25 +12:00
|
|
|
|
if (test_dist2 < dist2)
|
|
|
|
|
{
|
|
|
|
|
dist2 = test_dist2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (float)Math.Sqrt(dist2);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
public static Bitmap DrawReflection(Bitmap bmp, int percentage, int maxAlpha, int minAlpha, int offset, bool skew, int skewSize)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap reflection = AddReflection(bmp, percentage, maxAlpha, minAlpha);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
if (skew)
|
|
|
|
|
{
|
|
|
|
|
reflection = AddSkew(reflection, skewSize, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap bmpResult = new Bitmap(reflection.Width, bmp.Height + reflection.Height + offset);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (bmp)
|
2016-11-13 06:22:49 +13:00
|
|
|
|
using (reflection)
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
2020-03-22 10:41:08 +13:00
|
|
|
|
g.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
|
|
|
|
|
g.DrawImage(reflection, 0, bmp.Height + offset, reflection.Width, reflection.Height);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return bmpResult;
|
2016-11-13 06:22:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Bitmap AddSkew(Image img, int x, int y)
|
|
|
|
|
{
|
|
|
|
|
Bitmap result = img.CreateEmptyBitmap(Math.Abs(x), Math.Abs(y));
|
|
|
|
|
|
|
|
|
|
using (img)
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(result))
|
2016-11-13 06:22:49 +13:00
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
|
|
|
|
int startX = -Math.Min(0, x);
|
|
|
|
|
int startY = -Math.Min(0, y);
|
|
|
|
|
int endX = Math.Max(0, x);
|
|
|
|
|
int endY = Math.Max(0, y);
|
|
|
|
|
Point[] destinationPoints = { new Point(startX, startY), new Point(startX + img.Width - 1, endY), new Point(endX, startY + img.Height - 1) };
|
|
|
|
|
g.DrawImage(img, destinationPoints);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Bitmap AddReflection(Image img, int percentage, int maxAlpha, int minAlpha)
|
|
|
|
|
{
|
2019-05-24 07:59:16 +12:00
|
|
|
|
percentage = percentage.Clamp(1, 100);
|
|
|
|
|
maxAlpha = maxAlpha.Clamp(0, 255);
|
|
|
|
|
minAlpha = minAlpha.Clamp(0, 255);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
Bitmap reflection;
|
|
|
|
|
|
|
|
|
|
using (Bitmap bitmapRotate = (Bitmap)img.Clone())
|
|
|
|
|
{
|
|
|
|
|
bitmapRotate.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
|
|
|
|
reflection = bitmapRotate.Clone(new Rectangle(0, 0, bitmapRotate.Width, (int)(bitmapRotate.Height * ((float)percentage / 100))), PixelFormat.Format32bppArgb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(reflection, true))
|
|
|
|
|
{
|
|
|
|
|
int alphaAdd = maxAlpha - minAlpha;
|
|
|
|
|
float reflectionHeight = reflection.Height - 1;
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < reflection.Height; ++y)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < reflection.Width; ++x)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x, y);
|
|
|
|
|
byte alpha = (byte)(maxAlpha - (alphaAdd * (y / reflectionHeight)));
|
|
|
|
|
|
|
|
|
|
if (color.Alpha > alpha)
|
|
|
|
|
{
|
|
|
|
|
color.Alpha = alpha;
|
|
|
|
|
unsafeBitmap.SetPixel(x, y, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return reflection;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 18:08:41 +13:00
|
|
|
|
public static Bitmap DrawBorder(Bitmap bmp, Color borderColor, int borderSize, BorderType borderType, DashStyle dashStyle = DashStyle.Solid)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2021-03-16 18:08:41 +13:00
|
|
|
|
using (Pen borderPen = new Pen(borderColor, borderSize) { Alignment = PenAlignment.Inset, DashStyle = dashStyle })
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
return DrawBorder(bmp, borderPen, borderType);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2021-03-16 18:08:41 +13:00
|
|
|
|
public static Bitmap DrawBorder(Bitmap bmp, Color fromBorderColor, Color toBorderColor, LinearGradientMode gradientType, int borderSize, BorderType borderType,
|
|
|
|
|
DashStyle dashStyle = DashStyle.Solid)
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
int width = bmp.Width;
|
|
|
|
|
int height = bmp.Height;
|
2013-11-09 13:56:37 +13:00
|
|
|
|
|
|
|
|
|
if (borderType == BorderType.Outside)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2013-11-09 13:56:37 +13:00
|
|
|
|
width += borderSize * 2;
|
|
|
|
|
height += borderSize * 2;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-09 13:56:37 +13:00
|
|
|
|
using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, width, height), fromBorderColor, toBorderColor, gradientType))
|
2021-03-16 18:08:41 +13:00
|
|
|
|
using (Pen borderPen = new Pen(brush, borderSize) { Alignment = PenAlignment.Inset, DashStyle = dashStyle })
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
return DrawBorder(bmp, borderPen, borderType);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 18:08:41 +13:00
|
|
|
|
public static Bitmap DrawBorder(Bitmap bmp, GradientInfo gradientInfo, int borderSize, BorderType borderType, DashStyle dashStyle = DashStyle.Solid)
|
2019-09-15 19:05:55 +12:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
int width = bmp.Width;
|
|
|
|
|
int height = bmp.Height;
|
2019-09-15 19:05:55 +12:00
|
|
|
|
|
|
|
|
|
if (borderType == BorderType.Outside)
|
|
|
|
|
{
|
|
|
|
|
width += borderSize * 2;
|
|
|
|
|
height += borderSize * 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (LinearGradientBrush brush = gradientInfo.GetGradientBrush(new Rectangle(0, 0, width, height)))
|
2021-03-16 18:08:41 +13:00
|
|
|
|
using (Pen borderPen = new Pen(brush, borderSize) { Alignment = PenAlignment.Inset, DashStyle = dashStyle })
|
2019-09-15 19:05:55 +12:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
return DrawBorder(bmp, borderPen, borderType);
|
2019-09-15 19:05:55 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
public static Bitmap DrawBorder(Bitmap bmp, Pen borderPen, BorderType borderType)
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
Bitmap bmpResult;
|
2013-11-09 13:56:37 +13:00
|
|
|
|
|
|
|
|
|
if (borderType == BorderType.Inside)
|
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
bmpResult = bmp;
|
2013-11-09 13:56:37 +13:00
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
g.DrawRectangleProper(borderPen, 0, 0, bmp.Width, bmp.Height);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int borderSize = (int)borderPen.Width;
|
2020-03-22 10:16:55 +13:00
|
|
|
|
bmpResult = bmp.CreateEmptyBitmap(borderSize * 2, borderSize * 2);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
using (bmp)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-09 13:56:37 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
g.DrawRectangleProper(borderPen, 0, 0, bmpResult.Width, bmpResult.Height);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
g.SetHighQuality();
|
2020-03-22 10:16:55 +13:00
|
|
|
|
g.DrawImage(bmp, borderSize, borderSize, bmp.Width, bmp.Height);
|
2013-11-09 13:56:37 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
return bmpResult;
|
2013-11-09 13:56:37 +13:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 07:02:08 +13:00
|
|
|
|
public static Bitmap CreateBitmap(int width, int height, Color color)
|
|
|
|
|
{
|
|
|
|
|
if (width > 0 && height > 0)
|
|
|
|
|
{
|
|
|
|
|
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
|
|
|
|
g.Clear(color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-09 13:56:37 +13:00
|
|
|
|
public static Bitmap FillBackground(Image img, Color color)
|
|
|
|
|
{
|
|
|
|
|
using (Brush brush = new SolidBrush(color))
|
|
|
|
|
{
|
|
|
|
|
return FillBackground(img, brush);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Bitmap FillBackground(Image img, Color fromColor, Color toColor, LinearGradientMode gradientType)
|
|
|
|
|
{
|
|
|
|
|
using (LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, img.Width, img.Height), fromColor, toColor, gradientType))
|
|
|
|
|
{
|
|
|
|
|
return FillBackground(img, brush);
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 19:51:05 +12:00
|
|
|
|
public static Bitmap FillBackground(Image img, GradientInfo gradientInfo)
|
|
|
|
|
{
|
|
|
|
|
using (LinearGradientBrush brush = gradientInfo.GetGradientBrush(new Rectangle(0, 0, img.Width, img.Height)))
|
|
|
|
|
{
|
|
|
|
|
return FillBackground(img, brush);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-09 13:56:37 +13:00
|
|
|
|
public static Bitmap FillBackground(Image img, Brush brush)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2014-04-24 18:30:32 +12:00
|
|
|
|
Bitmap result = img.CreateEmptyBitmap();
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(result))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, 0, 0, result.Width, result.Height);
|
|
|
|
|
g.DrawImage(img, 0, 0, result.Width, result.Height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
public static Bitmap DrawCheckers(Image img)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2018-06-07 01:39:28 +12:00
|
|
|
|
return DrawCheckers(img, 10, SystemColors.ControlLight, SystemColors.ControlLightLight);
|
2013-11-17 02:24:03 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
public static Bitmap DrawCheckers(Image img, int checkerSize, Color checkerColor1, Color checkerColor2)
|
2013-11-17 02:24:03 +13:00
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
Bitmap bmpResult = img.CreateEmptyBitmap();
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
using (img)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2019-06-03 07:45:15 +12:00
|
|
|
|
using (Image checker = CreateCheckerPattern(checkerSize, checkerSize, checkerColor1, checkerColor2))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
using (Brush checkerBrush = new TextureBrush(checker, WrapMode.Tile))
|
|
|
|
|
{
|
2020-03-22 10:16:55 +13:00
|
|
|
|
g.FillRectangle(checkerBrush, new Rectangle(0, 0, bmpResult.Width, bmpResult.Height));
|
2013-11-17 02:24:03 +13:00
|
|
|
|
g.SetHighQuality();
|
|
|
|
|
g.DrawImage(img, 0, 0, img.Width, img.Height);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
return bmpResult;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
public static Bitmap DrawCheckers(int width, int height)
|
2019-06-01 09:19:37 +12:00
|
|
|
|
{
|
2019-06-03 07:45:15 +12:00
|
|
|
|
return DrawCheckers(width, height, 10, SystemColors.ControlLight, SystemColors.ControlLightLight);
|
2019-06-01 09:19:37 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:16:55 +13:00
|
|
|
|
public static Bitmap DrawCheckers(int width, int height, int checkerSize, Color checkerColor1, Color checkerColor2)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2013-11-17 02:24:03 +13:00
|
|
|
|
Bitmap bmp = new Bitmap(width, height);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
2019-06-03 07:45:15 +12:00
|
|
|
|
using (Image checker = CreateCheckerPattern(checkerSize, checkerSize, checkerColor1, checkerColor2))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
using (Brush checkerBrush = new TextureBrush(checker, WrapMode.Tile))
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(checkerBrush, new Rectangle(0, 0, bmp.Width, bmp.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap CreateCheckerPattern()
|
2016-04-22 01:32:36 +12:00
|
|
|
|
{
|
2017-03-25 10:16:44 +13:00
|
|
|
|
return CreateCheckerPattern(10, 10);
|
2016-04-22 01:32:36 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap CreateCheckerPattern(int width, int height)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2018-06-07 01:39:28 +12:00
|
|
|
|
return CreateCheckerPattern(width, height, SystemColors.ControlLight, SystemColors.ControlLightLight);
|
2013-11-10 17:22:59 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap CreateCheckerPattern(int width, int height, Color checkerColor1, Color checkerColor2)
|
2013-11-10 17:22:59 +13:00
|
|
|
|
{
|
|
|
|
|
Bitmap bmp = new Bitmap(width * 2, height * 2);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
2019-06-03 07:45:15 +12:00
|
|
|
|
using (Brush brush1 = new SolidBrush(checkerColor1))
|
|
|
|
|
using (Brush brush2 = new SolidBrush(checkerColor2))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2013-11-10 17:22:59 +13:00
|
|
|
|
g.FillRectangle(brush1, 0, 0, width, height);
|
|
|
|
|
g.FillRectangle(brush1, width, height, width, height);
|
|
|
|
|
g.FillRectangle(brush2, width, 0, width, height);
|
|
|
|
|
g.FillRectangle(brush2, 0, height, width, height);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsImagesEqual(Bitmap bmp1, Bitmap bmp2)
|
|
|
|
|
{
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap1 = new UnsafeBitmap(bmp1))
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap2 = new UnsafeBitmap(bmp2))
|
|
|
|
|
{
|
|
|
|
|
return unsafeBitmap1 == unsafeBitmap2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-18 22:47:37 +12:00
|
|
|
|
public static bool IsImageTransparent(Bitmap bmp)
|
|
|
|
|
{
|
2021-04-06 06:45:52 +12:00
|
|
|
|
if (bmp.PixelFormat == PixelFormat.Format32bppArgb || bmp.PixelFormat == PixelFormat.Format32bppPArgb)
|
2017-06-18 22:47:37 +12:00
|
|
|
|
{
|
2017-06-20 04:50:28 +12:00
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly))
|
|
|
|
|
{
|
|
|
|
|
return unsafeBitmap.IsTransparent();
|
|
|
|
|
}
|
2017-06-18 22:47:37 +12:00
|
|
|
|
}
|
2017-06-20 04:50:28 +12:00
|
|
|
|
|
|
|
|
|
return false;
|
2017-06-18 22:47:37 +12:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-03 23:53:49 +13:00
|
|
|
|
public static bool AddMetadata(Image img, int id, string text)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-09-20 19:48:45 +12:00
|
|
|
|
PropertyItem pi = (PropertyItem)typeof(PropertyItem).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { }, null).Invoke(null);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
pi.Id = id;
|
|
|
|
|
pi.Len = text.Length + 1;
|
|
|
|
|
pi.Type = 2;
|
|
|
|
|
byte[] bytesText = Encoding.ASCII.GetBytes(text + " ");
|
|
|
|
|
bytesText[bytesText.Length - 1] = 0;
|
|
|
|
|
pi.Value = bytesText;
|
2019-09-20 19:48:45 +12:00
|
|
|
|
img.SetPropertyItem(pi);
|
|
|
|
|
return true;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
2019-09-20 19:48:45 +12:00
|
|
|
|
DebugHelper.WriteException(e, "AddMetadata reflection failed.");
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void CopyMetadata(Image fromImage, Image toImage)
|
|
|
|
|
{
|
|
|
|
|
PropertyItem[] propertyItems = fromImage.PropertyItems;
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2013-11-03 23:53:49 +13:00
|
|
|
|
foreach (PropertyItem pi in propertyItems)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
toImage.SetPropertyItem(pi);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
DebugHelper.WriteException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Method to rotate an Image object. The result can be one of three cases:
|
|
|
|
|
/// - upsizeOk = true: output image will be larger than the input and no clipping occurs
|
|
|
|
|
/// - upsizeOk = false & clipOk = true: output same size as input, clipping occurs
|
|
|
|
|
/// - upsizeOk = false & clipOk = false: output same size as input, image reduced, no clipping
|
|
|
|
|
///
|
|
|
|
|
/// Note that this method always returns a new Bitmap object, even if rotation is zero - in
|
|
|
|
|
/// which case the returned object is a clone of the input object.
|
|
|
|
|
/// </summary>
|
2020-03-22 11:07:38 +13:00
|
|
|
|
/// <param name="bmp">input Image object, is not modified</param>
|
2013-11-03 23:53:49 +13:00
|
|
|
|
/// <param name="angleDegrees">angle of rotation, in degrees</param>
|
|
|
|
|
/// <param name="upsize">see comments above</param>
|
|
|
|
|
/// <param name="clip">see comments above, not used if upsizeOk = true</param>
|
|
|
|
|
/// <returns>new Bitmap object, may be larger than input image</returns>
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap RotateImage(Bitmap bmp, float angleDegrees, bool upsize, bool clip)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
// Test for zero rotation and return a clone of the input image
|
|
|
|
|
if (angleDegrees == 0f)
|
2020-03-22 11:07:38 +13:00
|
|
|
|
{
|
|
|
|
|
return (Bitmap)bmp.Clone();
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
// Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
|
2020-03-22 11:07:38 +13:00
|
|
|
|
int oldWidth = bmp.Width;
|
|
|
|
|
int oldHeight = bmp.Height;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
int newWidth = oldWidth;
|
|
|
|
|
int newHeight = oldHeight;
|
|
|
|
|
float scaleFactor = 1f;
|
|
|
|
|
|
|
|
|
|
// If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
|
|
|
|
|
if (upsize || !clip)
|
|
|
|
|
{
|
|
|
|
|
double angleRadians = angleDegrees * Math.PI / 180d;
|
|
|
|
|
|
|
|
|
|
double cos = Math.Abs(Math.Cos(angleRadians));
|
|
|
|
|
double sin = Math.Abs(Math.Sin(angleRadians));
|
2018-05-17 01:27:11 +12:00
|
|
|
|
newWidth = (int)Math.Round((oldWidth * cos) + (oldHeight * sin));
|
|
|
|
|
newHeight = (int)Math.Round((oldWidth * sin) + (oldHeight * cos));
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If upsizing not wanted and clipping not OK need a scaling factor
|
|
|
|
|
if (!upsize && !clip)
|
|
|
|
|
{
|
|
|
|
|
scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
|
|
|
|
|
newWidth = oldWidth;
|
|
|
|
|
newHeight = oldHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the new bitmap object.
|
2020-03-22 11:07:38 +13:00
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap();
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
// Create the Graphics object that does the work
|
2020-03-22 11:07:38 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
|
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
// Set up the built-in transformation matrix to do the rotation and maybe scaling
|
2016-11-13 06:22:49 +13:00
|
|
|
|
g.TranslateTransform(newWidth / 2f, newHeight / 2f);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
if (scaleFactor != 1f)
|
2020-03-22 11:07:38 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
g.ScaleTransform(scaleFactor, scaleFactor);
|
2020-03-22 11:07:38 +13:00
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
g.RotateTransform(angleDegrees);
|
|
|
|
|
g.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
// Draw the result
|
2020-03-22 11:07:38 +13:00
|
|
|
|
g.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmpResult;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
public static Bitmap AddShadow(Bitmap bmp, float opacity, int size)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return AddShadow(bmp, opacity, size, 1, Color.Black, new Point(0, 0));
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
public static Bitmap AddShadow(Bitmap bmp, float opacity, int size, float darkness, Color color, Point offset)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
Bitmap shadowImage = null;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
shadowImage = bmp.CreateEmptyBitmap(size * 2, size * 2);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
ColorMatrix maskMatrix = new ColorMatrix();
|
|
|
|
|
maskMatrix.Matrix00 = 0;
|
|
|
|
|
maskMatrix.Matrix11 = 0;
|
|
|
|
|
maskMatrix.Matrix22 = 0;
|
|
|
|
|
maskMatrix.Matrix33 = opacity;
|
|
|
|
|
maskMatrix.Matrix40 = ((float)color.R).Remap(0, 255, 0, 1);
|
|
|
|
|
maskMatrix.Matrix41 = ((float)color.G).Remap(0, 255, 0, 1);
|
|
|
|
|
maskMatrix.Matrix42 = ((float)color.B).Remap(0, 255, 0, 1);
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Rectangle shadowRectangle = new Rectangle(size, size, bmp.Width, bmp.Height);
|
|
|
|
|
maskMatrix.Apply(bmp, shadowImage, shadowRectangle);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
|
|
|
|
if (size > 0)
|
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
BoxBlur(shadowImage, size);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (darkness > 1)
|
|
|
|
|
{
|
|
|
|
|
ColorMatrix alphaMatrix = new ColorMatrix();
|
|
|
|
|
alphaMatrix.Matrix33 = darkness;
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap shadowImage2 = alphaMatrix.Apply(shadowImage);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
shadowImage.Dispose();
|
|
|
|
|
shadowImage = shadowImage2;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap bmpResult = shadowImage.CreateEmptyBitmap(Math.Abs(offset.X), Math.Abs(offset.Y));
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
|
|
|
|
g.DrawImage(shadowImage, Math.Max(0, offset.X), Math.Max(0, offset.Y), shadowImage.Width, shadowImage.Height);
|
2020-03-22 10:41:08 +13:00
|
|
|
|
g.DrawImage(bmp, Math.Max(size, -offset.X + size), Math.Max(size, -offset.Y + size), bmp.Width, bmp.Height);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return bmpResult;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
if (bmp != null) bmp.Dispose();
|
2013-11-03 23:53:49 +13:00
|
|
|
|
if (shadowImage != null) shadowImage.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
public static Bitmap Sharpen(Bitmap bmp, double strength)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
if (bmp != null)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
using (bmp)
|
2016-11-13 06:22:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
Bitmap sharpenImage = (Bitmap)bmp.Clone();
|
|
|
|
|
int width = sharpenImage.Width;
|
|
|
|
|
int height = sharpenImage.Height;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
// Create sharpening filter.
|
|
|
|
|
const int filterSize = 5;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
double[,] filter = new double[,]
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2018-05-17 02:29:34 +12:00
|
|
|
|
{ -1, -1, -1, -1, -1 },
|
|
|
|
|
{ -1, 2, 2, 2, -1 },
|
|
|
|
|
{ -1, 2, 16, 2, -1 },
|
|
|
|
|
{ -1, 2, 2, 2, -1 },
|
|
|
|
|
{ -1, -1, -1, -1, -1 }
|
2016-11-13 06:22:49 +13:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
double bias = 1.0 - strength;
|
|
|
|
|
double factor = strength / 16.0;
|
|
|
|
|
|
|
|
|
|
const int s = filterSize / 2;
|
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
Color[,] result = new Color[sharpenImage.Width, sharpenImage.Height];
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
|
|
|
|
// Lock image bits for read/write.
|
2020-03-21 14:41:34 +13:00
|
|
|
|
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
// Declare an array to hold the bytes of the bitmap.
|
|
|
|
|
int bytes = pbits.Stride * height;
|
|
|
|
|
byte[] rgbValues = new byte[bytes];
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
// Copy the RGB values into the array.
|
|
|
|
|
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
int rgb;
|
|
|
|
|
// Fill the color array with the new sharpened color values.
|
|
|
|
|
for (int x = s; x < width - s; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = s; y < height - s; y++)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
double red = 0.0, green = 0.0, blue = 0.0;
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
for (int filterX = 0; filterX < filterSize; filterX++)
|
|
|
|
|
{
|
|
|
|
|
for (int filterY = 0; filterY < filterSize; filterY++)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
int imageX = (x - s + filterX + width) % width;
|
|
|
|
|
int imageY = (y - s + filterY + height) % height;
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
rgb = (imageY * pbits.Stride) + (3 * imageX);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
red += rgbValues[rgb + 2] * filter[filterX, filterY];
|
|
|
|
|
green += rgbValues[rgb + 1] * filter[filterX, filterY];
|
|
|
|
|
blue += rgbValues[rgb + 0] * filter[filterX, filterY];
|
|
|
|
|
}
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
rgb = (y * pbits.Stride) + (3 * x);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
int r = Math.Min(Math.Max((int)((factor * red) + (bias * rgbValues[rgb + 2])), 0), 255);
|
|
|
|
|
int g = Math.Min(Math.Max((int)((factor * green) + (bias * rgbValues[rgb + 1])), 0), 255);
|
|
|
|
|
int b = Math.Min(Math.Max((int)((factor * blue) + (bias * rgbValues[rgb + 0])), 0), 255);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
result[x, y] = Color.FromArgb(r, g, b);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-21 14:41:34 +13:00
|
|
|
|
}
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
// Update the image with the sharpened pixels.
|
|
|
|
|
for (int x = s; x < width - s; x++)
|
|
|
|
|
{
|
|
|
|
|
for (int y = s; y < height - s; y++)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2020-03-21 14:41:34 +13:00
|
|
|
|
rgb = (y * pbits.Stride) + (3 * x);
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
rgbValues[rgb + 2] = result[x, y].R;
|
|
|
|
|
rgbValues[rgb + 1] = result[x, y].G;
|
|
|
|
|
rgbValues[rgb + 0] = result[x, y].B;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-13 06:22:49 +13:00
|
|
|
|
|
2020-03-21 14:41:34 +13:00
|
|
|
|
// Copy the RGB values back to the bitmap.
|
|
|
|
|
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
|
|
|
|
|
// Release image bits.
|
|
|
|
|
sharpenImage.UnlockBits(pbits);
|
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
return sharpenImage;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-21 14:41:34 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
return null;
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
public static void HighlightImage(Bitmap bmp)
|
2013-11-03 23:53:49 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
HighlightImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
public static void HighlightImage(Bitmap bmp, Color highlightColor)
|
|
|
|
|
{
|
|
|
|
|
HighlightImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height), highlightColor);
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
public static void HighlightImage(Bitmap bmp, Rectangle rect)
|
|
|
|
|
{
|
|
|
|
|
HighlightImage(bmp, rect, Color.Yellow);
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
2013-11-06 06:58:22 +13:00
|
|
|
|
|
2016-11-13 06:22:49 +13:00
|
|
|
|
public static void HighlightImage(Bitmap bmp, Rectangle rect, Color highlightColor)
|
2013-11-06 06:58:22 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
2013-11-06 06:58:22 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
for (int y = rect.Y; y < rect.Height; y++)
|
2013-11-06 06:58:22 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
for (int x = rect.X; x < rect.Width; x++)
|
2013-11-06 06:58:22 +13:00
|
|
|
|
{
|
2016-11-13 06:22:49 +13:00
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x, y);
|
|
|
|
|
color.Red = Math.Min(color.Red, highlightColor.R);
|
|
|
|
|
color.Green = Math.Min(color.Green, highlightColor.G);
|
|
|
|
|
color.Blue = Math.Min(color.Blue, highlightColor.B);
|
|
|
|
|
unsafeBitmap.SetPixel(x, y, color);
|
2013-11-06 06:58:22 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-21 06:45:11 +13:00
|
|
|
|
|
2016-11-14 05:33:53 +13:00
|
|
|
|
public static void Pixelate(Bitmap bmp, int pixelSize)
|
2016-11-14 02:59:06 +13:00
|
|
|
|
{
|
|
|
|
|
if (pixelSize > 1)
|
|
|
|
|
{
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < unsafeBitmap.Height; y += pixelSize)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < unsafeBitmap.Width; x += pixelSize)
|
|
|
|
|
{
|
|
|
|
|
int xLimit = Math.Min(x + pixelSize, unsafeBitmap.Width);
|
2016-11-29 02:24:27 +13:00
|
|
|
|
int yLimit = Math.Min(y + pixelSize, unsafeBitmap.Height);
|
|
|
|
|
int pixelCount = (xLimit - x) * (yLimit - y);
|
2018-10-19 10:27:01 +13:00
|
|
|
|
float r = 0, g = 0, b = 0, a = 0;
|
|
|
|
|
float weightedCount = 0;
|
2016-11-14 02:59:06 +13:00
|
|
|
|
|
|
|
|
|
for (int y2 = y; y2 < yLimit; y2++)
|
|
|
|
|
{
|
|
|
|
|
for (int x2 = x; x2 < xLimit; x2++)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x2, y2);
|
|
|
|
|
|
2019-09-20 19:48:45 +12:00
|
|
|
|
float pixelWeight = color.Alpha / 255f;
|
2018-10-19 10:27:01 +13:00
|
|
|
|
|
|
|
|
|
r += color.Red * pixelWeight;
|
|
|
|
|
g += color.Green * pixelWeight;
|
|
|
|
|
b += color.Blue * pixelWeight;
|
|
|
|
|
a += color.Alpha * pixelWeight;
|
|
|
|
|
|
|
|
|
|
weightedCount += pixelWeight;
|
2016-11-14 02:59:06 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-19 10:27:01 +13:00
|
|
|
|
ColorBgra averageColor = new ColorBgra((byte)(b / weightedCount), (byte)(g / weightedCount), (byte)(r / weightedCount), (byte)(a / pixelCount));
|
2016-11-14 02:59:06 +13:00
|
|
|
|
|
|
|
|
|
for (int y2 = y; y2 < yLimit; y2++)
|
|
|
|
|
{
|
|
|
|
|
for (int x2 = x; x2 < xLimit; x2++)
|
|
|
|
|
{
|
|
|
|
|
unsafeBitmap.SetPixel(x2, y2, averageColor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 12:00:55 +13:00
|
|
|
|
public static void BoxBlur(Bitmap bmp, int range)
|
2018-07-07 01:16:49 +12:00
|
|
|
|
{
|
|
|
|
|
BoxBlur(bmp, range, new Rectangle(0, 0, bmp.Width, bmp.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://lotsacode.wordpress.com/2010/12/08/fast-blur-box-blur-with-accumulator/
|
|
|
|
|
public static void BoxBlur(Bitmap bmp, int range, Rectangle rect)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
if (range > 1)
|
|
|
|
|
{
|
2016-11-14 12:27:24 +13:00
|
|
|
|
if (range.IsEvenNumber())
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
range++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
|
|
|
|
{
|
2018-07-07 01:16:49 +12:00
|
|
|
|
BoxBlurHorizontal(unsafeBitmap, range, rect);
|
|
|
|
|
BoxBlurVertical(unsafeBitmap, range, rect);
|
|
|
|
|
BoxBlurHorizontal(unsafeBitmap, range, rect);
|
|
|
|
|
BoxBlurVertical(unsafeBitmap, range, rect);
|
2016-11-14 12:00:55 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
private static void BoxBlurHorizontal(UnsafeBitmap unsafeBitmap, int range, Rectangle rect)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
2018-07-07 01:16:49 +12:00
|
|
|
|
int left = rect.X;
|
|
|
|
|
int top = rect.Y;
|
|
|
|
|
int right = rect.Right;
|
|
|
|
|
int bottom = rect.Bottom;
|
2016-11-14 12:00:55 +13:00
|
|
|
|
int halfRange = range / 2;
|
2018-07-07 01:16:49 +12:00
|
|
|
|
ColorBgra[] newColors = new ColorBgra[unsafeBitmap.Width];
|
2016-11-14 12:00:55 +13:00
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int y = top; y < bottom; y++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
int hits = 0;
|
|
|
|
|
int r = 0;
|
|
|
|
|
int g = 0;
|
|
|
|
|
int b = 0;
|
|
|
|
|
int a = 0;
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int x = left - halfRange; x < right; x++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
int oldPixel = x - halfRange - 1;
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (oldPixel >= left)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(oldPixel, y);
|
|
|
|
|
|
|
|
|
|
if (color.Bgra != 0)
|
|
|
|
|
{
|
|
|
|
|
r -= color.Red;
|
|
|
|
|
g -= color.Green;
|
|
|
|
|
b -= color.Blue;
|
|
|
|
|
a -= color.Alpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hits--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int newPixel = x + halfRange;
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (newPixel < right)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(newPixel, y);
|
|
|
|
|
|
|
|
|
|
if (color.Bgra != 0)
|
|
|
|
|
{
|
|
|
|
|
r += color.Red;
|
|
|
|
|
g += color.Green;
|
|
|
|
|
b += color.Blue;
|
|
|
|
|
a += color.Alpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hits++;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (x >= left)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
newColors[x] = new ColorBgra((byte)(b / hits), (byte)(g / hits), (byte)(r / hits), (byte)(a / hits));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int x = left; x < right; x++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
unsafeBitmap.SetPixel(x, y, newColors[x]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
private static void BoxBlurVertical(UnsafeBitmap unsafeBitmap, int range, Rectangle rect)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
2018-07-07 01:16:49 +12:00
|
|
|
|
int left = rect.X;
|
|
|
|
|
int top = rect.Y;
|
|
|
|
|
int right = rect.Right;
|
|
|
|
|
int bottom = rect.Bottom;
|
2016-11-14 12:00:55 +13:00
|
|
|
|
int halfRange = range / 2;
|
2018-08-23 22:25:02 +12:00
|
|
|
|
ColorBgra[] newColors = new ColorBgra[unsafeBitmap.Height];
|
2016-11-14 12:00:55 +13:00
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int x = left; x < right; x++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
int hits = 0;
|
|
|
|
|
int r = 0;
|
|
|
|
|
int g = 0;
|
|
|
|
|
int b = 0;
|
|
|
|
|
int a = 0;
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int y = top - halfRange; y < bottom; y++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
int oldPixel = y - halfRange - 1;
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (oldPixel >= top)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x, oldPixel);
|
|
|
|
|
|
|
|
|
|
if (color.Bgra != 0)
|
|
|
|
|
{
|
|
|
|
|
r -= color.Red;
|
|
|
|
|
g -= color.Green;
|
|
|
|
|
b -= color.Blue;
|
|
|
|
|
a -= color.Alpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hits--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int newPixel = y + halfRange;
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (newPixel < bottom)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x, newPixel);
|
|
|
|
|
|
|
|
|
|
if (color.Bgra != 0)
|
|
|
|
|
{
|
|
|
|
|
r += color.Red;
|
|
|
|
|
g += color.Green;
|
|
|
|
|
b += color.Blue;
|
|
|
|
|
a += color.Alpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hits++;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
if (y >= top)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
newColors[y] = new ColorBgra((byte)(b / hits), (byte)(g / hits), (byte)(r / hits), (byte)(a / hits));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 01:16:49 +12:00
|
|
|
|
for (int y = top; y < bottom; y++)
|
2016-11-14 12:00:55 +13:00
|
|
|
|
{
|
|
|
|
|
unsafeBitmap.SetPixel(x, y, newColors[y]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 07:45:26 +12:00
|
|
|
|
public static void ColorDepth(Bitmap bmp, int bitsPerChannel = 4)
|
|
|
|
|
{
|
|
|
|
|
if (bitsPerChannel < 1 || bitsPerChannel > 8)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double colorsPerChannel = Math.Pow(2, bitsPerChannel);
|
|
|
|
|
double colorInterval = 255 / (colorsPerChannel - 1);
|
|
|
|
|
|
|
|
|
|
byte Remap(byte color, double interval)
|
|
|
|
|
{
|
2020-09-15 12:36:49 +12:00
|
|
|
|
return (byte)Math.Round(Math.Round(color / interval) * interval);
|
2020-09-15 07:45:26 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < unsafeBitmap.Height; y++)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < unsafeBitmap.Width; x++)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(x, y);
|
|
|
|
|
color.Red = Remap(color.Red, colorInterval);
|
|
|
|
|
color.Green = Remap(color.Green, colorInterval);
|
|
|
|
|
color.Blue = Remap(color.Blue, colorInterval);
|
|
|
|
|
unsafeBitmap.SetPixel(x, y, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 05:33:53 +13:00
|
|
|
|
// http://incubator.quasimondo.com/processing/superfast_blur.php
|
|
|
|
|
public static void FastBoxBlur(Bitmap bmp, int radius)
|
|
|
|
|
{
|
|
|
|
|
if (radius < 1) return;
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
|
|
|
|
{
|
|
|
|
|
int w = unsafeBitmap.Width;
|
|
|
|
|
int h = unsafeBitmap.Height;
|
|
|
|
|
int wm = w - 1;
|
|
|
|
|
int hm = h - 1;
|
|
|
|
|
int wh = w * h;
|
|
|
|
|
int div = radius + radius + 1;
|
|
|
|
|
byte[] r = new byte[wh];
|
|
|
|
|
byte[] g = new byte[wh];
|
|
|
|
|
byte[] b = new byte[wh];
|
|
|
|
|
byte[] a = new byte[wh];
|
|
|
|
|
int rsum, gsum, bsum, asum, x, y, i, p, p1, p2, yp, yi, yw;
|
|
|
|
|
int[] vmin = new int[Math.Max(w, h)];
|
|
|
|
|
int[] vmax = new int[Math.Max(w, h)];
|
|
|
|
|
|
|
|
|
|
byte[] dv = new byte[256 * div];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 256 * div; i++)
|
|
|
|
|
{
|
|
|
|
|
dv[i] = (byte)(i / div);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yw = yi = 0;
|
|
|
|
|
|
|
|
|
|
for (y = 0; y < h; y++)
|
|
|
|
|
{
|
|
|
|
|
rsum = gsum = bsum = asum = 0;
|
|
|
|
|
|
|
|
|
|
for (i = -radius; i <= radius; i++)
|
|
|
|
|
{
|
|
|
|
|
p = (yi + Math.Min(wm, Math.Max(i, 0)));
|
|
|
|
|
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(p);
|
|
|
|
|
rsum += color.Red;
|
|
|
|
|
gsum += color.Green;
|
|
|
|
|
bsum += color.Blue;
|
|
|
|
|
asum += color.Alpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
|
{
|
|
|
|
|
r[yi] = dv[rsum];
|
|
|
|
|
g[yi] = dv[gsum];
|
|
|
|
|
b[yi] = dv[bsum];
|
|
|
|
|
a[yi] = dv[asum];
|
|
|
|
|
|
|
|
|
|
if (y == 0)
|
|
|
|
|
{
|
|
|
|
|
vmin[x] = Math.Min(x + radius + 1, wm);
|
|
|
|
|
vmax[x] = Math.Max(x - radius, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p1 = (yw + vmin[x]);
|
|
|
|
|
p2 = (yw + vmax[x]);
|
|
|
|
|
|
|
|
|
|
ColorBgra color1 = unsafeBitmap.GetPixel(p1);
|
|
|
|
|
ColorBgra color2 = unsafeBitmap.GetPixel(p2);
|
|
|
|
|
|
|
|
|
|
rsum += color1.Red - color2.Red;
|
|
|
|
|
gsum += color1.Green - color2.Green;
|
|
|
|
|
bsum += color1.Blue - color2.Blue;
|
|
|
|
|
asum += color1.Alpha - color2.Alpha;
|
|
|
|
|
|
|
|
|
|
yi++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yw += w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
|
{
|
|
|
|
|
rsum = gsum = bsum = asum = 0;
|
|
|
|
|
yp = -radius * w;
|
|
|
|
|
|
|
|
|
|
for (i = -radius; i <= radius; i++)
|
|
|
|
|
{
|
|
|
|
|
yi = Math.Max(0, yp) + x;
|
|
|
|
|
rsum += r[yi];
|
|
|
|
|
gsum += g[yi];
|
|
|
|
|
bsum += b[yi];
|
|
|
|
|
asum += a[yi];
|
|
|
|
|
yp += w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yi = x;
|
|
|
|
|
|
|
|
|
|
for (y = 0; y < h; y++)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = new ColorBgra(dv[bsum], dv[gsum], dv[rsum], dv[asum]);
|
|
|
|
|
unsafeBitmap.SetPixel(yi, color);
|
|
|
|
|
|
|
|
|
|
if (x == 0)
|
|
|
|
|
{
|
|
|
|
|
vmin[y] = Math.Min(y + radius + 1, hm) * w;
|
|
|
|
|
vmax[y] = Math.Max(y - radius, 0) * w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p1 = x + vmin[y];
|
|
|
|
|
p2 = x + vmax[y];
|
|
|
|
|
|
|
|
|
|
rsum += r[p1] - r[p2];
|
|
|
|
|
gsum += g[p1] - g[p2];
|
|
|
|
|
bsum += b[p1] - b[p2];
|
|
|
|
|
asum += a[p1] - a[p2];
|
|
|
|
|
|
|
|
|
|
yi += w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
public static Bitmap TornEdges(Bitmap bmp, int tornDepth, int tornRange, AnchorStyles sides, bool curvedEdges)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2017-05-15 18:31:39 +12:00
|
|
|
|
if (tornDepth < 1 || tornRange < 1 || sides == AnchorStyles.None)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return bmp;
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-15 18:31:39 +12:00
|
|
|
|
List<Point> points = new List<Point>();
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
int horizontalTornCount = bmp.Width / tornRange;
|
|
|
|
|
int verticalTornCount = bmp.Height / tornRange;
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-08-02 23:59:30 +12:00
|
|
|
|
if (horizontalTornCount < 2 && verticalTornCount < 2)
|
|
|
|
|
{
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sides.HasFlag(AnchorStyles.Top) && horizontalTornCount > 1)
|
2017-05-15 18:31:39 +12:00
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
for (int x = 0; x < horizontalTornCount - 1; x++)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
points.Add(new Point(tornRange * x, RandomFast.Next(0, tornDepth)));
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
points.Add(new Point(0, 0));
|
2020-03-22 10:41:08 +13:00
|
|
|
|
points.Add(new Point(bmp.Width - 1, 0));
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-08-02 23:59:30 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Right) && verticalTornCount > 1)
|
2017-05-15 18:31:39 +12:00
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
for (int y = 0; y < verticalTornCount - 1; y++)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
points.Add(new Point(bmp.Width - 1 - RandomFast.Next(0, tornDepth), tornRange * y));
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
points.Add(new Point(bmp.Width - 1, 0));
|
|
|
|
|
points.Add(new Point(bmp.Width - 1, bmp.Height - 1));
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-08-02 23:59:30 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Bottom) && horizontalTornCount > 1)
|
2017-05-15 18:31:39 +12:00
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
for (int x = 0; x < horizontalTornCount - 1; x++)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
points.Add(new Point(bmp.Width - 1 - (tornRange * x), bmp.Height - 1 - RandomFast.Next(0, tornDepth)));
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
points.Add(new Point(bmp.Width - 1, bmp.Height - 1));
|
|
|
|
|
points.Add(new Point(0, bmp.Height - 1));
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-08-02 23:59:30 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Left) && verticalTornCount > 1)
|
2017-05-15 18:31:39 +12:00
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
for (int y = 0; y < verticalTornCount - 1; y++)
|
2016-11-14 10:04:13 +13:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
points.Add(new Point(RandomFast.Next(0, tornDepth), bmp.Height - 1 - (tornRange * y)));
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-22 10:41:08 +13:00
|
|
|
|
points.Add(new Point(0, bmp.Height - 1));
|
2017-05-15 20:20:32 +12:00
|
|
|
|
points.Add(new Point(0, 0));
|
2017-05-15 18:31:39 +12:00
|
|
|
|
}
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap();
|
2016-11-14 10:04:13 +13:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (bmp)
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
|
|
|
|
using (TextureBrush brush = new TextureBrush(bmp))
|
2017-05-15 18:31:39 +12:00
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
2017-05-15 18:50:14 +12:00
|
|
|
|
|
2017-05-15 20:20:32 +12:00
|
|
|
|
Point[] fillPoints = points.Distinct().ToArray();
|
|
|
|
|
|
2017-05-15 18:50:14 +12:00
|
|
|
|
if (curvedEdges)
|
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
g.FillClosedCurve(brush, fillPoints);
|
2017-05-15 18:50:14 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-05-15 20:20:32 +12:00
|
|
|
|
g.FillPolygon(brush, fillPoints);
|
2017-05-15 18:50:14 +12:00
|
|
|
|
}
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
2020-03-22 10:41:08 +13:00
|
|
|
|
|
|
|
|
|
return bmpResult;
|
2016-11-14 10:04:13 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
public static Bitmap Slice(Bitmap bmp, int minSliceHeight, int maxSliceHeight, int minSliceShift, int maxSliceShift)
|
2019-06-26 03:33:29 +12:00
|
|
|
|
{
|
2020-07-21 07:31:03 +12:00
|
|
|
|
if (minSliceHeight < 1)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(minSliceHeight));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxSliceHeight < 1)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(maxSliceHeight));
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
Bitmap bmpResult = bmp.CreateEmptyBitmap();
|
2019-06-26 03:33:29 +12:00
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
using (Graphics g = Graphics.FromImage(bmpResult))
|
2019-06-26 03:33:29 +12:00
|
|
|
|
{
|
|
|
|
|
int y = 0;
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
while (y < bmp.Height)
|
2019-06-26 03:33:29 +12:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
Rectangle sourceRect = new Rectangle(0, y, bmp.Width, RandomFast.Next(minSliceHeight, maxSliceHeight));
|
2019-06-26 03:33:29 +12:00
|
|
|
|
Rectangle destRect = sourceRect;
|
|
|
|
|
|
2020-04-24 01:41:09 +12:00
|
|
|
|
if (RandomFast.Next(1) == 0) // Shift left
|
2019-06-26 03:33:29 +12:00
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
destRect.X = RandomFast.Next(-maxSliceShift, -minSliceShift);
|
2019-06-26 03:33:29 +12:00
|
|
|
|
}
|
|
|
|
|
else // Shift right
|
|
|
|
|
{
|
2020-04-24 01:41:09 +12:00
|
|
|
|
destRect.X = RandomFast.Next(minSliceShift, maxSliceShift);
|
2019-06-26 03:33:29 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
g.DrawImage(bmp, destRect, sourceRect, GraphicsUnit.Pixel);
|
2019-06-26 03:33:29 +12:00
|
|
|
|
|
|
|
|
|
y += sourceRect.Height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 10:41:08 +13:00
|
|
|
|
return bmpResult;
|
2019-06-26 03:33:29 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-06 10:37:56 +12:00
|
|
|
|
public static string OpenImageFileDialog(Form form = null, string initialDirectory = null)
|
2015-10-01 02:35:45 +13:00
|
|
|
|
{
|
2020-07-06 10:37:56 +12:00
|
|
|
|
string[] images = OpenImageFileDialog(false, form, initialDirectory);
|
2015-10-01 02:35:45 +13:00
|
|
|
|
|
|
|
|
|
if (images != null && images.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
return images[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-06 10:37:56 +12:00
|
|
|
|
public static string[] OpenImageFileDialog(bool multiselect, Form form = null, string initialDirectory = null)
|
2013-11-21 06:45:11 +13:00
|
|
|
|
{
|
|
|
|
|
using (OpenFileDialog ofd = new OpenFileDialog())
|
|
|
|
|
{
|
|
|
|
|
ofd.Filter = "Image files (*.png, *.jpg, *.jpeg, *.jpe, *.jfif, *.gif, *.bmp, *.tif, *.tiff)|*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif;*.bmp;*.tif;*.tiff|" +
|
2016-09-02 23:44:38 +12:00
|
|
|
|
"PNG (*.png)|*.png|JPEG (*.jpg, *.jpeg, *.jpe, *.jfif)|*.jpg;*.jpeg;*.jpe;*.jfif|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|TIFF (*.tif, *.tiff)|*.tif;*.tiff";
|
2013-11-21 06:45:11 +13:00
|
|
|
|
|
2015-10-01 02:35:45 +13:00
|
|
|
|
ofd.Multiselect = multiselect;
|
|
|
|
|
|
2020-07-06 10:37:56 +12:00
|
|
|
|
if (!string.IsNullOrEmpty(initialDirectory))
|
|
|
|
|
{
|
|
|
|
|
ofd.InitialDirectory = initialDirectory;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-27 02:54:10 +13:00
|
|
|
|
if (ofd.ShowDialog(form) == DialogResult.OK)
|
2013-11-21 06:45:11 +13:00
|
|
|
|
{
|
2015-10-01 02:35:45 +13:00
|
|
|
|
return ofd.FileNames;
|
2013-11-21 06:45:11 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-19 09:38:30 +12:00
|
|
|
|
public static ImageFormat GetImageFormat(string filePath)
|
2014-05-04 12:53:11 +12:00
|
|
|
|
{
|
2014-07-19 09:38:30 +12:00
|
|
|
|
ImageFormat imageFormat = ImageFormat.Png;
|
2014-08-28 12:53:38 +12:00
|
|
|
|
string ext = Helpers.GetFilenameExtension(filePath);
|
2014-05-04 12:53:11 +12:00
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(ext))
|
|
|
|
|
{
|
2019-03-04 05:21:05 +13:00
|
|
|
|
if (ext.Equals("png", StringComparison.OrdinalIgnoreCase))
|
2014-05-04 12:53:11 +12:00
|
|
|
|
{
|
2019-03-04 05:21:05 +13:00
|
|
|
|
imageFormat = ImageFormat.Png;
|
|
|
|
|
}
|
|
|
|
|
else if (ext.Equals("jpg", StringComparison.OrdinalIgnoreCase) || ext.Equals("jpeg", StringComparison.OrdinalIgnoreCase) ||
|
|
|
|
|
ext.Equals("jpe", StringComparison.OrdinalIgnoreCase) || ext.Equals("jfif", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
imageFormat = ImageFormat.Jpeg;
|
|
|
|
|
}
|
|
|
|
|
else if (ext.Equals("gif", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
imageFormat = ImageFormat.Gif;
|
|
|
|
|
}
|
|
|
|
|
else if (ext.Equals("bmp", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
imageFormat = ImageFormat.Bmp;
|
|
|
|
|
}
|
|
|
|
|
else if (ext.Equals("tif", StringComparison.OrdinalIgnoreCase) || ext.Equals("tiff", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
imageFormat = ImageFormat.Tiff;
|
2014-05-04 12:53:11 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-19 09:38:30 +12:00
|
|
|
|
|
|
|
|
|
return imageFormat;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 11:26:53 +12:00
|
|
|
|
public static bool SaveImage(Image img, string filePath)
|
2014-07-19 09:38:30 +12:00
|
|
|
|
{
|
2018-11-17 20:37:16 +13:00
|
|
|
|
Helpers.CreateDirectoryFromFilePath(filePath);
|
2020-02-05 03:23:53 +13:00
|
|
|
|
ImageFormat imageFormat = GetImageFormat(filePath);
|
2018-11-17 20:37:16 +13:00
|
|
|
|
|
2020-02-05 03:23:53 +13:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
img.Save(filePath, imageFormat);
|
2020-09-20 11:26:53 +12:00
|
|
|
|
return true;
|
2020-02-05 03:23:53 +13:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
e.ShowError();
|
|
|
|
|
}
|
2020-09-20 11:26:53 +12:00
|
|
|
|
|
|
|
|
|
return false;
|
2014-05-04 12:53:11 +12:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 04:25:01 +13:00
|
|
|
|
public static string SaveImageFileDialog(Image img, string filePath = "", bool useLastDirectory = true)
|
2013-11-21 06:45:11 +13:00
|
|
|
|
{
|
|
|
|
|
using (SaveFileDialog sfd = new SaveFileDialog())
|
|
|
|
|
{
|
2019-11-05 23:33:12 +13:00
|
|
|
|
sfd.Filter = "PNG (*.png)|*.png|JPEG (*.jpg, *.jpeg, *.jpe, *.jfif)|*.jpg;*.jpeg;*.jpe;*.jfif|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|TIFF (*.tif, *.tiff)|*.tif;*.tiff";
|
|
|
|
|
sfd.DefaultExt = "png";
|
2020-03-21 14:41:34 +13:00
|
|
|
|
|
2018-12-10 04:25:01 +13:00
|
|
|
|
string initialDirectory = null;
|
|
|
|
|
|
|
|
|
|
if (useLastDirectory && !string.IsNullOrEmpty(HelpersOptions.LastSaveDirectory) && Directory.Exists(HelpersOptions.LastSaveDirectory))
|
|
|
|
|
{
|
|
|
|
|
initialDirectory = HelpersOptions.LastSaveDirectory;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-05 00:31:35 +12:00
|
|
|
|
if (!string.IsNullOrEmpty(filePath))
|
|
|
|
|
{
|
|
|
|
|
string folder = Path.GetDirectoryName(filePath);
|
2018-12-10 04:25:01 +13:00
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(initialDirectory) && !string.IsNullOrEmpty(folder) && Directory.Exists(folder))
|
2014-05-05 00:31:35 +12:00
|
|
|
|
{
|
2018-12-10 04:25:01 +13:00
|
|
|
|
initialDirectory = folder;
|
2014-05-05 00:31:35 +12:00
|
|
|
|
}
|
2018-12-10 04:25:01 +13:00
|
|
|
|
|
2019-11-05 23:33:12 +13:00
|
|
|
|
sfd.FileName = Path.GetFileName(filePath);
|
|
|
|
|
|
|
|
|
|
string ext = Helpers.GetFilenameExtension(filePath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(ext))
|
|
|
|
|
{
|
|
|
|
|
ext = ext.ToLowerInvariant();
|
|
|
|
|
|
|
|
|
|
switch (ext)
|
|
|
|
|
{
|
|
|
|
|
case "png":
|
|
|
|
|
sfd.FilterIndex = 1;
|
|
|
|
|
break;
|
|
|
|
|
case "jpg":
|
|
|
|
|
case "jpeg":
|
|
|
|
|
case "jpe":
|
|
|
|
|
case "jfif":
|
|
|
|
|
sfd.FilterIndex = 2;
|
|
|
|
|
break;
|
|
|
|
|
case "gif":
|
|
|
|
|
sfd.FilterIndex = 3;
|
|
|
|
|
break;
|
|
|
|
|
case "bmp":
|
|
|
|
|
sfd.FilterIndex = 4;
|
|
|
|
|
break;
|
|
|
|
|
case "tif":
|
|
|
|
|
case "tiff":
|
|
|
|
|
sfd.FilterIndex = 5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-05 00:31:35 +12:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 04:25:01 +13:00
|
|
|
|
sfd.InitialDirectory = initialDirectory;
|
2013-11-21 06:45:11 +13:00
|
|
|
|
|
2018-12-10 04:25:01 +13:00
|
|
|
|
if (sfd.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(sfd.FileName))
|
2013-11-21 06:45:11 +13:00
|
|
|
|
{
|
2014-05-04 12:53:11 +12:00
|
|
|
|
SaveImage(img, sfd.FileName);
|
2018-12-10 04:25:01 +13:00
|
|
|
|
HelpersOptions.LastSaveDirectory = Path.GetDirectoryName(sfd.FileName);
|
2014-05-05 00:31:35 +12:00
|
|
|
|
return sfd.FileName;
|
2013-11-21 06:45:11 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-05 00:31:35 +12:00
|
|
|
|
|
|
|
|
|
return null;
|
2013-11-21 06:45:11 +13:00
|
|
|
|
}
|
2013-11-21 06:59:00 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static Bitmap LoadImage(string filePath)
|
2013-11-21 06:59:00 +13:00
|
|
|
|
{
|
2021-06-15 00:18:09 +12:00
|
|
|
|
if (!string.IsNullOrEmpty(filePath))
|
2013-12-25 10:29:41 +13:00
|
|
|
|
{
|
2021-06-15 00:18:09 +12:00
|
|
|
|
try
|
2013-12-25 10:29:41 +13:00
|
|
|
|
{
|
2018-03-22 18:16:01 +13:00
|
|
|
|
filePath = Helpers.GetAbsolutePath(filePath);
|
2017-12-22 23:59:52 +13:00
|
|
|
|
|
2018-03-22 18:16:01 +13:00
|
|
|
|
if (!string.IsNullOrEmpty(filePath) && Helpers.IsImageFile(filePath) && File.Exists(filePath))
|
2017-12-22 23:59:52 +13:00
|
|
|
|
{
|
2018-03-23 19:27:21 +13:00
|
|
|
|
// http://stackoverflow.com/questions/788335/why-does-image-fromfile-keep-a-file-handle-open-sometimes
|
2020-03-22 11:07:38 +13:00
|
|
|
|
Bitmap bmp = (Bitmap)Image.FromStream(new MemoryStream(File.ReadAllBytes(filePath)));
|
2017-12-22 23:59:52 +13:00
|
|
|
|
|
2018-03-22 18:16:01 +13:00
|
|
|
|
if (HelpersOptions.RotateImageByExifOrientationData)
|
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
RotateImageByExifOrientationData(bmp);
|
2018-03-22 18:16:01 +13:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
return bmp;
|
2018-03-22 18:16:01 +13:00
|
|
|
|
}
|
2013-12-25 10:29:41 +13:00
|
|
|
|
}
|
2021-06-15 00:18:09 +12:00
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
DebugHelper.WriteException(e);
|
|
|
|
|
}
|
2013-11-21 06:59:00 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2015-09-25 22:02:31 +12:00
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap LoadImageWithFileDialog()
|
2017-12-25 08:04:12 +13:00
|
|
|
|
{
|
|
|
|
|
string filepath = OpenImageFileDialog();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(filepath))
|
|
|
|
|
{
|
|
|
|
|
return LoadImage(filepath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 05:09:06 +12:00
|
|
|
|
public static Bitmap CombineImages(List<Bitmap> images, Orientation orientation = Orientation.Vertical,
|
|
|
|
|
ImageCombinerAlignment alignment = ImageCombinerAlignment.LeftOrTop, int space = 0, bool autoFillBackground = false)
|
2015-09-25 22:02:31 +12:00
|
|
|
|
{
|
2015-09-30 21:28:54 +13:00
|
|
|
|
int width, height;
|
2021-04-07 05:09:06 +12:00
|
|
|
|
int imageCount = images.Count;
|
2020-03-24 09:44:50 +13:00
|
|
|
|
int spaceSize = space * (imageCount - 1);
|
2015-09-30 21:28:54 +13:00
|
|
|
|
|
|
|
|
|
if (orientation == Orientation.Vertical)
|
|
|
|
|
{
|
|
|
|
|
width = images.Max(x => x.Width);
|
|
|
|
|
height = images.Sum(x => x.Height) + spaceSize;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
width = images.Sum(x => x.Width) + spaceSize;
|
|
|
|
|
height = images.Max(x => x.Height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bitmap bmp = new Bitmap(width, height);
|
2015-09-25 22:02:31 +12:00
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
|
|
|
|
g.SetHighQuality();
|
2015-09-30 21:28:54 +13:00
|
|
|
|
int position = 0;
|
2015-09-25 22:02:31 +12:00
|
|
|
|
|
2021-04-07 05:09:06 +12:00
|
|
|
|
for (int i = 0; i < imageCount; i++)
|
2015-09-25 22:02:31 +12:00
|
|
|
|
{
|
2021-04-07 05:09:06 +12:00
|
|
|
|
Bitmap image = images[i];
|
|
|
|
|
|
|
|
|
|
if (autoFillBackground && i == 0)
|
|
|
|
|
{
|
|
|
|
|
Color backgroundColor = image.GetPixel(image.Width - 1, image.Height - 1);
|
|
|
|
|
g.Clear(backgroundColor);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 21:28:54 +13:00
|
|
|
|
Rectangle rect;
|
|
|
|
|
|
|
|
|
|
if (orientation == Orientation.Vertical)
|
|
|
|
|
{
|
2020-03-24 09:44:50 +13:00
|
|
|
|
int x;
|
|
|
|
|
switch (alignment)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case ImageCombinerAlignment.LeftOrTop:
|
|
|
|
|
x = 0;
|
|
|
|
|
break;
|
|
|
|
|
case ImageCombinerAlignment.Center:
|
2020-04-12 02:00:14 +12:00
|
|
|
|
x = (width / 2) - (image.Width / 2);
|
2020-03-24 09:44:50 +13:00
|
|
|
|
break;
|
|
|
|
|
case ImageCombinerAlignment.RightOrBottom:
|
|
|
|
|
x = width - image.Width;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rect = new Rectangle(x, position, image.Width, image.Height);
|
2015-09-30 21:28:54 +13:00
|
|
|
|
position += image.Height + space;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-24 09:44:50 +13:00
|
|
|
|
int y;
|
|
|
|
|
switch (alignment)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case ImageCombinerAlignment.LeftOrTop:
|
|
|
|
|
y = 0;
|
|
|
|
|
break;
|
|
|
|
|
case ImageCombinerAlignment.Center:
|
2020-04-12 02:00:14 +12:00
|
|
|
|
y = (height / 2) - (image.Height / 2);
|
2020-03-24 09:44:50 +13:00
|
|
|
|
break;
|
|
|
|
|
case ImageCombinerAlignment.RightOrBottom:
|
|
|
|
|
y = height - image.Height;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rect = new Rectangle(position, y, image.Width, image.Height);
|
2015-09-30 21:28:54 +13:00
|
|
|
|
position += image.Width + space;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.DrawImage(image, rect);
|
2015-09-25 22:02:31 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
2016-05-04 03:02:26 +12:00
|
|
|
|
|
2021-06-15 00:18:09 +12:00
|
|
|
|
public static Bitmap CombineImages(IEnumerable<string> imageFiles, Orientation orientation = Orientation.Vertical,
|
|
|
|
|
ImageCombinerAlignment alignment = ImageCombinerAlignment.LeftOrTop, int space = 0, bool autoFillBackground = false)
|
|
|
|
|
{
|
|
|
|
|
List<Bitmap> images = new List<Bitmap>();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
foreach (string filePath in imageFiles)
|
|
|
|
|
{
|
|
|
|
|
Bitmap bmp = LoadImage(filePath);
|
|
|
|
|
|
|
|
|
|
if (bmp != null)
|
|
|
|
|
{
|
|
|
|
|
images.Add(bmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (images.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
return CombineImages(images, orientation, alignment, space, autoFillBackground);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
foreach (Bitmap bmp in images)
|
|
|
|
|
{
|
|
|
|
|
if (bmp != null)
|
|
|
|
|
{
|
|
|
|
|
bmp.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static List<Bitmap> SplitImage(Image img, int rowCount, int columnCount)
|
2019-09-29 04:55:26 +13:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
List<Bitmap> images = new List<Bitmap>();
|
2019-09-29 04:55:26 +13:00
|
|
|
|
|
|
|
|
|
int width = img.Width / columnCount;
|
|
|
|
|
int height = img.Height / rowCount;
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < rowCount; y++)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < columnCount; x++)
|
|
|
|
|
{
|
|
|
|
|
Bitmap bmp = new Bitmap(width, height);
|
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
|
|
|
|
Rectangle destRect = new Rectangle(0, 0, width, height);
|
|
|
|
|
Rectangle srcRect = new Rectangle(x * width, y * height, width, height);
|
|
|
|
|
g.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
images.Add(bmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return images;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-22 12:22:43 +13:00
|
|
|
|
public static Bitmap CreateColorPickerIcon(Color color, Rectangle rect, int holeSize = 0)
|
2016-05-04 03:02:26 +12:00
|
|
|
|
{
|
|
|
|
|
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
|
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
{
|
2016-10-13 21:47:26 +13:00
|
|
|
|
DrawColorPickerIcon(g, color, rect, holeSize);
|
2016-05-04 03:02:26 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 21:47:26 +13:00
|
|
|
|
public static void DrawColorPickerIcon(Graphics g, Color color, Rectangle rect, int holeSize = 0)
|
2016-05-04 03:02:26 +12:00
|
|
|
|
{
|
2020-09-30 10:52:20 +13:00
|
|
|
|
if (color.IsTransparent())
|
2016-05-04 03:02:26 +12:00
|
|
|
|
{
|
2017-03-25 10:16:44 +13:00
|
|
|
|
using (Image checker = CreateCheckerPattern(rect.Width / 2, rect.Height / 2))
|
2016-05-04 03:02:26 +12:00
|
|
|
|
{
|
|
|
|
|
g.DrawImage(checker, rect);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (SolidBrush brush = new SolidBrush(color))
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(brush, rect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.DrawRectangleProper(Pens.Black, rect);
|
2016-10-13 21:47:26 +13:00
|
|
|
|
|
|
|
|
|
if (holeSize > 0)
|
|
|
|
|
{
|
|
|
|
|
g.CompositingMode = CompositingMode.SourceCopy;
|
|
|
|
|
|
2018-05-17 01:27:11 +12:00
|
|
|
|
Rectangle holeRect = new Rectangle((rect.Width / 2) - (holeSize / 2), (rect.Height / 2) - (holeSize / 2), holeSize, holeSize);
|
2016-10-13 21:47:26 +13:00
|
|
|
|
|
|
|
|
|
g.FillRectangle(Brushes.Transparent, holeRect);
|
|
|
|
|
g.DrawRectangleProper(Pens.Black, holeRect);
|
|
|
|
|
}
|
2016-05-04 03:02:26 +12:00
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
public static Rectangle FindAutoCropRectangle(Bitmap bmp, bool sameColorCrop = false,
|
|
|
|
|
AnchorStyles sides = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
|
|
|
|
Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
2018-06-14 23:12:19 +12:00
|
|
|
|
|
|
|
|
|
if (sides == AnchorStyles.None)
|
|
|
|
|
{
|
|
|
|
|
return source;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-29 08:10:45 +13:00
|
|
|
|
Rectangle crop = source;
|
|
|
|
|
|
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly))
|
|
|
|
|
{
|
|
|
|
|
bool leave = false;
|
|
|
|
|
|
2017-11-29 10:06:50 +13:00
|
|
|
|
ColorBgra checkColor = unsafeBitmap.GetPixel(0, 0);
|
2018-06-14 22:11:19 +12:00
|
|
|
|
uint mask = checkColor.Alpha == 0 ? 0xFF000000 : 0xFFFFFFFF;
|
|
|
|
|
uint check = checkColor.Bgra & mask;
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Left))
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
// Find X (Left to right)
|
|
|
|
|
for (int x = 0; x < bmp.Width && !leave; x++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
for (int y = 0; y < bmp.Height; y++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if ((unsafeBitmap.GetPixel(x, y).Bgra & mask) != check)
|
|
|
|
|
{
|
|
|
|
|
crop.X = x;
|
|
|
|
|
crop.Width -= x;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
// If all pixels same color
|
|
|
|
|
if (!leave)
|
|
|
|
|
{
|
|
|
|
|
return crop;
|
|
|
|
|
}
|
2017-11-29 10:32:02 +13:00
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
leave = false;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Top))
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
// Find Y (Top to bottom)
|
|
|
|
|
for (int y = 0; y < bmp.Height && !leave; y++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
for (int x = 0; x < bmp.Width; x++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if ((unsafeBitmap.GetPixel(x, y).Bgra & mask) != check)
|
|
|
|
|
{
|
|
|
|
|
crop.Y = y;
|
|
|
|
|
crop.Height -= y;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
leave = false;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
|
|
|
|
if (!sameColorCrop)
|
|
|
|
|
{
|
2018-06-14 22:19:51 +12:00
|
|
|
|
checkColor = unsafeBitmap.GetPixel(bmp.Width - 1, bmp.Height - 1);
|
2018-06-14 22:11:19 +12:00
|
|
|
|
mask = checkColor.Alpha == 0 ? 0xFF000000 : 0xFFFFFFFF;
|
|
|
|
|
check = checkColor.Bgra & mask;
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Right))
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
// Find Width (Right to left)
|
|
|
|
|
for (int x = bmp.Width - 1; x >= 0 && !leave; x--)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
for (int y = 0; y < bmp.Height; y++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if ((unsafeBitmap.GetPixel(x, y).Bgra & mask) != check)
|
|
|
|
|
{
|
|
|
|
|
crop.Width = x - crop.X + 1;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
leave = false;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if (sides.HasFlag(AnchorStyles.Bottom))
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
// Find Height (Bottom to top)
|
|
|
|
|
for (int y = bmp.Height - 1; y >= 0 && !leave; y--)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
for (int x = 0; x < bmp.Width; x++)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
2018-06-14 23:12:19 +12:00
|
|
|
|
if ((unsafeBitmap.GetPixel(x, y).Bgra & mask) != check)
|
|
|
|
|
{
|
|
|
|
|
crop.Height = y - crop.Y + 1;
|
|
|
|
|
leave = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return crop;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 23:12:19 +12:00
|
|
|
|
public static Bitmap AutoCropImage(Bitmap bmp, bool sameColorCrop = false,
|
|
|
|
|
AnchorStyles sides = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right)
|
2017-11-29 08:10:45 +13:00
|
|
|
|
{
|
|
|
|
|
Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
2018-06-14 23:12:19 +12:00
|
|
|
|
Rectangle rect = FindAutoCropRectangle(bmp, sameColorCrop, sides);
|
2017-11-29 08:10:45 +13:00
|
|
|
|
|
|
|
|
|
if (source != rect)
|
|
|
|
|
{
|
|
|
|
|
Bitmap croppedBitmap = CropBitmap(bmp, rect);
|
|
|
|
|
|
|
|
|
|
if (croppedBitmap != null)
|
|
|
|
|
{
|
|
|
|
|
bmp.Dispose();
|
|
|
|
|
return croppedBitmap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 00:32:15 +13:00
|
|
|
|
return bmp;
|
2017-11-29 08:10:45 +13:00
|
|
|
|
}
|
2017-12-22 22:47:12 +13:00
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
public static RotateFlipType RotateImageByExifOrientationData(Bitmap bmp, bool removeExifOrientationData = true)
|
2017-12-22 22:47:12 +13:00
|
|
|
|
{
|
|
|
|
|
int orientationId = 0x0112;
|
|
|
|
|
RotateFlipType rotateType = RotateFlipType.RotateNoneFlipNone;
|
|
|
|
|
|
2020-03-22 11:07:38 +13:00
|
|
|
|
if (bmp.PropertyIdList.Contains(orientationId))
|
2017-12-22 22:47:12 +13:00
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
PropertyItem propertyItem = bmp.GetPropertyItem(orientationId);
|
2017-12-22 22:47:12 +13:00
|
|
|
|
rotateType = GetRotateFlipTypeByExifOrientationData(propertyItem.Value[0]);
|
|
|
|
|
|
|
|
|
|
if (rotateType != RotateFlipType.RotateNoneFlipNone)
|
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
bmp.RotateFlip(rotateType);
|
2017-12-22 22:47:12 +13:00
|
|
|
|
|
|
|
|
|
if (removeExifOrientationData)
|
|
|
|
|
{
|
2020-03-22 11:07:38 +13:00
|
|
|
|
bmp.RemovePropertyItem(orientationId);
|
2017-12-22 22:47:12 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rotateType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static RotateFlipType GetRotateFlipTypeByExifOrientationData(int orientation)
|
|
|
|
|
{
|
|
|
|
|
switch (orientation)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case 1:
|
|
|
|
|
return RotateFlipType.RotateNoneFlipNone;
|
|
|
|
|
case 2:
|
|
|
|
|
return RotateFlipType.RotateNoneFlipX;
|
|
|
|
|
case 3:
|
|
|
|
|
return RotateFlipType.Rotate180FlipNone;
|
|
|
|
|
case 4:
|
|
|
|
|
return RotateFlipType.Rotate180FlipX;
|
|
|
|
|
case 5:
|
|
|
|
|
return RotateFlipType.Rotate90FlipX;
|
|
|
|
|
case 6:
|
|
|
|
|
return RotateFlipType.Rotate90FlipNone;
|
|
|
|
|
case 7:
|
|
|
|
|
return RotateFlipType.Rotate270FlipX;
|
|
|
|
|
case 8:
|
|
|
|
|
return RotateFlipType.Rotate270FlipNone;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-06 13:56:38 +12:00
|
|
|
|
|
2020-07-06 10:37:56 +12:00
|
|
|
|
public static void SelectiveColor(Bitmap bmp, Color lightColor, Color darkColor, int paletteSize = 2)
|
2018-05-06 13:56:38 +12:00
|
|
|
|
{
|
2020-07-06 10:37:56 +12:00
|
|
|
|
paletteSize = Math.Max(paletteSize, 2);
|
|
|
|
|
|
|
|
|
|
Dictionary<int, Color> colors = new Dictionary<int, Color>();
|
|
|
|
|
for (int i = 0; i < paletteSize; i++)
|
|
|
|
|
{
|
|
|
|
|
Color color = ColorHelpers.Lerp(lightColor, darkColor, (float)i / (paletteSize - 1));
|
|
|
|
|
int perceivedBrightness = ColorHelpers.PerceivedBrightness(color);
|
|
|
|
|
if (!colors.ContainsKey(perceivedBrightness))
|
|
|
|
|
{
|
|
|
|
|
colors.Add(perceivedBrightness, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-06 13:56:38 +12:00
|
|
|
|
using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < unsafeBitmap.PixelCount; i++)
|
|
|
|
|
{
|
|
|
|
|
ColorBgra color = unsafeBitmap.GetPixel(i);
|
2020-07-06 10:37:56 +12:00
|
|
|
|
int perceivedBrightness = ColorHelpers.PerceivedBrightness(color.ToColor());
|
|
|
|
|
KeyValuePair<int, Color> closest =
|
|
|
|
|
colors.Aggregate((current, next) => Math.Abs(current.Key - perceivedBrightness) < Math.Abs(next.Key - perceivedBrightness) ? current : next);
|
|
|
|
|
Color newColor = closest.Value;
|
2018-05-06 13:56:38 +12:00
|
|
|
|
color.Red = newColor.R;
|
|
|
|
|
color.Green = newColor.G;
|
|
|
|
|
color.Blue = newColor.B;
|
|
|
|
|
unsafeBitmap.SetPixel(i, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-15 13:25:57 +13:00
|
|
|
|
|
|
|
|
|
public static Size GetImageFileDimensions(string filePath)
|
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
using (Bitmap bmp = LoadImage(filePath))
|
2018-12-15 13:25:57 +13:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
if (bmp != null)
|
2018-12-15 13:25:57 +13:00
|
|
|
|
{
|
2020-03-22 12:22:43 +13:00
|
|
|
|
return bmp.Size;
|
2018-12-15 13:25:57 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Size.Empty;
|
|
|
|
|
}
|
2020-07-06 10:37:56 +12:00
|
|
|
|
|
|
|
|
|
public static InterpolationMode GetInterpolationMode(ImageInterpolationMode interpolationMode)
|
|
|
|
|
{
|
|
|
|
|
switch (interpolationMode)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case ImageInterpolationMode.HighQualityBicubic:
|
|
|
|
|
return InterpolationMode.HighQualityBicubic;
|
|
|
|
|
case ImageInterpolationMode.Bicubic:
|
|
|
|
|
return InterpolationMode.Bicubic;
|
|
|
|
|
case ImageInterpolationMode.HighQualityBilinear:
|
|
|
|
|
return InterpolationMode.HighQualityBilinear;
|
|
|
|
|
case ImageInterpolationMode.Bilinear:
|
|
|
|
|
return InterpolationMode.Bilinear;
|
|
|
|
|
case ImageInterpolationMode.NearestNeighbor:
|
|
|
|
|
return InterpolationMode.NearestNeighbor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Size ApplyAspectRatio(int width, int height, Bitmap bmp)
|
|
|
|
|
{
|
|
|
|
|
int newWidth, newHeight;
|
|
|
|
|
|
|
|
|
|
if (width == 0)
|
|
|
|
|
{
|
|
|
|
|
newWidth = (int)Math.Round((float)height / bmp.Height * bmp.Width);
|
|
|
|
|
newHeight = height;
|
|
|
|
|
}
|
|
|
|
|
else if (height == 0)
|
|
|
|
|
{
|
|
|
|
|
newWidth = width;
|
|
|
|
|
newHeight = (int)Math.Round((float)width / bmp.Width * bmp.Height);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newWidth = width;
|
|
|
|
|
newHeight = height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Size(newWidth, newHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Size ApplyAspectRatio(Size size, Bitmap bmp)
|
|
|
|
|
{
|
|
|
|
|
return ApplyAspectRatio(size.Width, size.Height, bmp);
|
|
|
|
|
}
|
2020-10-05 09:21:02 +13:00
|
|
|
|
|
|
|
|
|
public static Bitmap NonIndexedBitmap(Bitmap bmp)
|
|
|
|
|
{
|
|
|
|
|
if (bmp != null && bmp.PixelFormat.HasFlag(PixelFormat.Indexed))
|
|
|
|
|
{
|
|
|
|
|
using (bmp)
|
|
|
|
|
{
|
|
|
|
|
return bmp.Clone(new Rectangle(0, 0, bmp.Width, bmp.Height), PixelFormat.Format32bppArgb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
2020-10-09 03:21:30 +13:00
|
|
|
|
|
|
|
|
|
public static Bitmap DrawGrip(Color color, Color shadow)
|
|
|
|
|
{
|
|
|
|
|
int size = 16;
|
|
|
|
|
Bitmap bmp = new Bitmap(size, size);
|
|
|
|
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bmp))
|
|
|
|
|
using (SolidBrush brush = new SolidBrush(color))
|
|
|
|
|
using (SolidBrush shadowBrush = new SolidBrush(shadow))
|
|
|
|
|
{
|
|
|
|
|
int x = size / 2;
|
|
|
|
|
int boxSize = 2;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
g.FillRectangle(shadowBrush, x - boxSize, (i * 4) + 2, boxSize, boxSize);
|
|
|
|
|
g.FillRectangle(brush, x - boxSize - 1, (i * 4) + 1, boxSize, boxSize);
|
|
|
|
|
|
|
|
|
|
g.FillRectangle(shadowBrush, x + 2, (i * 4) + 2, boxSize, boxSize);
|
|
|
|
|
g.FillRectangle(brush, x + 1, (i * 4) + 1, boxSize, boxSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
}
|
2021-07-15 03:30:26 +12:00
|
|
|
|
|
2021-07-15 03:35:47 +12:00
|
|
|
|
public static MemoryStream SavePNG(Image img, PNGBitDepth bitDepth)
|
2021-07-15 03:30:26 +12:00
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
2021-07-15 03:35:47 +12:00
|
|
|
|
SavePNG(img, ms, bitDepth);
|
2021-07-15 03:30:26 +12:00
|
|
|
|
return ms;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 03:35:47 +12:00
|
|
|
|
public static void SavePNG(Image img, Stream stream, PNGBitDepth bitDepth)
|
2021-07-15 03:30:26 +12:00
|
|
|
|
{
|
|
|
|
|
if (bitDepth == PNGBitDepth.Automatic)
|
|
|
|
|
{
|
|
|
|
|
if (IsImageTransparent((Bitmap)img))
|
|
|
|
|
{
|
|
|
|
|
bitDepth = PNGBitDepth.Bit32;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bitDepth = PNGBitDepth.Bit24;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bitDepth == PNGBitDepth.Bit32)
|
|
|
|
|
{
|
|
|
|
|
if (img.PixelFormat != PixelFormat.Format32bppArgb && img.PixelFormat != PixelFormat.Format32bppRgb)
|
|
|
|
|
{
|
|
|
|
|
using (Bitmap bmpNew = ((Bitmap)img).Clone(new Rectangle(0, 0, img.Width, img.Height), PixelFormat.Format32bppArgb))
|
|
|
|
|
{
|
|
|
|
|
bmpNew.Save(stream, ImageFormat.Png);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (bitDepth == PNGBitDepth.Bit24)
|
|
|
|
|
{
|
|
|
|
|
if (img.PixelFormat != PixelFormat.Format24bppRgb)
|
|
|
|
|
{
|
|
|
|
|
using (Bitmap bmpNew = ((Bitmap)img).Clone(new Rectangle(0, 0, img.Width, img.Height), PixelFormat.Format24bppRgb))
|
|
|
|
|
{
|
|
|
|
|
bmpNew.Save(stream, ImageFormat.Png);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img.Save(stream, ImageFormat.Png);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MemoryStream PNGStripChunks(MemoryStream stream, params string[] chunks)
|
|
|
|
|
{
|
|
|
|
|
MemoryStream output = new MemoryStream();
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
byte[] signature = new byte[8];
|
|
|
|
|
stream.Read(signature, 0, 8);
|
|
|
|
|
output.Write(signature, 0, 8);
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
byte[] lenBytes = new byte[4];
|
|
|
|
|
if (stream.Read(lenBytes, 0, 4) != 4)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BitConverter.IsLittleEndian)
|
|
|
|
|
{
|
|
|
|
|
Array.Reverse(lenBytes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int len = BitConverter.ToInt32(lenBytes, 0);
|
|
|
|
|
|
|
|
|
|
if (BitConverter.IsLittleEndian)
|
|
|
|
|
{
|
|
|
|
|
Array.Reverse(lenBytes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] type = new byte[4];
|
|
|
|
|
stream.Read(type, 0, 4);
|
|
|
|
|
|
|
|
|
|
byte[] data = new byte[len + 4];
|
|
|
|
|
stream.Read(data, 0, data.Length);
|
|
|
|
|
|
|
|
|
|
string strType = Encoding.ASCII.GetString(type);
|
|
|
|
|
|
|
|
|
|
if (!chunks.Contains(strType))
|
|
|
|
|
{
|
|
|
|
|
output.Write(lenBytes, 0, lenBytes.Length);
|
|
|
|
|
output.Write(type, 0, type.Length);
|
|
|
|
|
output.Write(data, 0, data.Length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MemoryStream PNGStripColorSpaceInformation(MemoryStream stream)
|
|
|
|
|
{
|
|
|
|
|
// http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
|
|
|
|
|
// 4.2.2.1. gAMA Image gamma
|
|
|
|
|
// 4.2.2.2. cHRM Primary chromaticities
|
|
|
|
|
// 4.2.2.3. sRGB Standard RGB color space
|
|
|
|
|
// 4.2.2.4. iCCP Embedded ICC profile
|
|
|
|
|
return PNGStripChunks(stream, "gAMA", "cHRM", "sRGB", "iCCP");
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 09:02:22 +12:00
|
|
|
|
public static MemoryStream SaveJPEG(Image img, int quality)
|
2021-07-15 03:35:47 +12:00
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
2021-07-19 09:02:22 +12:00
|
|
|
|
SaveJPEG(img, ms, quality);
|
2021-07-15 03:35:47 +12:00
|
|
|
|
return ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void SaveJPEG(Image img, string filePath, int quality)
|
|
|
|
|
{
|
|
|
|
|
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
|
|
|
|
{
|
|
|
|
|
SaveJPEG(img, fs, quality);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 09:02:22 +12:00
|
|
|
|
public static void SaveJPEG(Image img, Stream stream, int quality)
|
2021-07-19 03:53:25 +12:00
|
|
|
|
{
|
|
|
|
|
quality = quality.Clamp(0, 100);
|
|
|
|
|
|
|
|
|
|
using (EncoderParameters encoderParameters = new EncoderParameters(1))
|
|
|
|
|
{
|
|
|
|
|
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
|
|
|
|
|
img.Save(stream, ImageFormat.Jpeg.GetCodecInfo(), encoderParameters);
|
2021-07-15 03:35:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 04:11:49 +12:00
|
|
|
|
public static MemoryStream SaveJPEGAutoQuality(Image img, int sizeLimit, int qualityDecrement = 5, int minQuality = 0, int maxQuality = 100)
|
|
|
|
|
{
|
|
|
|
|
qualityDecrement = qualityDecrement.Clamp(1, 100);
|
|
|
|
|
minQuality = minQuality.Clamp(0, 100);
|
|
|
|
|
maxQuality = maxQuality.Clamp(0, 100);
|
|
|
|
|
|
|
|
|
|
if (minQuality >= maxQuality)
|
|
|
|
|
{
|
2021-07-19 09:02:22 +12:00
|
|
|
|
return SaveJPEG(img, minQuality);
|
2021-07-15 04:11:49 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryStream ms = null;
|
|
|
|
|
|
|
|
|
|
for (int quality = maxQuality; quality >= minQuality; quality -= qualityDecrement)
|
|
|
|
|
{
|
|
|
|
|
if (ms != null)
|
|
|
|
|
{
|
|
|
|
|
ms.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 09:02:22 +12:00
|
|
|
|
ms = SaveJPEG(img, quality);
|
2021-07-15 04:11:49 +12:00
|
|
|
|
|
2021-07-19 05:41:33 +12:00
|
|
|
|
//DebugHelper.WriteLine($"Quality: {quality}% - Size: {ms.Length.ToSizeString()}");
|
2021-07-15 04:11:49 +12:00
|
|
|
|
|
|
|
|
|
if (ms.Length <= sizeLimit)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ms;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 03:35:47 +12:00
|
|
|
|
public static MemoryStream SaveGIF(Image img, GIFQuality quality)
|
2021-07-15 03:30:26 +12:00
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
2021-07-15 03:35:47 +12:00
|
|
|
|
SaveGIF(img, ms, quality);
|
2021-07-15 03:30:26 +12:00
|
|
|
|
return ms;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 03:35:47 +12:00
|
|
|
|
public static void SaveGIF(Image img, Stream stream, GIFQuality quality)
|
2021-07-15 03:30:26 +12:00
|
|
|
|
{
|
|
|
|
|
if (quality == GIFQuality.Default)
|
|
|
|
|
{
|
|
|
|
|
img.Save(stream, ImageFormat.Gif);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Quantizer quantizer;
|
|
|
|
|
|
|
|
|
|
switch (quality)
|
|
|
|
|
{
|
|
|
|
|
case GIFQuality.Grayscale:
|
|
|
|
|
quantizer = new GrayscaleQuantizer();
|
|
|
|
|
break;
|
|
|
|
|
case GIFQuality.Bit4:
|
|
|
|
|
quantizer = new OctreeQuantizer(15, 4);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
case GIFQuality.Bit8:
|
|
|
|
|
quantizer = new OctreeQuantizer(255, 4);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (Bitmap quantized = quantizer.Quantize(img))
|
|
|
|
|
{
|
|
|
|
|
quantized.Save(stream, ImageFormat.Gif);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-03 23:53:49 +13:00
|
|
|
|
}
|
2017-02-10 23:49:49 +13:00
|
|
|
|
}
|