Switch branches/tags
Nothing to show
Commits on Sep 14, 2018
  1. Paint tools: SHIFT key now creates contiguous strokes (lines)

    tannerhelland committed Sep 14, 2018
    This feature is also provided by Photoshop, GIMP, etc.
    I hope this is a useful stand-in until a proper line/shape tool becomes available.
  2. New math helper functions

    tannerhelland committed Sep 14, 2018
    These are part of some in-progress attempts to optimize some area filters.  The area filter changes aren't quite ready for primetime, but I'm uploading these in case any math experts out there want to give 'em a quick review for correctness
Commits on Sep 5, 2018
  1. Fix critical error when setting TIFF options during batch process

    tannerhelland committed Sep 5, 2018
    Fixes #269.
    Many thanks to @shishi2017 for finding and reporting
Commits on Aug 9, 2018
  1. update contributors list

    tannerhelland committed Aug 9, 2018
    relates to #268
Commits on Aug 2, 2018
  1. Shadow/Highlight adjustments: fix bug when highlight and shadow radiu…

    tannerhelland committed Aug 2, 2018
    …s match...
    ...but the shadow adjustment is 0.  An optimization that skips generating a shadow reference DIB when the shadow adjustment is 0 was causing the highlight reference DIB to *also* be skipped (because its radius is the same as the shadow radius, so it attempts to re-use the shadow reference DIB  - which doesn't exist, because the shadow amount is 0!).
    This was a dumb oversight on my part.  Many thanks to @strnghrs for finding and reporting it.
    Fixes #268.
Commits on Aug 1, 2018
  1. FreeImage: update to 3.18 official

    tannerhelland committed Aug 1, 2018
    After 3.5 years, FreeImage has [finally released an official update](
    I've been custom-building PD's copy in the interim as it needed a host of important bug- and vuln-fixes, but now that an official release is out, I'm happy to ship the official version instead.  (This also seems to help with erroneous virus scanner results, as FreeImage is a common target for false-positives, and their "official" build gets more people self-reporting it as safe.)
  2. Fix potential crash when the Choose Theme dialog is canceled

    tannerhelland committed Aug 1, 2018
    The new UIImages cache will have already cleared its internal cache (it must in order to live-update the "choose theme" dialog properly), but it was only rebuilding its cache if OK was selected.  This subsequently breaks everything if CANCEL was selected, but a fix is simple - the cache needs to be forcibly rebuilt *regardless* of how the dialog is closed.
Commits on Jul 27, 2018
  1. Reduce unnecessary string copying

    tannerhelland committed Jul 27, 2018
    Way too many ByVal [x] as String instances in the project; this is a quick attempt to rectify some of the worst offenders.
  2. Metadata embedding: report more debug info

    tannerhelland committed Jul 27, 2018
    Also, use the temp folder for temporary XML metadata requests (rather than the target folder of the saved image).  This covers a case where metadata embedding fails for some reason, and a copy of the temporary XML file is left behind in an arbitrary location.
Commits on Jul 16, 2018
  1. Implement automatic spritesheets for caching some UI elements

    tannerhelland committed Jul 16, 2018
    At present, only the pdButtonToolbox control (used for toolbox buttons, primarily) is using this new UIImages cache.  I want to roll this change out slowly, as it potentially affects a ton of UI code, and it would be easy to accidentally break things.
    The new UIImages cache is basically an automatic spritesheet generator.  When you use it to load an image resource, it automatically adds the resource to a spritesheet, and instead of returning a bare DIB, it returns an index into the spritesheet.  You can then pass the index to a UIImages.PaintCachedImage() function which handles the messy details of converting indices into actual image coordinates, and painting them accordingly.
    On a cold startup, this cuts PD's GDI object count by a solid 10%, which is great!  Rolling this feature out to additional places will allow me to continue to reduce object usage, as necessary.  (The biggest gain will come from caching menu icons, but unfortunately, that requires us to go fully owner-drawn as the "built-in" menu icons feature in Windows require you to supply a unique DIB for each menu icon... ugh.)
    I believe I've properly covered all the weird circumstances that may lead to broken UI images, including live-changing the UI theme, but if you run into any oddities, please let me know.
    (Also included in this commit is an updated German language file from Roy K - thank you, Roy!)
Commits on Jul 13, 2018
  1. Fix competing window order between dropdown and tooltips

    tannerhelland committed Jul 13, 2018
    This was most apparent when raising the Typography tool's Font Selection dropdown (which is quite tall), and accidentally mousing over the color picker controls - because they now raise tooltips immediately, even when they don't have focus, they were competing with the dropdown for top-level window status.
    Several solutions are possible; I've opted for simply not showing tooltips on underlying controls when a dropdown is raised.
  2. Measure tool: new features, bug-fixes

    tannerhelland committed Jul 13, 2018
    - Measurements now provide an option to either 1) share measurements between images (so switching images does *not* change the active measurement), or 2) let each image have its own measurements
    - There is now a dedicated "rotate 90" button for easily re-orienting a given measurement
    - A bug with "straighten layer to match measurement angle" has been resolved
