Here's a quick way to get those pixels into an array using GetDIBits. There is a slight caveat extacting the data this way inverts the Y axis and alters the colours (they become BGRA as opposed to RGBA). I've included a conversion function to toggle between RGBA and BGRA. There is also an option to produce a two Dimensional array but you will find the single dimensional array performs better. If you want to use this with the two dimensional array you will have to invert the Y coordinates in your Database, much better would be to then convert each pair of X and inverted Y integers into a single Long and use a one dimensional array, this is easier than it sounds and can be done quickly.
Also seeing as you are only interested in a few colours it's worth converting the image data from a long array and into a byte array that just holds the colours you are interested in, see the retByteData function.
vb Code:
'Module code
Option Explicit
Private Declare Function GetObjectA Lib "GDI32" (ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
Private Declare Function GetDIBits Lib "GDI32" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long)
Private Type BITMAP '24 bytes
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private Type RGBQUAD
Blue As Byte
Green As Byte
Red As Byte
Alpha As Byte
End Type
Private Type BITMAPINFOHEADER '40 bytes
bmSize As Long
bmWidth As Long
bmHeight As Long
bmPlanes As Integer
bmBitCount As Integer
bmCompression As Long
bmSizeImage As Long
bmXPelsPerMeter As Long
bmYPelsPerMeter As Long
bmClrUsed As Long
bmClrImportant As Long
End Type
Private Type BITMAPINFO
bmHeader As BITMAPINFOHEADER
bmColors(0 To 255) As RGBQUAD
End Type
Public Function RetDIBDataLong(PictureObject As Object, Optional Return2DArray As Boolean) As Long()
Dim BM As BITMAP, bmi As BITMAPINFO, lngImageData() As Long
'The PictureObject must be an object with both a HDC and Image property
I think I'm done optimizing my main loop. Using the method you guys suggested (user defined types) - and avoiding strings - significantly speeds up the process.
I don't think the code can be done better, below is an idea of what it looks like... It's designed to find matching pixels (same X,Y from my sub-array (from 1 millions record array - about 500 records each times) as the X,Y from the small image I'm comparing it to).
Code:
Do While xSmall <= intSmallUBound - 1 And xHuge <= HugeType(i).Length - 1
If HugeType(i).Coords(xHuge).x < (arrSmallSubArray(0, xSmall) + xAdd) Then
xHuge = xHuge + 1
ElseIf HugeType(i).Coords(xHuge).x > (arrSmallSubArray(0, xSmall) + xAdd) Then
xSmall = xSmall + 1
ElseIf HugeType(i).Coords(xHuge).y < (arrSmallSubArray(1, xSmall) + yAdd) Then
xHuge = xHuge + 1
ElseIf HugeType(i).Coords(xHuge).y > (arrSmallSubArray(1, xSmall) + yAdd) Then
xSmall = xSmall + 1
Else
xSmall = xSmall + 1
xHuge = xHuge + 1
MatchCount = MatchCount + 1
End If
Loop
My next step is to read, understand and implement Milk's piece of code. Btw thank you, that seems to be pretty complete. That's the last portion slowing down my application with those .pset and .point calls. I'm hoping to have time testing this next week.
I've got a (maybe a little off-topic) question. When populating a recordset, why must I use .movelast to be able to retrieve .recordcount? If I just populate it and check the .recordcount it will always show "0". Not a big problem, but I find it weird to have to use .movelast and .movefirst to retrieve that info.
When populating a recordset, why must I use .movelast to be able to retrieve .recordcount? If I just populate it and check the .recordcount it will always show "0". Not a big problem, but I find it weird to have to use .movelast and .movefirst to retrieve that info.
It's an optimization; the server doesn't send the entire recordset until it has to.
If you have some time, I've always been curious if the With operator speeds up UDT array access. If you change the code you posted to the following, is it any faster?
Code:
With HugeType(i)
Do While xSmall <= intSmallUBound - 1 And xHuge <= .Length - 1
If .Coords(xHuge).x < (arrSmallSubArray(0, xSmall) + xAdd) Then
xHuge = xHuge + 1
ElseIf .Coords(xHuge).x > (arrSmallSubArray(0, xSmall) + xAdd) Then
xSmall = xSmall + 1
ElseIf .Coords(xHuge).y < (arrSmallSubArray(1, xSmall) + yAdd) Then
xHuge = xHuge + 1
ElseIf .Coords(xHuge).y > (arrSmallSubArray(1, xSmall) + yAdd) Then
xSmall = xSmall + 1
Else
xSmall = xSmall + 1
xHuge = xHuge + 1
MatchCount = MatchCount + 1
End If
Loop
End With
Also, I see couple tweaks you could do to squeeze a little more speed out of the posted code.
Code:
With HugeType(i)
Do While xSmall < intSmallUBound And xHuge < .Length ' removing the two subtractions should help a bit
Select Case arrSmallSubArray(0, xSmall) + xAdd
Case Is > .Coords(xHuge).x: xHuge = xHuge + 1
Case Is < .Coords(xHuge).x: xSmall = xSmall + 1
Case Else
Select Case arrSmallSubArray(1, xSmall) + yAdd
Case Is > .Coords(xHuge).y: xHuge = xHuge + 1
Case Is < .Coords(xHuge).y: xSmall = xSmall + 1
Case Else
xSmall = xSmall + 1
xHuge = xHuge + 1
MatchCount = MatchCount + 1
End Select
End Select
Loop
End With
While I concede that this logic is much less readable, it should be perceptibly more efficient. Every iteration of the Do...Loop has at least 2 fewer arithmetic operations, and each match saves another two operations. Given the huge number of passes this loop is making, this could add up to a fairly decent speed boost.