PhotoDemon 5.4 is live – now with German, French, and Dutch language support

Summary

PhotoDemon 5.4 is complete. New features include language support (German, French, and Dutch), a full-featured batch processing wizard, shadow/highlight correction, nine new distort tools, vignetting, median noise removal, JPEG and PNG optimization, and more. Download it here.

Kaleidoscope is probably the least practical (but most fun!) new tool in 5.4.  :)
Kaleidoscope is probably the least practical (but most fun!) new tool in 5.4. Also, German!

Highlight feature: support for multiple languages!

This is the biggest addition in version 5.4, and I can only claim partial credit for it. Primary credit goes to Frank Donckers, a fellow VB programmer who prototyped the initial translation engine for me. As if that isn’t incredible enough, Frank also supplied the translations for French, German, and Dutch (Flemish), so I owe him an enormous debt of gratitude. Thank you, Frank!

One of the neatest aspects of this feature is the ability to change the language at run-time via the Language menu. Unlike every program I have ever used, no restart is required. PhotoDemon will dynamically change the program’s entire language immediately, and if you change your mind, you can switch to any other language at any time.

I hope these three languages are only the beginning. If you speak a language other than English, please consider contributing a new PhotoDemon translation! No programming knowledge is required, and you will receive full credit for your work. Contact me for more details.

Nine new Distort-style tools

Add and remove lens distortion. Swirl. Ripple. Pinch and whirl. Waves. Kaleidoscope. Polar conversion (both directions). Figured glass (dents).

The new Ripple tool.  All distort tools use resampling for improved image quality, and all provide real-time previews.
The new Ripple tool. All distort tools use resampling for improved image quality, and all provide real-time previews.
The new Figured Glass tool uses Perlin Noise to provide a warped glass look to images.
The new Figured Glass tool uses Perlin Noise to provide a warped glass look to images. (Note: the source image is a promotional photo for ABC’s Once Upon a Time.)

Vastly improved file format support

The new JPEG export dialog.  Optimization is a lossless way to reduce file size - very handy for JPEGs headed to the web.
The new JPEG export dialog. Optimization is a lossless way to reduce file size – very handy for JPEGs headed to the web.

JPEGs now support automatic EXIF rotation on import, and a variety of options on export (Huffman table optimization, progressive scan, thumbnail embedding, specific subsampling). TIFFs support CMYK encoding and a number of compression schemes (none, PackBits, LZW, CCITT 3 and 4, zLib, and more). PNG exporting supports variable compression strength, interlacing, and background color chunk preservation. PPMs can be exported with RAW or ASCII encoding. BMP and TGA files now support RLE encoding. And for icons, animated GIFs, and multipage TIFFs, all images inside a file can now be loaded (instead of just the first one).

These format settings can be accessed from the Tools -> Options menu, and the new Batch Process tool also provides direct access.

Revamped standard tools, including Box Blur, Gaussian Blur, Smart Blur, and Unsharp Masking.

Smart blur can be used to smooth out specific features, like skin, while leaving edges and fine details intact.  (Image of the lovely and talented Rashida Jones, via Glamour)
Smart blur can be used to smooth out specific features, like skin, while leaving edges and fine details intact. (Image of the lovely and talented Rashida Jones, via Glamour)

PhotoDemon is now a much better photo editor, thanks to the revamp of its core convolution filters. Larger tool dialogs make it easier to see the result of your actions. Better performance means real-time previews, even at enormous radii (up to 200px for all filters, plus 500px for box blur!). And all convolution algorithms now use specialized edge handling code to make sure every part of the image – from center to border – is handled correctly.

Also, the program’s Gaussian Blur is now a true gaussian blur. There are no shortcuts, no estimations, and it’s still fast enough to preview in real-time.

New advanced color tools, including Shadow/Midtone/Highlight adjustments, color balancing, and monochrome-to-grayscale recovery

Shadow / Midtone / Highlight correction allows for detailed recovery of light and dark parts of an image.  Thanks to deviantart user deviantsnark for the sample image.
Shadow / Midtone / Highlight correction allows for detailed recovery of light and dark sections of an image. Thanks to dA user deviantsnark for the Borderlands wallpaper.
Color balance provides a per-color way to adjust the hue of an image (versus hue / saturation adjustments, which apply equally to all colors).  Thanks to dA user LadyGT for the beautiful artwork.
Color balance provides a per-color way to adjust the hue of an image (versus hue / saturation adjustments, which apply equally to all colors). Thanks to dA user LadyGT for the beautiful Tomb Raider artwork.

New stylize tools, including Film Grain, Vignetting, Modern Art, Trace Contour, Film Noir, and Comic Book

Vignetting refers to the rounded halo around the edges of the image.  The new tool allows you to add halos of any size, softness (how blurry the edges are), transparency, and color, and it can automatically fit the effect to any aspect ratio.  Thanks to dA user chrismickens for the great Mad Men artwork.
Vignetting refers to the rounded halo around the edges of the image. The new tool allows you to add halos of any size, softness (how blurry the edges are), transparency, and color, and it can automatically fit the effect to any aspect ratio. Thanks to dA user chrismickens for the great Mad Men artwork.
PhotoDemon now allows you to add artificial film grain to any image.  This effect was famously used in the Mass Effect trilogy of games to create a more gritty, realistic look.
PhotoDemon now allows you to add artificial film grain to any image. This effect was famously used in the Mass Effect trilogy to create a more gritty, realistic look.
Contour tracing uses a stack of unique algorithms to "paint" the edges of an image.  It is also a useful edge detection tool.
Contour tracing uses a unique stack of algorithms to “paint” the main features of an image. It is also a useful edge detection tool.

Noise removal via Median Filtering

Median filtering serves two main purposes: removal of image noise (unwanted pixel variance), and recovery of damaged images.  The severely damaged image above is courtesy Wikipedia; the after image is pure PhotoDemon (note that it recovers better than the Wikipedia example!).
Median filtering serves two main purposes: removal of image noise (unwanted pixel variance), and recovery of damaged images. The severely damaged image above is courtesy Wikipedia; the after image is PhotoDemon’s correction (note that it recovers more than the Wikipedia example!)

Automatic image cropping

If an image has empty space around the edges - like this Firefox wallpaper - Autocrop can automatically crop it for you.  The feature supports thresholding, so it works equally well on lossy formats like JPEG.
If an image has empty space around the edges – like this Firefox wallpaper – Autocrop can automatically remove it for you. Autocrop supports thresholding, so it works just fine on JPEGs.

New Batch Process Wizard

If I had to pick a personal “favorite” new feature in this release, it would be the brand-new batch processing wizard. This tool is a highlight of PhotoDemon’s emphasis on usability, and I researched more than a dozen other image batch processing tools while building it. I could be biased, but I believe PhotoDemon is now the best general-purpose image batch processor available on the web.

The first page of the new Batch Process wizard.  This step is by far the most intricate, and a ton of work went into exposing full functionality without overwhelming the user.  To my knowledge, PhotoDemon is the only batch processor that allows you to create your own batch list from any number of source directories spread across any number of drives.
The first page of the new Batch Process wizard. This step is by far the most intricate, and a ton of work went into exposing full functionality without overwhelming the user. To my knowledge, PhotoDemon is the only batch processor that allows you to create your own batch list from any number of source directories spread across any number of drives.

Drag-and-drop is now supported when building the list of images to be processed – not only from within the dialog, by dragging between list boxes, but also from Windows Explorer. Live previews make it much easier to find the images you want, while helpful instructions on the left-hand side expose some of the more nuanced functionality.

Once a list of images has been created, you can optionally choose to apply photo editing actions to each image.  Unlike other batch processors, PhotoDemon allows you to use any photo editing actions provided by the program.
Once a list of images has been created, you can optionally choose to apply photo editing actions to each image. Unlike other batch processors, PhotoDemon allows you to use any photo editing actions provided by the program – not just a tiny subset.

Page 2 is the barest page of the new wizard. The current version allows you to skip photo editing actions (if you want to just do a batch rename or format conversion, for example), or you can apply any recorded macro. In the next release, I will add a set of “one-click” presets for common actions, like resizing, or optimizing images for the web.

Once you've created a list of images and chosen any photo editing actions, an output image format can be set.  New to this version, PhotoDemon can retain original image formats - allowing you to apply actions to mixed PNG/JPEG collections, for example.  Alternatively, you can select a single output format, with access to the program's full range of detailed format settings.
Once you’ve created a list of images and chosen any photo editing actions, an output image format can be set. New to this version, PhotoDemon can retain original image formats – allowing you to apply actions to mixed PNG/JPEG collections, for example.

Page 3 asks you to choose an output format. If you want to retain original image formats, that’s cool too – PhotoDemon now supports this! Alternatively, you can select a single output format, with access to the program’s full range of detailed format settings. In the example above, you can see all the options available for JPEGs, including new support for optimization (lossless file size reduction), thumbnails, progressive encoding, and specific subsampling.

The last step of the wizard asks you to choose a location to save all the processed files.  If desired, a number of rename options are also available.
The last step of the wizard asks you to choose a location to save all the processed files. If desired, a number of rename options are also available.

The final page asks you to select an output folder where PhotoDemon can save the processed images. New to this release is a wide range of renaming options – things like adding custom text to each filename, removing text from each filename, changing case, and replacing spaces with underscores for web-bound images. Additionally, original filenames can be retained, or PhotoDemon can just use ascending numbers.

So that’s the new batch wizard! I’d love feedback from power users, as there are a lot of moving parts to the batch tool, and while I have been very thorough in my own testing, it’s impossible to test every combination of variables. So if you find anything that doesn’t work, please let me know.

Improved features: Gamma Correction, Dilate, Erode, Monochrome Conversion, Histogram and Printing

