Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
sbaeumlisberger committed Apr 15, 2020
2 parents 3d52b59 + 4ca84e7 commit 4015e76
Show file tree
Hide file tree
Showing 126 changed files with 1,326 additions and 610 deletions.
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) stakx 2016-2017
Copyright (c) 2016-2017 stakx
Copyright (c) 2020 S. Bäumlisberger

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
42 changes: 10 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# stakx.WIC
# WIC-DotNet

A COM interop library for .NET that makes Windows Imaging Component (WIC) available to managed code.
A .NET Standard library that makes the Windows Imaging Component (WIC) available to managed code so that it can be used in .NET Core, .NET Framework and UWP (.NET Native). The library provides a thin layer of abstractions and extension methods to make it easier to work with the raw WIC interface.

[Get it from NuGet](https://www.nuget.org/packages/WIC.DotNet/)

## What is the Windows Imaging Component (WIC)?

Expand All @@ -17,46 +19,22 @@ Windows Imaging Component allows you to accomplish tasks such as:
* converting bitmaps to different pixel formats (bit depth, channels)
* transforming bitmaps (clip, flip horizontally or vertically, rotate by 90° angles, scale)

## Why would you want to use stakx.WIC?

I originally started this project because I needed a library to do some fairly simple image processing…
inside an ASP.NET web application. And it is this last bit that introduces a few problems.
The .NET Framework does not come with any image processing functionality that is officially supported in server-side code:

1. There is the `System.Drawing` namespace.
The facilities in this namespace are based on the GDI+ API. It is officially unsupported in server-side code.
(Although it usually works just fine, if you know how to avoid memory leaks and work around performance issues;
regarding this topic, I'd recommend Nathanael Jones' article ["20 Image Resizing Pitfalls"][20-pitfalls].)

2. There is the `System.Windows.Media.Imaging` namespace.
This one belongs to the Windows Presentation Foundation (WPF) UI framework and contains thin wrappers around the WIC.
Being part of a UI framework, it is also not an ideal candidate for server-side scenarios.
(But like GDI+, it often works just fine.)

The nice thing about the WIC is that it is officially supported on Windows servers!
So if you can find a way to use it directly, then you're all good.
And this is exactly what this project offers: a set of type definitions so that you can use the WIC from .NET.

[20-pitfalls]: http://www.nathanaeljones.com/blog/2009/20-image-resizing-pitfalls

## How do you get started?

1. Familiarize yourself with the WIC, if you don't know it yet.
See e.g. the [Windows Imaging Component documentation on MSDN][msdn].

2. Add the [NuGet package `stakx.WIC`][nuget-package] to your project.
Alternatively, you can compile this project yourself (you will need Visual Studio 2017 for this), and then
add a reference to the built `stakx.WIC.dll` assembly to your project.
2. Add the NuGet package [`WIC-DotNet`][nuget-package] to your project.
Alternatively, you can compile this project yourself (you will need Visual Studio 2017 for this), and then add a reference to the built `WIC.dll` assembly to your project.

3. In your code, start by instantiating a `WICImagingFactory` object.
Most other WIC components can be created directly or indirectly through this factory object.

[msdn]: https://msdn.microsoft.com/en-us/library/windows/desktop/ee719902.aspx
[nuget-package]: https://www.nuget.org/packages/stakx.WIC/
[nuget-package]: https://www.nuget.org/packages/WIC.DotNet/

## Is there any example code?

This project page does currently not provide any samples to show how exactly one would get stuff done using the WIC API.
Until I find the time to write some sample code in the future, please refer to the the WIC samples on [MSDN].
I have strived to keep the COM interop types in this project as close as possible to the original COM type definitions.
This should make it fairly easy for you to draw the connection between what you read on MSDN, and this .NET library.
You can find some samples in the [Samples] directory and you can also refer to the the WIC samples on [MSDN].

[Samples]: https://github.com/sbaeumlisberger/WIC-DotNet/tree/develop/Samples
6 changes: 6 additions & 0 deletions Samples/EncodePNG/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
59 changes: 59 additions & 0 deletions Samples/EncodePNG/EncodePNG.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{88AA03C5-8492-4B69-90D1-37AC0159A320}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>EncodePNG</RootNamespace>
<AssemblyName>EncodePNG</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\WIC\WIC.csproj">
<Project>{9d77f053-8dc1-4128-a447-cb0d67e686b4}</Project>
<Name>WIC</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
78 changes: 78 additions & 0 deletions Samples/EncodePNG/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using WIC;
using WIC.Constants;

namespace Sample
{
class Program
{
static IEnumerable<IWICBitmapEncoderInfo> EnumEncoders(IWICImagingFactory wic)
{
return wic.CreateComponentEnumerator(WICComponentType.WICEncoder, WICComponentEnumerateOptions.WICComponentEnumerateDefault)
.AsEnumerable()
.OfType<IWICBitmapEncoderInfo>();
}

static void Main(string[] args)
{
const int width = 256;
const int height = 256;
const int bytesPerPixel = 3;

var wif = new WICImagingFactory();

// find the PNG encoder information
var pngEncoderInfo = EnumEncoders(wif)
.Where(y => y.GetFriendlyName() == "PNG Encoder")
.First();

// create the PNG encoder
var pngEncoder = wif.CreateEncoder(pngEncoderInfo.GetContainerFormat());

using (var stream = File.Create("result.png"))
{
pngEncoder.Initialize(stream.AsCOMStream(), WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache);

var frame = pngEncoder.CreateNewFrame();

frame.Initialize(null);

// set pixel format
var format = WICPixelFormat.WICPixelFormat24bppBGR;
frame.SetPixelFormat(ref format);

// check if the pixel format was accepted
if (format != WICPixelFormat.WICPixelFormat24bppBGR)
{
throw new ArgumentException("The requested pixel format was not accepted");
}

frame.SetResolution(new Resolution(96, 96));
frame.SetSize(width, height);

var image = new byte[width * height * bytesPerPixel];

// create a RGB gradient image
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
image[(y * width + x) * bytesPerPixel + 0] = (byte)x; // blue
image[(y * width + x) * bytesPerPixel + 1] = (byte)y; // green
image[(y * width + x) * bytesPerPixel + 2] = (byte)(255 - y); // red
}
}

// write it to the frame
IWICBitmapFrameEncodeExtensions.WritePixels(frame, height, width * bytesPerPixel, image);

// commit everything to stream
frame.Commit();
pngEncoder.Commit();
}
}
}
}
36 changes: 36 additions & 0 deletions Samples/EncodePNG/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("EncodePNG")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("EncodePNG")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("88aa03c5-8492-4b69-90d1-37ac0159a320")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
12 changes: 12 additions & 0 deletions Samples/InPlaceMetadataEncoding/InPlaceMetadataEncoding.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\WIC\WIC.csproj" />
</ItemGroup>

