It's not just a program, it's a platform!


Consequent multitasking
and extendibility with plugins are the two main principles that make Picturenaut special. The program is built in layers, where the core program is just a hub for managing tasks and interfacing with the OS itself. All image-related operations, even seemingly built-in ones, are implemented via a plugin API.

pnaut_architecture

The plugin SDK is public and free, making Picturenaut an ideal platform for rapid prototyping new algorithms. We believe this is the better alternative to making the entire program open-source. A plugin API allows us to keep the main code organized while still enabling interested coders to extend the program’s functionality.


Core Functions


Picturenaut provides several core functions, that plugin developers don't have to care about. It has an advanced HDR viewport with zoom and pan capabilities. It performs realtime gamma and exposure correction, even with adaptive auto-exposure on the displayed region. It auto-detects plugins and makes them available in the menu and to internal routines.
Highslide JS
The main viewport of Picturenaut, with all the important handling widgets.


Most importantly, the core program takes care of:

  • Memory allocation
  • Task management
  • User interface
  • Viewport updating
  • Image buffer handling
  • Undo and redo

Task management


Every operation is run in a separate task without blocking program execution. That even includes image loading and saving, which is unheard of in any other image editor. Operations that qualify for multitasking (like filters or tonemappers) are distributed across available CPUs. Every task can be cancelled by the user, and operations may specify chained tasks that are executed in succession.


Plugin Types


Plugins are written in C++ and compiled into DLL files. There are several types available, intended for different stages of the HDR imaging pipeline. The plugin type defines how Picturenaut will present this plugin to the user and to other plugins. Although all plugin types have access to all utility functions, some plugin types draw an extra benefit from beeing presented in the proper place.


1. Format Plugins


Additional image file formats, both HDR and LDR, can be implemented as format plugins. Just like format plugins for Photoshop, this type provides functions for loading and saving, and is automatically recognized in Picturenaut's file dialog. An open source example included in the SDK is the RAW format plugin, that links in Dave Coffin’s dcraw libraries.
Highslide JS
Picturenaut's own HDR generator is in fact a plugin.

2. Create Plugins


This type of plugin can create new 32-bit floating point image buffers of arbitrary size and number.
For example, Picturenaut's own HDR merging function is implemented as Create Plugin. Other candidates for potential Create Plugins could be a DSLR remote capturing interface or a physical sky generator. Such plugins would automatically appear in the "File" menu, and they may open their own interface window.

Highslide JS
New Filter plugins will show up in the Filter dropdown. They can open submenus, just like this HDRShop emulator.

3. Filter Plugins


This is the most generic type of plugin, allowing the highest level of freedom. It has full read/ write access to all image data and may create new image buffers, metadata, layers, or an entirely new image. Filter Plugins may open their own interface, optionally with realtime refresh of the image's main viewport.
One example, found in the standard installation, is a generic HDRShop emulator. It will detect existing HDRShop plugins, provide an interface for them, and execute them. While this is useful for legacy purpose, this bridge plugin uses only a fraction of Picturenaut's Plugin API capabilities.
Filter plugins waiting to be written are a Panoramic Rewarp, Blur, Sharpen, even a paint engine or a layer manager.


4. Tonemapping Plugins


This is the most convenient plugin type. It will automatically integrate in Picturenaut's tonemapping dialog, completely equipped with realtime preview, interactive histogram, post-gamma correction and settings load/save functionality. Even the main viewport will stay responsive for panning and zooming, and with the Preview box enabled it will show the current TMO result applied to the full image. This allows a true WYSIWYG experience for the user.
All your plugin needs to do is provide the tonemapping algorithm and a parameter list (which will turn into live feedback sliders automatically).

Highslide JS
The standard tonemapper interface is already rich with supplemental features.

Technically, the Tonemapping plugin is a sub-class of the Filter plugin class. You’re essentially adding a new method Picturenaut’s open-ended “Tonemapping Filter” plugin. Of course, you may also create your very own interface by implementing the TMO as real Filter plugin.


Code Example: Basic Exposure TMO



The simplicity of Picturenaut's Plugin API is best illustrated with a code example. The C++ source code below is all you need to create a fully functional tonemapping operator.
You don't need to copy the code from here, this example is included in the SDK package.

/**
 *   Exposure Tone-Mapping Example
 *
 * Copyright (c) 2005-2009 Marc Mehl
 */

#if _MSC_VER >= 1200
#pragma warning (disable : 4786)
#endif

First, link in the required libraries from the plugin SDK.
#include "picturenaut/pnapp.h"
#include "mexptmo.h"
#include "resource.h"
#include 

Then define the user parameters needed and configure the slider defaults and ranges.
/**
 * Creates an exposure tone-mapper.
 */
MExpTmo::MExpTmo() {
  paramList.pEntries = &exposureEntry;
  paramList.pListName = pn_text("exposure");

  exposureEntry.paramType = pn::tmoparamSLIDER;
  exposureEntry.value._double = -2.64;
  exposureEntry.pInitValue = pn_text("-16.0|16.0");
  exposureEntry.pName = pn_text("exposure");
  exposureDisplayName = pn::PNApp::loadString(IDS_EXPOSURE);
  exposureEntry.pDisplayName = exposureDisplayName.c_str();
  exposureEntry.pNext = &offsetEntry;

  offsetEntry.paramType = pn::tmoparamSLIDER;
  offsetEntry.value._double = 0.0;
  offsetEntry.pInitValue = pn_text("-1.0|1.0");
  offsetEntry.pName = pn_text("offset");
  offsetDisplayName = pn::PNApp::loadString(IDS_OFFSET);
  offsetEntry.pDisplayName = offsetDisplayName.c_str();
  offsetEntry.pNext = 0;
} // MExpTmo::MExpTmo

