From: charles on
On 9 July, 03:47, Ulrich Korndoerfer
<ulrich_wants_nos...(a)prosource.de> wrote:

> Never mind. My example given was "top off the head", in the meanwhile
> I got something better ;-)
> When interested, see below.
> '**************************************************************************­*****
> '* Private API method declarations, types, consts
> '**************************************************************************­*****

Thanks Ulrich. That's almost as fast as Mike's code so it should be
quite useful. There is one little problem with your code though in
that it crashes on some bitmaps and I get the "Send Error Report to
Microsoft" message box. Any idea what might be causing that?

Charles

From: ralph on
On Fri, 9 Jul 2010 04:17:21 -0700 (PDT), charles
<cbabbage59(a)yahoo.com> wrote:

>On 9 July, 03:47, Ulrich Korndoerfer
><ulrich_wants_nos...(a)prosource.de> wrote:
>
>> Never mind. My example given was "top off the head", in the meanwhile
>> I got something better ;-)
>> When interested, see below.
>> '**************************************************************************�*****
>> '* Private API method declarations, types, consts
>> '**************************************************************************�*****
>
>Thanks Ulrich. That's almost as fast as Mike's code so it should be
>quite useful. There is one little problem with your code though in
>that it crashes on some bitmaps and I get the "Send Error Report to
>Microsoft" message box. Any idea what might be causing that?
>

Can't help with the specific error, but while developing it is useful
to turn off the "Send Error Report to Microsoft" option off, and to
install a JIT debugger to catch the error for your review.
(How depends on your O/S and what debugger you choose. Retrieve
details in Help.)

I prefer WinDbg.
http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx

This will at least provide some clues as to where and what.

-ralph
From: Mike Williams on
"charles" <cbabbage59(a)yahoo.com> wrote in message
news:d49aefcb-5406-452f-b17b-3814715774af(a)a30g2000yqn.googlegroups.com...

> Thanks Mike. Just what I needed.

You're welcome. By the way, although the code I previously posted is very
fast it does slow down a little when dealing with images that are not an
exact multiple of 4 pixels wide. Here is a slightly modified version in
which there is no slowdown when dealing with such images.

The code is quite long, because it is built for speed rather than neatness,
and so I have removed all the comment lines in order to comply with the
Posting Restrictions [Maximum Content] Order (2010) recently issued by Larry
Serflaten, the newly self appointed group moderator.

Mike

Option Explicit
Private Declare Function LoadImage Lib "user32" Alias _
"LoadImageA" (ByVal hInst As Long, ByVal lpsz As String, _
ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, _
ByVal un2 As Long) As Long
Private Declare Function GetObject Lib "gdi32" _
Alias "GetObjectA" (ByVal hObject As Long, _
ByVal nCount As Long, lpObject As Any) As Long
Private Declare Function DeleteObject Lib "gdi32" _
(ByVal hObject As Long) As Long
Private Declare Function VarPtrArray Lib "msvbvm60.dll" _
Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, _
ByVal ByteLen As Long)
Private Const IMAGE_BITMAP = 0
Private Const LR_LOADFROMFILE = &H10
Private Const LR_CREATEDIBSECTION As Long = &H2000
Private Const LR_VGACOLOR As Long = &H80
Private Type SAFEARRAY1D
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
cElements As Long
lLbound As Long
End Type
Private Type BITMAP
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private SourceWidth As Long, SourceHeight As Long
Private clrs(0 To 2 ^ 21 - 1) As Byte
Private TwoToThePowerOf(0 To 7) As Byte
Private Declare Function timeGetTime _
Lib "winmm.dll" () As Long
Private Declare Function timeBeginPeriod _
Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Private Declare Function timeEndPeriod _
Lib "winmm.dll" (ByVal uPeriod As Long) As Long

Private Sub Form_Load()
timeBeginPeriod 1
Dim n As Long
For n = 0 To 7
TwoToThePowerOf(n) = 2 ^ n
Next n
End Sub

