Skip to content

Commit

Permalink
Genius workaround for loss of alpha during HALFTONE stretching
Browse files Browse the repository at this point in the history
Long story short, this fixes the problem with checkerboarding not
working on previews.  It also means that layers can be created from
existing layers at smaller sizes and HALFTONE stretching can be used
WITH preservation of alpha channels.

Genius, really.
  • Loading branch information
tannerhelland committed Sep 30, 2012
1 parent 8166dd0 commit 9e416e0
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 30 deletions.
75 changes: 59 additions & 16 deletions Classes/pdLayer.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ Attribute VB_Exposed = False
'PhotoDemon Image Layer class
'Copyright ©2011-2012 by Tanner Helland
'Created: 29/August/12
'Last updated: 13/September/12
'Last update: improved all comments to match the completed state of the class
'Last updated: 30/September/12
'Last update: added a workaround for resizing transparent images using HALFTONE mode
'
'This marvelous class is PhotoDemon's replacement for picture boxes (which it used to store images in previous versions).
' pdLayer is a powerful DIB class responsible for managing all image data in memory. Some of the benefits provided by
Expand Down Expand Up @@ -119,7 +119,7 @@ End Type

'Drawing API functions
Private Declare Function BitBlt Lib "gdi32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function StretchBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal dstX As Long, ByVal dstY As Long, ByVal dstWidth As Long, ByVal dstHeight As Long, ByVal hSrcDC As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal srcWidth As Long, ByVal srcHeight As Long, ByVal rastOp As Long) As Long
Private Declare Function StretchBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal dstX As Long, ByVal dstY As Long, ByVal dstWidth As Long, ByVal dstHeight As Long, ByVal hSrcDC As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal SrcWidth As Long, ByVal SrcHeight As Long, ByVal rastOp As Long) As Long
Private Declare Function SetStretchBltMode Lib "gdi32" (ByVal hDestDC As Long, ByVal nStretchMode As Long) As Long
Private Const STRETCHBLT_COLORONCOLOR As Long = 3
Private Const STRETCHBLT_HALFTONE As Long = 4
Expand Down Expand Up @@ -155,7 +155,7 @@ Private Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal x1 As Long,
Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long

'Convert a system color (such as "button face" or "inactive window") to a literal RGB value
Private Declare Function TranslateColor Lib "olepro32.dll" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As Long, col As Long) As Long
Private Declare Function TranslateColor Lib "OLEPRO32.DLL" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As Long, col As Long) As Long