Commits on Jul 12, 2018
  1. New tool: Measure

    tannerhelland committed Jul 12, 2018
    Alongside adding rulers to PD, I've also wanted to add a non-destructive "measurement" tool.  This tool allows you to measure arbitrary things on an image, including distances and angles.
    I'm quite happy with PD's implementation (which borrows liberally from aspects of GIMP's Measure UI - I know, this isn't usually a good idea, but their on-canvas UI for this tool is actually quite good!).  Anchor points can be moved at will, and if you change the active measurement unit to something other than "pixels", the measurement tool will automatically be updated to match.
    You can also switch between images *without losing your current measurement*, which is very nice for aligning elements across images.
    Another idea I've borrowed from GIMP (who in turn borrowed it from Photoshop) is the addition of dedicated buttons for "straighten image/layer to this angle".  This works very well for things like straightening a horizon, as you can just draw a line across the horizon, then smack the "straighten" button to auto-orient the photo accordingly.  Unlike GIMP, however, PD's implementation is smart enough to recognize horizontal vs vertical edges, and it will automatically adjust its internal techniques to match whatever you're trying to align (woohoo).
    As part of this work, I've fixed some bugs in both the ruler UI and the straighten tool.  I also cleaned up some code organization issues related to UI tools; hence the large number of touched files.
Commits on Jul 10, 2018
  1. Rulers: fix display issue when undo-ing an action that changed image …

    tannerhelland committed Jul 10, 2018
    The rulers were not correctly syncing to the undone size change
  2. Image > Resize: new downscale-optimized functions available

    tannerhelland committed Jul 10, 2018
    Downsampling and upsampling images are actually completely different challenges, mathematically speaking.  Upsampling requires us to "invent" new pixels that don't exist in the original image, while downsampling requires us to "merge" multiple pixels from the original image into a new pixel.
    GDI+ is actually quite nice in this regard, as it provides two different sub-modes for both bilinear and bicubic filtering: a normal mode, and a pre-filtered mode that greatly improves the quality during extreme downsampling.  Tasks like downsampling a photo to icon or gravatar size are the largest beneficiaries of prefiltering, with a much sharper and clearer image when prefiltering is used.
    PhotoDemon now exposes these to the user as directly selectable options.  Additionally, bilinear and bicubic resample modes have received meaningful performance improvements.
    I have also simplified the UI on the resize dialog.  There is no longer a toggle for "friendly" vs "technical" resample modes - instead, technical names are always provided.  For beginners, a new "Automatic" option (selected by default) will take care of filter selection for you, so you don't have to mess with resampling at all.
Commits on Jul 6, 2018
  1. Metadata engine: overhaul internals to preserve floating-point resolu…

    tannerhelland committed Jul 6, 2018
    Relates to #260.
    PD exposes image resolution as floating-point in various dialogs (Image > Resize, Canvas Resize, Content-Aware Resize), but internally, a number of places cast those floating-point values to integers.  PhotoShop internally supports the notion of floating-point DPIs, so we may as well too.
    As of this commit, all of PD's internal DPI code is now safe for floating-point data.
    As part of this overhaul, I've modified the way we read DPI from files (again).  A hierarchy is now used for retrieving metadata:
    - If a Photoshop-specific DPI segment is found, we try to read it first.  We do this because PS only writes valid values to its own metadata segment; traditional segments - like EXIF or JFIF - may receive totally different values from what the user sees on-screen.
    - If an EXIF segment is found, we use it next.  EXIF has been the primary interop method for 20+ years now, and all photo editing software *should* support it.
    - ...But, if need be, we will also perform a final check for any format-specific resolution metadata.  In JPEGs this may be JFIF; in PNGs, a pHYs chunk.  It varies.  We're not picky about *where* it's found - as long as it looks like valid resolution data, we'll accept it.
    I think this hierarchy makes the most sense for modern images.  I always have mixed feelings about "playing nicely" with badly formatted Photoshop files, but because PD should "just work" for the average user, I think this is an okay trade-off.  It's always frustrating when Photoshop appears to save and load data correctly, but other programs don't - so even though we're not at fault, we'll try to work around Adobe's bullshit... again.
