Skip to content

Commit 647927e

Browse files
committed
Batch process: add support for animated images
This has been on my to-do list ever since I tackled animation back in v8.0, but finally it's ready! You can now use PD's batch converter to process animated images, including tasks like mass-converting animated GIFs to APNG or animated WebP instead. Most of this is handled silently by PD. It automatically detects files with animation data and switches to animation import/export engines instead of static ones. The new major new UI element is that when you're exporting to a fixed destination format (for e.g. mass-converting from one file format to another), there is now a checkbox for auto-detecting animation files, and an associated button for setting fixed animation export parameters. This how you set things like WebP animation export quality for destination files. I also fixed a bunch of minor UI bugs and quirks in the batch process wizard while here. (The batch processor tends to get ignored during dev cycles, then mass tested-and-fixed just before release... so I'm actually ahead of the game this time? lol)
1 parent 1de515b commit 647927e

11 files changed

+352
-225
lines changed

Classes/cFileDialogVista.cls

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,10 @@ End Function
365365
Public Function DialogShow(ByVal ownerHwnd As Long, _
366366
Optional ByVal Mode As FileDialogModeEnum = FDLG_FILEOPEN, _
367367
Optional dialogTitle As String) As Long
368-
368+
369+
'Added by Tanner: notify the UI subsystem that an OS-owned dialog is active
370+
Interface.NotifySystemDialogState True
371+
369372
' The user-selected item(s) are passed via the Result property of this class
370373
' Results are in the form of an IShellItem object passed to you
371374
' There are a few IShell_[xxx] methods provided so that you can get
@@ -537,6 +540,9 @@ Public Function DialogShow(ByVal ownerHwnd As Long, _
537540
Call Me.Clear
538541
If pReturn Then Set m_Result = pvPointerToIUnknown(pReturn, True)
539542

543+
'Added by Tanner: notify the UI subsystem that an OS-owned dialog is inactive
544+
Interface.NotifySystemDialogState False
545+
540546
End Function
541547
'--------------------------------------------------------------------------------------------------
542548

Classes/cUnicodeBrowseFolders.cls

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ Private m_InitPath As String ' startup path for the dialog, either string or
113113

114114
Public Function ShowBrowseForFolder(ByVal ownerHwnd As Long, Optional releasePIDL As Boolean = True) As Boolean
115115

116+
'Added by Tanner: notify the UI subsystem that an OS-owned dialog is active
117+
Interface.NotifySystemDialogState True
118+
116119
' Depending on what flags you set, you may get a returned path and/or file name or nothing.
117120
' If the user makes a selection to a virtual path/object, you will not get a path returned.
118121

@@ -174,6 +177,9 @@ Public Function ShowBrowseForFolder(ByVal ownerHwnd As Long, Optional releasePID
174177
obif.lpfnCallback = 0&
175178
End If
176179

180+
'Added by Tanner: notify the UI subsystem that an OS-owned dialog is inactive
181+
Interface.NotifySystemDialogState False
182+
177183
End Function
178184

179185
' forces an error to occur if user cancels/closes dialog without selecting anything

Classes/pdTimerAnimation.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ End Function
7878
Friend Sub NotifyFrameCount(ByVal newFrameCount As Long)
7979

8080
m_FrameCount = newFrameCount
81-
ReDim m_FrameTimesMS(0 To m_FrameCount - 1) As Long
81+
If (m_FrameCount > 0) Then ReDim m_FrameTimesMS(0 To m_FrameCount - 1) As Long
8282

8383
'Reset frame time trackers (debug only)
8484
m_FramesDisplayed = 0

Classes/pdWebP.cls

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ End Function
618618
'Save a pdImage (with animation data) to an animated WebP file
619619
Friend Function SaveAnimatedWebP_ToFile(ByRef srcImage As pdImage, ByRef srcOptions As String, ByRef dstFile As String) As Boolean
620620

621-
Const FUNC_NAME As String = "SaveWebP_ToFile"
621+
Const FUNC_NAME As String = "SaveAnimatedWebP_ToFile"
622622
SaveAnimatedWebP_ToFile = False
623623

624624
'Basic input validation
@@ -630,6 +630,7 @@ Friend Function SaveAnimatedWebP_ToFile(ByRef srcImage As pdImage, ByRef srcOpti
630630
'Before doing anything else, make sure the source image has multiple layers. (If it doesn't,
631631
' we'll silently forward it to the single-frame saver.)
632632
If (srcImage.GetNumOfLayers < 2) Then
633+
PDDebug.LogAction "single frame file found - rerouting to static exporter"
633634
SaveAnimatedWebP_ToFile = Me.SaveWebP_ToFile(srcImage, srcOptions, dstFile)
634635
Exit Function
635636
End If
@@ -1471,7 +1472,7 @@ Private Sub InternalError(ByVal funcName As String, Optional ByRef errString As
14711472
If (libReturn <> VP8_STATUS_OK) Then
14721473
PDDebug.LogAction funcName & "returned error #" & libReturn & "(" & errString & ")", PDM_External_Lib
14731474
Else
1474-
PDDebug.LogAction funcName & " error:" & errString, PDM_External_Lib
1475+
PDDebug.LogAction funcName & "error: " & errString, PDM_External_Lib
14751476
End If
14761477
End Sub
14771478

Forms/File_BatchWizard.frm

Lines changed: 281 additions & 177 deletions
Large diffs are not rendered by default.

Forms/File_Export_AnimatedGIF.frm

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ Attribute VB_Exposed = False
185185
'Animated GIF export dialog
186186
'Copyright 2012-2021 by Tanner Helland
187187
'Created: 26/August/19
188-
'Last updated: 06/September/19
189-
'Last update: separate animated GIF and PNG export dialogs
188+
'Last updated: 28/October/21
189+
'Last update: prep dialog for compatibility with batch processor
190190
'
191191
'In v8.0, PhotoDemon gained the ability to export animated GIF files.
192192
'
@@ -282,7 +282,7 @@ Public Sub ShowDialog(Optional ByRef srcImage As pdImage = Nothing)
282282

283283
End If
284284

285-
'Next, prepare various controls on the metadata panel
285+
'Next, prepare various controls on the metadata panel (TODO)
286286
'mtdManager.SetParentImage m_SrcImage, PDIF_GIF
287287

288288
'Apply translations and visual themes
@@ -301,7 +301,10 @@ Public Sub ShowDialog(Optional ByRef srcImage As pdImage = Nothing)
301301
End Sub
302302

303303
Private Sub btnPlay_Click(Index As Integer, ByVal Shift As ShiftConstants)
304-
304+
305+
'Failsafe check for batch process mode (which won't supply a source image)
306+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
307+
305308
Select Case Index
306309

307310
'Play/pause
@@ -348,7 +351,7 @@ End Sub
348351
Private Sub cmdBar_OKClick()
349352
m_Timer.StopTimer
350353
m_FormatParamString = GetExportParamString
351-
'm_MetadataParamString = mtdManager.GetMetadataSettings
354+
'm_MetadataParamString = mtdManager.GetMetadataSettings (TODO)
352355
m_UserDialogAnswer = vbOK
353356
Me.Visible = False
354357
End Sub
@@ -362,7 +365,7 @@ Private Sub cmdBar_ReadCustomPresetData()
362365

363366
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
364367
' default to a "fixed" frame time suggestion
365-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
368+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
366369

367370
End Sub
368371

@@ -385,7 +388,7 @@ Private Sub cmdBar_ResetClick()
385388

386389
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
387390
' default to a "fixed" frame time suggestion
388-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
391+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
389392

390393
End Sub
391394

@@ -479,7 +482,9 @@ Private Sub UpdateAgainstCurrentTheme()
479482
End Sub
480483

481484
Private Sub m_Timer_DrawFrame(ByVal idxFrame As Long)
482-
485+
486+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
487+
483488
'Render the current frame
484489
RenderAnimationFrame
485490

@@ -494,7 +499,7 @@ End Sub
494499
' like frame delay times)
495500
Private Sub UpdateAnimationSettings()
496501

497-
If (m_SrcImage Is Nothing) Then Exit Sub
502+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
498503

499504
'Suspend automatic control-based updates while we get everything synchronized
500505
m_DoNotUpdate = True
@@ -588,6 +593,7 @@ Private Sub RenderAnimationFrame()
588593

589594
If m_DoNotUpdate Then Exit Sub
590595
If (m_AniFrame Is Nothing) Then Exit Sub
596+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
591597

592598
Dim idxFrame As Long
593599
idxFrame = m_Timer.GetCurrentFrame()

Forms/File_Export_AnimatedPNG.frm

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ Attribute VB_Exposed = False
164164
'Animated PNG export dialog
165165
'Copyright 2012-2021 by Tanner Helland
166166
'Created: 26/August/19
167-
'Last updated: 06/September/19
168-
'Last update: separate from original animated GIF export dialog, as the two exporters have different needs
167+
'Last updated: 28/October/21
168+
'Last update: prep dialog for compatibility with batch processor
169169
'
170170
'In v8.0, PhotoDemon gained the ability to export animated PNG files. This dialog exposes relevant
171171
' export parameters to the user.
@@ -272,6 +272,9 @@ End Sub
272272

273273
Private Sub btnPlay_Click(Index As Integer, ByVal Shift As ShiftConstants)
274274

275+
'Failsafe check for batch process mode (which won't supply a source image)
276+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
277+
275278
Select Case Index
276279

277280
'Play/pause
@@ -332,7 +335,7 @@ Private Sub cmdBar_ReadCustomPresetData()
332335

333336
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
334337
' default to a "fixed" frame time suggestion
335-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
338+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
336339

337340
End Sub
338341

@@ -355,7 +358,7 @@ Private Sub cmdBar_ResetClick()
355358

356359
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
357360
' default to a "fixed" frame time suggestion
358-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
361+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
359362

360363
End Sub
361364

@@ -440,7 +443,9 @@ Private Sub UpdateAgainstCurrentTheme()
440443
End Sub
441444

442445
Private Sub m_Timer_DrawFrame(ByVal idxFrame As Long)
443-
446+
447+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
448+
444449
'Render the current frame
445450
RenderAnimationFrame
446451

@@ -455,7 +460,7 @@ End Sub
455460
' like frame delay times)
456461
Private Sub UpdateAnimationSettings()
457462

458-
If (m_SrcImage Is Nothing) Then Exit Sub
463+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
459464

460465
'Suspend automatic control-based updates while we get everything synchronized
461466
m_DoNotUpdate = True
@@ -549,6 +554,7 @@ Private Sub RenderAnimationFrame()
549554

550555
If m_DoNotUpdate Then Exit Sub
551556
If (m_AniFrame Is Nothing) Then Exit Sub
557+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
552558

553559
Dim idxFrame As Long
554560
idxFrame = m_Timer.GetCurrentFrame()

Forms/File_Export_AnimatedWebP.frm

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ Attribute VB_Exposed = False
175175
'Animated WebP export dialog
176176
'Copyright 2019-2021 by Tanner Helland
177177
'Created: 26/August/19
178-
'Last updated: 30/September/21
179-
'Last update: wrap up feature support unique to WebP
178+
'Last updated: 28/October/21
179+
'Last update: prep dialog for compatibility with batch processor
180180
'
181181
'In v9.0, PhotoDemon gained the ability to export animated WebP files. This dialog exposes relevant
182182
' export parameters to the user.
@@ -293,6 +293,9 @@ End Sub
293293

294294
Private Sub btnPlay_Click(Index As Integer, ByVal Shift As ShiftConstants)
295295

296+
'Failsafe check for batch process mode (which won't supply a source image)
297+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
298+
296299
Select Case Index
297300