'Clipboard interaction
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Expand Down Expand Up @@ -344,18 +344,61 @@ Public Function createFromExistingLayer(ByRef srcLayer As pdLayer, Optional ByVa

'If either dimension of the new image will be smaller than the source, request halftoning
If useHalftoning Then

If srcLayer.getLayerColorDepth = 32 Then
SetStretchBltMode layerDC, STRETCHBLT_COLORONCOLOR
Else
SetStretchBltMode layerDC, STRETCHBLT_HALFTONE
End If
SetStretchBltMode layerDC, STRETCHBLT_HALFTONE
Else
SetStretchBltMode layerDC, STRETCHBLT_COLORONCOLOR
End If

'Resize and copy the image data
StretchBlt layerDC, 0, 0, newWidth, newHeight, srcLayer.getLayerDC, 0, 0, srcLayer.getLayerWidth, srcLayer.getLayerHeight, vbSrcCopy

'Now comes a nasty hack; HALFTONE stretching does not preserve the alpha channel, but COLORONCOLOR does. So for 32bpp
' images, we now make a second copy of the original image using COLORONCOLOR - which means it contains valid alpha values.
' Then we copy those into our current image, effectively taking the best of both HALFTONE and COLORONCOLOR stretching.
If srcLayer.getLayerColorDepth = 32 Then

Dim hackLayer As pdLayer
Set hackLayer = New pdLayer
hackLayer.createBlank newWidth, newHeight, 32

SetStretchBltMode hackLayer.getLayerDC, STRETCHBLT_COLORONCOLOR
StretchBlt hackLayer.getLayerDC, 0, 0, newWidth, newHeight, srcLayer.getLayerDC, 0, 0, srcLayer.getLayerWidth, srcLayer.getLayerHeight, vbSrcCopy

'At this point, hacklayer contains transparency data roughly equivalent to our half-toned images. (Not perfect, but close.)
' We need to copy that data into our image, then release the hack layer.

'Start, as always, with a SafeArray
Dim iData() As Byte
Dim tmpSA As SAFEARRAY2D
prepInternalSafeArray tmpSA
CopyMemory ByVal VarPtrArray(iData()), VarPtr(tmpSA), 4

'Now make a second array that points to the hacked layer alpha data
Dim aData() As Byte
Dim srcSA As SAFEARRAY2D
prepExternalSafeArray hackLayer, srcSA
CopyMemory ByVal VarPtrArray(aData()), VarPtr(srcSA), 4

Dim x As Long, y As Long, QuickX As Long

'Now loop through the image, copying alpha values as we go
For x = 0 To layerWidth - 1
QuickX = x * 4
For y = 0 To layerHeight - 1
iData(QuickX + 3, y) = aData(QuickX + 3, y)
Next y
Next x

'Decommission both arrays
CopyMemory ByVal VarPtrArray(iData), 0&, 4
CopyMemory ByVal VarPtrArray(aData), 0&, 4

'Release the temporary layer
hackLayer.eraseLayer
Set hackLayer = Nothing

End If

createFromExistingLayer = True
Exit Function

Expand Down Expand Up @@ -564,20 +607,20 @@ Public Sub renderToPictureBox(ByRef dstPicture As PictureBox)
dstWidth = dstPicture.ScaleWidth
dstHeight = dstPicture.ScaleHeight

Dim srcWidth As Single, srcHeight As Single
srcWidth = layerWidth
srcHeight = layerHeight
Dim SrcWidth As Single, SrcHeight As Single
SrcWidth = layerWidth
SrcHeight = layerHeight

'Calculate the aspect ratio of this layer and the target picture box
Dim srcAspect As Single, dstAspect As Single
srcAspect = srcWidth / srcHeight
srcAspect = SrcWidth / SrcHeight
dstAspect = dstWidth / dstHeight

Dim dWidth As Long, dHeight As Long

If srcAspect > dstAspect Then
dWidth = dstWidth
dHeight = CSng(srcHeight / srcWidth) * dWidth + 0.5
dHeight = CSng(SrcHeight / SrcWidth) * dWidth + 0.5
PreviewY = CInt((dstHeight - dHeight) / 2)
PreviewX = 0
'PreviewWidth and PreviewHeight are only generated so that the "Print Preview" function works (it relies on those values
Expand All @@ -587,7 +630,7 @@ Public Sub renderToPictureBox(ByRef dstPicture As PictureBox)
StretchBlt dstPicture.hDC, 0, PreviewY, dWidth, dHeight, layerDC, 0, 0, layerWidth, layerHeight, vbSrcCopy
Else
dHeight = dstHeight
dWidth = CSng(srcWidth / srcHeight) * dHeight + 0.5
dWidth = CSng(SrcWidth / SrcHeight) * dHeight + 0.5
PreviewX = CInt((dstWidth - dWidth) / 2)
PreviewY = 0
PreviewWidth = dWidth
Expand Down
22 changes: 11 additions & 11 deletions Forms/VBP_FormRechannel.frm
Original file line number Diff line number Diff line change
Expand Up @@ -426,16 +426,16 @@ Private Sub CmdOK_Click()

Dim rechannelMethod As Long

If optChannel(0) Then rechannelMethod = 0
If optChannel(1) Then rechannelMethod = 1
If optChannel(2) Then rechannelMethod = 2
If optChannel(3) Then rechannelMethod = 3
If optChannel(4) Then rechannelMethod = 4
If optChannel(5) Then rechannelMethod = 5
If optChannel(6) Then rechannelMethod = 6
If optChannel(7) Then rechannelMethod = 7
If optChannel(8) Then rechannelMethod = 8
If optChannel(9) Then rechannelMethod = 9
If OptChannel(0) Then rechannelMethod = 0
If OptChannel(1) Then rechannelMethod = 1
If OptChannel(2) Then rechannelMethod = 2
If OptChannel(3) Then rechannelMethod = 3
If OptChannel(4) Then rechannelMethod = 4
If OptChannel(5) Then rechannelMethod = 5
If OptChannel(6) Then rechannelMethod = 6
If OptChannel(7) Then rechannelMethod = 7
If OptChannel(8) Then rechannelMethod = 8
If OptChannel(9) Then rechannelMethod = 9

Process Rechannel, rechannelMethod

Expand Down Expand Up @@ -553,7 +553,7 @@ Public Sub RechannelImage(ByVal rType As Byte, Optional ByVal toPreview As Boole
bK = Minimum(cK, mK, yK)

invBK = 1 - bK
If invBK = 0 Then invBK = 1
If invBK = 0 Then invBK = 0.0001

If rType = 6 Then
cK = ((cK - bK) / invBK) * 255
Expand Down
28 changes: 26 additions & 2 deletions Modules/VBP_DrawModule.bas
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,36 @@ Public Sub DrawPreviewImage(ByRef dstPicture As PictureBox, Optional ByVal useOt

Dim tmpLayer As pdLayer

'Start by calculating the aspect ratio of both the current image and the previewing picture box
Dim dstWidth As Single, dstHeight As Single
dstWidth = dstPicture.ScaleWidth
dstHeight = dstPicture.ScaleHeight

Dim SrcWidth As Single, SrcHeight As Single
SrcWidth = pdImages(CurrentImage).mainLayer.getLayerWidth
SrcHeight = pdImages(CurrentImage).mainLayer.getLayerHeight

Dim srcAspect As Single, dstAspect As Single
srcAspect = SrcWidth / SrcHeight
dstAspect = dstWidth / dstHeight

'Now, use that aspect ratio to determine a proper size for our temporary layer
Dim newWidth As Long, newHeight As Long

If srcAspect > dstAspect Then
newWidth = dstWidth
newHeight = CSng(SrcHeight / SrcWidth) * newWidth + 0.5
Else
newHeight = dstHeight
newWidth = CSng(SrcWidth / SrcHeight) * newHeight + 0.5
End If

'Normally this will draw a preview of FormMain.ActiveForm's relevant image. However, another picture source can be specified.
If useOtherPictureSrc = False Then

If pdImages(CurrentImage).mainLayer.getLayerColorDepth = 32 Then
Set tmpLayer = New pdLayer
tmpLayer.createFromExistingLayer pdImages(CurrentImage).mainLayer
tmpLayer.createFromExistingLayer pdImages(CurrentImage).mainLayer, newWidth, newHeight, True
If forceWhiteBackground Then tmpLayer.compositeBackgroundColor 255, 255, 255 Else tmpLayer.compositeBackgroundColor
tmpLayer.renderToPictureBox dstPicture
Else
Expand All @@ -34,7 +58,7 @@ Public Sub DrawPreviewImage(ByRef dstPicture As PictureBox, Optional ByVal useOt

If otherPictureSrc.getLayerColorDepth = 32 Then
Set tmpLayer = New pdLayer
tmpLayer.createFromExistingLayer otherPictureSrc
tmpLayer.createFromExistingLayer otherPictureSrc, newWidth, newHeight, True
If forceWhiteBackground Then tmpLayer.compositeBackgroundColor 255, 255, 255 Else tmpLayer.compositeBackgroundColor
tmpLayer.renderToPictureBox dstPicture
Else
Expand Down
2 changes: 1 addition & 1 deletion PhotoDemon.vbp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Description="PhotoDemon"
CompatibleMode="0"
MajorVer=5
MinorVer=1
RevisionVer=19
RevisionVer=20
AutoIncrementVer=1
ServerSupportFiles=0
VersionComments="�2000-2012 Tanner Helland - www.tannerhelland.com"
Expand Down

0 comments on commit 9e416e0

Please sign in to comment.