Next, the TMO needs to be initialized with Picturenaut's internal image buffers. Note that an image may internally contain more than RGB buffers, like additional channels and layers. You may also create temporary buffers for preprocessing.
/**
 * Initializes this tone-mapper.
 *
 * @param inputBuffers list of input buffers
 * @return true on success; false otherwise
 */
bool
MExpTmo::init(const pn::PNImageBufferList &inputBuffers) {
  return true;
} // MExpTmo::init


/**
 * Returns a list of TMO parameters.
 *
 * @return a list of TMO parameters
 */
pn::PPNTMOPARAMLIST
MExpTmo::getParameterList() const {
  return (pn::PPNTMOPARAMLIST)&paramList;
} // MExpTmo::getParameterList

The onTonemap procedure is where you start your TMO algorithm. Unless otherwise specified, the requested image buffers are served in tiles, enabling your algorithm to run multithreaded and on very big images. You can specify a tile margin to ensure large kernel operations are processed cor- rectly. Picturenaut will automatically re-assemble tiles into the full-size image.
/**
 * Tonemaps an image.
 *
 * @param pDrawingInfo points to a drawing info
 */
void
MExpTmo::onTonemap(pn::PPNIMAGEDRAWINGINFO pDrawingInfo) {
  pn::PNIMAGEDESC *pImageIn = pDrawingInfo->image[0];
  pn::PNIMAGEDESC *pImageOut = pDrawingInfo->image[1];

  int width = pImageIn->size.cx;
  int height = pImageIn->size.cy;
  pn::PNBufferFormat &format = pImageIn->format;
  int numSamples = pn::PNImageBuffer::getNumberOfSamples(format);
  float *pSrcImage = (float *)pImageIn->pBuffer;
  float *pDstImage = (float *)pImageOut->pBuffer;
  double &exposure = this->exposureEntry.value._double;
  double &offset = this->offsetEntry.value._double;

  applyExposureMultiplier(pSrcImage, pImageIn->pitch,
                          pDstImage, pImageOut->pitch,
                          width, height, numSamples,
                          (float)exposure, (float)offset);
} // MExpTmo::onTonemap


/**
 * Applies an exposure multiplier on a RGB image.
 *
 * @param pSrcImage source image data pointer
 * @param srcPitch a source pitch
 * @param pDstImage destination image data pointer
 * @param dstPitch a destination pitch
 * @param width a width
 * @param heigt a height
 * @param numSamples number of samples
 * @param exposure exposure multiplier
 * @param offset value offset
 */
void
MExpTmo::applyExposureMultiplier(float *pSrcImage, int srcPitch,
                                 float *pDstImage, int dstPitch,
                                 int width, int height, int numSamples,
                                 float exposure, float offset) {
  float *ps = pSrcImage;
  float *pd = pDstImage;

  srcPitch /= sizeof(float);
  dstPitch /= sizeof(float);
  exposure = (float)pow((double)2.0, (double)exposure);

  for (int j = 0; j < height; ++j) {
    for (int i = 0; i < width; ++i) {
      for (int c = 0; c < numSamples; ++c, ++ps, ++pd) {
        *pd = (*ps + offset) * exposure;
      } // for
    } // for
    ps += srcPitch;
    pd += dstPitch;
  } // for
} // MExpTmo::applyExposureMultiplier


Result


Once the code above is compiled and saved in the picturenaut/plugins folder, this new TMO will be fully functional. It will simply show up in the drop-down menu of the tonemapping dialog.

Pnaut_TMO_plugin



Picturenaut's Plugin SDK is released under the MIT license. That means you may do with it whatever you want, as long as we're not held responsible for bad things happening.


Although we appreciate and encourage open-source contributions, this is not a requirement. We also welcome commercial plugins for Picturenaut, possibly adapted from existing Photoshop plugins. After all, we're providing a free alternative platform that will make your plugin more widely accessible...

Getting started with the SDK

1. Prerequisites

Knowledge of C/C++ required.
The SDK compiles with Microsoft compilers and MINGW (GCC for Windows). Borland wasn't tested but should work as well.
Make sure to have the latest version of Picturenaut. This SDK requires build 1526 or higher.

2. Installation

Unzip the archive anywhere! Make sure to keep the folder structure intact.

3. Compiling

There is a "project" folder, containing preconfigured project files and makefiles for several compilers. Pick one and compile the SDK. Then you'll find the generated libraries in the "lib" folder of that project. Each library has is linked static to the SDK.

4. Examples

In that SDK is an "examples" folder for a simple tone mapper and a format plugin. The project structure is set up to mirror that of the SDK itself. After compiling both examples successfully, you test them right away. The Compiler generated some dll files in the "bin" folder of the respective example. Just copy these into the "plug-in" folder of your Picturenaut installation.

Warning: The example DLLs will be named just like the DLL from the Picturenaut distribution. Make sure to have a backup.

If you need support, have a plugin for release, or just want to chat with the developer Marc, come to the Picturenaut DEV forum. See you there...