298301
'Play/pause
@@ -353,7 +356,7 @@ Private Sub cmdBar_ReadCustomPresetData()
353356

354357
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
355358
' default to a "fixed" frame time suggestion
356-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
359+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
357360

358361
End Sub
359362

@@ -376,7 +379,7 @@ Private Sub cmdBar_ResetClick()
376379

377380
'If all frames have undefined frame times (e.g. none embedded a frame time in the layer name),
378381
' default to a "fixed" frame time suggestion
379-
If m_FrameTimesUndefined Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
382+
If m_FrameTimesUndefined And (Not m_SrcImage Is Nothing) Then btsFrameTimes.ListIndex = 0 Else btsFrameTimes.ListIndex = 1
380383

381384
'WebP-specific values follow
382385
sldQuality.Value = 75
@@ -475,7 +478,9 @@ Private Sub UpdateAgainstCurrentTheme()
475478
End Sub
476479

477480
Private Sub m_Timer_DrawFrame(ByVal idxFrame As Long)
478-
481+
482+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
483+
479484
'Render the current frame
480485
RenderAnimationFrame
481486

@@ -490,7 +495,7 @@ End Sub
490495
' like frame delay times)
491496
Private Sub UpdateAnimationSettings()
492497

493-
If (m_SrcImage Is Nothing) Then Exit Sub
498+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
494499