Commits on Jul 5, 2018
  1. Continued work on cairo wrapper

    tannerhelland committed Jul 5, 2018
    Scaling via cairo has proven to be a bust, with performance (at similar quality settings) coming in 2x slower than GDI+.  I may not be using cairo optimally, and its API is unpleasantly complex for simple tasks like "emulate StretchBlt", but that's okay - image resizing isn't meant to be its primary use-case.  Mostly I was just hoping for an easy performance win, but alas, GDI+ has proven to be the continuing winner in that regard.
Commits on Jul 3, 2018
  1. pd2D: simplify interface; standardize against GDI+

    tannerhelland committed Jul 3, 2018
    Originally, my plan was to make pd2D a convenient front-end to multiple different graphics libraries (e.g. GDI+, Cairo, Skia, Direct2D).  If I were 10 years younger and/or had 10x more free time, this would still be a noble goal... but there's just no way I'm going to find time for it in the immediate future.
    So instead of hampering pd2D with a bunch of backend-switching code that provides no immediate benefit, I've instead restructured it as a GDI+ only wrapper.  This lets me cut a bunch of crufty code, simplify the overall interface, and improve performance across the board.
    As part of this overhaul, the pd2DPainter class has been retired.  Instead, all painting functions can be found in a new PD2D module.  Functions like PD2D.DrawEllipse or PD2D.FillRoundRectangle should be largely self-explanatory.  Surfaces, pens, brushes, and other related objects still live within their own classes, but all paint functionality is now supplied by the master PD2D module.  (This wouldn't work with other backends, as you typically need a "context" that holds all rendering settings - but GDI+ is stateless, so the old design was overkill.)
    Obviously, a ton of files are affected by this change, so apologies for the large commit size.
    I'm also starting work on a "safe" class-based Cairo wrapper, similar to PD2D (or more appropriately, my LittleCMS wrapper, which maintains all handles like profiles and color transforms inside their own classes, to effectively make things leak-proof).  This will be an ongoing project with features added as I need 'em, so no promises on anything comprehensive, at least not any time soon!
Commits on Jun 27, 2018
  1. Formulas in edit boxes: insta-display tooltip with expression result

    tannerhelland committed Jun 27, 2018
    Since spin controls tend to be quite small horizontally, it's easy to overflow them when entering a formula or expression.  Non-numeric entries now auto-trigger a tooltip display, which shows both the entire entered formula, and the calculated result (if the formula is valid).
    Invalid entries (whether numeric or full formulas) also get an auto-displayed tooltip, one that is custom-tailored to the particular error.  For example, out-of-range problems display differently than "invalid formula" ones.  This should make it trivial for users to correct any problems as-they-go.
Commits on Jun 26, 2018
  1. Evaluate basic expressions in all edit boxes

    tannerhelland committed Jun 26, 2018
    Relates to #263.  Many thanks to @jpbro and @vbRichClient for making this possible.
    PhotoDemon will now attempt to evaluate mathematical expressions entered into any pdSpinner control.  (That's the control that looks like a text box, updown, and "reset" button crammed into one.  It's also a component of every slider control.)
    + - * / ^ and % (mod) are currently supported.  Other operators and/or functions can be added if people need 'em.  Parentheses and order of operations are respected.  Parentheses that are not paired correctly (e.g. an opening parenthesis without a closing one) are resolved automatically, by acting as if the missing symbol exists at the start or end of the expression (whichever is required).  This means that "2 * (3 + 4" evaluates identically to "2 * (3 + 4)", which is important so that PD can return evaluation results *as* the user is typing.
    Spinner controls will automatically update their .Value property as an expression is entered, but they will *not* update the edit box contents with the final result until the edit box loses focus.  In adjustment/effect windows, this allows you to see what the adjustment/effect looks like "as you type", without requiring you to switch away from the edit box to force an evaluation.
    Finally, the spinner's existing UI for invalid entries is still in place - specifically, a chunky red border is displayed if the current formula is un-evaluateable.  Many thanks to @jpbro for the "CanEvaluate" function that makes this possible!  (Similarly, expressions that evaluate to a value outside the range of the control are still caught and reported correctly, I believe.)
    Notes to self:
    - a nice additional enhancement would be a tooltip that automatically pops up and displays the expression followed by "=<result>", if the control detects that the user is entering a formula
Commits on Jun 25, 2018
  1. Add experimental Cairo build for certain 2D tasks

    tannerhelland committed Jun 25, 2018
    Building Cairo on Windows is... not for the faint of heart.  Getting a proper stdcall variant built was an ugly project, and I'm not sure I'll have the willpower to ever do this again.
    But!  Now that I've got a working build, I plan to start doing some perf testing against it.  My build is based off the latest stable cairo code (1.14.12) but anyone with enough gumption/instanity to build an stdcall variant (with name-mangling fixed) can plug in their own build without trouble.  This is required by the LGPL, of course.
     Existing cairo wrappers, like [Olaf Schmidt's popular build]( can also be used, provided it's renamed to "cairo.dll" or the declares in the Plugin_Cairo module are searched-and-replaced, as appropriate.  Note that standard cdecl builds would also work, but you'd need to use the usual VB6 workarounds (e.g. DispCallFunc).
    I don't currently have plans to replace much existing PD code with cairo implementations.  Rather, I'm interested in using it to accelerate some tasks that are problematic in pure VB (like layer masks and/or non-standard blend-modes).  Having a functional cairo build will also give me an easy way to verify things like GDI+'s behavior against other well-known graphics tools, and potentially accelerate areas where GDI+ suffers.
    Also note: originally I had planned to make pd2D versatile enough to wrap any arbitrary 2D library, but the vast differences in implementations make this difficult.  I could probably get GDI+ and Cairo built against a similar VB6 interface (by possibly hiding Cairo contexts inside pd2D surface?), but even this would take careful planning.  As such, I'll probably segregate GDI+ and Cairo behavior for now, and only switch on Cairo where it's particularly relevant.
    Note that cairo is currently disabled on XP due to inexplicable crashes (with both the VS 2017 and VS 2015 XP build tools), and given the low usage numbers of PD on XP, I'm unlikely to revisit this any time soon.  XP users will have to live with pure-VB6 fallbacks.
Commits on Jun 21, 2018
  1. pdAccelerator: add focus tracking to ensure modifer hotkey states...

    tannerhelland committed Jun 21, 2018
    ...are correctly captured when PD loses focus.  This is necessary to prevent system hotkeys like Alt+Tab from breaking PD's key state tracker.  (For example, re: Alt+Tab.  PD won't realize that the Alt key has been released, because it detected the Alt key being pressed, but then PD lost focus, which caused key tracking to be disabled - meaning we can't physically detect a subsequent Alt key release, at least until focus returns!)
    The new solution is to manually erase key state tracking when PD loses focus, and manually re-capture modifier keystate when PD gains focus.
    Many thanks to @jpbro for first reporting this issue.  See #267 for additional details and discussion.
    (Also, this fixes #267)
Commits on Jun 19, 2018
  1. Implement Tools > Create macro > From session history menu

    tannerhelland committed Jun 19, 2018
    Relates to #265.
    This is my first take on @jpbro's suggestion for an "apply history to some other image" feature.  I've placed it in a new Tools > Create Macro menu (alongside macro recording) as that seemed most intuitive to me, but I am open to feedback/other suggestions!
    The code was quick to piece together as it borrows liberally from the Undo History window.  Two listboxes are provided: one for the user to select a "first action" from the current session's history, and another to select the "last action".  All actions between "first" and "last" (inclusive) are then auto-converted to PD's macro format and handed off to the macro engine.
    In a rare bit of proper code reuse, the existing Macro engine has been reworked to accept external action lists, which it then writes using the same exporter as recorded macros.  I've used this to verify that whether recording a macro, or exporting it retroactively via this new tool, the resulting macro file is byte-for-byte identical.
    I wouldn't be surprised if many people actually prefer this tool to the old method of recording macros, as this plays much better with the typical "experimenting as you go" approach to photo editing.
    Many thanks to @jpbro for this suggestion.  I'm going to leave #265 open for a bit longer (pending feedback and some additional testing) but hopefully the bulk of the work is ready for end users.
Commits on Jun 18, 2018
  1. Undo engine: record all user behavior in macro-compatible format

    tannerhelland committed Jun 18, 2018
    Relates to #265.  Thank you to @jpbro for pointing me down this path.
    In the past, PD's Undo engine only stored a subset of information that the Macro engine stores.  (For example, Undo tracks operation names, so it can display them in the Undo History browser, but it doesn't track other action attributes, like "is a dialog associated with this action?")
    As of this commit, this is no longer the case.  PD's Undo engine now tracks user operations in a manner very similar to the Macro engine.  This actually results in cleaner code in many places (because we can pass around bare PD_ProcessCall structs instead of manually stripping out individual elements), and it's a prerequisite to creating macro data from arbitrary points in a given editing session.
    While I was here, I also caught and fixed a few minor bugs...
    - The "Modify selection" Undo menu text - used when the user makes a non-destructive setting change via something other than the canvas, e.g. toggling a selection tool dropdown - was not being captured by the translation engine.
    - Rectangular selections *without* feathering were sometimes creating two Undo stack entries instead of one, because selection dimensions were being cast to integer in some places but not others.  (So one action was being created for the floating-point coords, and a second one for the integer coords - ugh!)
    This overhaul required some fairly deep changes to the Undo/Redo engine tracks data, so please let me know if you run into any unexpected behavior.
  2. pdSpinner: manually yield for mouse events during press+hold input

    tannerhelland committed Jun 18, 2018
    Relates to #266.  Thank you to @Alric-Rahl for reporting.
  3. Update gitignore

    tannerhelland committed Jun 18, 2018
  4. Remove Git LFS support; rewrite history (again)

    tannerhelland committed Jun 18, 2018
    Apologies for any trouble this causes with forks, but Git LFS has proven to be an endless source of headaches.  Its integration with GitHub is remarkably poor (e.g. "Download as Zip" permanently breaks, commit titles get converted to descriptions, it is impossible to ever *remove* blobs from LFS storage), and the stringent bandwidth and space requirements are going to *cost* me money in the long run.  Long story short, if you're going to make the painful decision to forcibly rewrite your git history, there are better (and cheaper) ways to reduce repo space.
    As such, I'm reverting to a repo copy prior to the Git LFS conversion, then re-applying all subsequent commits in a single "giant" commit.  I realize this is inelegant but I don't know a better way to do it.
    Commits included here:
    - create proper issue templates
    - convert various in-app and documentation links to the new https version of
    - new update patcher built around a dedicated GH-pages site (instead of relying on raw binary blobs inside a repo)
    - new builds of some 3rd-party plugins
Commits on Jun 1, 2018
  1. Round-rectangle selection tool: fix behavior at small sizes

    tannerhelland committed Jun 1, 2018
    It's possible that other selection tools needs a similar fix - I'll investigate those soon.
Commits on May 31, 2018
  1. Image importer: overhaul TIFF loading

    tannerhelland committed May 31, 2018
    TIFFs are a nightmare in many different ways, and unfortunately for us, FreeImage is prone to a ton of weird issues with 'em.  I don't know if I should blame FreeImage or libTiff for this (or the TIFF spec/lack-of-spec in general), but since multipage TIFFs are currently our only real mechanism for multi-layer interop with other software, I need to improve stability.
    I've now rewritten PD's GDI+ loader to support multi-page parsing, with full support for things like per-page color-management, orientation flags, and CMYK handling.  GDI+ (at least on Win 10) has proven to be far more reliable *and* fast at handling TIFF data, so it's wins all around.
    Because of this, I've also rewritten PD's TIFF import strategy to first attempt loading via GDI+, and if that fails, fall back to FreeImage.  On XP, this is useful as GDI+ was underdeveloped back then.  It also gives us twice the coverage of esoteric TIFF formats, which is more than most software offers.
    A lot of files were touched as part of this rewrite, but the new GDI+ loader is much cleaner (even for other file formats), so I think it's a win all-around.
    As part of the work, I stripped out a bunch of superfluous translation segments, and consolidated some overwrought GDI+ interface bits.
  2. GDI+: new interface for property retrieval, prep for multipage support

    tannerhelland committed May 31, 2018
    FreeImage is finicky with multipage images, and sometimes it fails for unknown reasons.  As I move more and more away from it, it'd be nice to have a GDI+ fallback for multipage TIFF files as that's currently our only real layer-friendly mechanism for interop with other software.
    In this commit, the GDI+ load function has been reworked against proper property retrieval (our old interface was hackish and bad), and multipage TIFFs are now correctly identified and reported back to the calling function.  Actual implementation of multipage > layer conversion is still TODO
  3. Automatic updates: switch to Git-LFS-compatible download strategy

    tannerhelland committed May 31, 2018
    I wish I'd known about this option earlier.  Instead of hard-coding a link to the raw update data, I now link using the "?raw=true" parameter.  GitHub will automatically redirect that to the actual update file, wherever it may reside.
    This should significantly improve update download speeds for most users.