Contents
Many image editors and viewers allow the user to adjust the image using effect or filter plugins. One of the most common types is the 8bf filter plugin used by Adobe® Photoshop® and other software that implements the Photoshop plugin API.
This library allows applications built with .NET 2.0 and later to use 3rd party 8bf filters. It can run either 32-bit or 64-bit filters and supports processing grayscale and RGBA images in 8 or 16 bits per channel depending on the supported image types of the selected filter. For 16 bit per channel images, the WIC-based classes in .NET 3.0 and later must be used due to the lack of reliable support for 16-bit images in GDI+.
The demo application is built with .NET 4.5.2, in order for the library work with .NET 3.5 and earlier you must rebuild the PSFilterHost
project with the appropriate target framework.
The latest version of this library can be downloaded using NuGet or from the project page on Codeplex.
The Adobe® Photoshop® SDK allows 3rd party developers to create Import, Export, Filter, Format and other types of plugins. Although it was not intended to help developers write a plugin host, it has also been used as a guide to hosting plugins in other applications.
This library is based on version 5.0 of the Photoshop SDK version 7.0 and later have a license agreement that prevents developers of other host applications from using the information that they contain. Because of this, the new PiPL and Property suite values must be decoded using the debug output from the PiPL resource loading and Property suite calls.
The first step is to search a directory (and optionally any sub directories) for 8bf filters. The directories that are searched may contain both filters and shortcuts to filters in other locations.
Dictionary<string, ToolStripItem> filters = new Dictionary<string, ToolStripItem>();
List<ToolStripItem> aboutMenuItems = new List<ToolStripItem>();
foreach (PluginData plugin in PSFilterHost.EnumerateFilters(path, SearchOption.AllDirectories))
{
ToolStripMenuItem child = new ToolStripMenuItem(plugin.Title, null, RunFilter_Click);
child.Name = plugin.Title;
child.Tag = plugin;
ToolStripMenuItem about = new ToolStripMenuItem(plugin.Title, null, ShowAboutDialog);
about.Tag = plugin;
if (filters.ContainsKey(plugin.Category))
{
ToolStripMenuItem parent = filters[plugin.Category];
if (!parent.DropDownItems.ContainsKey(plugin.Title))
{
parent.DropDownItems.Add(child);
if (plugin.HasAboutBox)
{
aboutMenuItems.Add(about);
}
}
}
else
{
ToolStripMenuItem parent = new ToolStripMenuItem(plugin.Category, null, child);
filters.Add(plugin.Category, parent);
if (plugin.HasAboutBox)
{
aboutMenuItems.Add(about);
}
}
}
Many filters do not support processing all of the PixelFormat
s that WIC can load. For example, some filters may not support Grayscale or 16-bit per channel images. The host application can disable filters which do not support processing the current PixelFormat
when it loads a new image.
private void EnableFiltersForImageFormat()
{
if (this.srcImage != null)
{
System.Windows.Media.PixelFormat format = this.srcImage.Format;
ToolStripItemCollection items = this.filtersToolStripMenuItem.DropDownItems;
for (int i = 0; i < items.Count; i++)
{
ToolStripMenuItem menu = (ToolStripMenuItem)items[i];
if (menu.HasDropDownItems)
{
ToolStripItemCollection nodes = menu.DropDownItems;
int nCount = nodes.Count;
List<bool> catEnabled = new List<bool>(nCount);
for (int j = 0; j < nCount; j++)
{
PluginData data = (PluginData)nodes[j].Tag;
bool enabled = data.SupportsImageMode(format);
catEnabled.Add(enabled);
nodes[j].Enabled = enabled;
}
menu.Enabled = catEnabled.Contains(true);
}
else
{
PluginData data = (PluginData)menu.Tag;
menu.Enabled = data.SupportsImageMode(format);
}
}
}
else
{
ToolStripItemCollection items = filtersToolStripMenuItem.DropDownItems;
for (int i = 0; i < items.Count; i++)
{
items[i].Enabled = false;
}
}
}
Callbacks
The abort callback allows the host to signal the filter to cancel any rendering currently in progress. The filter will poll this callback during long operations and stop processing if it returns true
.
private bool AbortFilterCallback()
{
return escapePressed;
}
The color picker callback allows the host to show its own color picker in place of the Windows color dialog when a filter requests that the user choose a color. The filter may specify a prompt for the user (e.g. Please choose a color:
), if the filter does not set a prompt, this will be an empty string
. The red, green and blue parameters specify the initial color that the filter wants selected in the host's color dialog.
private ColorPickerResult PickColorCallback(string prompt, byte red, byte green, byte blue)
{
ColorPickerResult color = null;
using (ColorPickerForm dialog = new ColorPickerForm(prompt))
{
dialog.SetDefaultColor(red, green, blue);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
color = new ColorPickerResult(dialog.UserPrimaryColor);
}
}
return color;
}
Methods
The SetColorProfiles
method allows the host to specify the International Color Consortium (ICC) color profiles of the document and monitor which are used to apply color correction to the preview image displayed by the filter.
this.hostColorProfiles = new HostColorManagement(documentProfile, monitorProfilePath);
Events
The UpdateProgress
event allows the filter to inform the host of its rendering progress.
private void UpdateProgress(object sender, FilterProgressEventArgs e)
{
this.toolStripProgressBar1.Value = e.Progress;
}
Properties
The HostInfo
property allows the filter to retrieve information about the current document from the host, such as the title and preferred ruler measurement unit.
this.hostInfo = new HostInformation();
this.hostInfo.Title = this.imageFileName;
this.hostInfo.RulerUnit = HostRulerUnit.Inches;
The PseudoResources
property allows the filter to store data which will persist for the current session, this can be used for communication between filters.
PseudoResourceCollection pseudoResources = null;
After the user selects a filter to run, it can be executed with the following steps:
- Calling one of the
PSFilterHost
constructor overloads.
- Optionally set the abort callback, color picker callback, progress event handler and other properties.
- Set the
FilterParameters
property to restore the last used settings (if any).
- Calling one of the
RunFilter
overloads with the PluginData
of the filter you want to execute.
- Update the output image and save the
FilterParameters
and other properties that the filter may have modified.
When the FilterParameters
have been set to execute a filter with settings from a previous session the RunFilter(PluginData)
method will not show the user interface. The RunFilter(PluginData, Boolean)
overload allows the host to display the filters user interface initialized to the previous settings. The host should display the filters user interface unless it was invoked through a 'Repeat Filter' command.
using (PSFilterHost host = new PSFilterHost(source, foreColor, backColor, selection, owner))
{
host.SetAbortCallback(new AbortFunc(AbortFilterCallback));
host.SetPickColorCallback(new PickColor(PickColorCallback));
host.UpdateProgress += new EventHandler(UpdateProgress);
if (this.filterParameters.ContainsKey(pluginData))
{
host.FilterParameters = this.filterParameters[pluginData];
}
if ((this.pseudoResources != null) && this.pseudoResources.Count > 0)
{
host.PseudoResources = this.pseudoResources;
}
host.HostInfo = this.hostInfo;
if (this.hostColorProfiles != null)
{
host.SetColorProfiles(this.hostColorProfiles);
}
if (host.RunFilter(pluginData, showUI))
{
this.destinationImage = host.Dest;
if (showUI)
{
if (this.filterParameters.ContainsKey(pluginData))
{
this.filterParameters[pluginData] = host.FilterParameters;
}
else
{
this.filterParameters.Add(pluginData, host.FilterParameters);
}
this.pseudoResources = host.PseudoResources;
this.hostInfo = host.HostInfo;
}
}
}
The ShowAboutBox
function displays the filter's about box (if it has one).
PSFilterHost.ShowAboutDialog(pluginData, parentWindowHandle);
The Photoshop API uses 16-bit signed integers for the image dimensions so this library supports a maximum of 32,000 pixels in width and/or height, Photoshop 7.0 and earlier were limited to 30,000 pixels which was increased to 300,000 pixels in Photoshop CS and later.
The EXIF and XMP metadata is exposed to the filters as a pointer to the start of the TIFF container for EXIF or the start of the XML packet for XMP.
The 5.0 PICA suites are only enabled for certain filters due to the fact that many filters do not check the return code before using a suite that may not be implemented.
Since the release of .NET 3.5, the C# and VB.NET compilers enable Data Execution Prevention for 32-bit applications. As many filters are not compatible with it, you must use editbin or a similar tool to clear the IMAGE_DLLCHARACTERISTICS_NX_COMPAT
flag (see NXCOMPAT and the C# compiler for more information).
12-27-2015
- Updated source code and demo application to match the 1.4.0.0 release on Codeplex
- Added a methods header to the Optional Callbacks, Events and Properties section
- Updated the Executing a filter section to use the new
SetColorProfiles(HostColorManagement)
method
10-19-2015
- Updated source code and demo application to match the 1.3.0.0 release on Codeplex
- Changed the Executing a filter section to use the new
RunFilter(PluginData, Boolean)
overload
04-12-2015
- Updated source code and demo application to match the 1.2.0.0 release on Codeplex
- Changed the Searching a directory for filters example code to use the new
System.IO.SearchOption
overload
12-03-2014
- Updated source code and demo application to match the 1.1.0.6 release on Codeplex
11-08-2014
- Added a section describing how to disable filters that do not support the current image mode
- Updated source code and demo application to match the 1.1.0.5 release on Codeplex
09-28-2014
- Added a section describing the optional callbacks, events and properties
08-30-2014
- Added some information about 32-bit host applications and Data Execution Prevention
- Updated source code and demo application to match the 1.1.0.4 release on Codeplex
08-25-2014