As is usual with each PhotoDemon update, a number of existing tools received redesigns or new features. Gamma correction now displays live gamma curves, and each color component (red, green, and blue) can be adjusted individually. Dilate and Erode use a new algorithm that’s significantly more optimized, meaning sizes up to 200px radius can be previewed in real-time. Monochrome conversion supports any two color (not just black and white), while the printing and histogram dialogs were completely overhauled to make them more user-friendly.

The new gamma correction dialog.  The old dialog forced users to correct only one channel at a time.  The new one allows for correcting all three, with a live preview of the new curves.  Thanks to dA user Kouken for the Persona fan art.
The new gamma correction dialog. The old dialog forced users to correct only one channel at a time. The new one allows for correcting all three, with a live preview of the new curves. Thanks to dA user Kouken for the Persona fan art.

Universal color depth support at import and export time

PhotoDemon can now write 1, 4, 8, 24, and 32bpp variations of every supported file format. By default, when saving images, color depth detection is completely automated – the program will count the number of colors in an image and automatically select the most appropriate color depth for the output file. Alternatively, you can set a preference to manually specify color depth at save time. This also works for grayscale images; for example, the JPEG encoder will now detect grayscale images and write out 8bpp JPEGs accordingly. Alpha thresholding is also available when saving 32bpp images to 8bpp (e.g. PNG to GIF).

When saving a 32bpp image with a complex alpha channel to a simple format like GIF, the program has to reduce the alpha channel to binary values.  A new threshold dialog helps you find the perfect value.
When saving a 32bpp image with a complex alpha channel to a simple format like GIF, the program has to reduce the alpha channel to binary values. A new threshold dialog helps you find the perfect value.

This feature was a nightmare to implement, as PhotoDemon supports a huge variety of file formats, and each one has a detailed list of color depths it does or does not support. Full support for transparency adds a whole other layer of complexity. But now that the feature is completely implemented and rigorously tested, I can’t imagine it any other way. Color depth is not something users should have to worry about, and automatic handling should be a feature of every photo editor (rather than pestering you for color depth every time you save… *cough* GIMP *cough*).

New feature: pngnq-s9 plugin for optimizing PNG files

At the request of a good friend, PhotoDemon now provides integrated support for the pngnq-s9 variety of the famous pngnq library. For the uninitiated, pngnq provides a way to reduce 32bpp PNG files to 8bpp while still preserving complex alpha channels, allowing for file size reductions of up to 75%. Pngnq provides superior results over other tools by using a neural network to reduce image colors, unlike the brute-force median cut algorithm used by software like pngquant. See here for a gallery of sample images if you’re curious.

Pngnq-s9 is a further improvement over stock pngnq, including cool features like YUV color space matching for better results, and the ability to preserve alpha values of 0 and 255. When saving 32bpp PNG files to 8bpp, PhotoDemon will now lean on pngnq-s9 to do the heavy lifting.

In the next version of PhotoDemon, pngnq-s9 support will be integrated into the batch process wizard as a new “optimize for web” option. For now, if you want to test out the feature, head to Tools -> Options -> Saving, and change the “set outgoing color depth” option to “ask me what color depth I want to use”. Then save a 32bpp PNG image to 8bpp and compare the file size.

New plugin manager and plugin downloader

Sometimes it makes sense for PhotoDemon to use an existing open-source project instead of me writing a new feature from scratch. These support libraries are included as “plugins”, and there are four of them in current version. Each one provides indispensable features (like scanner support) at a fraction of the cost involved to write such a feature from scratch.

Some of these plugins expose additional functionality, but it has always been a challenge for PhotoDemon to expose these additional features to the user. So the program now has a detailed plugin manager, where advanced users can change settings on a per-plugin basis, including activating or deactivating plugins as necessary. The manager also tracks availability and version numbers of each plugin.

It is now much, much easier for the program to keep its plugins up-to-date.  Advanced users may also find it useful to enable or disable plugins while testing various features.  All changes happen in real-time - no restart required.
It is now much, much easier for the program to keep its plugins up-to-date. Advanced users may also find it useful to enable or disable plugins while testing various features. All changes happen in real-time – no restart required.
The pngnq-s9 page of the plugin manager.  Advanced or esoteric plugin features can be adjusted here, which keeps the program's main preferences dialog uncluttered.
The pngnq-s9 page of the plugin manager. Advanced or esoteric plugin features can be adjusted here, which helps keep the main “Options” dialog uncluttered.

Many canvas and interface improvements

Larger effect and tool previews. Persistent zoom-in/zoom-out buttons. Image URLs and files can now be directly pasted as new images. Improved drag/drop support, including drag/drop from common dialogs. New “Safe” save behavior to avoid overwriting original files. New Close All Images menu. New algorithms for auto-zoom when images are loaded, meaning much better results at all screen sizes. Tool and file panels can now be hidden. Higher-quality dynamic icons for the program, taskbar, child windows, and Recent Images list. Improved support for low screen resolutions.

Program-wide performance improvements

More aggressive memory management means lower resource usage. Program loading has been heavily streamlined, and now happens in less than a second on modern hardware. Image loading is much faster and more robust, including better support for damaged or incomplete image files.

More robust and comprehensive error handling

When loading multiple images, the program will now suppress warnings and failures (such as invalid files) until all images have been loaded. Many subclassing issues have been resolved – so no more surprise crashes! Overall this release should be extremely stable.

Many miscellaneous bug fixes and improvements

This article is already way too long, so I won’t bore you with a list of all the minor fixes and improvements. For a full list, see the commit log at https://github.com/tannerhelland/PhotoDemon/commits/master

In Conclusion…

This release was a lot bigger than I’d like future releases to be. The biggest delay came from adding language support, as that affected every piece of text in every part of the program (nearly 10,000 words in total!). Now that language support is complete, I foresee future releases being much tidier and quicker.

A developer’s work is never done, and a roadmap for version 5.6 is already being worked on. Some features that didn’t make the cut for 5.4 – like improvements to the selection tool, or a “smart resize” option – were cut at the last minute, and they will be among the first features added to 5.6. The batch process wizard will see a number of additions, and I’d love to add some advanced multilanguage features, like a way for casual users to fix or adjust translations on-the-fly. I also think I’m finally ready to tackle the monumental task of writing a user manual… should be fun!

As always, the best way to stay abreast of PhotoDemon development is the official code repository at https://github.com/tannerhelland/PhotoDemon

But for now, I hope you enjoy all the new features in 5.4, and please remember to donate if you find the software useful.

PhotoDemon 5.4 Beta Now Available

  1. Summary
  2. Download
  3. List of what’s new and improved
  4. Known bugs

Summary

PhotoDemon 5.4 is nearing completion, and I need help testing it. Version 5.4 provides a bunch of new features, including French, German, and Dutch (Flemish) language support. If you can help translate PhotoDemon into another language, please let me know! The translation process is very simple, and it requires no programming experience or special software.

Version 5.4 also includes nine new distort tools, tons of new file format features including specialized PNG and JPEG optimization, improved memory management, a new plugin manager, real-time Gaussian, Smart, and Box blur tools with variable radius, a full Unsharp Mask tool, vignetting, median filtering, adding film grain, automatic cropping, contour tracing, a new Batch Wizard, redesigned tool interfaces, and more. Please download the beta and let me know if you find any bugs.

Download

The PhotoDemon 5.4 beta comes in two flavors:

Remember – if you are an advanced user, you can always download the most recent development build of PhotoDemon’s source code from its GitHub page.

PhotoDemon is funded by donations from users like you.
Please consider a small donation to fund development and to help me support my family.
Even $1.00 helps. Thank you!

List of what’s new and improved in v5.4 (so far)

  • Official support for multiple languages. This is the biggest addition in version 5.4, and I can only claim partial credit for it. Primary credit goes to Frank Donckers, a fellow VB programmer and the one who prototyped the initial translation engine. Frank also supplied the translations for French, German, and Dutch (Flemish), so I owe him an enormous debt of gratitude.
  • Vastly improved file format support. JPEGs now support automatic EXIF rotation on import, and a variety of options on export (Huffman table optimization, progressive scan, thumbnail embedding, specific subsampling). TIFF exporting supports CMYK encoding and a number of compression schemes (none, PackBits, LZW, CCITT 3 and 4, zLib, and more). PNG exporting supports variable compression strength, interlacing, and background color chunk. PPM exporting supports RAW or ASCII encoding. BMP and TGA now support RLE encoding. For ICO files, all icons inside the file can now be loaded (instead of just the first one).
  • Nine new Distort-style tools. Add and remove lens distortion. Swirl. Ripple. Pinch and whirl. Waves. Kaleidoscope. Polar conversion (both directions). Figured glass (dents).
  • New and improved standard tools, including Box Blur, Gaussian Blur, Smart Blur, and Unsharp Masking. Each of these functions now supports variable radii (up to hundreds of pixels), and all have been heavily optimized. Gaussian Blur is the fastest VB-only true gaussian ever written. (Not a joke.)
  • Tons of new tools, including Film Grain, Color Balance, Vignetting, Autocrop, Median, Modern Art, Trace Contour, Shadow/Midtone/Highlight, Monochrome -> Grayscale conversion, Film Noir, and Comic Book. All tools include real-time previews. A number of existing tools received big updates as well – particularly Gamma Correction, Dilate, Erode, Monochrome Conversion, and Printing.
  • New Batch Process wizard. This replaces the old Batch Convert tool, which was an interface nightmare. The new tool supports a number of new features, including drag/drop support of batch lists, live image previews, and tons of file renaming options (prefix, suffix, case conversion, removing text, conversion of spaces to underscores for web).
  • Universal color depth support at import and export time. PhotoDemon can now write 1, 4, 8, 24, and 32bpp variations of every supported file format. Color depth detection is automatic at save time – the program will count the number of colors in an image and automatically save to the most appropriate color depth. Alternatively, you can set a preference to manually specify color depth at save time. This also works for grayscale images; for example, the JPEG encoder will now detect grayscale images and write out 8bpp JPEGs accordingly. Alpha thresholding is also available when saving 32bpp images to 8bpp (e.g. PNG to GIF).
  • New pngnq-s9 plugin for optimizing PNG files. Pngnq-s9 is an optimized and feature-rich variant of the original pngnq optimization library. Pngnq-s9 works by converting 32bpp PNG files to 8bpp with a heavily optimized palette, including support for variable alpha channels. File size savings of over 50% are common. See the Options -> Plugin Manager -> pngnq-s9 menu for a full list of tunable parameters.
  • New plugin manager and plugin downloader. Plugins can now be individually enabled/disabled, and missing plugins can be automatically downloaded. All plugin installation and activation/deactivation can be applied without a program restart.
  • Many canvas and interface improvements. Larger effect and tool previews. Persistent zoom-in/zoom-out buttons. Image URLs and files can now be directly pasted as new images. Improved drag/drop support, including drag/drop from common dialogs. New “Safe” save behavior to avoid overwriting original files. New Close All Images menu. New algorithms for auto-zoom when images are loaded, meaning much better results at all screen sizes. Tool and file panels can now be hidden. Higher-quality dynamic icons for the program, taskbar, child windows, and Recent Images list. Improved support for low screen resolutions.
  • Many performance improvements. More aggressive memory management means lower resource usage. Program loading has been heavily streamlined, and now happens in less than a second on modern hardware. Image loading is much faster and more robust, including better support for damaged or incomplete image files.
  • Much more robust and comprehensive error handling. When loading multiple images, the program will now suppress warnings and failures (such as invalid files) until all images have been loaded. Many subclassing issues have been resolved – so no more surprise crashes! Overall this release should be extremely stable.
  • Many miscellaneous bug fixes and improvements. For a full list, see the commit log at https://github.com/tannerhelland/PhotoDemon/commits/master