Private Function CountColours(bmpFile As String) As Long
Dim n As Long, temp As Long, GetLastPixel As Long
Dim j As Long, p As Long, used As Long, foursMinusThree As Long
Dim allclrsByte As Long, bitmask As Byte
Dim myDIB As Long, bmpInf As BITMAP
Dim sa1 As SAFEARRAY1D, sa2 As SAFEARRAY1D
Dim sa3 As SAFEARRAY1D, sa4 As SAFEARRAY1D
Dim sa5 As SAFEARRAY1D, bytePos As Long
Dim SourceArray1() As Long, SourceArray2() As Long
Dim SourceArray3() As Long, SourceArray4() As Long
Dim sourcearray5() As Byte, done As Boolean
Dim z As Byte, scanline As Long, LastLine As Boolean
Dim bmpLongs As Long, oddPixels As Long, scanLongs As Long
Dim twelves As Long
Dim fours As Long, offset As Long
myDIB = LoadImage(0&, bmpFile, _
IMAGE_BITMAP, 0, 0, _
LR_LOADFROMFILE Or LR_CREATEDIBSECTION)
GetObject myDIB, Len(bmpInf), bmpInf
If myDIB = 0 Or bmpInf.bmBitsPixel <> 24 Then
MsgBox "Not a valid full colour 24 bit .bmp file"
Exit Function
End If
Erase clrs()
GetObject myDIB, Len(bmpInf), bmpInf
SourceWidth = bmpInf.bmWidth
SourceHeight = bmpInf.bmHeight
bmpLongs = (bmpInf.bmWidthBytes \ 4) * SourceHeight
twelves = ((SourceWidth * 3) \ 12)
fours = twelves * 3
scanLongs = bmpInf.bmWidthBytes \ 4
oddPixels = SourceWidth - twelves * 4
On Error GoTo finish
With sa1
.cDims = 1
.cbElements = 4
.lLbound = 0
.cElements = bmpLongs
.pvData = bmpInf.bmBits
End With
LSet sa2 = sa1: sa2.pvData = sa1.pvData + 3
LSet sa3 = sa1: sa3.pvData = sa1.pvData + 6
LSet sa4 = sa1: sa4.pvData = sa1.pvData + 9
CopyMemory ByVal VarPtrArray(SourceArray1), VarPtr(sa1), 4
CopyMemory ByVal VarPtrArray(SourceArray2), VarPtr(sa2), 4
CopyMemory ByVal VarPtrArray(SourceArray3), VarPtr(sa3), 4
CopyMemory ByVal VarPtrArray(SourceArray4), VarPtr(sa4), 4
LSet sa5 = sa1: sa5.cbElements = 1: sa5.cElements = bmpLongs * 4
CopyMemory ByVal VarPtrArray(sourcearray5), VarPtr(sa5), 4
foursMinusThree = fours - 3
For scanline = 0 To SourceHeight - 1
If scanline = (SourceHeight - 1) Then
LastLine = True
GetLastPixel = 0
End If
For j = 0 To foursMinusThree Step 3
p = SourceArray1(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
p = SourceArray2(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
p = SourceArray3(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
If (LastLine = False) Or (j <> foursMinusThree) Then
p = SourceArray4(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
Else
GetLastPixel = ((j + offset) * 4) + 9
End If
Next j
If GetLastPixel <> 0 Then
p = &H10000 * sourcearray5(GetLastPixel + 2) _
+ &H100& * sourcearray5(GetLastPixel + 1) _
+ sourcearray5(GetLastPixel)
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
GetLastPixel = 0
End If
If oddPixels <> 0 Then
temp = oddPixels
If (LastLine = False) Or (temp <> 1) Then
p = SourceArray1(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
Else
GetLastPixel = (j + offset) * 4
Exit For
End If
temp = temp - 1
If temp <> 0 Then
If (LastLine = False) Or (temp <> 1) Then
p = SourceArray2(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
Else
GetLastPixel = ((j + offset) * 4) + 3
Exit For
End If
End If
temp = temp - 1
If temp <> 0 Then
If (LastLine = False) Or (temp <> 1) Then
p = SourceArray3(j + offset) And &HFFFFFF
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
Else
GetLastPixel = ((j + offset) * 4) + 6
Exit For
End If
End If

End If
offset = offset + scanLongs
Next scanline
If GetLastPixel <> 0 Then
p = &H10000 * sourcearray5(GetLastPixel + 2) _
+ &H100& * sourcearray5(GetLastPixel + 1) _
+ sourcearray5(GetLastPixel)
allclrsByte = p \ 8
bitmask = TwoToThePowerOf(7 - p Mod 8)
z = clrs(allclrsByte)
If (z And bitmask) = 0 Then
used = used + 1
clrs(allclrsByte) = z Or bitmask
End If
End If
CountColours = used: done = True
finish:
CopyMemory ByVal VarPtrArray(SourceArray1), 0&, 4
CopyMemory ByVal VarPtrArray(SourceArray2), 0&, 4
CopyMemory ByVal VarPtrArray(SourceArray3), 0&, 4
CopyMemory ByVal VarPtrArray(SourceArray4), 0&, 4
CopyMemory ByVal VarPtrArray(sourcearray5), 0&, 4
DeleteObject myDIB
If Not done Then CountColours = 0
End Function

Private Sub Command1_Click()
Dim tStart As Long, tFinish As Long
Dim s1 As String, UniqueColours As Long
tStart = timeGetTime
UniqueColours = CountColours("c:\temp\jessica1.bmp")
tFinish = timeGetTime
If UniqueColours > 0 Then
s1 = Format(SourceWidth) & " x " & Format(SourceHeight) _
& " pixel bmp file containing " & Format(UniqueColours) _
& " unique colours (" & Format(tFinish - tStart, "0.0") _
& " milliseconds)"
MsgBox s1
Else
MsgBox "Error dealing with bmp file"
End If
End Sub



From: Mike Williams on
"charles" <cbabbage59(a)yahoo.com> wrote in message
news:4bf04fa4-b6ff-4708-b5e2-75dba5315ef6(a)c10g2000yqi.googlegroups.com...

Thanks Ulrich. That's almost as fast as Mike's code so it should be
quite useful. There is one little problem with your code though in
that it crashes on some bitmaps and I get the "Send Error Report to
Microsoft" message box. Any idea what might be causing that?

I waited a reasonable time for Ulrich to respond to your question about his
code crashing on some bitmaps, but since he has not yet done so I'm sure he
won't mind if I respond to it myself.

In order not to upset Larry Serflaten, who abhors detailed responses, I'll
explain it only in as much detail as is necessary for you to see what the
problem actually is. The reason Ulrich's code bombs out and crashes on some
images and not on others is that on some specific sized bitmaps his code is
addressing a data byte that is outside the block of data that the system has
allocated to the DIB.

Generally the system allocates memory for such things in multiples of 4KB,
so it rounds up the actual required memory for the DIB to the nearest 4KB
and then allocates that amount of memory. In some cases the actual DIB data
(the three bytes per pixel data for the bitmap) does not actually fill the
entire block that has been allocated, and so if your code inadvertently
accesses a memory location that is slightly outside the actual DIB pixel
data there is no problem. However, in cases where the pixel area of the DIB
is such that it actually requires /exactly/ a multiple of 4KB then there
will be a problem if your code inadvertently accesses a byte that is outside
of the DIB data, even if it is just a single byte outside the actual DIB
data, since that byte will also be outside of the block of memory that has
been allocated by the system.

In the case of Ulrich's code (and also in the case of my own code) the data
for the DIB is actually accessed as a 4 byte Long for each pixel, with an
appropriate mask being applied to the long (and in Ulrich's case some
appropriate conversions) depending on the location of the three bytes of
pixel data within that Long. This means that the three bytes for the very
last pixel of the DIB will usually be in a Long that extends one byte
outside the actual DIB data area. This does not matter when the memory block
allocated to the DIB by the system is greater than the actual data size of
the DIB (the "nearest 4KB thing") but it /does/ matter when the memory
allocated by the system for the DIB is /exactly/ the same size as the
required DIB data. In such a case the "extra byte being addressed" lies
outside the allocated memory block, which is what causes the crash. In my
own code I have taken steps to prevent such an illegal memory access, but in
Ulrich's code he has not done so, hence the crash in Ulrich's code on some
bitmaps.

Full colour 24 bit Bitmaps that occupy an /exact/ multiple of 4KB of DIB
data are in fact quite common (1024 x 768, 2048 x 1536, 3072 x 2304 and 3200
x 2400 and some others are all fairly common bitmap sizes that fall into
this category) and Ulrich's code, as it stands, will crash on all of those
bitmaps.

Actually, looking back on what I have just written, there is more than I had
intended to write and I do hope that I have not upset Larry Serflaten, the
newly self appointed group moderator who believes that, in his own words,
my know-it-all replies border on narcissism with a touch of superiority
complex which he just finds a bit distasteful, time after time. If this
response has upset Larry again then I do apologise for it, but I really do
feel that it is more important for the OP to get his answer than it is for
Larry's sensibilties to be protected :-)

Mike




From: Larry Serflaten on

"Mike Williams" <Mike(a)WhiskeyAndCoke.com> wrote

> newly self appointed group moderator who believes that, in his own words,
> my know-it-all replies border on narcissism with a touch of superiority
> complex which he just finds a bit distasteful, time after time.

nar�cis�sism - noun
1.inordinate fascination with oneself; excessive self-love; vanity.
2.Psychoanalysis. erotic gratification derived from admiration of one's
own physical or mental attributes, being a normal condition at the
infantile level of personality development.

Are you not quite all grown up yet Mike?

You can stop your childish games anytime now.....

LFS