</Project>
48 changes: 48 additions & 0 deletions Samples/InPlaceMetadataEncoding/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.IO;
using WIC;

namespace InPlaceMetadataEncoding
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Encoding metadata in-place...");
try
{
string filePath = args[0];

var wic = new WICImagingFactory();

using var fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite);

var decoder = wic.CreateDecoderFromStream(fileStream.AsCOMStream(), WICDecodeOptions.WICDecodeMetadataCacheOnDemand);

var frame = decoder.GetFrame(0);

var mtadataEncoder = wic.CreateFastMetadataEncoderFromFrameDecode(frame);

var metadataWriter = mtadataEncoder.GetMetadataQueryWriter();

metadataWriter.SetMetadataByName("System.Keywords", new string[] { "in-place", "metadata", "encoding" });

mtadataEncoder.Commit();

Console.WriteLine("Metadata successfully encoded!");
}
catch (Exception ex) when (ex.HResult == WinCodecError.PROPERTY_NOT_SUPPORTED)
{
Console.WriteLine("The file format does not support the requested metadata.");
}
catch (Exception ex) when (ex.HResult == WinCodecError.TOOMUCHMETADATA
|| ex.HResult == WinCodecError.INSUFFICIENTBUFFER
|| ex.HResult == WinCodecError.IMAGE_METADATA_HEADER_UNKNOWN
|| ex.HResult == WinCodecError.UNSUPPORTED_OPERATION)
{
Console.WriteLine("The file has not enough padding for the requested metadata.");
}
Console.ReadKey();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\WIC\WIC.csproj" />
</ItemGroup>

</Project>
66 changes: 66 additions & 0 deletions Samples/LosslessReEncodeWithMetadata/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.IO;
using WIC;

namespace LosslessReEncodeWithMetadata
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Re-encoding image lossless with metadata...");
try
{
string filePath = args[0];

var wic = new WICImagingFactory();

using var fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite);

var decoder = wic.CreateDecoderFromStream(fileStream.AsCOMStream(), WICDecodeOptions.WICDecodeMetadataCacheOnDemand/*lossless decoding/encoding*/);

var frame = decoder.GetFrame(0);

using var memoryStream = new MemoryStream();

var encoder = wic.CreateEncoder(decoder.GetContainerFormat());

encoder.Initialize(memoryStream.AsCOMStream(), WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache);

var frameEncoder = encoder.CreateNewFrame();
frameEncoder.Initialize(null);
frameEncoder.SetSize(frame.GetSize()); // lossless decoding/encoding
frameEncoder.SetResolution(frame.GetResolution()); // lossless decoding/encoding
frameEncoder.SetPixelFormat(frame.GetPixelFormat()); // lossless decoding/encoding

frameEncoder.AsMetadataBlockWriter().InitializeFromBlockReader(frame.AsMetadataBlockReader());

var metadataWriter = frameEncoder.GetMetadataQueryWriter();

metadataWriter.SetMetadataByName("System.Keywords", new string[] { "lossless", "re-encode", "with", "metadata" });

frameEncoder.WriteSource(frame);

frameEncoder.Commit();
encoder.Commit();

memoryStream.Flush();
memoryStream.Position = 0;
fileStream.Position = 0;
fileStream.SetLength(0);
memoryStream.CopyTo(fileStream);

Console.WriteLine("Image successfully lossless re-encoded with metadata!");
}
catch (Exception ex) when (ex.HResult == WinCodecError.PROPERTY_NOT_SUPPORTED)
{
Console.WriteLine("The file format does not support the requested metadata.");
}
catch (Exception ex) when (ex.HResult == WinCodecError.UNSUPPORTED_OPERATION)
{
Console.WriteLine("The file format does not support any metadata.");
}
Console.ReadKey();
}
}
}
Loading

0 comments on commit 4015e76

Please sign in to comment.