diff --git a/Classes/pdImage.cls b/Classes/pdImage.cls index 3901c059aa..c5d4d361e6 100644 --- a/Classes/pdImage.cls +++ b/Classes/pdImage.cls @@ -460,7 +460,7 @@ Public Sub moveLayerByIndex(ByVal srcLayerIndex As Long, ByVal moveLayerUp As Bo End Sub 'Used by various layer movement functions. Given two layer indices, swap them. -Private Sub swapTwoLayers(ByVal srcLayerID_1 As Long, ByVal srcLayerID_2 As Long) +Public Sub swapTwoLayers(ByVal srcLayerID_1 As Long, ByVal srcLayerID_2 As Long) 'Create a temporary reference to the first layer Dim tmpLayerRef As pdLayer @@ -1100,3 +1100,30 @@ Friend Sub fillRectForLayer(ByRef srcLayer As pdLayer, ByRef dstRect As RECT) End With End Sub + +'Get number of visible/hidden layers +Public Function getNumOfVisibleLayers() As Long + + Dim i As Long, visLayerCount As Long + visLayerCount = 0 + + For i = 0 To numOfLayers - 1 + If imgLayers(i).getLayerVisibility Then visLayerCount = visLayerCount + 1 + Next i + + getNumOfVisibleLayers = visLayerCount + +End Function + +Public Function getNumOfHiddenLayers() As Long + + Dim i As Long, hidLayerCount As Long + hidLayerCount = 0 + + For i = 0 To numOfLayers - 1 + If Not imgLayers(i).getLayerVisibility Then hidLayerCount = hidLayerCount + 1 + Next i + + getNumOfHiddenLayers = hidLayerCount + +End Function diff --git a/Forms/VBP_FormMain.frm b/Forms/VBP_FormMain.frm index de67153634..0c2f57d58e 100644 --- a/Forms/VBP_FormMain.frm +++ b/Forms/VBP_FormMain.frm @@ -1346,7 +1346,7 @@ Private Sub MnuLayer_Click(Index As Integer) 'Merge visible layers Case 12 - MsgBox "This action isn't implemented yet. I hope to get to it by the end of April 2014.", vbOKOnly + vbInformation + vbApplicationModal, "Under construction" + Process "Merge visible layers", , , 1 End Select diff --git a/Modules/VBP_LayerHandler.bas b/Modules/VBP_LayerHandler.bas index 763f0fe9fa..89a9ff5bb1 100644 --- a/Modules/VBP_LayerHandler.bas +++ b/Modules/VBP_LayerHandler.bas @@ -111,6 +111,9 @@ Public Sub setLayerVisibilityByIndex(ByVal dLayerIndex As Long, ByVal layerVisib 'Redraw the layer box, but note that thumbnails don't need to be re-cached toolbar_Layers.forceRedraw False + 'Synchronize the interface to the new image + syncInterfaceToCurrentImage + 'Redraw the viewport, but only if requested If alsoRedrawViewport Then ScrollViewport pdImages(g_CurrentImage), FormMain.mainCanvas(0) @@ -333,3 +336,63 @@ Public Sub flattenImage() ScrollViewport pdImages(g_CurrentImage), FormMain.mainCanvas(0) End Sub + +'Given a multi-layered image, merge all visible layers, while ignoring any hidden ones. Note that flattening does *not* +' remove alpha! It simply merges all visible layers. +Public Sub mergeVisibleLayers() + + 'If there's only one visible layer, this function should not be called - but just in case, exit in advance. + If pdImages(g_CurrentImage).getNumOfLayers = 1 Then Exit Sub + + 'SIf there's only one visible layer, this function should not be called - but just in case, exit in advance. + If pdImages(g_CurrentImage).getNumOfVisibleLayers = 1 Then Exit Sub + + 'By this point, we can assume there are at least two visible layers in the image. Rather than deal with the messiness + ' of finding the lowest base layer and gradually merging everything into it, we're going to just create a new blank + ' layer at the base of the image, then merge everything with it until finally all visible layers have been merged. + + 'Insert a new layer at the bottom of the layer stack. + pdImages(g_CurrentImage).createBlankLayer 0 + + 'Technically, the command above does not actually insert a new layer at the base of the image. Per convention, + ' it always inserts the requested layer at the spot one *above* the requested spot. To work around this, swap + ' our newly created layer with the layer at position 0. + pdImages(g_CurrentImage).swapTwoLayers 0, 1 + + 'Fill that new layer with a blank DIB at the dimensions of the image. + Dim tmpDIB As pdDIB + Set tmpDIB = New pdDIB + tmpDIB.createBlank pdImages(g_CurrentImage).Width, pdImages(g_CurrentImage).Height, 32, 0 + pdImages(g_CurrentImage).getLayerByIndex(0).CreateNewImageLayer tmpDIB, , g_Language.TranslateMessage("Merged layers") + + 'With that done, merging visible layers is actually not that hard. Loop through the layer collection, + ' merging visible layers with the base layer, until all visible layers have been merged. + Dim i As Long + For i = 1 To pdImages(g_CurrentImage).getNumOfLayers - 1 + + 'If this layer is visible, merge it with the base layer + If pdImages(g_CurrentImage).getLayerByIndex(i).getLayerVisibility Then + pdImages(g_CurrentImage).mergeTwoLayers pdImages(g_CurrentImage).getLayerByIndex(i), pdImages(g_CurrentImage).getLayerByIndex(0), True + End If + + Next i + + 'Now that our base layer contains the result of merging all visible layers, we can now delete all + ' other visible layers. + For i = pdImages(g_CurrentImage).getNumOfLayers - 1 To 1 Step -1 + If pdImages(g_CurrentImage).getLayerByIndex(i).getLayerVisibility Then + pdImages(g_CurrentImage).deleteLayerByIndex i + End If + Next i + + 'Mark the new merged layer as the active one. (This will also re-synchronize the interface against the new image.) + setActiveLayerByIndex 0, False + + 'Redraw the layer box, and note that thumbnails need to be re-cached + toolbar_Layers.forceRedraw True + + 'Redraw the viewport + ScrollViewport pdImages(g_CurrentImage), FormMain.mainCanvas(0) + +End Sub + diff --git a/Modules/VBP_MiscInterface.bas b/Modules/VBP_MiscInterface.bas index 89c01ae49f..4993851458 100644 --- a/Modules/VBP_MiscInterface.bas +++ b/Modules/VBP_MiscInterface.bas @@ -244,7 +244,8 @@ Public Sub syncInterfaceToCurrentImage() metaToggle tSelectionTransform, False End If - 'Update all layer menus; some will be disabled under certain circumstances + 'Update all layer menus; some will be disabled depending on just how many layers are available, how many layers + ' are visible, and other criteria. If pdImages(g_CurrentImage).getNumOfLayers > 0 Then 'If only one layer is present, a number of layer menu items (Delete, Flatten, Merge, Order) will be disabled. @@ -271,6 +272,13 @@ Public Sub syncInterfaceToCurrentImage() 'Delete If Not FormMain.MnuLayer(1).Enabled Then FormMain.MnuLayer(1).Enabled = True + + 'Delete hidden layers is only available if one or more layers are hidden + If pdImages(g_CurrentImage).getNumOfHiddenLayers > 0 Then + FormMain.MnuLayerDelete(1).Visible = False + Else + FormMain.MnuLayerDelete(1).Visible = True + End If 'Merge up/down are not available for layers at the top and bottom of the image If isLayerAllowedToMergeAdjacent(pdImages(g_CurrentImage).getActiveLayerIndex, False) <> -1 Then @@ -307,8 +315,12 @@ Public Sub syncInterfaceToCurrentImage() 'Flatten If Not FormMain.MnuLayer(11).Enabled Then FormMain.MnuLayer(11).Enabled = True - 'Merge visible - If Not FormMain.MnuLayer(12).Enabled Then FormMain.MnuLayer(12).Enabled = True + 'Merge visible is only available if two or more layers are visible + If pdImages(g_CurrentImage).getNumOfVisibleLayers > 1 Then + If Not FormMain.MnuLayer(12).Enabled Then FormMain.MnuLayer(12).Enabled = True + Else + FormMain.MnuLayer(12).Enabled = False + End If End If diff --git a/Modules/VBP_ProcessorModule.bas b/Modules/VBP_ProcessorModule.bas index 34c1d340a7..b03428a9d2 100644 --- a/Modules/VBP_ProcessorModule.bas +++ b/Modules/VBP_ProcessorModule.bas @@ -468,6 +468,9 @@ Public Sub Process(ByVal processID As String, Optional showDialog As Boolean = F Case "Flatten image" Layer_Handler.flattenImage + 'Merge visible layers + Case "Merge visible layers" + Layer_Handler.mergeVisibleLayers 'SELECTION FUNCTIONS ' Any action that operates on selections - creating them, moving them, erasing them, etc diff --git a/PhotoDemon.vbp b/PhotoDemon.vbp index d88645b2e2..475221fc7b 100644 --- a/PhotoDemon.vbp +++ b/PhotoDemon.vbp @@ -219,7 +219,7 @@ Description="PhotoDemon Photo Editor" CompatibleMode="0" MajorVer=6 MinorVer=3 -RevisionVer=113 +RevisionVer=114 AutoIncrementVer=1 ServerSupportFiles=0 VersionComments="©2000-2014 Tanner Helland - photodemon.org"