KGy SOFT Drawing Libraries offer advanced bitmap data manipulation and image processing features such as quantizing and dithering. The libraries consist of multiple packages. A core library contains the technology-agnostic and platform independent functionality and there are specialized libraries built upon the core package.
Starting with version 7.0.0 KGy SOFT Drawing Libraries are available in multiple packages:
This package contains the platform-independent core functionality that mainly resides in the KGySoft.Drawing.Imaging namespace.
Main highlights:
- Creating managed bitmap data of any pixel format.
- Creating bitmap data for any preallocated buffer using any pixel format. This allows accessing the pixels of bitmaps of any technology if the bitmap data is exposed as a pointer or array.
- Quantizing using predefined or optimized colors
- Dithering using ordered, error diffusion, random noise or interleaved gradient noise dithering techniques
- Creating GIF animations even in high color
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing.Core
This package provides special support for System.Drawing
types such as Bitmap
, Metafile
, Image
, Icon
, Graphics
. In .NET 7 and above this package can be used on Windows only. When targeting earlier versions, Unix/Linux based systems are also supported (if the libgdiplus library is installed).
Main highlights:
- Fast direct native Bitmap data access for every PixelFormat
- Quantizing and dithering
- Creating GIF animations even in high color
- Several built-in icons as well as simple access to Windows associated and stock icons.
- Extracting bitmaps from multi-frame bitmaps and icons.
- Creating combined icons and multi-resolution bitmaps.
- Saving metafiles in EMF/WMF formats.
- Advanced support for saving images as Icon, BMP, JPEG, PNG, GIF and TIFF formats.
- Converting between various pixel formats preserving transparency if possible.
- Useful extensions for the Icon, Bitmap, Image, Metafile and Graphics types.
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing
This package helps accessing the bitmap data of the WriteableBitmap
type in WPF supporting all of its possible pixel formats. It also allows direct read-only access to the bitmap data of any BitmapSource
.
Main highlights:
- Fast direct native WriteableBitmap data access for every PixelFormat
- Converting between various pixel formats with optional dithering.
- Useful extensions for the BitmapSource, Color, PixelFormat and WriteableBitmap types.
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing.Wpf
This package helps accessing the bitmap data of the WriteableBitmap
type in UWP (Universal Windows Platform). This library requires targeting at least Windows 10.0.16299.0 (Fall Creators Update, version 1709) so it can reference the .NET Standard 2.0 version of the dependent core libraries.
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing.Uwp
This package helps accessing the bitmap data of the WriteableBitmap
type of the Windows App SDK used in WinUI applications. This library requires targeting at least .NET 5 and Windows 10.0.17763.0 (October 2018 release, version 1809).
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing.WinUI
This package provides dedicated support for the SKBitmap
, SKPixmap
, SKImage
and SKSurface
types of SkiaSharp. All pixel formats are supported (and unlike SkiaSharp's own GetPixel
, IReadableBitmapData.GetPixel
also works correctly for all pixel formats), though for the fastest direct support the color space should be either sRGB or linear. The library also offers direct pixel format conversion with optional quantizing and dithering.
The package can be downloaded directly from NuGet or by using the Package Manager Console:
PM> Install-Package KGySoft.Drawing.SkiaSharp
See the Examples folder for example applications for using KGy SOFT Drawing Libraries in various environments such as MAUI, UWP, WinForms, WinUI, WPF and Xamarin.
KGy SOFT Drawing MAUI Example App running on Android Phone.
See the Examples folder for all of the example applications.
KGy SOFT Imaging Tools is a Windows Forms 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
, WPF
, SkiaSharp
and KGySoft
types including Bitmap
, Metafile
, Icon
, Graphics
, WriteableBitmap
, SKBitmap
and more.
ScreenToGif is a WPF desktop application that can be used to create and save animations. Among others, it can use KGy SOFT Drawing Libraries to save GIF animations using various quantizers and ditherers.
KGy SOFT GIF encoder options in ScreenToGif
Find the project site at kgysoft.net
-
You can find the online KGy SOFT Drawing Libraries documentation here. Please note that it refers to the combined documentation of the available packages. Check the indicated Assembly at the help pages that also designates the corresponding package.
-
See this link to access the online documentation of all KGy SOFT libraries.
- KGySoft.Drawing.Core change log
- KGySoft.Drawing change log
- KGySoft.Drawing.Wpf change log
- KGySoft.Drawing.Uwp change log
- KGySoft.Drawing.WinUI change log
- KGySoft.Drawing.SkiaSharp change log
(Requires the KGySoft.Drawing package for the GDI+ Icon
type)
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.
(This example requires the KGySoft.Drawing package for the GDI+ Bitmap
type but works similarly also for any other bitmaps you can create an IBitmapData
instance for.)
As it is well known, Bitmap.SetPixel
/GetPixel
methods are very slow, and Bitmap.SetPixel
does not even 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 a 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 the
GetReadWriteBitmapData
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.
The previous example showed how to obtain an IReadWriteBitmapData
for a GDI+ Bitmap
. But by using the different specific available packages the corresponding GetReadWriteBitmapData
method will be available also for other bitmap types such as SKBitmap
of SkiaSharp, or the WriteableBitmap
type of WPF, UWP or WinUI platforms offering fast GetPixel
and SetPixel
methods that are normally not available for a WiteableBitmap
at all.
(These examples require the KGySoft.Drawing.Core package.)
Not only for the well-known Bitmap
, WriteableBitmap
or SKBitmap
types can you obtain a managed accessor (as described above) but you can also create a completely managed bitmap data instance by the BitmapDataFactory
class. See the BitmapDataExtensions
for the available operations on a bitmap data:
// Creating a completely managed, platform independent bitmap data.
// This overload allocates an internal managed storage.
using var managedBitmapData = BitmapDataFactory.CreateBitmapData(
new Size(256, 128), KnownPixelFormat.Format32bppArgb);
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:
// interpreting a byte array as 8 bpp grayscale pixels
public static IReadWriteBitmapData GetBitmapData(byte[] pixelBuffer, int width, int height)
{
// As the result is interpreted as a grayscale image, writing operations
// such as SetPixel will automatically adjust the colors to a grayscale value
return BitmapDataFactory.CreateBitmapData(pixelBuffer, new Size(width, height),
stride: width, // Row size in bytes. For 8 bpp pixels it can be the same as width.
pixelFormat: KnownPixelFormat.Format8bppIndexed, // Indexed: pixels are palette entries
palette: Palette.Grayscale256()); // Using a palette of 256 grayscale entries
}
(This example requires the KGySoft.Drawing.Core package and WPF. Actually you can simply use the KGySoft.Drawing.Wpf package for WPF.)
The BitmapDataFactory
class has also CreateBitmapData
overloads to support unmanaged memory. This makes possible to support any bitmap representation that exposes 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 expose such a pointer or stream:
💡 Tip: In fact, if you use the
WriteableBitmap
of WPF/UWP/WinUI platforms, then you can simply use theGetReadWriteBitmapData
extensions from their corresponding package. But this is how you can turn a bitmap of any environment into a managed bitmap data that does not have direct support yet.
// Though naming is different, PixelFormats.Pbgra32 is the same as KnownPixelFormat.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,
KnownPixelFormat.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();
(This example requires the KGySoft.Drawing.Core package and WPF. Actually you can simply use the KGySoft.Drawing.Wpf package for WPF.)
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 happens 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:
// Though Gray8 format also has built-in support in KGySoft.Drawing.Core
// (see KnownPixelFormat.Format8bppGrayScale) here we pretend as if it was not supported natively.
// So this is our bitmap with the custom pixel format:
var bitmap = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormats.Gray8, null);
// We need to specify a configuration that tells some info about the pixel format
// and how pixels can be got/set from known color formats.
var customConfig = new CustomBitmapDataConfig
{
PixelFormat = new PixelFormatInfo { BitsPerPixel = 8, Grayscale = true },
BackBufferIndependentPixelAccess = true,
BackColor = Color.Silver.ToColor32(), // black if not specified
// In this example we specify Color32 access but you can use other color types
// if they fit better for the format (eg. Color64, ColorF or their premultiplied counterparts).
// Note that the setter blends possible alpha colors with the back color.
RowGetColor32 = (row, x) => Color32.FromGray(row.UnsafeGetRefAs<byte>(x)),
RowSetColor32 = (row, x, c) => row.UnsafeGetRefAs<byte>(x) =
c.Blend(row.BitmapData.BackColor, row.BitmapData.WorkingColorSpace).GetBrightness(),
// Now we specify also a dispose callback to be executed when the returned instance is disposed:
DisposeCallback = () =>
{
bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
bitmap.Unlock();
}
};
// Returning an IReadWriteBitmapData instance that wraps our native bitmap with the custom format:
return BitmapDataFactory.CreateBitmapData(
bitmap.BackBuffer, new Size(bitmap.PixelWidth, bitmap.PixelHeight), bitmap.BackBufferStride,
customConfig);
💡 Tip: See also the Xamarin and MAUI examples that demonstrate how to create a bitmap data for SkiaSharp's
SKBitmap
type as if there was no dedicated package for SkiaSharp.
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.
Most pixel formats use the sRGB color space, in which alpha blending (and also other operations) may provide incorrect results.
By default it depends on the used pixel format which color space is used in KGy SOFT Drawing Libraries. The default pixel format in most rendering engines use some sRGB format (usually a premultiplied one), which is optimized for blending in the sRGB color space. When creating a managed bitmap data by the CreateBitmapData
overloads or by the GetReadable/Writable/ReadWriteBitmapData
methods of the specific libraries you can use the overloads that have a WorkingColorSpace
parameter.
💡 Tip: See the
WorkingColorSpace
enumeration for more information and image examples about working in the sRGB and linear color spaces.
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 "Girl with a Pearl Earring" | |
Test image "Girl with a Pearl Earring" quantized with system default 8 BPP palette, no dithering | |
Test image "Girl with a Pearl Earring" quantized with system default 8 BPP palette using Bayer 8x8 dithering | |
Test image "Girl with a Pearl Earring" 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: Use
KGy SOFT Imaging Tools
from the KGySoft.Drawing.Tools repository to try image quantization and dithering in a real application. See also the Examples folder for example applications in vairous environments.
Quantizing and Dithering in KGy SOFT Imaging Tools
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.
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 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.
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.