KGy SOFT Drawing Libraries
KGy SOFT Drawing Libraries offer advanced drawing features both for completely managed bitmap data as well as native System.Drawing types on multiple platforms. Multiple versions of .NET Framework and .NET Core are supported. Tested on Windows and Linux (both in Mono and .NET Core environments).
Table of Contents:
Download:
Download Binaries:
The binaries can be downloaded as a NuGet package directly from nuget.org
However, the preferred way is to install the package in VisualStudio either by looking for the KGySoft.Drawing
package in the Nuget Package Manager GUI, or by sending the following command at the Package Manager Console prompt:
PM> Install-Package KGySoft.Drawing
Demo Application and Debugger Visualizers:
KGy SOFT Imaging Tools is a desktop application in the KGySoft.Drawing.Tools repository, which nicely demonstrates a sort of features of Drawing Libraries, such as quantizing and dithering, resizing, adjusting brightness, contrast and gamma, etc. The tool is packed also with some debugger visualizers for several System.Drawing
types including Bitmap
, Metafile
, Icon
, Graphics
and more.
Project Site
Find the project site at kgysoft.net
Documentation
Release Notes
See the change log.
Examples
Icon Manipulation
Icon images of different resolutions and color depth can be extracted from an Icon
, whereas Bitmap
and Icon
instances can be combined into a new Icon
. PNG compressed icons are also supported.
// extracting the 256x256 image from an icon:
Bitmap bmp = Icons.Information.ExtractBitmap(new Size(256, 256));
// combining an existing icon with a bitmap:
Icon combined = myIcon.Combine(bmp);
💡 Tip: See more details at the Icons and IconExtensions classes.
Fast Bitmap Manipulation
As it is well known, Bitmap.SetPixel
/GetPixel
methods are very slow. Additionally, they do not support every pixel format. A typical solution can be to obtain a BitmapData
by the LockBits
method, which has further drawbacks: you need to use unsafe code and pointers, and the way you need to access the bitmap data depends on the actual PixelFormat
of the bitmap.
KGy SOFT Drawing Libraries offer very fast and convenient way to overcome these issues. A managed accessor can be obtained by the GetReadableBitmapData
, GetWritableBitmapData
and GetReadWriteBitmapData
methods:
var targetFormat = PixelFormat.Format8bppIndexed; // feel free to try other formats as well
using (Bitmap bmpSrc = Icons.Shield.ExtractBitmap(new Size(256, 256)))
using (Bitmap bmpDst = new Bitmap(256, 256, targetFormat))
{
using (IReadableBitmapData dataSrc = bmpSrc.GetReadableBitmapData())
using (IWritableBitmapData dataDst = bmpDst.GetWritableBitmapData())
{
IReadableBitmapDataRow rowSrc = dataSrc.FirstRow;
IWritableBitmapDataRow rowDst = dataDst.FirstRow;
do
{
for (int x = 0; x < dataSrc.Width; x++)
rowDst[x] = rowSrc[x]; // works also between different pixel formats
} while (rowSrc.MoveNextRow() && rowDst.MoveNextRow());
}
bmpSrc.SaveAsPng(@"c:\temp\bmpSrc.png");
bmpDst.SaveAsPng(@"c:\temp\bmpDst.png"); // or saveAsGif/SaveAsTiff to preserve the indexed format
}
💡 Tip: See more examples with images at theGetReadWriteBitmapData
extension method.
If you know the actual pixel format you can also access the raw data in a managed way. See the IReadableBitmapDataRow.ReadRaw
and IWritableBitmapDataRow.WriteRaw
methods for details and examples.
Managed Bitmap Data Manipulation
Not only for the native Bitmap
type can you obtain a managed accessor (as described above) but you can also create a completely managed bitmap data instance by the BitmapDataFactory
class. There are more benefits of using managed bitmap data: not just that they don't use any GDI or other native resources but also that they support every PixelFormat
on any platform. See the BitmapDataExtensions
for the available operations on bitmap data where bitmap data can be either a managed one or a managed accessor to a native Bitmap
instance.
Self-allocating vs. Preallocated Buffers
The BitmapDataFactory
class has many CreateBitmapData
overloads. The ones whose first parameter is Size
allocate the underlying buffer by themselves, which is not directly accessible from outside. But you are also able to use predefined arrays of any primitive element type (one or two dimensional ones), and also ArraySection<T>
or Array2D<T>
buffers to create a managed bitmap data for.
WriteableBitmap and Other 3rd Party Bitmap Types Support
The BitmapDataFactory
class has also CreateBitmapData
overloads to support unmanaged memory. This makes possible to support any bitmap representation that expose its buffer by a pointer.
For example, this is how you can create a managed accessor for a WriteableBitmap
instance commonly used in WPF/WinRT/UWP and other XAML-based environments, which exposes such a pointer:
// Though naming is different, PixelFormats.Pbgra32 is the same as PixelFormat.Format32bppPArgb.
var bitmap = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormats.Pbgra32, null);
// creating the managed bitmap data for WriteableBitmap:
using (var bitmapData = BitmapDataFactory.CreateBitmapData(
bitmap.BackBuffer,
new Size(bitmap.PixelWidth, bitmap.PixelHeight),
bitmap.BackBufferStride,
PixelFormat.Format32bppPArgb)
{
// Do whatever with bitmapData
}
// Actualizing changes. But see also the next example to see how to do these along with disposing.
bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
bitmap.Unlock();
Supporting Custom Pixel Formats
The previous example demonstrated how we can create a managed accessor for a WriteableBitmap
. But it worked only because we used a pixel format that happen to have built-in support also in KGy SOFT Drawing Libraries. In fact, the libraries provide support for any custom pixel format. The CreateBitmapData
methods have several overloads that allow you to specify a custom pixel format along with a couple of delegates to be called when pixels are read or written:
// Gray8 format has no built-in support
var bitmap = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormats.Gray8, null);
// But we can specify how to use it
var customPixelFormat = new PixelFormatInfo { BitsPerPixel = 8, Grayscale = true };
Func<ICustomBitmapDataRow, int, Color32> getPixel =
(row, x) => Color32.FromGray(row.UnsafeGetRefAs<byte>(x));
Action<ICustomBitmapDataRow, int, Color32> setPixel =
(row, x, c) => row.UnsafeGetRefAs<byte>(x) = c.Blend(row.BitmapData.BackColor).GetBrightness();
// Now we specify also a dispose callback to be executed when the returned instance is disposed:
return BitmapDataFactory.CreateBitmapData(
bitmap.BackBuffer, new Size(bitmap.PixelWidth, bitmap.PixelHeight), bitmap.BackBufferStride,
customPixelFormat, getPixel, setPixel,
disposeCallback: () =>
{
bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
bitmap.Unlock();
});
Note that there are different overloads for indexed formats where you have to specify how to read/write a palette index. Please also note that these delegates work with 32-bit color structures (just like usual GetPixel
/SetPixel
) so wider formats will be quantized into the ARGB8888 color space (or BGRA8888, using the alternative terminology) when getting/setting pixels but this is how regular formats work, too. Anyway, you can always access the actual underlying data of whatever format by the aforementioned IReadableBitmapDataRow.ReadRaw
and IWritableBitmapDataRow.WriteRaw
methods.
Quantizing and Dithering
KGy SOFT Drawing Libraries offer quantizing (reducing the number of colors of an image) and dithering (techniques for preserving the details of a quantized image) in several ways:
- The
ImageExtensions.ConvertPixelFormat
/BitmapDataExtensions.Clone
extension methods return newBitmap
/IReadWriteBitmapData
instances as the result of the quantizing/dithering. - The
BitmapExtensions.Quantize
/BitmapDataExtensions.Quantize
andBitmapExtensions.Dither
/BitmapDataExtensions.Dither
extension methods modify the originalBitmap
/IReadWriteBitmapData
instance. - Some
ImageExtensions.DrawInto
/BitmapDataExtensions.DrawInto
overloads can use quantizing and dithering when drawing different instances into each other. - Several further extension methods in the
BitmapExtensions
/BitmapDataExtensions
classes have anIDitherer
parameter.
💡 Tip:
- For built-in quantizers see the
PredefinedColorsQuantizer
andOptimizedPaletteQuantizer
classes. See their members for code samples and image examples.- For built-in ditherers see the
OrderedDitherer
,ErrorDiffusionDitherer
,RandomNoiseDitherer
andInterleavedGradientNoiseDitherer
classes. See their members for code samples and image examples.
See the following table for the possible results (click the images for displaying in full size):
Description | Image Example |
---|---|
Original image: Color hues with alpha gradient | |
Color hues quantized with custom 8 color palette and silver background, no dithering. The bottom part turns white because white is the nearest color to silver. | |
Color hues quantized with custom 8 color palette and silver background, using Bayer 8x8 dithering | |
Original image: Grayscale color shades | |
Grayscale color shades quantized with black and white palette, no dithering | |
Grayscale color shades quantized with black and white palette, using blue noise dithering | |
Original test image "Lena" | |
Test image "Lena" quantized with system default 8 BPP palette, no dithering | |
Test image "Lena" quantized with system default 8 BPP palette using Bayer 8x8 dithering | |
Test image "Lena" quantized with system default 8 BPP palette using Floyd-Steinberg dithering | |
Original test image "Cameraman" | |
Test image "Cameraman" quantized with black and white palette, no dithering | |
Test image "Cameraman" quantized with black and white palette using Floyd-Steinberg dithering |
💡 Tip: UseKGy SOFT Imaging Tools
from the KGySoft.Drawing.Tools repository to try image quantization and dithering in an application.
Quantizing and Dithering in KGy SOFT Imaging Tools
Advanced GIF Encoder with High Color Support
The KGy SOFT Drawing Libraries make possible creating high quality GIF images and animations:
- For
Image
types the simplest and highest-level access is provided by theImageExtension
class and itsSaveAs*
methods. - Alternatively, you can use the static methods of the
GifEncoder
class to create animations or even high color still images. See also theAnimatedGifConfiguration
class. - To create a GIF image or animation completely manually you can instantiate the
GifEncoder
class that provides you the lowest-level access.
Examples:
Description | Image Example |
---|---|
True color GIF animation. The last frame has 29,731 colors. The Granger Rainbow has been generated from an alpha gradient bitmap by this code. | |
Warning icon encoded as a high color GIF. It has only single bit transparency but otherwise its colors have been preserved. It consists of 18 layers and has 4,363 colors. | |
Test image "Lena" encoded as a true color GIF. It consists of 983 layers and has 148,702 colors. The file size is about twice as large as the PNG encoded version (by allowing full scanning the number of layers could be decreased to 584 but the file size would be even larger). | |
Test image "Lena" encoded as a high color GIF. Before encoding it was prequantized with RGB565 16-bit quantizer using Floyd-Steinberg dithering. It consists of 18 layers and has 4,451 colors. The file size is about 80% of the original PNG encoded version but could be even smaller without the dithering. |
⚠️ Note: Please note that multi layered high color GIF images might be mistakenly rendered as animations by some decoders, including browsers. Still images do not contain the Netscape application extension and do not have any delays. Such images are processed properly by GDI+ on Windows, by theSystem.Drawing.Bitmap
andImage
classes and applications relying on GDI+ decoders such as Windows Paint or KGy SOFT Imaging Tools.
License
This repository is under the KGy SOFT License 1.0, which is a permissive GPL-like license. It allows you to copy and redistribute the material in any medium or format for any purpose, even commercially. The only thing is not allowed is to distribute a modified material as yours: though you are free to change and re-use anything, do that by giving appropriate credit. See the LICENSE file for details.