Known bugs

Here is a list of known bugs with the current beta. These bugs will be fixed before the final release.

  • When a new language is selected, some text may not be translated. This is not a problem with the translation engine – it is a problem with the translation files, which are still being finalized. All text will be translated in the final release.
  • When using a language other than English, some text may overflow its boundaries or disappear off the page. This is a known problem that is still being worked on. All text – in any language – should fit properly in the final release.

Hooking modern Windows common dialogs: some notes

Background: common dialog hooking is used to append your own controls to a Windows common dialog box.

A good friend recently sent me a number of resources related to hooking common dialog controls. I’ve been interested in common dialog hooking for PhotoDemon, as it would allow me to add support for image previewing right in the dialog itself. This isn’t as necessary in modern versions of Windows (7 in particular includes a number of GDI+ improvements, making Explorer very robust with standard formats), but it can be helpful for unsupported formats like RAW photographs.

Unfortunately, several days of research have shown that it is not possible to hook a Vista or Windows 7 style dialog and maintain the modern layout. Let me explain with pictures:

This is the standard common dialog control in Windows Vista and 7 (and presumably 8 as well, though I haven't verified this myself).  The biggest improvements over past common dialogs include breadcrumb navigation at the top, a dedicated refresh button, and a persistent search bar.
This is the standard common dialog control in Windows Vista and 7 (and presumably 8 as well, though I haven’t verified this myself). The biggest improvements over past common dialogs include a dedicated left-hand file tree, breadcrumb navigation at the top, and a persistent search bar.

The common dialog above comes directly from PhotoDemon’s current common dialog implementation. This is the native common dialog control on Vista and 7. I very much like it. As a comparison, here is the same dialog in Windows XP:

Same folder as the previous image.  The XP common dialog provides no breadcrumb nav, folder tree, dedicated refresh, or search bar.  Note also how many TIFF formats do not display correctly - including XP not recognizing the MINISWHITE flag on the CCITT TIFF files.  Kinda interesting.
Same folder and images as the previous image. There is no breadcrumb nav, dedicated refresh, or search bar. Note also how many TIFF formats do not display correctly – including XP not recognizing the MINISWHITE flag on the CCITT TIFF files. Kinda interesting.

I strongly prefer the Vista/7-style dialog, particularly the breadcrumb nav and the persistent folder tree on the left.

Unfortunately, it is impossible to hook the Vista/7 dialog and maintain the native Vista/7 appearance. If you attempt to hook it, Windows will ALWAYS drop back to a previous generation common dialog. Here are some images to demonstrate, using a basic image preview hook:

This is what happens when you attempt to hook a dialog, and all you provide is the OFN_ENABLEHOOK flag.  Not pretty (and the hook doesn't even work right).
This is what happens when you attempt to hook a dialog, and all you provide is the OFN_ENABLEHOOK flag. Not pretty (and the hook doesn’t even work correctly).
Here is the same dialog, but with the OFN_EXPLORER flag set.  Note that hooking now works properly, but the common dialog itself has been reduced to XP style - the left-hand folder tree is gone, and the top bar has no breadcrumbs or search.
Here is the same dialog, but with the OFN_EXPLORER flag set. Note that hooking now works properly, but the common dialog itself has been reduced to XP style – the left-hand folder tree is gone, and the top bar has no breadcrumbs or search.

Note that the image above uses an older version of the OPENFILENAME struct, namely (this is its declaration in VB):

Private Type OPENFILENAME
    lStructSize       As Long
    hwndOwner         As Long
    hInstance         As Long
    lpstrFilter       As String
    lpstrCustomFilter As String
    nMaxCustFilter    As Long
    nFilterIndex      As Long
    lpstrFile         As String
    nMaxFile          As Long
    lpstrFileTitle    As String
    nMaxFileTitle     As Long
    lpstrInitialDir   As String
    lpstrTitle        As String
    Flags             As Long
    nFileOffset       As Integer
    nFileExtension    As Integer
    lpstrDefExt       As String
    lCustData         As Long
    lpfnHook          As Long
    lpTemplateName    As String
End Type

If you modify the struct to its newest version (as described here), you can slightly improve the dialog to look like this:

The newest version of the OPENFILENAME struct enables the places bar on the left.  I don't find this particularly useful - certainly not as useful as a folder pane - but perhaps some might find it preferable.
The newest version of the OPENFILENAME struct enables the places bar on the left. I don’t find this particularly useful – certainly not as useful as a folder pane – but perhaps some might find it preferable.

That is the best you can get if you want to hook a common dialog in Vista or 7.

Reasons for this have been speculated on by more qualified individuals than I. Over at stackoverflow, David H explains:

The reason for this is that MS completely re-organised the file dialogs for Vista. Hooks are used to extend a file dialog by supplying a resource file. This gives the customiser too much power. They can all too easily modify standard elements of the dialog and indeed many apps did so. The reorganisation of the dialogs would have broken many apps that used hooks. Those would have tried to manipulate elements of the dialog that were not there, or were implemented differently. Legacy versions of the dialogs remain for such apps to “get their hooks into”.

You are correct that it is impossible to get the new look when you use a hook. Instead you need to use the IFileDialogCustomize interface to customise the dialog. This is less powerful but does result in appearance and behaviour that is more consistent with the standard part of the dialog.

(More information is available here for those who are interested.)

Unfortunately, I am not aware of any way to access the iFileDialogCustomize interface in classic VB. If someone knows how, I’d love to hear it.

The take home message of all this is – if you work in classic VB and you want to hook a common dialog, you need to be content with an XP-style dialog. There is currently no way to maintain a Vista/7 style dialog while hooking.

For this reason, I’m going to stick with the stock common dialog control in PhotoDemon. I may look at a dedicated “browse” window in the future, which would allow for full image previewing, but I’m afraid such a feature is not on the roadmap for the next few versions.

All hooking-related screenshots were created using Carles PV’s iBMP project, which made it very easy to modify various hooking parameters and test the output. Thanks, Carles!

Image Dithering: Eleven Algorithms and Source Code

Dithering: An Overview

Today’s graphics programming topic – dithering – is one I receive a lot of emails about, which some may find surprising. You might think that dithering is something programmers shouldn’t have to deal with in 2012. Doesn’t dithering belong in the annals of technology history, a relic of times when “16 million color displays” were something programmers and users could only dream of? In an age when cheap mobile phones operate in full 32bpp glory, why am I writing an article about dithering?

Actually, dithering is still a surprisingly applicable technique, not just for practical reasons (such as preparing a full-color image for output on a non-color printer), but for artistic reasons as well. Dithering also has applications in web design, where it is a useful technique for reducing images with high color counts to lower color counts, reducing file size (and bandwidth) without harming quality. It also has uses when reducing 48 or 64bpp RAW-format digital photos to 24bpp RGB for editing.

And these are just image dithering uses – dithering still has extremely crucial roles to play in audio, but I’m afraid I won’t be discussing audio dithering here. Just image dithering.

In this article, I’m going to focus on three things:

  • a basic discussion of how image dithering works
  • eleven specific two-dimensional dithering formulas, including famous ones like “Floyd-Steinberg”
  • how to write a general-purpose dithering engine

Update 11 June 2016: some of the sample images in this article have been updated to better reflect the various dithering algorithms. Thank you to commenters who noted problems with the previous images!

Dithering: Some Examples

Consider the following full-color image, a wallpaper of the famous “companion cube” from Portal:

This will be our demonstration image for this article.  I chose it because it has a nice mixture of soft gradients and hard edges.
This will be our demonstration image for this article. I chose it because it has a nice mixture of soft gradients and hard edges.

On a modern LCD or LED screen – be it your computer monitor, smartphone, or TV – this full-color image can be displayed without any problems. But consider an older PC, one that only supports a limited palette. If we attempt to display the image on such a PC, it might look something like this:

This is the same image as above, but restricted to a websafe palette.
This is the same image as above, but restricted to a websafe palette.

Pretty nasty, isn’t it? Consider an even more dramatic example, where we want to print the cube image on a black-and-white printer. Then we’re left with something like this:

At this point, the image is barely recognizable.
At this point, the image is barely recognizable.

Problems arise any time an image is displayed on a device that supports less colors than the image contains. Subtle gradients in the original image may be replaced with blobs of uniform color, and depending on the restrictions of the device, the original image may become unrecognizable.

Dithering is an attempt to solve this problem. Dithering works by approximating unavailable colors with available colors, by mixing and matching available colors in a way that mimicks unavailable ones. As an example, here is the cube image once again reduced to the colors of a theoretical old PC – only this time, dithering has been applied:

A big improvement over the non-dithered version!
A big improvement over the non-dithered version!

If you look closely, you can see that this image uses the same colors as its non-dithered counterpart – but those few colors are arranged in a way that makes it seem like many more colors are present.

As another example, here is a black-and-white version of the image with similar dithering applied:

The specific algorithm used on this image is "2-row Sierra" dithering.
The specific algorithm used on this image is “2-row Sierra” dithering.

Despite only black and white being used, we can still make out the shape of the cube, right down to the hearts on either side. Dithering is an extremely powerful technique, and it can be used in ANY situation where data has to be represented at a lower resolution than it was originally created for. This article will focus specifically on images, but the same techniques can be applied to any 2-dimensional data (or 1-dimensional data, which is even simpler!).

The Basic Concept Behind Dithering

Boiled down to its simplest form, dithering is fundamentally about error diffusion.

Error diffusion works as follows: let’s pretend to reduce a grayscale photograph to black and white, so we can print it on a printer that only supports pure black (ink) or pure white (no ink). The first pixel in the image is dark gray, with a value of 96 on a scale from 0 to 255, with zero being pure black and 255 being pure white.

Here is an example of the RGB values in the example.
Here is a visualization of the RGB values in our example.

When converting such a pixel to black or white, we use a simple formula – is the color value closer to 0 (black) or 255 (white)? 96 is closer to 0 than to 255, so we make the pixel black.

At this point, a standard approach would simply move to the next pixel and perform the same comparison. But a problem arises if we have a bunch of “96 gray” pixels – they all get turned to black, and we’re left with a huge chunk of empty black pixels, which doesn’t represent the original gray color very well at all.

Error diffusion takes a smarter approach to the problem. As you might have inferred, error diffusion works by “diffusing” – or spreading – the error of each calculation to neighboring pixels. If it finds a pixel of 96 gray, it too determines that 96 is closer to 0 than to 255 – and so it makes the pixel black. But then the algorithm makes note of the “error” in its conversion – specifically, that the gray pixel we have forced to black was actually 96 steps away from black.

When it moves to the next pixel, the error diffusion algorithm adds the error of the previous pixel to the current pixel. If the next pixel is also 96 gray, instead of simply forcing that to black as well, the algorithm adds the error of 96 from the previous pixel. This results in a value of 192, which is actually closer to 255 – and thus closer to white! So it makes this particular pixel white, and it again makes note of the error – in this case, the error is -63, because 192 is 63 less than 255, which is the value this pixel was forced to.

As the algorithm proceeds, the “diffused error” results in an alternating pattern of black and white pixels, which does a pretty good job of mimicking the “96 gray” of the section – much better just forcing the color to black over and over again. Typically, when we finish processing a line of the image, we discard the error value we’ve been tracking and start over again at an error of “0” with the next line of the image.

Here is an example of the cube image from above with this exact algorithm applied – specifically, each pixel is converted to black or white, the error of the conversion is noted, and it is passed to the next pixel on the right:

This is the simplest possible application of error diffusion dithering.
This is the simplest possible application of error diffusion dithering.

Unfortunately, error diffusion dithering has problems of its own. For better or worse, dithering always leads to a spotted or stippled appearance. This is an inevitable side-effect of working with a small number of available colors – those colors are going to be repeated over and over again, because there are only so many of them.

In the simple error diffusion example above, another problem is evident – if you have a block of very similar colors, and you only push the error to the right, all the “dots” end up in the same place! This leads to funny lines of dots, which is nearly as distracting as the original, non-dithered version.

The problem is that we’re only using a one-dimensional error diffusion. By only pushing the error in one direction (right), we don’t distribute it very well. Since an image has two dimensions – horizontal and vertical – why not push the error in multiple directions? This will spread it out more evenly, which in turn will avoid the funny “lines of speckles” seen in the error diffusion example above.

Two-Dimensional Error Diffusion Dithering

There are many ways to diffuse an error in two dimensions. For example, we can spread the error to one or more pixels on the right, one or more pixels on the left, one or more pixels up, and one or more pixels down.

For simplicity of computation, all standard dithering formulas push the error forward, never backward. If you loop through an image one pixel at a time, starting at the top-left and moving right, you never want to push errors backward (e.g. left and/or up). The reason for this is obvious – if you push the error backward, you have to revisit pixels you’ve already processed, which leads to more errors being pushed backward, and you end up with an infinite cycle of error diffusion.

So for standard loop behavior (starting at the top-left of the image and moving right), we only want to push pixels right and down.

Apologies for the crappy image - but I hope it helps illustrate the gist of proper error diffusion.
Apologies for the crappy image – but I hope it helps illustrate the gist of proper error diffusion.

As for how specifically to propagate the error, a great number of individuals smarter than I have tackled this problem head-on. Let me share their formulas with you.

(Note: these dithering formulas are available multiple places online, but the best, most comprehensive reference I have found is this one.)

Floyd-Steinberg Dithering

The first – and arguably most famous – 2D error diffusion formula was published by Robert Floyd and Louis Steinberg in 1976. It diffuses errors in the following pattern:


       X   7
   3   5   1

     (1/16)

In the notation above, “X” refers to the current pixel. The fraction at the bottom represents the divisor for the error. Said another way, the Floyd-Steinberg formula could be written as:


           X    7/16
   3/16  5/16   1/16

But that notation is long and messy, so I’ll stick with the original.

To use our original example of converting a pixel of value “96” to 0 (black) or 255 (white), if we force the pixel to black, the resulting error is 96. We then propagate that error to the surrounding pixels by dividing 96 by 16 ( = 6), then multiplying it by the appropriate values, e.g.:


           X     +42
   +18    +30    +6

By spreading the error to multiple pixels, each with a different value, we minimize any distracting bands of speckles like the original error diffusion example. Here is the cube image with Floyd-Steinberg dithering applied:

Floyd-Steinberg dithering
Floyd-Steinberg dithering

Not bad, eh?

Floyd-Steinberg dithering is easily the most well-known error diffusion algorithm. It provides reasonably good quality, while only requiring a single forward array (a one-dimensional array the width of the image, which stores the error values pushed to the next row). Additionally, because its divisor is 16, bit-shifting can be used in place of division – making it quite fast, even on old hardware.

As for the 1/3/5/7 values used to distribute the error – those were chosen specifically because they create an even checkerboard pattern for perfectly gray images. Clever!

One warning regarding “Floyd-Steinberg” dithering – some software may use other, simpler dithering formulas and call them “Floyd-Steinberg”, hoping people won’t know the difference. This excellent dithering article describes one such “False Floyd-Steinberg” algorithm:


   X   3
   3   2

   (1/8)

This simplification of the original Floyd-Steinberg algorithm not only produces markedly worse output – but it does so without any conceivable advantage in terms of speed (or memory, as a forward-array to store error values for the next line is still required).

But if you’re curious, here’s the cube image after a “False Floyd-Steinberg” application:

Much more speckling than the legit Floyd-Steinberg algorithm - so don't use this formula!
Much more speckling than the legit Floyd-Steinberg algorithm – so don’t use this formula!

Jarvis, Judice, and Ninke Dithering

In the same year that Floyd and Steinberg published their famous dithering algorithm, a lesser-known – but much more powerful – algorithm was also published. The Jarvis, Judice, and Ninke filter is significantly more complex than Floyd-Steinberg:


             X   7   5 
     3   5   7   5   3
     1   3   5   3   1

           (1/48)

With this algorithm, the error is distributed to three times as many pixels as in Floyd-Steinberg, leading to much smoother – and more subtle – output. Unfortunately, the divisor of 48 is not a power of two, so bit-shifting can no longer be used – but only values of 1/48, 3/48, 5/48, and 7/48 are used, so these values can each be calculated but once, then propagated multiple times for a small speed gain.

Another downside of the JJN filter is that it pushes the error down not just one row, but two rows. This means we have to keep two forward arrays – one for the next row, and another for the row after that. This was a problem at the time the algorithm was first published, but on modern PCs or smartphones this extra requirement makes no difference. Frankly, you may be better off using a single error array the size of the image, rather than erasing the two single-row arrays over and over again.

Jarvis, Judice, Ninke dithering
Jarvis, Judice, Ninke dithering

Stucki Dithering

Five years after Jarvis, Judice, and Ninke published their dithering formula, Peter Stucki published an adjusted version of it, with slight changes made to improve processing time:


             X   8   4 
     2   4   8   4   2
     1   2   4   2   1

           (1/42)

The divisor of 42 is still not a power of two, but all the error propagation values are – so once the error is divided by 42, bit-shifting can be used to derive the specific values to propagate.

For most images, there will be minimal difference between the output of Stucki and JJN algorithms, so Stucki is often used because of its slight speed increase.

Stucki dithering
Stucki dithering

Atkinson Dithering

During the mid-1980’s, dithering became increasingly popular as computer hardware advanced to support more powerful video drivers and displays. One of the best dithering algorithms from this era was developed by Bill Atkinson, a Apple employee who worked on everything from MacPaint (which he wrote from scratch for the original Macintosh) to HyperCard and QuickDraw.

Atkinson’s formula is a bit different from others in this list, because it only propagates a fraction of the error instead of the full amount. This technique is sometimes offered by modern graphics applications as a “reduced color bleed” option. By only propagating part of the error, speckling is reduced, but contiguous dark or bright sections of an image may become washed out.


         X   1   1 
     1   1   1
         1

       (1/8)

Atkinson dithering
Atkinson dithering

Burkes Dithering

Seven years after Stucki published his improvement to Jarvis, Judice, Ninke dithering, Daniel Burkes suggested a further improvement:


             X   8   4 
     2   4   8   4   2

           (1/32)

Burkes’s suggestion was to drop the bottom row of Stucki’s matrix. Not only did this remove the need for two forward arrays, but it also resulted in a divisor that was once again a multiple of 2. This change meant that all math involved in the error calculation could be accomplished by simple bit-shifting, with only a minor hit to quality.

Burkes dithering
Burkes dithering

Sierra Dithering

The final three dithering algorithms come from Frankie Sierra, who published the following matrices in 1989 and 1990:


             X   5   3
     2   4   5   4   2
         2   3   2
           (1/32)


             X   4   3
     1   2   3   2   1
           (1/16)


         X   2
     1   1
       (1/4)

These three filters are commonly referred to as “Sierra”, “Two-Row Sierra”, and “Sierra Lite”. Their output on the sample cube image is as follows:

Sierra (sometimes called Sierra-3)
Sierra (sometimes called Sierra-3)
Two-row Sierra
Two-row Sierra
Sierra Lite
Sierra Lite

Other dithering considerations

If you compare the images above to the dithering results of another program, you may find slight differences. This is to be expected. There are a surprising number of variables that can affect the precise output of a dithering algorithm, including:

  • Integer or floating point tracking of errors. Integer-only methods lose some resolution due to quantization errors.
  • Color bleed reduction. Some software reduces the error by a set value – maybe 50% or 75% – to reduce the amount of “bleed” to neighboring pixels.
  • The threshold cut-off for black or white. 127 or 128 are common, but on some images it may be helpful to use other values.
  • For color images, how luminance is calculated can make a big difference. I use the HSL luminance formula ( [max(R,G,B) + min(R,G,B)] / 2). Others use ([r+g+b] / 3) or one of the ITU formulas. YUV or CIELAB will offer even better results.
  • Gamma correction or other pre-processing modifications. It is often beneficial to normalize an image before converting it to black and white, and whichever technique you use for this will obviously affect the output.
  • Loop direction. I’ve discussed a standard “left-to-right, top-to-bottom” approach, but some clever dithering algorithms will follow a serpentine path, where left-to-right directionality is reversed each line. This can reduce spots of uniform speckling and give a more varied appearance, but it’s more complicated to implement.

For the demonstration images in this article, I have not performed any pre-processing to the original image. All color matching is done in the RGB space with a cut-off of 127 (values <= 127 are set to 0). Loop direction is standard left-to-right, top-to-bottom.

Which specific techniques you may want to use will vary according to your programming language, processing constraints, and desired output.

I count 9 algorithms, but you promised 11! Where are the other two?

So far I’ve focused purely on error-diffusion dithering, because it offers better results than static, non-diffusion dithering.

But for sake of completeness, here are demonstrations of two standard “ordered dither” techniques. Ordered dithering leads to far more speckling (and worse results) than error-diffusion dithering, but they require no forward arrays and are very fast to apply. For more information on ordered dithering, check out the relevant Wikipedia article.

Ordered dither using a 4x4 Bayer matrix
Ordered dither using a 4×4 Bayer matrix
Ordered dither using an 8x8 Bayer matrix
Ordered dither using an 8×8 Bayer matrix

With these, the article has now covered a total of 11 different dithering algorithms.

Writing your own general-purpose dithering algorithm

Earlier this year, I wrote a fully functional, general-purpose dithering engine for PhotoDemon (an open-source photo editor). Rather than post the entirety of the code here, let me refer you to the relevant page on GitHub. The black and white conversion engine starts at line 350. If you have any questions about the code – which covers all the algorithms described on this page – please let me know and I’ll post additional explanations.

That engine works by allowing you to specify any dithering matrix in advance, just like the ones on this page. Then you hand that matrix over to the dithering engine and it takes care of the rest.

The engine is designed around monochrome conversion, but it could easily be modified to work on color palettes as well. The biggest difference with a color palette is that you must track separate errors for red, green, and blue, rather than a single luminance error. Otherwise, all the math is identical.

 

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Announcing PhotoDemon 5.2 – Selections, HSL, Rotation, HDR, and More

Summary

PhotoDemon v5.2 is now available. New features include selection tools, arbitrary rotation, HSL adjustments, CMYK support, new user preferences, multiple monitor support, and more. Download the update here.

PhotoDemon 5.2
Version 5.2 includes many new tools and features, including PhotoDemon’s first on-canvas tool – “Selections”.

New Feature: Selection Tool

Selections have been one of the top-requested PhotoDemon features since it first released, so I’m glad to finally be able to offer them. A lot of work went into making selections as user-friendly and powerful as possible.

Three render modes are provided. On-canvas resizing and moving are fully supported, as are adjustments by textbox (see screenshot above). Everything in the Color and Filter menus will operate on a selection if available, as well as the Edit -> Copy command.

(Note: as of this v5.2, selections are not yet tied into Undo/Redo, and selections will not be recorded as part of a Macro. These features will be added in the next release.)

New Feature: Crop to Selection

Finally!

New Feature: HSL Adjustments

PhotoShop and GIMP users should be happy about this tool.

New Feature: Arbitrary (Free) Rotation

Arbitrary rotation comes courtesy of the FreeImage library. A 3-shear method is used: very fast, very high quality.

New Feature: CMY/K Rechanneling

Both CMY and CMYK rechanneling are now available.

New Feature: Sepia (W3C formula)

Here’s the sepia version of the photo from the Rechannel screenshot. I still prefer PhotoDemon’s “Antique” filter for most photos, but this sepia formula (from the W3C spec) provides a pleasant, flat alternative.

New Feature: Preferences Dialog (rewritten from scratch)

Preferences, preferences, and more preferences. The old Preferences dialog was pretty lame, so it was due for an overhaul. Tons of new settings have been added, and they are now organized by category.

New preferences include:

Interface:

  • Render drop shadows between images and canvas (similar to Paint.NET)
  • Full or compact file paths for image windows and Recent File shortcuts
  • Improved font rendering on Vista, Windows 7, and Windows 8 (via Segoe UI)
  • Remember the main window’s location between sessions

Loading and Saving:

  • Tone map imported HDR and RAW images
  • Options for importing all frames or pages of multi-image files (animated GIFs, multipage TIFFs)

Tools:

  • Automatically clear selections after “Crop to Selection” is used

Transparency handling:

  • Pick your own transparency checkerboard colors
  • Pick from three transparency checkerboard sizes (4×4, 8×8, 16×16)
  • Allow PhotoDemon to automatically remove empty alpha channels from imported images

All preferences from v5.0 remain present, and there is now an option to reset all preferences to their default state – so experiment away!

New Feature: Recent File Previews (Vista, Windows 7, Windows 8 only)

Now that recent file previews are available, I honestly can’t use any software that *doesn’t* provide the feature. It makes locating the right file significantly easier – especially with digital camera filenames like IMG_0366.jpg.

New Feature: Multi-Image File Support (animated GIFs, multipage TIFFs)

PhotoDemon will now recognize when you try to load image files that are actually composed of multiple images. You are given the option to import every image, or just the first one (which is what most other software does). The default behavior can be changed in the Edit -> Preferences menu.

New Feature: Waaaay better transparency handling, including adding/removing alpha channels

It’s hard to overstate how much better transparency support is in v5.2 compared to v5.0. Images with alpha-channels are now rendered as alpha in all viewport, filter, and tool screens. When printing, saving as 24bpp, or copying to the clipboard, transparent images are automatically composited against a white background. As mentioned previously, user preferences have been added for transparency checkerboard color and sizes.

PhotoDemon also allows you to add or remove alpha channels entirely. Here’s an example of an image with an alpha channel, and the associated “Image Mode” setting:

Note how the top-level “Mode” icon has changed to match the current mode – this saves you from having to go to the sub-menu to check. I’m a big fan of small touches like this.

And here it is again, after clicking the “Mode -> Photo (RGB | 24bpp | no transparency)” option:

No more alpha!

Finally, PhotoDemon now validates all incoming alpha channels. If an image has a blank or irrelevant alpha channel, PhotoDemon will automatically remove it for you. This frees up RAM, improves performance, and leads to a much smaller file size upon saving. (Note: this feature can be disabled from the Edit -> Preferences menu if you want to maintain blank alpha channels for some reason.)

New Feature: Custom “Confirm Unsaved Image(s)” Prompt

This is the new “unsaved images” prompt in PhotoDemon. A preview is now provided – again, very important for digital photos with obscure names – and the options have been reworked to make them as crystal-clear as possible. Also handy is the “Repeat this action for all unsaved images” option, which will either save or not save all unsaved images per your request.

Improved Feature: Edge Detection

Edge detection now allows for on-black or on-white processing. Generally speaking, on-white is used for artistic purposes, while on-black is used for technical and research ones. (Thanks to Yvonne Strahovski, who appears in the sample image above.)

New Feature: Thermograph Filter

This Wikipedia article describes thermography in great detail. PhotoDemon’s thermography filter works by correlating luminance with heat, and analyzing the image accordingly. Here’s a sample, using a picture of the lovely Alison Brie, of Mad Men and Community fame:

New Feature: JPEG 2000 (JP2/J2K), Industrial Light and Magic (EXR), High-Dynamic Range (HDR) and Digital Fax (G3) image support

PhotoDemon now supports importing the four image types mentioned above, and it also supports JPEG 2000 exporting.

Other New and Improved Features:

  • Much faster resize operations, thanks to an updated FreeImage library (v3.15.4)
  • Multiple monitor support during screen captures (File -> Import -> Screen Capture)
  • Many miscellaneous interface improvements, including generally larger command buttons, text boxes, labels, and more uniform form layouts.
  • Many new and improved menu icons.
  • Heavily optimized viewport rendering. PhotoDemon now uses a triple-buffer rendering pipeline to speed up actions like zooming, scrolling, and using on-canvas tools like the new Selection Tool. Even when working with 32bpp images, all actions render in real-time.
  • Bilinear interpolation is now used during Isometric Conversion. This results in a much higher-quality transform. Hard edges are still left along the image border to make mask generation easy for game designers.
  • Vastly improved image previewing when importing from VB binary files.
  • Better text validation throughout the software. Invalid values are now handled much more elegantly.
  • More accelerator hotkey support, including changes to match Windows standards (such as Ctrl+Y for Redo, instead of the previous Ctrl+Alt+Z).
  • Update checks are now performed every ten days (instead of every time the program is run).
  • All extra program data – including plugins, preferences, saved filters and macros – have been moved to a single /Data subfolder. If you run PhotoDemon on your desktop, this should make things much cleaner for you.
  • PhotoDemon’s current and max memory usage is now displayed in the Preferences -> Advanced panel.
  • Tons of miscellaneous bug fixes, tweaks, and optimizations. For a full list of changes, visit https://github.com/tannerhelland/PhotoDemon/commits/master

In Conclusion…

Not bad for two months work, eh? I hope you enjoy all the new features in 5.2., and please remember to donate if you find the software useful!

How to use a scanner (or TWAIN-compatible digital camera) in VB6

Today’s project demonstrates how to implement full scanner support from within a VB6 project. As a bonus, it also provides support for TWAIN-compatible digital cameras.

Before we begin

Because VB6 does not include a native scanner library, some sort of third-party DLL is required for scanner access.  My preferred choice is the free, public-domain EZTW32 library, which I have happily used for many years.  (The first version of EZTW32 was released in 1994!)  This project uses the most recent version of EZTW32 at the time of this writing: v1.19, updated 2009.02.22.  You can check for a newer version of the library here.

There are two versions of the EZTW32 library: a free, public-domain library – called “classic” – and a more sophisticated, paid version – called “pro.”  This project utilizes only the classic version.  The paid version includes many advanced features, and if you are interested in anything beyond simply capturing images from a scanner, it may be worth a look.  A full description of the “pro” version’s feature set is available here.

While the EZTW32 library provides many ways of interacting with the scanner, this project will focus on the following:

Private Declare Function TWAIN_IsAvailable Lib "EZTW32.dll" () As Long
Private Declare Function TWAIN_SelectImageSource Lib "EZTW32.dll" (ByVal hwndApp As Long) As Long
Private Declare Function TWAIN_AcquireToFilename Lib "EZTW32.dll" (ByVal hwndApp As Long, _
 ByVal sFile As String) As Long

The sample project includes a copy of v1.19 of the EZTW32 library.  If you would like to download a newer version of the library, simply copy the new version of EZTW32.dll into the same directory as the executable file (or .VBP file).  It should work without a problem.

Bonus tip: how to load DLLs from any location

Because a 3rd-party library is required to access a scanner in VB6, it is useful to know how to load a DLL from any location.  By default, VB6 will attempt to load a DLL from the computer’s system folder.  This is not ideal when developing portable applications (e.g. applications that can be run without requiring an installer), because it requires the user to manually copy files into their system folder… and that’s bad for a variety of reasons (security, potential for mistakes, etc).  It is possible to load a DLL from other locations using the regsvr32 command, but I don’t like regsvr32 because it adds additional entries to the user’s system registry, and it must be re-run every time the project is moved to a new folder.

So the best solution, in my opinion, is to tell VB to expect the DLL to appear in the same folder as the project itself.  (Alternatively, a /plugins/ sub-directory could be used.)  We do this by adding the following code to the General Declarations section of the project:

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName _
 As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Dim hLib as Long

Then, in the Form_Load sub, add this code to determine the project directory and tell VB to load any EZTW32 functions from the DLL in that directory:

Dim ProgramPath as String
ProgramPath = App.Path
If Right(ProgramPath, 1) <> "\" Then ProgramPath = ProgramPath & "\"

hLib = LoadLibrary(ProgramPath & "EZTW32.dll")

Finally, add this to the Form_Unload sub to release the DLL when the program terminates:

FreeLibrary hLib

The sample project (available below) demonstrates this technique, so feel free to download that instead of copying-and-pasting the code from this page.

What this project demonstrates

Because this project is focused on the basics of using a scanner, it demonstrates only the following:

  • How to check if the system offers scanner support (e.g. is a scanner driver loaded?)
  • On systems with multiple scanners, allow the user to select which scanner they want to use
  • Load the scanner’s built-in software and allow the user to preview and scan an image
  • Send the scanned image to a temporary file, then load that temporary file into a VB picture box

All of the above also applies to TWAIN-compatible digital cameras, which the software treats just like a scanner.

Finally, as mentioned earlier, EZTW32 has a paid “pro” version that offers additional features. You can learn more about the “pro” version here.

Caveats

As you may have inferred from its title, EZTW32 relies on the TWAIN protocol for accessing a scanner. TWAIN is one of several ways to interact with a scanner; other common options include WIA (Windows Image Acquisition) on Windows, and SANE (Scanner Access Now Easy) on Linux.

TWAIN has some advantages and disadvantages compared to WIA and SANE. One of TWAIN’s unique characteristics is that it requires the scanner to provide its own user interface. The advantage of this approach is that on a given system, TWAIN-compatible programs all launch the same scanner user interface – the interface provided by the scanner itself. This is good for casual users, because regardless of whether they use this program or Photoshop or GIMP, the scanner interface will always be the exact same. The downside is that if you want to implement custom scanner features or options, TWAIN makes it difficult.

Canon scanner user interface
Because TWAIN relies upon the scanner to provide its own user interface, the user will see a different scan window depending on their scanner brand. My Canon scanner software looks like this.

Another advantage of TWAIN is its longevity. The TWAIN standard has been around since 1992, so pretty much every scanner made in the last 20 years will offer TWAIN drivers. By comparison, WIA didn’t exist until the year 2000, and it wasn’t until Windows Vista that most scanners offered WIA support.

One disadvantage of TWAIN is that Windows Vista and Windows 7 give WIA preferential treatment. If you set up a new scanner using the default Windows Hardware Wizard, it may only load WIA drivers – meaning you’ll need to hunt down the install CD that came with your scanner, or download the latest driver bundle from the scanner manufacturer’s website. I discovered this the hard way when testing this program with my Canon all-in-one printer/scanner/fax. If my program can’t find your scanner, download the free GIMP image software from this link and use the File -> Create… -> Scanner/Camera… option. If my program can’t find your scanner, and GIMP can’t find your scanner, you probably don’t have TWAIN drivers installed. If GIMP works but my program does not, send me a message and I’ll investigate further.

Download the sample project

My sample project is pretty minimalist:

Scanner project user interface
The sample project keeps things simple.

I tried to keep the code as small and simple as possible. Again, the latest version of the EZTW32 dll (v1.19) is included in the download. Future versions of the file should be backwards-compatible; simply replace the existing dll with a newer version.

 

DISCLAIMER: These download files are regularly scanned to ensure they remain free from malicious content. Unfortunately, some virus scanners will flag these .zip files as suspicious simply because they contain source code and/or executable files. I have submitted my projects to a number of companies in an attempt to rectify these false-positives. Some have been cooperative. Others have not. If your virus scanner alerts you regarding these files, please allow the file to be submitted for further analysis (if your program allows for that). This should help ensure that any false-positive warnings gradually disappear for all users.

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Seven grayscale conversion algorithms (with pseudocode and VB6 source code)

I have uploaded a great many image processing demonstrations over the years, but today’s project – grayscale conversion techniques – is actually the image processing technique that generates the most email queries for me.  I’m glad to finally have a place to send those queries!

Despite many requests for a grayscale demonstration, I have held off coding anything until I could really present something unique.  I don’t like adding projects to this site that offer nothing novel or interesting, and there are already hundreds of downloads – in every programming language – that demonstrate standard color-to-grayscale conversions.   So rather than add one more “here’s a grayscale algorithm” article, I have spent the past week collecting every known grayscale conversion routine.  To my knowledge, this is the only project on the Internet that presents seven unique grayscale conversion algorithms, and at least two of the algorithms – custom # of grayscale shades with and without dithering – were written from scratch for this very article.

So without further ado, here are seven unique ways to convert a full-color image to grayscale.  (Note: I highly recommend reading the full article so you understand how the various algorithms work and what their purposes might be, but if all you want is the source code, you’ll find it past all the pictures and just above the donation link.)

Grayscale – An Introduction

Black and white (or monochrome) photography dates back to the mid-19th century.  Despite the eventual introduction of color photography, monochromatic photography remains popular.  If anything, the digital revolution has actually increased the popularity of monochromatic photography because any digital camera is capable of taking black-and-white photographs (whereas analog cameras required the use of special monochromatic film).  Monochromatic photography is sometimes considered the “sculpture” variety of photographic art.  It tends to abstract the subject, allowing the photographer to focus on form and interpretation instead of simply reproducing reality.

Because the terminology black-and-white is imprecise – black-and-white photography actually consists of many shades of gray – this article will refer to such images as grayscale.

Several other technical terms will be used throughout my explanations.  The first is color space.  A color space is a way to visualize a shape or object that represents all available colors.  Different ways of representing color lead to different color spaces.  The RGB color space is represented as a cube, HSL can be a cylinder, cone, or bicone, YIQ and YPbPr have more abstract shapes.  This article will primarily reference the RGB and HSL color spaces.

I will also refer frequently to color channels.  Most digital images are comprised of three separate color channels: a red channel, a green channel, and a blue channel.  Layering these channels on top of each other creates a full-color image.  Different color models have different channels (sometimes the channels are colors, sometimes they are other values like lightness or saturation), but this article will primarily focus on RGB channels.

How all grayscale algorithms fundamentally work

All grayscale algorithms utilize the same basic three-step process:

  1. Get the red, green, and blue values of a pixel
  2. Use fancy math to turn those numbers into a single gray value
  3. Replace the original red, green, and blue values with the new gray value

When describing grayscale algorithms, I’m going to focus on step 2 – using math to turn color values into a grayscale value. So, when you see a formula like this:

Gray = (Red + Green + Blue) / 3

Recognize that the actual code to implement such an algorithm looks like:


For Each Pixel in Image {

   Red = Pixel.Red
   Green = Pixel.Green
   Blue = Pixel.Blue

   Gray = (Red + Green + Blue) / 3

   Pixel.Red = Gray
   Pixel.Green = Gray
   Pixel.Blue = Gray

}

On to the algorithms!

Sample Image:

Promo art for The Secret of Monkey Island: Special Edition, ©2009 LucasArts
This bright, colorful promo art for The Secret of Monkey Island: Special Edition will be used to demonstrate each of our seven unique grayscale algorithms.

Method 1 – Averaging (aka “quick and dirty”)

Grayscale - average method
Grayscale image generated from the formula: Average(Red, Green, Blue)

This method is the most boring, so let’s address it first.  “Averaging” is the most common grayscale conversion routine, and it works like this:

Gray = (Red + Green + Blue) / 3

Fast, simple – no wonder this is the go-to grayscale algorithm for rookie programmers.  This formula generates a reasonably nice grayscale equivalent, and its simplicity makes it easy to implement and optimize (look-up tables work quite well).  However, this formula is not without shortcomings – while fast and simple, it does a poor job of representing shades of gray relative to the way humans perceive luminosity (brightness).  For that, we need something a bit more complex.

Method 2 – Correcting for the human eye (sometimes called “luma” or “luminance,” though such terminology isn’t really accurate)

Grayscale generated using values related to cone density in the human eye
Grayscale generated using a formula similar to (Red * 0.3 + Green * 0.59 + Blue * 0.11)

It’s hard to tell a difference between this image and the one above, so let me provide one more example.  In the image below, method #1 or the “average method” covers the top half of the picture, while method #2 covers the bottom half:

Grayscale methods 1 and 2 compared
If you look closely, you can see a horizontal line running across the center of the image. The top half (the average method) is more washed-out than the bottom half. This is especially visible in the middle-left segment of the image, beneath the cheekbone of the background skull.

The difference between the two methods is even more pronounced when flipping between them at full-size, as you can do in the provided source code.  Now might be a good time to download my sample project (available at the bottom of this article) so you can compare the various algorithms side-by-side.

This second algorithm plays off the fact that cone density in the human eye is not uniform across colors.  Humans perceive green more strongly than red, and red more strongly than blue.  This makes sense from an evolutionary biology standpoint – much of the natural world appears in shades of green, so humans have evolved greater sensitivity to green light.  (Note: this is oversimplified, but accurate.)

Because humans do not perceive all colors equally, the “average method” of grayscale conversion is inaccurate.  Instead of treating red, green, and blue light equally, a good grayscale conversion will weight each color based on how the human eye perceives it.  A common formula in image processors (Photoshop, GIMP) is:

Gray = (Red * 0.3 + Green * 0.59 + Blue * 0.11)

Surprising to see such a large difference between the red, green, and blue coefficients, isn’t it?  This formula requires a bit of extra computation, but it results in a more dynamic grayscale image.  Again, downloading the sample program is the best way to appreciate this, so I recommend grabbing the code, experimenting with it, then returning to this article.

It’s worth noting that there is disagreement on the best formula for this type of grayscale conversion.  In my project, I have chosen to go with the original ITU-R recommendation (BT.709, specifically) which is the historical precedent.  This formula, sometimes called Luma, looks like this:

Gray = (Red * 0.2126 + Green * 0.7152 + Blue * 0.0722)

Some modern digital image and video formats use a different recommendation (BT.601), which calls for slightly different coefficients:

Gray = (Red * 0.299 + Green * 0.587 + Blue * 0.114)

A full discussion of which formula is “better” is beyond the scope of this article.  For further reading, I strongly suggest the work of Charles Poynton.  For 99% of programmers, the difference between these two formulas is irrelevant.  Both are perceptually preferable to the “average method” discussed at the top of this article.

Method 3 – Desaturation

Grayscale generated from a Desaturate algorithm
A desaturated image. Desaturating an image takes advantage of the ability to treat the (R, G, B) colorspace as a 3-dimensional cube. Desaturation approximates a luminance value for each pixel by choosing a corresponding point on the neutral axis of the cube.

Next on our list of methods is desaturation.

There are various ways to describe the color of a pixel.  Most programmers use the RGB color model, where each color is described by its red, green, and blue components.  While this is a nice way for a machine to describe color, the RGB color space can be difficult for humans to visualize.  If I tell you, “oh, I just bought a car.  Its color is RGB(122, 0, 255),” you probably can’t picture the color I’m describing.  If, however, I say, “I just bought a car.  It is a bright, vivid shade of violet,” you can probably picture the color in question.  (Note: this is a hypothetical example.  I do not drive a purple car.  :)

For this reason (among others), the HSL color space is sometimes used to describe colors.  HSL stands for hue, saturation, lightnessHue could be considered the name of the color – red, green, orange, yellow, etc.  Mathematically, hue is described as an angular dimension on the color wheel (range [0,360]), where pure red occurs at 0°, pure green at 120°, pure blue at 240°, then back to pure red at 360°.  Saturation describes how vivid a color is; a very vivid color has full saturation, while gray has no saturation.  Lightness describes the brightness of a color; white has full lightness, while black has zero lightness.

Desaturating an image works by converting an RGB triplet to an HSL triplet, then forcing the saturation to zero. Basically, this takes a color and converts it to its least-saturated variant.  The mathematics of this conversion are more complex than this article warrants, so I’ll simply provide the shortcut calculation.  A pixel can be desaturated by finding the midpoint between the maximum of (R, G, B) and the minimum of (R, G, B), like so:

Gray = ( Max(Red, Green, Blue) + Min(Red, Green, Blue) ) / 2

In terms of the RGB color space, desaturation forces each pixel to a point along the neutral axis running from (0, 0, 0) to (255, 255, 255).  If that makes no sense, take a moment to read this wikipedia article about the RGB color space.

Desaturation results in a flatter, softer grayscale image.  If you compare this desaturated sample to the human-eye-corrected sample (Method #2), you should notice a difference in the contrast of the image.  Method #2 seems more like an Ansel Adams photograph, while desaturation looks like the kind of grayscale photo you might take with a cheap point-and-shoot camera.  Of the three methods discussed thus far, desaturation results in the flattest (least contrast) and darkest overall image.

Method 4 – Decomposition (think of it as de-composition, e.g. not the biological process!)

Decomposition - Max Values
Decomposition using maximum values
Decomposition - Minimum Values
Decomposition using minimum values

Decomposing an image (sounds gross, doesn’t it?) could be considered a simpler form of desaturation.  To decompose an image, we force each pixel to the highest (maximum) or lowest (minimum) of its red, green, and blue values.  Note that this is done on a per-pixel basis – so if we are performing a maximum decompose and pixel #1 is RGB(255, 0, 0) while pixel #2 is RGB(0, 0, 64), we will set pixel #1 to 255 and pixel #2 to 64.  Decomposition only cares about which color value is highest or lowest – not which channel it comes from.

Maximum decomposition:

Gray = Max(Red, Green, Blue)

Minimum decomposition:

Gray = Min(Red, Green, Blue)

As you can imagine, a maximum decomposition provides a brighter grayscale image, while a minimum decomposition provides a darker one.

This method of grayscale reduction is typically used for artistic effect.

Method 5 – Single color channel

Grayscale - red channel only
Grayscale generated by using only red channel values.
Grayscale - green channel only
Grayscale generated by using only green channel values.
Grayscale - blue channel only
Grayscale generated by using only blue channel values.

Finally, we reach the fastest computational method for grayscale reduction – using data from a single color channel.  Unlike all the methods mentioned so far, this method requires no calcuations.  All it does is pick a single channel and make that the grayscale value, as in:

Gray = Red

…or:

Gray = Green

…or:

Gray = Blue

Believe it or not, this shitty algorithm is the one most digital cameras use for taking “grayscale” photos.  CCDs in digital cameras are comprised of a grid of red, green, and blue sensors, and rather than perform the necessary math to convert RGB values to gray ones, they simply grab a single channel (green, for the reasons mentioned in Method #2 – human eye correction) and call that the grayscale one.  For this reason, most photographers recommend against using your camera’s built-in grayscale option.  Instead, shoot everything in color and then perform the grayscale conversion later, using whatever method leads to the best result.

It is difficult to predict the results of this method of grayscale conversion.  As such, it is usually reserved for artistic effect.

Method 6 – Custom # of gray shades

Grayscale using only 4 shades
Grayscale using only 4 shades - black, dark gray, light gray, and white

Now it’s time for the fun algorithms.  Method #6, which I wrote from scratch for this project, allows the user to specify how many shades of gray the resulting image will use.  Any value between 2 and 256 is accepted; 2 results in a black-and-white image, while 256 gives you an image identical to Method #1 above.  This project only uses 8-bit color channels, but for 16 or 24-bit grayscale images (and their resulting 65,536 and 16,777,216 maximums) this code would work just fine.

The algorithm works by selecting X # of gray values, equally spread (inclusively) between zero luminance – black – and full luminance – white.  The above image uses four shades of gray.  Here is another example, using sixteen shades of gray:

Grayscale using 16 shades of gray
In this image, we use 16 shades of gray spanning from black to white

This grayscale algorithm is a bit more complex. It looks something like:


ConversionFactor = 255 / (NumberOfShades - 1)
AverageValue = (Red + Green + Blue) / 3
Gray = Integer((AverageValue / ConversionFactor) + 0.5) * ConversionFactor

Notes:
-NumberOfShades is a value between 2 and 256
-technically, any grayscale algorithm could be used to calculate AverageValue; it simply provides
 an initial gray value estimate
-the "+ 0.5" addition is an optional parameter that imitates rounding the value of an integer
 conversion; YMMV depending on which programming language you use, as some round automatically

I enjoy the artistic possibilities of this algorithm.  The attached source code renders all grayscale images in real-time, so for a better understanding of this algorithm, load up the sample code and rapidly scroll between different numbers of gray shades.

Method 7 - Custom # of gray shades with dithering (in this example, horizontal error-diffusion dithering)

Grayscale - four shades, dithered
This image also uses only four shades of gray (black, dark gray, light gray, white), but it adds full error-diffusion dithering support

Our final algorithm is perhaps the strangest one of all.  Like the previous method, it allows the user to specify any value in the [2,256] range, and the algorithm will automatically calculate the best spread of grayscale values for that range.  However, this algorithm also adds full dithering support.

What is dithering, you ask?  In image processing, dithering uses optical illusions to make an image look more colorful than than it actually is.  Dithering algorithms work by interspersing whatever colors are available into new patterns - ordered or random - that fool the human eye into perceiving more colors than are actually present.  If that makes no sense, take a look at this gallery of dithered images.

There are many different dithering algorithms.  The one I provide is one of the simpler error-diffusion mechanisms: a one-dimensional diffusion that bleeds color conversion errors from left to right.

If you look at the image above, you'll notice that only four colors are present - black, dark gray, light gray, and white - but because these colors are mixed together, from a distance this image looks much sharper than the four-color non-dithered image under Method #6.  Here is a side-by-side comparison:

Side-by-side of dithered and non-dithered 4-color grayscale images
The left side of the image is a 4-shade non-dithered image; the right side is a 4-shade image WITH dithering

When few colors are available, dithering preserves more nuances than a non-dithered image, but the trade-off is a "dirty," speckled look.  Some dithering algorithms are better than others; the one I've used falls somewhere in the middle, which is why I selected it.

As a final example, here is a 16-color grayscale image with full dithering, followed by a side-by-side comparison with the non-dithered version:

Grayscale image, 16 shades, dithered
Hard to believe only 16 shades of gray are used in this image, isn't it?
Grayscale, 16 shades, dithered vs non-dithered
As the number of shades of gray in an image increases, dithering artifacts become less and less noticeable. Can you tell which side of the image is dithered and which is not?

Because the code for this algorithm is fairly complex, I'm going to refer you to the download for details. Simply open the Grayscale.frm file in your text editor of choice, then find the drawGrayscaleCustomShadesDithered sub. It has all the gory details, with comments.

Conclusion

If you're reading this from a slow Internet connection, I apologize for the image-heavy nature of this article.  Unfortunately, the only way to really demonstrate all these grayscale techniques is by showing many examples!

The source code for this project, like all image processing code on this site, runs in real-time.  The GUI is simple and streamlined, automatically hiding and displaying relevant user-adjustable options as you click through the various algorithms:

GUI of the provided source code
GUI of the provided source code. The program also allows you to load your own images.

Each algorithm is provided as a stand-alone method, accepting a source and destination picturebox as parameters.  I designed it this way so you can grab whatever algorithms interest you and drop them straight into an existing project, without need for modification.

Comments and suggestions are welcome.  If you know of any interesting grayscale conversion algorithms I might have missed, please let me know.

(Fun fact: want to convert a grayscale image back to color?  If so, check out my real-time image colorization project.)

 

DISCLAIMER: These download files are regularly scanned to ensure they remain free from malicious content. Unfortunately, some virus scanners will flag these .zip files as suspicious simply because they contain source code and/or executable files. I have submitted my projects to a number of companies in an attempt to rectify these false-positives. Some have been cooperative. Others have not. If your virus scanner alerts you regarding these files, please allow the file to be submitted for further analysis (if your program allows for that). This should help ensure that any false-positive warnings gradually disappear for all users.

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Stained Glass Effect (using VB6 and GIMP)

I wanted to title this article “a novel method for matrix randomization using polygons and custom differential post-processing blending“… but that was a bit long, even for me.

Why such a complex title?

It all started with a strange idea I had today.  I was thinking of common ways to randomize image data (don’t ask why), and it struck me that the most common randomization method – varying RGB data of single pixels – is not the most interesting way to go about it.  Why not use lines, triangles, or other polygons to randomize an image?  How would that look?

To test my theory, I wrote a quick program that selects two random pixels in an image, averages their colors, then draws a line of that averaged color between the two points. When repeated over and over again, such an algorithm leads to some interesting effects…

I started with this God of War 3 image...
...and got this (100,000 iterations of lines with max length 42).
Here's the same image, but with lines of max length 85.

Kinda cool.  I’m not sure what to call this effect… although it looks “furry” to me.  Should we invent a new word – furrification?

Once I had lines working, my next curiosity involved polygons.  Here’s the same picture, but with triangle randomization:

Same parameters as the first line-based randomization above (100,000 iterations, 42 max length).
Same parameters as the second line-based image above (100,000 iterations, max length 85)

This is also a cool effect, especially when you watch it in action.  (The program refreshes the screen every 100 iterations.)

While I didn’t go to the trouble of implementing additional polygons, the code is primed and ready for it.  In fact, it would be trivial to draw polygons of any segment count.

Once I had my newly randomized images, I decided to pop into GIMP and do a bit of post-processing.  It was then that I realized this could be used to create pretty sweet stained glass images:

Sweet!

It’s trivial to create an image like this – simply open up your base image, then add a triangle randomized copy over the top as a new layer.  Set the layer mode to “difference” and bam: stained glass!

Same effect, but with a larger triangle size.

Other blending modes provide interesting effects – for example, multiply:

Same two images as the first stained glass example - just the blending mode has changed.

Anyway, I thought this was an interesting exploration in using a randomized copy of an image as an overlay.

 

DISCLAIMER: These download files are regularly scanned to ensure they remain free from malicious content. Unfortunately, some virus scanners will flag these .zip files as suspicious simply because they contain source code and/or executable files. I have submitted my projects to a number of companies in an attempt to rectify these false-positives. Some have been cooperative. Others have not. If your virus scanner alerts you regarding these files, please allow the file to be submitted for further analysis (if your program allows for that). This should help ensure that any false-positive warnings gradually disappear for all users.

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Generating Emboss / Engrave / Relief Filters (in VB6)

Bayonetta is quite possibly the best action game I've ever played - yes, even better than God of War III

I’ve had this code ready for months, but I couldn’t bring myself to post it until I added something more exciting than just “emboss” and “engrave.”

Today is that day!

First, let me mention what emboss/engrave actually do. These are two of the simplest image processing filters, and they can be efficiently implemented in pretty much any programming language. They both operate on the same principle – for each pixel, subtract the RGB values of one or more neighboring pixels in a particular direction. This leads to an image where low-contrast areas are all black, while high-contrast areas (edges) are varying colors of brighter intensity. Most emboss/engrave filters add 127 to the RGB values so that uniformly contrasted areas are gray. As a bonus feature, I’ve added a “color” option to this code, so you can emboss/engrave an image to any hue. In the Bayonetta example above, the left side of the picture is embossed to something around #81a3fe.

Relief is a variation on emboss/engrave, where the base color is not artificially generated but is based off the current pixel. This acts almost like a sharpen filter, but because the filter doesn’t operate in all directions, it lends the image a more artificial look. (Hence the “relief” moniker – on certain images, it looks like an image has been chipped out of stone and then painted.) Try it on a photo – they tend to work best!

 

DISCLAIMER: These download files are regularly scanned to ensure they remain free from malicious content. Unfortunately, some virus scanners will flag these .zip files as suspicious simply because they contain source code and/or executable files. I have submitted my projects to a number of companies in an attempt to rectify these false-positives. Some have been cooperative. Others have not. If your virus scanner alerts you regarding these files, please allow the file to be submitted for further analysis (if your program allows for that). This should help ensure that any false-positive warnings gradually disappear for all users.

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Drawing the Mandelbrot Set / Fractal (in VB6)

One possible view of the Mandelbrot set (generated with this project)

Today’s very cool project demonstrates a proof of concept implementation for rendering the famed Mandelbrot set (or “Mandelbrot fractal”) using VB6.  It’s a bit of a feat, since VB6 isn’t exactly optimized for recursion-heavy calculations…but you know me.  :)  I love making VB do things it was never meant to do!

I’m very happy with how the project turned out.  It may not be the fastest or prettiest or most accurate implementation (for example, at very high zoom-in you’ll start to see noticeable quality degradation), but I’ve deliberately kept the project as short and sweet as possible to maximize adaptability and learning potential.

To enhance the experience, I’ve added several user-controlled options to help you get a feel for how the rendering works.  First is the scroll-bar at the top of the screen, which allows you to preferentially set speed or accuracy as the program’s objective.  (For those familiar with how the Mandelbrot set works, the scroll bar controls the “escape time” of the recursive function.)

Another neat option is a click-and-drag feature that allows you to draw new rendering boundaries.  This makes it simple to zoom as deep as you want into the fractal.

Finally, rather than a drab grayscale rendering, I’ve chosen a more purplish gradient.  The code can be easily modified to favor another color if you so choose.

The code is reasonably optimized, so a complete view should render within several seconds on most modern hardware.  If speed is a concern, note that the white areas of the image take the longest to process – so minimizing the amount of white you select will maximize render speed.  Also, due to the inherent limits of the “Double” type in VB6, if you zoom in really, really far the program may lock up.  (Simply click the close button to force-terminate the project if this happens.)

The download link can be found below these sample images.

 

DISCLAIMER: These download files are regularly scanned to ensure they remain free from malicious content. Unfortunately, some virus scanners will flag these .zip files as suspicious simply because they contain source code and/or executable files. I have submitted my projects to a number of companies in an attempt to rectify these false-positives. Some have been cooperative. Others have not. If your virus scanner alerts you regarding these files, please allow the file to be submitted for further analysis (if your program allows for that). This should help ensure that any false-positive warnings gradually disappear for all users.

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!