Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1627 lines (1188 sloc) 61.4 KB
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "pdPixelIterator"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'***************************************************************************
'PhotoDemon Pixel Iterator class
'Copyright 2015-2018 by Tanner Helland
'Created: 04/December/14
'Last updated: 07/June/17
'Last update: add support for generic byte-array traversal
'
'By the time the 7.0 release rolled around, a ridiculous number of PD effects were managing their own pixel iteration
' methods. Most of these were a variant of a "sliding window" implementation, where a running histogram is maintained
' for a given pixel region, and when moving to the next pixel, instead of recalculating the entire region from scratch,
' the histograms from the previous pixel are simply updated against the new region, typically by subtracting a line of
' pixels on the left or top, and adding a new line of pixels on the right or bottom.
'
'These methods tended to use a single copy+paste chunk of code that was pretty advanced, supporting serpentine scanning
' and a number of other neat features, but maintenance quickly became unwieldy because any improvements to the method
' required copy+pasting the changes across dozens of functions. Worse still, the function only supported rectangular
' regions, and a lot of region functions look more natural when a circle is used.
'
'So as part of the 7.0 release, this class was created. This class is designed to abstract away the messy duties of
' iterating per-pixel regions, while supporting a bunch more features than PD's old implementation. Because this class
' uses generic histograms, many functions can tap into it, without needing to modify the actual iteration code.
'
'Please note that this class has several dependencies throughout PD, including pdDIB, and some specialized enums
' (located in PD's central Public_Enums_and_Types module).
'
'All source code in this file is licensed under a modified BSD license. This means you may use the code in your own
' projects IF you provide attribution. For more information, please visit https://photodemon.org/license/
'
'***************************************************************************
Option Explicit
Private Declare Sub SafeArrayLock Lib "oleaut32" (ByVal ptrToSA As Long)
Private Declare Sub SafeArrayUnlock Lib "oleaut32" (ByVal ptrToSA As Long)
'Current (x, y) position of the iterator. Call InitializeIterator() to reset these to (0, 0).
Private m_X As Long, m_Y As Long
'Initial and final loop boundaries
Private m_InitX As Long, m_InitY As Long
Private m_FinalX As Long, m_FinalY As Long
'Shape of the current iterator, including boundary distance
Private m_WindowShape As PD_PIXEL_REGION_SHAPE
Private m_XBLeft As Long, m_XBRight As Long, m_XBSize As Long
Private m_YBTop As Long, m_YBBottom As Long, m_YBSize As Long
'Other relevant DIB properties
Private m_DibPointer As Long, m_dibHeight As Long, m_dibStride As Long, m_dibColorDepth As Long, m_PixelWidth As Long
'Some functions may choose to use a byte array target instead of a DIB. That's fine!
Private m_BytesSA2D As SafeArray2D, m_Bytes() As Byte
Private m_BytesSA1D As SafeArray1D, m_ByteLine() As Byte
Private m_ByteArrayPtr As Long, m_ArrayWidth As Long, m_ArrayHeight As Long
'Pixel array (alias only; the actual pixel bits are stored by the caller, and treated as read-only by this class).
' In some situations it is advantageous to address the pixels using a 1D array, as VB is significantly slower when
' addressing 2D pixel positions.
Private m_PixelSA2D As SafeArray2D, m_Pixels() As Byte
Private m_PixelSA1D As SafeArray1D, m_PixelLine() As Byte
'If the function wants alpha tracked, this will be set to TRUE.
' (NOTE: this tracker has been manually disabled, because no PD functions use it as present. Skipping it on inner
' loops provides a small performance boost - so if you *do* need alpha iterated in the future, you will need
' to uncomment various lines where this is implemented.)
Private m_AlphaSupport As Boolean
'This class currently supports two modes of operation: RGBA, and Luminance. While we could track both simultaneously,
' there is a performance penalty to this, so it's easier to simply track one or the other.
' (NOTE: this value is set by the respective LockTargetHistograms function)
Private m_HistogramMode As PD_PIXEL_ITERATOR_MODE
Private m_LuminanceMode As PD_LUMINANCE_MODE
'Histogram arrays. Managing these are a bit tricky, because these are simply "trick" wrappers against arrays
' provided by the caller.
Private m_RedSA As SafeArray1D, m_GreenSA As SafeArray1D, m_BlueSA As SafeArray1D, m_AlphaSA As SafeArray1D
Private m_Red() As Long, m_Green() As Long, m_Blue() As Long, m_Alpha() As Long
'Note that the caller can also use the pixel iterator in luminance mode. This relies on only a single destination
' luminance array, and dedicated movement/calculation functions are used.
Private m_LuminanceSA As SafeArray1D
Private m_Luminance() As Long
'Number of pixels in the histogram. This is required for things like median calculations.
Private m_NumOfPixels As Long
'Non-standard shapes require an intermediate image; we use this as our guide for how to process kernel edges.
Private m_ShapeImage As pdDIB, m_ReferenceMap() As Byte
'Non-standard shapes require us to generate custom boundary indices. These tell us where the kernel region ends
' in each of the forward/backward x/y directions.
Private m_XLeft() As Long, m_XRight() As Long, m_YTop() As Long, m_YBottom() As Long
'This function is the first one you need to call. It will initialize a bunch of internal bits against the target DIB,
' bits that are required prior to actually iterating through individual pixels.
'
'Returns: TRUE if successful. DO NOT PROCEED with pixel iterating if the function returns FALSE.
Friend Function InitializeIterator(ByRef targetDIB As pdDIB, ByVal xRadius As Long, ByVal yRadius As Long, Optional ByVal windowShape As PD_PIXEL_REGION_SHAPE = PDPRS_Rectangle) As Boolean
'Reset the iterator coordinates and pixel count
m_X = 0
m_Y = 0
m_NumOfPixels = 0
'Cache loop boundaries
m_InitX = 0
m_InitY = 0
m_FinalX = targetDIB.GetDIBWidth - 1
m_FinalY = targetDIB.GetDIBHeight - 1
'Cache shape and assumed bounds. (In the future, these bounds may change according to automated trim functions,
' but for now, they are hard-coded per the user's request.)
m_WindowShape = windowShape
If (xRadius < 1) Then xRadius = 1
If (yRadius < 1) Then yRadius = 1
m_XBLeft = xRadius
m_XBRight = xRadius
m_YBTop = yRadius
m_YBBottom = yRadius
'At certain sizes, some window shapes are unreliable. Force these to use the failsafe rectangular engine.
If (m_WindowShape = PDPRS_Circle) And ((xRadius <= 1) Or (yRadius <= 1)) Then m_WindowShape = PDPRS_Rectangle
'Apply some failsafe dimension testing to the incoming bounds
If (m_XBRight > (m_FinalX - m_InitX)) Then
m_XBRight = (m_FinalX - m_InitX)
m_XBLeft = m_XBRight
End If
If (m_YBBottom > (m_FinalY - m_InitY)) Then
m_YBBottom = (m_FinalY - m_InitY)
m_YBTop = m_YBBottom
End If
'Store the final kernel size
m_XBSize = m_XBLeft + m_XBRight + 1
m_YBSize = m_YBTop + m_YBBottom + 1
'Retrieve other relevant DIB properties
m_DibPointer = targetDIB.GetDIBPointer
m_dibHeight = targetDIB.GetDIBHeight
m_dibStride = targetDIB.GetDIBStride
m_dibColorDepth = targetDIB.GetDIBColorDepth
m_PixelWidth = m_dibColorDepth \ 8
'Set alpha to a default value, based on the source image's color depth
'm_AlphaSupport = (m_dibColorDepth = 32)
InitializeIterator = True
End Function
'Same idea as the previous function, except that a target byte array is used instead. (This can yield significantly
' better performance, but obviously it requires you to only need one histogram of data.)
'
'Returns: TRUE if successful. DO NOT PROCEED with pixel iterating if the function returns FALSE.
Friend Function InitializeIterator_ByteArray(ByRef srcByteArray() As Byte, ByVal arrayWidth As Long, ByVal arrayHeight As Long, ByVal xRadius As Long, ByVal yRadius As Long, Optional ByVal windowShape As PD_PIXEL_REGION_SHAPE = PDPRS_Rectangle) As Boolean
'Reset the iterator coordinates and pixel count
m_X = 0
m_Y = 0
m_NumOfPixels = 0
'Cache loop boundaries
m_InitX = 0
m_InitY = 0
m_FinalX = arrayWidth - 1
m_FinalY = arrayHeight - 1
'Cache shape and assumed bounds. (In the future, these bounds may change according to automated trim functions,
' but for now, they are hard-coded per the user's request.)
m_WindowShape = windowShape
If (xRadius < 1) Then xRadius = 1
If (yRadius < 1) Then yRadius = 1
m_XBLeft = xRadius
m_XBRight = xRadius
m_YBTop = yRadius
m_YBBottom = yRadius
'At certain sizes, some window shapes are unreliable. Force these to use the failsafe rectangular engine.
If (m_WindowShape = PDPRS_Circle) And ((xRadius <= 1) Or (yRadius <= 1)) Then m_WindowShape = PDPRS_Rectangle
'Apply some failsafe dimension testing to the incoming bounds
If (m_XBRight > (m_FinalX - m_InitX)) Then
m_XBRight = (m_FinalX - m_InitX)
m_XBLeft = m_XBRight
End If
If (m_YBBottom > (m_FinalY - m_InitY)) Then
m_YBBottom = (m_FinalY - m_InitY)
m_YBTop = m_YBBottom
End If
'Store the final kernel size
m_XBSize = m_XBLeft + m_XBRight + 1
m_YBSize = m_YBTop + m_YBBottom + 1
'Cache misc pointers and other items
m_ByteArrayPtr = VarPtr(srcByteArray(0, 0))
m_ArrayWidth = arrayWidth
m_ArrayHeight = arrayHeight
InitializeIterator_ByteArray = True
End Function
'After you've initialized the iterator, call this function to setup the initial pixel region. The caller must supply
' their own histogram arrays; we will wrap these with some "trick" internal array references, to avoid the need for
' passing these on every pixel request.
'
'IMPORTANT NOTE: PRIOR TO CALLING THIS FUNCTION, you must redim these arrays to range [0, 255]. Do not ReDim them until
' you have completed your function and freed the histograms safely (via ReleaseTargetHistograms, below).
'
'OTHER IMPORTANT NOTE: at present, PD does *not* make use of the dstAlpha parameter anywhere. To improve performance,
' I have manually disabled alpha channel support in the inner loops. If alpha iterating becomes important in the future,
' you will need to manually uncomment those lines of code.
'
'This function will return the pixel count of the first window in the image. DO NOT PROCEED if it returns zero.
Friend Function LockTargetHistograms_RGBA(ByRef dstRed() As Long, ByRef dstGreen() As Long, ByRef dstBlue() As Long, ByRef dstAlpha() As Long, Optional ByVal calcAlpha As Boolean = True) As Long
'Put the iterator in RGB mode
m_HistogramMode = PDPIM_RGBA
'Alias our internal histogram arrays around the destination ones. As you might expect, you MUST NOT attempt
' to erase or ReDim the target arrays until the iterator has finished.
Alias1DArray dstRed, m_Red, m_RedSA
Alias1DArray dstGreen, m_Green, m_GreenSA
Alias1DArray dstBlue, m_Blue, m_BlueSA
'NOTE: see comments elsewhere on m_AlphaSupport. It is currently disabled; you'll need to uncomment various bits
' to make it work.
calcAlpha = False
If calcAlpha And m_AlphaSupport Then
Alias1DArray dstAlpha, m_Alpha, m_AlphaSA
Else
m_AlphaSupport = False
End If
'Point our internal 2D pixel array at the target DIB and generate the initial histogram window
AliasPixelArray
LockTargetHistograms_RGBA = GenerateInitialWindow()
End Function
'When the iterator is finished (due to any condition - success, error, etc), the caller MUST call this function to
' release our aliases to their histogram arrays and DIB.
Friend Function ReleaseTargetHistograms_RGBA(ByRef dstRed() As Long, ByRef dstGreen() As Long, ByRef dstBlue() As Long, ByRef dstAlpha() As Long) As Boolean
Unalias1DArray dstRed, m_Red
Unalias1DArray dstGreen, m_Green
Unalias1DArray dstBlue, m_Blue
If m_AlphaSupport Then Unalias1DArray dstAlpha, m_Alpha
'While we're here, release our DIB reference, too
UnaliasPixelArray
ReleaseTargetHistograms_RGBA = True
End Function
'Luminance counterpart to the RGBA lock function above. All the same caveats and rules apply.
'
'IMPORTANT NOTE: PRIOR TO CALLING THIS FUNCTION, you must redim the target array to range [0, 255]. Do not ReDim
' it until you have completed your function and freed the histogram safely (via ReleaseTargetHistograms, below).
'
'This function will return the pixel count of the first window in the image. DO NOT PROCEED if it returns zero.
Friend Function LockTargetHistograms_Luminance(ByRef dstLum() As Long, Optional ByVal lumMode As PD_LUMINANCE_MODE = PDLM_VALUE) As Long
'Put the iterator in Luminance mode
m_HistogramMode = PDPIM_Luminance
m_LuminanceMode = lumMode
'Alias our internal histogram arrays around the destination ones. As you might expect, you MUST NOT attempt
' to erase or ReDim the target arrays until the iterator has finished.
Alias1DArray dstLum, m_Luminance, m_LuminanceSA
'Point our internal 2D pixel array at the target DIB and generate the initial histogram window
AliasPixelArray
LockTargetHistograms_Luminance = GenerateInitialWindow()
End Function
'When the iterator is finished (due to any condition - success, error, etc), the caller MUST call this function to
' release our aliases to their histogram arrays and DIB.
Friend Function ReleaseTargetHistograms_Luminance(ByRef dstLum() As Long) As Boolean
Unalias1DArray dstLum, m_Luminance
'While we're here, release our DIB reference, too
UnaliasPixelArray
ReleaseTargetHistograms_Luminance = True
End Function
'Byte array counterpart to the lock functions above. All the same caveats and rules apply.
'
'IMPORTANT NOTE: PRIOR TO CALLING THIS FUNCTION, you must redim the target array to range [0, 255]. Do not ReDim
' it until you have completed your function and freed the histogram safely (via ReleaseTargetHistograms, below).
'
'This function will return the pixel count of the first window in the image. DO NOT PROCEED if it returns zero.
Friend Function LockTargetHistograms_ByteArray(ByRef dstHist() As Long) As Long
'Put the iterator in byte array mode
m_HistogramMode = PDPIM_ByteArray
'Alias our internal histogram array around the destination ones. As you might expect, you MUST NOT attempt
' to erase or ReDim the target array until the iterator has finished.
Alias1DArray dstHist, m_Luminance, m_LuminanceSA
'Point our internal 2D pixel array at the target DIB and generate the initial histogram window
AliasByteArray
LockTargetHistograms_ByteArray = GenerateInitialWindow_ByteArray()
End Function
'When the iterator is finished (due to any condition - success, error, etc), the caller MUST call this function to
' release our aliases to their histogram arrays and DIB.
Friend Function ReleaseTargetHistograms_ByteArray(ByRef dstHist() As Long) As Boolean
Unalias1DArray dstHist, m_Luminance
'While we're here, release our DIB reference, too
UnaliasByteArray
ReleaseTargetHistograms_ByteArray = True
End Function
'Point an internal 1D array at some other 1D array. Any arrays aliased this way must be freed via Unalias1DArray,
' or VB will crash.
Private Sub Alias1DArray(ByRef orig1DArray() As Long, ByRef new1DArray() As Long, ByRef newArraySA As SafeArray1D)
'Retrieve a copy of the original 1D array's SafeArray struct
Dim ptrSrc As Long
GetMem4 VarPtrArray(orig1DArray()), ptrSrc
CopyMemory ByVal VarPtr(newArraySA), ByVal ptrSrc, LenB(newArraySA)
'newArraySA now contains the full SafeArray of the original array. Copy this over our current array.
CopyMemory ByVal VarPtrArray(new1DArray()), VarPtr(newArraySA), 4&
'Add a lock to the original array, to prevent potential crashes from unknowing users. (Thanks to @Kroc for this tip.)
SafeArrayLock ptrSrc
End Sub
'Counterpart to Alias1DArray, above. Do NOT call this function on arrays that were not originally processed by that function.
Private Sub Unalias1DArray(ByRef orig1DArray() As Long, ByRef new1DArray() As Long)
'Wipe the array pointer
CopyMemory ByVal VarPtrArray(new1DArray), 0&, 4&
'Remove a lock from the original array; this allows the user to safely release the array on their own terms
Dim ptrSrc As Long
GetMem4 VarPtrArray(orig1DArray()), ptrSrc
SafeArrayUnlock ptrSrc
End Sub
'Alias/unalias the target pixel array, specifically.
Private Sub AliasPixelArray()
With m_PixelSA2D
.cbElements = 1
.cDims = 2
.cLocks = 1
.Bounds(0).lBound = 0
.Bounds(0).cElements = m_dibHeight
.Bounds(1).lBound = 0
.Bounds(1).cElements = m_dibStride
.pvData = m_DibPointer
End With
CopyMemory ByVal VarPtrArray(m_Pixels()), VarPtr(m_PixelSA2D), 4&
End Sub
Private Sub UnaliasPixelArray()
CopyMemory ByVal VarPtrArray(m_Pixels()), 0&, 4&
End Sub
Private Sub AliasByteArray()
With m_BytesSA2D
.cbElements = 1
.cDims = 2
.cLocks = 1
.Bounds(0).lBound = 0
.Bounds(0).cElements = m_ArrayHeight
.Bounds(1).lBound = 0
.Bounds(1).cElements = m_ArrayWidth
.pvData = m_ByteArrayPtr
End With
CopyMemory ByVal VarPtrArray(m_Bytes()), VarPtr(m_BytesSA2D), 4&
End Sub
Private Sub UnaliasByteArray()
CopyMemory ByVal VarPtrArray(m_Bytes()), 0&, 4&
End Sub
'Generating the initial histogram window has variable complexity, depending on the shape being used. This function
' is universal regardless of histogram type or window shape.
Private Function GenerateInitialWindow() As Long
Select Case m_WindowShape
Case PDPRS_Rectangle
GenerateInitialWindow = GenerateInitialWindow_Square()
Case PDPRS_Circle
'Generate an initial shape
CreateShape PDPRS_Circle
'Generate boundary indices
FindBoundaries
'Actually generate the initial histogram window
GenerateInitialWindow = GenerateInitialWindow_ArbitraryShape()
End Select
End Function
'Generating the initial histogram window has variable complexity, depending on the shape being used. This function
' is universal regardless of histogram type or window shape.
Private Function GenerateInitialWindow_ByteArray() As Long
Select Case m_WindowShape
Case PDPRS_Rectangle
GenerateInitialWindow_ByteArray = GenerateInitialWindowBytes_Square()
Case PDPRS_Circle
'Generate an initial shape
CreateShape PDPRS_Circle
'Generate boundary indices
FindBoundaries
'Actually generate the initial histogram window
GenerateInitialWindow_ByteArray = GenerateInitialWindowBytes_ArbitraryShape()
End Select
End Function
'Create the reference map for a non-rectangular kernel
Private Sub CreateShape(ByVal srcShape As PD_PIXEL_REGION_SHAPE)
If (m_ShapeImage Is Nothing) Then Set m_ShapeImage = New pdDIB
m_ShapeImage.CreateBlank m_XBSize, m_YBSize, 24, 0&
Select Case srcShape
'At small sizes, the GDI+ circle rendering algorithm doesn't work very well. To bypass this, we activate
' antialiasing when the search radius is only 1 px wide (for a net search diameter of 3).
Case PDPRS_Circle
Dim useAA As Boolean
useAA = (m_XBSize <= 3) Or (m_YBSize <= 3)
GDI_Plus.GDIPlusFillEllipseToDC m_ShapeImage.GetDIBDC, 0, 0, m_ShapeImage.GetDIBWidth, m_ShapeImage.GetDIBHeight, RGB(255, 255, 255), useAA, 255, True
End Select
'TODO: as a failsafe, scan the DIB and trim any empty lines, because they'll grossly mess up our calculations.
' (Weird shapes may not extend all the way to the edge of the target DIB, and it's difficult to predict this
' in advance.)
'Convert the reference image into a 2D byte array, which is much faster to access and process.
DIBs.GetDIBGrayscaleMap m_ShapeImage, m_ReferenceMap
m_ShapeImage.EraseDIB
End Sub
'Using the reference map created by CreateShape, generate all four custom boundary arrays.
Private Sub FindBoundaries()
'The reference map was created from a simple white-on-black DIB. Black pixels (0) are ignored. White pixels
' (non-zero) are included. As such, this step just involves scanning each edge of the reference map, and storing
' the matching boundary coordinate at each point of each edge. This tells us where to add and subtract histogram
' values when moving between rows and/or columns.
'Each direction is handled in turn.
Dim x As Long, y As Long
ReDim m_XLeft(0 To m_YBSize) As Long
ReDim m_XRight(0 To m_YBSize) As Long
'For the x-directions, process each row in turn
For y = 0 To m_YBSize - 1
x = 0
Do
If (m_ReferenceMap(x, y) > 0) Then Exit Do
'Move to the next column
x = x + 1
'As a failsafe for blank lines, set a dummy value if the entire line is blank
If (x > m_XBSize - 1) Then
x = m_XBLeft
Exit Do
End If
Loop
'x now points at the position on this line where the first valid kernel position lies, when scanning from
' the left boundary. Calculate a position relative to [0] (basically, remove the kernel's left offset).
m_XLeft(y) = x - m_XBLeft
'Repeat the above steps, but scanning from the right this time
x = m_XBSize - 1
Do
If (m_ReferenceMap(x, y) > 0) Then Exit Do
x = x - 1
If (x < 0) Then
x = m_XBLeft
Exit Do
End If
Loop
m_XRight(y) = x - m_XBLeft
Next y
'Now we're going to repeat the above steps, but for the y boundaries (instead of x)
ReDim m_YTop(0 To m_XBSize) As Long
ReDim m_YBottom(0 To m_XBSize) As Long
For x = 0 To m_XBSize - 1
y = 0
Do
If (m_ReferenceMap(x, y) > 0) Then Exit Do
y = y + 1
If (y > m_YBSize - 1) Then
y = m_YBTop
Exit Do
End If
Loop
m_YTop(x) = y - m_YBTop
y = m_YBSize - 1
Do
If (m_ReferenceMap(x, y) > 0) Then Exit Do
y = y - 1
If (y < 0) Then
y = m_YBTop
Exit Do
End If
Loop
m_YBottom(x) = y - m_YBTop
Next x
'We have now generated a map of the edges for each side of the kernel.
End Sub
'Populate an initial window of values for a square shape. The cope is optimized for this specific use-case, and it's not
' meant to be applied to other shapes!
Private Function GenerateInitialWindow_Square() As Long
Dim x As Long, y As Long
Dim r As Long, g As Long, b As Long, a As Long
Dim xStart As Long, xFinal As Long
xStart = m_InitX * m_PixelWidth
xFinal = (m_InitX + m_XBRight) * m_PixelWidth
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
With m_PixelSA1D
.cbElements = 1
.cDims = 1
.cLocks = 1
.lBound = 0
.cElements = m_dibStride
.pvData = m_DibPointer
End With
PutMem4 VarPtrArray(m_PixelLine()), VarPtr(m_PixelSA1D)
For y = m_InitY To m_InitY + m_YBBottom
m_PixelSA1D.pvData = m_DibPointer + y * m_dibStride
For x = xStart To xFinal Step m_PixelWidth
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
m_NumOfPixels = m_NumOfPixels + 1
Next x
Next y
PutMem4 VarPtrArray(m_PixelLine()), 0&
GenerateInitialWindow_Square = m_NumOfPixels
End Function
'Populate an initial window of values for a square shape. The cope is optimized for this specific use-case, and it's not
' meant to be applied to other shapes!
Private Function GenerateInitialWindowBytes_Square() As Long
Dim x As Long, y As Long, v As Long
Dim xStart As Long, xFinal As Long
xStart = m_InitX
xFinal = (m_InitX + m_XBRight)
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
With m_BytesSA1D
.cbElements = 1
.cDims = 1
.cLocks = 1
.lBound = 0
.cElements = m_ArrayWidth
.pvData = m_ByteArrayPtr
End With
PutMem4 VarPtrArray(m_ByteLine()), VarPtr(m_BytesSA1D)
For y = m_InitY To m_InitY + m_YBBottom
m_BytesSA1D.pvData = m_ByteArrayPtr + y * m_ArrayWidth
For x = xStart To xFinal
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
Next x
Next y
PutMem4 VarPtrArray(m_ByteLine()), 0&
GenerateInitialWindowBytes_Square = m_NumOfPixels
End Function
'Populate an initial window of values for a square shape. The cope is optimized for this specific use-case, and it's not
' meant to be applied to other shapes!
Private Function GenerateInitialWindow_ArbitraryShape() As Long
Dim x As Long, y As Long
Dim xStart As Long, xFinal As Long
xStart = m_InitX * m_PixelWidth
xFinal = (m_InitX + m_XBRight) * m_PixelWidth
Dim r As Long, g As Long, b As Long, a As Long
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
With m_PixelSA1D
.cbElements = 1
.cDims = 1
.cLocks = 1
.lBound = 0
.cElements = m_dibStride
.pvData = m_DibPointer
End With
PutMem4 VarPtrArray(m_PixelLine()), VarPtr(m_PixelSA1D)
For y = m_InitY To m_InitY + m_YBBottom
m_PixelSA1D.pvData = m_DibPointer + y * m_dibStride
For x = xStart To xFinal Step m_PixelWidth
'Only calculate pixels that lie inside the reference map
If (m_ReferenceMap(((x * 0.25) - m_InitX) + m_XBLeft, (y - m_InitY) + m_YBTop) > 0) Then
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
m_NumOfPixels = m_NumOfPixels + 1
End If
Next x
Next y
PutMem4 VarPtrArray(m_PixelLine()), 0&
GenerateInitialWindow_ArbitraryShape = m_NumOfPixels
End Function
'Populate an initial window of values for a square shape. The cope is optimized for this specific use-case, and it's not
' meant to be applied to other shapes!
Private Function GenerateInitialWindowBytes_ArbitraryShape() As Long
Dim x As Long, y As Long, v As Long
Dim xStart As Long, xFinal As Long
xStart = m_InitX
xFinal = (m_InitX + m_XBRight)
'To improve performance, we're going to wrap 1D arrays around both the current image scanline, and the reference map.
' This doesn't provide a huge performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
With m_BytesSA1D
.cbElements = 1
.cDims = 1
.cLocks = 1
.lBound = 0
.cElements = m_ArrayWidth
.pvData = m_ByteArrayPtr
End With
PutMem4 VarPtrArray(m_ByteLine()), VarPtr(m_BytesSA1D)
For y = m_InitY To m_InitY + m_YBBottom
m_BytesSA1D.pvData = m_ByteArrayPtr + y * m_ArrayWidth
For x = xStart To xFinal
'Only calculate pixels that lie inside the reference map
If (m_ReferenceMap((x - m_InitX) + m_XBLeft, (y - m_InitY) + m_YBTop) > 0) Then
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
End If
Next x
Next y
PutMem4 VarPtrArray(m_ByteLine()), 0&
GenerateInitialWindowBytes_ArbitraryShape = m_NumOfPixels
End Function
'Given source RGB values, return a corresponding luminance value (high-quality calculation)
Private Function GetLuminanceFromRGB(ByVal srcR As Long, ByVal srcG As Long, ByVal srcB As Long) As Long
If (m_LuminanceMode = PDLM_VALUE) Then
GetLuminanceFromRGB = Colors.GetLuminance(srcR, srcG, srcB)
Else
GetLuminanceFromRGB = Colors.GetHQLuminance(srcR, srcG, srcB)
End If
End Function
'After the caller has successfully processed a pixel, they can call these functions to move to the next pixel
' in the X or Y direction. Because this class uses serpentine scanning, the caller is responsible for changing
' direction on each Y increment.
Friend Function MoveXRight() As Long
Dim yTop As Long, yBottom As Long, y As Long
Dim xLeft As Long, xRight As Long, x As Long
Dim r As Long, g As Long, b As Long, a As Long
'Move our target pixel coordinate to the right
m_X = m_X + 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out Y bounds first
yTop = m_Y - m_YBTop
yBottom = m_Y + m_YBBottom
If (yTop < m_InitY) Then yTop = m_InitY
If (yBottom > m_FinalY) Then yBottom = m_FinalY
'If the *left* x-bound is within bounds, remove a line of pixels from the window.
xLeft = (m_X - m_XBLeft) - 1
If (xLeft >= m_InitX) Then
x = xLeft * m_PixelWidth
For y = yTop To yBottom
b = m_Pixels(x, y)
g = m_Pixels(x + 1, y)
r = m_Pixels(x + 2, y)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_Pixels(x + 3, y)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
Next y
'Infer the number of pixels removed from the histogram(s)
m_NumOfPixels = m_NumOfPixels - (yBottom - yTop + 1)
End If
'If the *right* x-bound is within bounds, add a new line of pixels to the window.
xRight = m_X + m_XBRight
If (xRight <= m_FinalX) Then
x = xRight * m_PixelWidth
For y = yTop To yBottom
b = m_Pixels(x, y)
g = m_Pixels(x + 1, y)
r = m_Pixels(x + 2, y)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_Pixels(x + 3, y)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
Next y
'Infer the number of pixels added to the histogram(s)
m_NumOfPixels = m_NumOfPixels + (yBottom - yTop) + 1
End If
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
'Start with trailing pixels
For y = -m_YBTop To m_YBBottom
'Calculate the position of the trailing boundary pixel in this column
tmpY = y + m_Y
'If y-coordinate lies out of bounds, ignore it
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
'This y-boundary potentially lies in-bounds. Check the matching (x) position.
tmpX = (m_X + m_XLeft(y + m_YBTop)) - 1
'If this x-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
tmpX = tmpX * m_PixelWidth
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If m_HistogramMode = PDPIM_RGBA Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching (x) position on the right.
tmpX = m_X + m_XRight(y + m_YBTop)
'If this x-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
tmpX = tmpX * m_PixelWidth
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next y
End If
MoveXRight = m_NumOfPixels
End Function
'Equivalent of MoveXRight(), above, but designed against non-DIB byte arrays
Friend Function MoveXRight_Byte() As Long
Dim yTop As Long, yBottom As Long, y As Long
Dim xLeft As Long, xRight As Long
Dim v As Long
'Move our target pixel coordinate to the right
m_X = m_X + 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out Y bounds first
yTop = m_Y - m_YBTop
yBottom = m_Y + m_YBBottom
If (yTop < m_InitY) Then yTop = m_InitY
If (yBottom > m_FinalY) Then yBottom = m_FinalY
'Next, figure out X bounds
xLeft = (m_X - m_XBLeft) - 1
xRight = m_X + m_XBRight
'If the *left* x-bound is within bounds, remove a line of pixels from the window.
If (xLeft >= m_InitX) Then
For y = yTop To yBottom
v = m_Bytes(xLeft, y)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
Next y
End If
'If the *right* x-bound is within bounds, add a new line of pixels to the window.
If (xRight <= m_FinalX) Then
For y = yTop To yBottom
v = m_Bytes(xRight, y)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
Next y
End If
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
'Start with trailing pixels
For y = -m_YBTop To m_YBBottom
'Calculate the position of the trailing boundary pixel in this column
tmpY = y + m_Y
'If y-coordinate lies out of bounds, ignore it
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
'This y-boundary potentially lies in-bounds. Check the matching (x) position.
tmpX = (m_X + m_XLeft(y + m_YBTop)) - 1
'If this x-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching (x) position on the right.
tmpX = m_X + m_XRight(y + m_YBTop)
'If this x-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next y
End If
MoveXRight_Byte = m_NumOfPixels
End Function
Friend Function MoveYDown() As Long
Dim xLeft As Long, xRight As Long, x As Long
Dim yTop As Long, yBottom As Long
Dim r As Long, g As Long, b As Long, a As Long
'Move our target pixel coordinate down
m_Y = m_Y + 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out X bounds first.
xLeft = m_X - m_XBLeft
If (xLeft < m_InitX) Then xLeft = m_InitX
xRight = m_X + m_XBRight
If (xRight > m_FinalX) Then xRight = m_FinalX
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
' (Note that the SafeArray header was already populated correctly in the initialization step, so we don't need to
' initialize it here.)
PutMem4 VarPtrArray(m_PixelLine()), VarPtr(m_PixelSA1D)
'If the *top* y-bound is within bounds, remove a line of pixels from the window.
yTop = (m_Y - m_YBTop) - 1
If (yTop >= 0) Then
m_PixelSA1D.pvData = m_DibPointer + yTop * m_dibStride
For x = xLeft * m_PixelWidth To xRight * m_PixelWidth Step m_PixelWidth
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
Next x
'Infer the number of pixels removed from the histogram(s)
m_NumOfPixels = m_NumOfPixels - (xRight - xLeft + 1)
End If
'If the *bottom* y-bound is within bounds, add a new line of pixels to the window.
yBottom = m_Y + m_YBBottom
If (yBottom <= m_FinalY) Then
m_PixelSA1D.pvData = m_DibPointer + yBottom * m_dibStride
For x = xLeft * m_PixelWidth To xRight * m_PixelWidth Step m_PixelWidth
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
Next x
'Infer the number of pixels added to the histogram(s)
m_NumOfPixels = m_NumOfPixels + (xRight - xLeft) + 1
End If
'Regardless of pixels added or removed, we need to free our array reference before exiting
PutMem4 VarPtrArray(m_PixelLine()), 0&
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
tmpY = m_Y
'Start with trailing pixels
For x = -m_XBLeft To m_XBRight
'Calculate the position of the trailing boundary pixel in this column
tmpX = x + m_X
'If this x-coordinate lies out of bounds, ignore it
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
tmpX = tmpX * m_PixelWidth
'This x-boundary potentially lies in-bounds. Check the matching y-position.
tmpY = m_Y + m_YTop(x + m_XBLeft) - 1
'If this y-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching y-position on the bottom.
tmpY = m_Y + m_YBottom(x + m_XBLeft)
'If this y-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next x
End If
MoveYDown = m_NumOfPixels
End Function
Friend Function MoveYDown_Byte() As Long
Dim xLeft As Long, xRight As Long, x As Long
Dim yTop As Long, yBottom As Long
Dim v As Long
'Move our target pixel coordinate down
m_Y = m_Y + 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out X bounds first.
xLeft = m_X - m_XBLeft
If (xLeft < m_InitX) Then xLeft = m_InitX
xRight = m_X + m_XBRight
If (xRight > m_FinalX) Then xRight = m_FinalX
'Next, figure out Y bounds.
yTop = (m_Y - m_YBTop) - 1
yBottom = m_Y + m_YBBottom
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
' (Note that the SafeArray header was already populated correctly in the initialization step, so we don't need to
' initialize it here.)
PutMem4 VarPtrArray(m_ByteLine()), VarPtr(m_BytesSA1D)
'If the *top* y-bound is within bounds, remove a line of pixels from the window.
If (yTop >= 0) Then
m_BytesSA1D.pvData = m_ByteArrayPtr + yTop * m_ArrayWidth
For x = xLeft To xRight
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
Next x
End If
'If the *bottom* y-bound is within bounds, add a new line of pixels to the window.
If (yBottom <= m_FinalY) Then
m_BytesSA1D.pvData = m_ByteArrayPtr + yBottom * m_ArrayWidth
For x = xLeft To xRight
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
Next x
End If
PutMem4 VarPtrArray(m_ByteLine()), 0&
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
tmpY = m_Y
'Start with trailing pixels
For x = -m_XBLeft To m_XBRight
'Calculate the position of the trailing boundary pixel in this column
tmpX = x + m_X
'If this x-coordinate lies out of bounds, ignore it
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
'This x-boundary potentially lies in-bounds. Check the matching y-position.
tmpY = m_Y + m_YTop(x + m_XBLeft) - 1
'If this y-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching y-position on the bottom.
tmpY = m_Y + m_YBottom(x + m_XBLeft)
'If this y-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next x
End If
MoveYDown_Byte = m_NumOfPixels
End Function
Friend Function MoveYUp() As Long
Dim xLeft As Long, xRight As Long, x As Long
Dim yTop As Long, yBottom As Long
Dim r As Long, g As Long, b As Long, a As Long
'Move our target pixel coordinate up
m_Y = m_Y - 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out X bounds first.
xLeft = m_X - m_XBLeft
If (xLeft < m_InitX) Then xLeft = m_InitX
xRight = m_X + m_XBRight
If (xRight > m_FinalX) Then xRight = m_FinalX
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
' (Note that the SafeArray header was already populated correctly in the initialization step, so we don't need to
' initialize it here.)
PutMem4 VarPtrArray(m_PixelLine()), VarPtr(m_PixelSA1D)
'If the *bottom* y-bound is within bounds, remove a new line of pixels from the window.
yBottom = m_Y + m_YBBottom + 1
If (yBottom <= m_FinalY) Then
m_PixelSA1D.pvData = m_DibPointer + yBottom * m_dibStride
For x = xLeft * m_PixelWidth To xRight * m_PixelWidth Step m_PixelWidth
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
Next x
'Infer the number of pixels removed from the histogram(s)
m_NumOfPixels = m_NumOfPixels - (xRight - xLeft + 1)
End If
'If the *top* y-bound is within bounds, add a line of pixels to the window.
yTop = m_Y - m_YBTop
If (yTop >= 0) Then
m_PixelSA1D.pvData = m_DibPointer + yTop * m_dibStride
For x = xLeft * m_PixelWidth To xRight * m_PixelWidth Step m_PixelWidth
b = m_PixelLine(x)
g = m_PixelLine(x + 1)
r = m_PixelLine(x + 2)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_PixelLine(x + 3)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
Next x
'Infer the number of pixels added to the histogram(s)
m_NumOfPixels = m_NumOfPixels + (xRight - xLeft) + 1
End If
PutMem4 VarPtrArray(m_PixelLine()), 0&
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
tmpY = m_Y
'Start with trailing pixels
For x = -m_XBLeft To m_XBRight
'Calculate the position of the trailing boundary pixel in this column
tmpX = x + m_X
'If this x-coordinate lies out of bounds, ignore it
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
tmpX = tmpX * m_PixelWidth
'This x-boundary potentially lies in-bounds. Check the matching y-position.
tmpY = m_Y + m_YBottom(x + m_XBLeft) + 1
'If this y-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) - 1
m_Green(g) = m_Green(g) - 1
m_Red(r) = m_Red(r) - 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) - 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) - 1
End If
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching y-position on the bottom.
tmpY = m_Y + m_YTop(x + m_XBLeft)
'If this y-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
b = m_Pixels(tmpX, tmpY)
g = m_Pixels(tmpX + 1, tmpY)
r = m_Pixels(tmpX + 2, tmpY)
If (m_HistogramMode = PDPIM_RGBA) Then
m_Blue(b) = m_Blue(b) + 1
m_Green(g) = m_Green(g) + 1
m_Red(r) = m_Red(r) + 1
'If m_AlphaSupport Then
' a = m_Pixels(tmpX + 3, tmpY)
' m_Alpha(a) = m_Alpha(a) + 1
'End If
Else
a = GetLuminanceFromRGB(r, g, b)
m_Luminance(a) = m_Luminance(a) + 1
End If
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next x
End If
MoveYUp = m_NumOfPixels
End Function
Friend Function MoveYUp_Byte() As Long
Dim xLeft As Long, xRight As Long, x As Long
Dim yTop As Long, yBottom As Long
Dim v As Long
'Move our target pixel coordinate up
m_Y = m_Y - 1
'Rectangular regions get special, optimized treatment
If (m_WindowShape = PDPRS_Rectangle) Then
'Figure out X bounds first.
xLeft = m_X - m_XBLeft
If (xLeft < m_InitX) Then xLeft = m_InitX
xRight = m_X + m_XBRight
If (xRight > m_FinalX) Then xRight = m_FinalX
'Next, figure out Y bounds.
yTop = m_Y - m_YBTop
yBottom = m_Y + m_YBBottom + 1
'To improve performance, we're going to wrap a 1D array around the current image scanline. This doesn't provide a huge
' performance benefit on small kernels, but on large kernels, it can be meaningfully faster.
' (Note that the SafeArray header was already populated correctly in the initialization step, so we don't need to
' initialize it here.)
PutMem4 VarPtrArray(m_ByteLine()), VarPtr(m_BytesSA1D)
'If the *bottom* y-bound is within bounds, remove a new line of pixels from the window.
If (yBottom <= m_FinalY) Then
m_BytesSA1D.pvData = m_ByteArrayPtr + yBottom * m_ArrayWidth
For x = xLeft To xRight
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
Next x
End If
'If the *top* y-bound is within bounds, add a line of pixels to the window.
If (yTop >= 0) Then
m_BytesSA1D.pvData = m_ByteArrayPtr + yTop * m_ArrayWidth
For x = xLeft To xRight
v = m_ByteLine(x)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
Next x
End If
PutMem4 VarPtrArray(m_ByteLine()), 0&
'Non-rectangular kernels require custom handling
Else
'Because kernels may be non-standard sizes, we don't know in advance if a given pixel needs to be added
' or removed. The only way to know is to scan each boundary pixel in turn, and see if it lies in-bounds.
Dim tmpY As Long, tmpX As Long
tmpY = m_Y
'Start with trailing pixels
For x = -m_XBLeft To m_XBRight
'Calculate the position of the trailing boundary pixel in this column
tmpX = x + m_X
'If this x-coordinate lies out of bounds, ignore it
If (tmpX >= m_InitX) And (tmpX <= m_FinalX) Then
'This x-boundary potentially lies in-bounds. Check the matching y-position.
tmpY = m_Y + m_YBottom(x + m_XBLeft) + 1
'If this y-coordinate lies in-bounds, this pixel can be removed from the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) - 1
m_NumOfPixels = m_NumOfPixels - 1
End If
'Repeat the above steps, but for the matching y-position on the bottom.
tmpY = m_Y + m_YTop(x + m_XBLeft)
'If this y-coordinate lies in-bounds, this pixel can be added to the kernel
If (tmpY >= m_InitY) And (tmpY <= m_FinalY) Then
v = m_Bytes(tmpX, tmpY)
m_Luminance(v) = m_Luminance(v) + 1
m_NumOfPixels = m_NumOfPixels + 1
End If
End If
Next x
End If
MoveYUp_Byte = m_NumOfPixels
End Function