495500
'Suspend automatic control-based updates while we get everything synchronized
496501
m_DoNotUpdate = True
@@ -590,6 +595,7 @@ Private Sub RenderAnimationFrame()
590595
If m_DoNotUpdate Then Exit Sub
591596
If (m_AniFrame Is Nothing) Then Exit Sub
592597
If (m_tmpAnimationFrame Is Nothing) Then Exit Sub
598+
If (m_FrameCount <= 0) Or (m_SrcImage Is Nothing) Then Exit Sub
593599

594600
Dim idxFrame As Long
595601
idxFrame = m_Timer.GetCurrentFrame()

Modules/Loading.bas

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,12 @@ Public Function LoadFileAsNewImage(ByRef srcFile As String, Optional ByVal sugge
307307

308308
PDDebug.LogAction "Finalizing image details..."
309309

310-
'The finalized pdImage object is finally worthy of being added to the master PD collection. Note that this function will
311-
' automatically update PDImages.GetActiveImageID() to point to the new image.
310+
'The finalized pdImage object is finally worthy of being added to the master PD collection.
311+
' (Note that this function will automatically update PDImages.GetActiveImageID() to point
312+
' at the new image.)
312313
PDImages.AddImageToMasterCollection targetImage
313314

315+
'The UI needs a *lot* of changes to reflect the state of the newly loaded image
314316
ImageImporter.ApplyPostLoadUIChanges srcFile, targetImage, addToRecentFiles
315317

316318
'Because ExifTool is sending us data in the background, we periodically yield for metadata piping.

Modules/Saving.bas

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ Public Function PhotoDemon_SaveImage(ByRef srcImage As pdImage, ByVal dstPath As
120120

121121
'After a successful dialog invocation, immediately save the metadata parameters to the parent pdImage object.
122122
' ExifTool will handle those settings separately, independent of the format-specific export engine.
123-
If Saving.GetExportParamsFromDialog(srcImage, saveFormat, saveParameters, metadataParameters) Then
123+
Dim useAnimationDialog As Boolean
124+
If (Not srcImage Is Nothing) Then useAnimationDialog = srcImage.IsAnimated Else useAnimationDialog = False
125+
If Saving.GetExportParamsFromDialog(srcImage, saveFormat, saveParameters, metadataParameters, useAnimationDialog) Then
124126
srcImage.ImgStorage.AddEntry "MetadataSettings", metadataParameters
125127

126128
'If the user cancels the dialog, exit immediately
@@ -325,7 +327,7 @@ End Sub
325327
' returned from the associated format-specific dialog.
326328
'
327329
'Returns: TRUE if dialog was closed via OK button; FALSE otherwise.
328-
Public Function GetExportParamsFromDialog(ByRef srcImage As pdImage, ByVal outputPDIF As PD_IMAGE_FORMAT, ByRef dstParamString As String, ByRef dstMetadataString As String) As Boolean
330+
Public Function GetExportParamsFromDialog(ByRef srcImage As pdImage, ByVal outputPDIF As PD_IMAGE_FORMAT, ByRef dstParamString As String, ByRef dstMetadataString As String, Optional ByVal displayAnimationVersion As Boolean = False) As Boolean
329331

330332
'As a failsafe, make sure the requested format even *has* an export dialog!
331333
If ImageFormats.IsExportDialogSupported(outputPDIF) Then
@@ -339,12 +341,8 @@ Public Function GetExportParamsFromDialog(ByRef srcImage As pdImage, ByVal outpu
339341
GetExportParamsFromDialog = (Dialogs.PromptBMPSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
340342

341343
Case PDIF_GIF
342-
If (Not srcImage Is Nothing) Then
343-
If srcImage.IsAnimated Then
344-
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedGIF(srcImage, dstParamString, dstMetadataString) = vbOK)
345-
Else
346-
GetExportParamsFromDialog = (Dialogs.PromptGIFSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
347-
End If
344+
If displayAnimationVersion Then
345+
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedGIF(srcImage, dstParamString, dstMetadataString) = vbOK)
348346
Else
349347
GetExportParamsFromDialog = (Dialogs.PromptGIFSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
350348
End If
@@ -362,12 +360,8 @@ Public Function GetExportParamsFromDialog(ByRef srcImage As pdImage, ByVal outpu
362360
GetExportParamsFromDialog = (Dialogs.PromptJXRSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
363361

364362
Case PDIF_PNG
365-
If (Not srcImage Is Nothing) Then
366-
If srcImage.IsAnimated Then
367-
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedPNG(srcImage, dstParamString, dstMetadataString) = vbOK)
368-
Else
369-
GetExportParamsFromDialog = (Dialogs.PromptPNGSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
370-
End If
363+
If displayAnimationVersion Then
364+
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedPNG(srcImage, dstParamString, dstMetadataString) = vbOK)
371365
Else
372366
GetExportParamsFromDialog = (Dialogs.PromptPNGSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
373367
End If
@@ -385,12 +379,8 @@ Public Function GetExportParamsFromDialog(ByRef srcImage As pdImage, ByVal outpu
385379
GetExportParamsFromDialog = (Dialogs.PromptTIFFSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
386380

387381
Case PDIF_WEBP
388-
If (Not srcImage Is Nothing) Then
389-
If srcImage.IsAnimated Then
390-
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedWebP(srcImage, dstParamString, dstMetadataString) = vbOK)
391-
Else
392-
GetExportParamsFromDialog = (Dialogs.PromptWebPSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
393-
End If
382+
If displayAnimationVersion Then
383+
GetExportParamsFromDialog = (Dialogs.PromptExportAnimatedWebP(srcImage, dstParamString, dstMetadataString) = vbOK)
394384
Else
395385
GetExportParamsFromDialog = (Dialogs.PromptWebPSettings(srcImage, dstParamString, dstMetadataString) = vbOK)
396386
End If

0 commit comments

Comments
 (0)