Ok, so, I got really bored and thought I would give loading MNG files ago, after looking at a few PNG/MNG files in hex.
There is TONS of things im not sure about (thats an under-statement) but it does just about work!
A few images have slightly messed up color for some reassion, and some of the multi framed images need abit more figgering out, as they dont always line up correctly.
Im pretty sure that multiple frames can/should be bound together to create 1 frame tho, but havent really looked into it that much.
Also, this requires transparent drawing, which I didnt bother with, so that doesnt help :P
I basicly built it around as many BNCS MNG's I could get my hands on, so it only supports 16 and 256 colors images and probly wont load other/non-W3 MNG's.
I also dunno where the frame delay is, so in my bot I just toggle the frames every 2 seconds.
Below, is my testing code (with the debug crap removed) and its not very error checky, and boy is it a mess!
Your welcome to use this code in anyway you see fit, it should be a good start/example of how to load MNG's!
If anyone would like to add to this, feel free, but I persionaly have lost interest and am now working on some other stuff.
It might be worth trying to find some documentation on MNG's, as I didnt bother and just worked from hex dumps mostly. (I got the PNG header from google tho)
If the quality of the way SC/D2 displays SMK stuff is to go by, this will work abit better than that, hence why I lost interest :)
Anyway, Injoy!
Some other notes:
The LoadMNG() function will return a BITMAPSTRUT array, which contains the handle to the image in memory, so you can then use a few GDI API's to draw etc.
Just remember to DeleteObject() on that handle when your finished/load a fresh, otherwise you will be eating up ram!
If your add banner is resizeable (like mine was when testing) you will need to do some math with the defalt animation width/height in the MNGDATAHEADER to scale everything correctly.
If you use somthing like transparentBitblt() and only clear the image on the 1st bitmap, that should massive improove the quality of multi frame animation (but will be limited to win2000 and later)
Other wise you will black out the last image and it wont look right (like i was doing in testing)
That said, only 1 multi framed mng was offseted strangely, so it might also be advised to wrap around the frames if part of them go over the orginal frame dimentions!
EDIT: ok, just glanced over the code after posting, and fixed the messed up colors. Turned out when i was shifting the pallet up for the bit map header, I was only shifting 768 bytes and not 1024 :p (woopsy)
So, color is perfect now!
Also, drawing with transparency should make the multi frame animations perfect as well, its only that 1 mng that was offsetted badly, but after seeing it on the client, it was offsetted badly there to!
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private Type RGBTriple
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
End Type
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type MNGDATAHEADER
FrameWidth As Long
FrameHeight As Long
Unknown0 As Long
Unknown1 As Long
Unknown2 As Long
Unknown3 As Long
Unknown4 As Long
Unknown5 As Long
Unknown6 As Long
End Type
Private Type PNGINFOHEADER 'IHDR
Width As Long
Height As Long
Bits As Byte
ColorType As Byte
Compresion As Byte
Filter As Byte
Interlace As Byte
Unknown1 As Long
Unknown2 As Long
End Type
Private Declare Function uncompress Lib "zlib.dll" (dest As Any, destLen As Any, _
src As Any, ByVal srcLen As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateDIBitmap Lib "gdi32" (ByVal hdc As Long, _
lpInfoHeader As Any, ByVal dwUsage As Long, lpInitBits As Any, _
lpInitInfo As Any, ByVal wUsage As Long) As Long
Public Type BITMAPSTRUT
Width As Long
Height As Long
Handle As Long
OffX As Long
OffY As Long
End Type
Public Function LoadMNG(ByVal strFilePath As String, BitMap() As BITMAPSTRUT) As Boolean
Dim FF As Integer
Dim S As String
Dim lngPos As Long
Dim bPal() As Byte
Dim bData() As Byte
Dim bOut() As Byte
Dim pIH As PNGINFOHEADER
Dim pBM As BITMAPINFOHEADER
Dim mDH As MNGDATAHEADER
Dim lngTest As Long 'check headers
Dim BitMapCount As Long
Dim lngBackCol As Long
Dim bBackCol As Boolean
BitMapCount = -1
lngPos = 1
If Dir(strFilePath) = vbNullString Then Exit Function
FF = FreeFile()
Open strFilePath For Binary As #FF
S = String(LOF(FF), 0)
Get #FF, 1, S
Close #FF
Call CopyMemory(lngTest, ByVal S, 4)
lngPos = lngPos + 12
If Not lngTest = &H474E4D8A Then '.MNG
Debug.Print "MNG Bad File Header ID"
Exit Function
End If
LoadMNGNewHeader:
Call CopyMemory(lngTest, ByVal Mid$(S, lngPos, 4), 4)
lngPos = lngPos + 4
If lngTest = &H5244484D Then 'MHDR
Call CopyMemory(mDH, ByVal Mid$(S, lngPos, Len(mDH)), Len(mDH))
lngPos = lngPos + Len(mDH)
mDH.FrameWidth = lngReverse(mDH.FrameWidth)
mDH.FrameHeight = lngReverse(mDH.FrameHeight)
If mDH.FrameWidth < 1 Or mDH.FrameHeight < 1 Then
Debug.Print "MNG Bad Animation Size " & mDH.FrameWidth & "/" & mDH.FrameHeight
Exit Function
End If
ElseIf lngTest = &H4D524554 Then 'TERM
lngPos = lngPos + 18
ElseIf lngTest = &H4B434142 Then 'BACK
bBackCol = True
Call CopyMemory(lngBackCol, ByVal Mid$(S, lngPos, 1) & Mid$(S, lngPos + 2, 1) & Mid$(S, lngPos + 4, 1), 3)
lngPos = lngPos + 14
ElseIf lngTest = &H44474B62 Then 'bKGD
lngPos = lngPos + 14
ElseIf lngTest = &H4D415246 Then 'FRAM
lngPos = lngPos + 18
ElseIf lngTest = &H454D4974 Then 'tIME
lngPos = lngPos + 15
ElseIf lngTest = &H52444849 Then 'IHDR (info)
Call CopyMemory(pIH, ByVal Mid$(S, lngPos, Len(pIH)), Len(pIH))
lngPos = lngPos + Len(pIH)
pIH.Width = lngReverse(pIH.Width)
pIH.Height = lngReverse(pIH.Height)
If pIH.Width < 1 Or pIH.Height < 1 Then
Debug.Print "MNG Bad Image Size " & pIH.Width & "/" & pIH.Height
Exit Function
End If
If Not pIH.Bits = 4 And Not pIH.Bits = 8 Then '8 bit only
Debug.Print "MNG Bad File Bits " & pIH.Bits
Exit Function
End If
If Not pIH.ColorType = 3 Then
Debug.Print "MNG Bad Color type"
Exit Function
End If
ElseIf lngTest = &H45544C50 Then 'PLTE (pallet)
'check we got a header
If Not pIH.Bits = 4 And Not pIH.Bits = 8 Then Exit Function
lngTest = InStr(lngPos, S, "IDATx") - lngPos
If lngTest < 1 Then
Debug.Print "MNG No Pallet"
Exit Function
End If
ReDim bPal(lngTest - 1)
Call CopyMemory(bPal(0), ByVal Mid$(S, lngPos, lngTest), lngTest)
lngPos = lngPos + lngTest
ElseIf lngTest = &H54414449 Then 'IDAT (image data)
If Not pIH.Bits = 4 And Not pIH.Bits = 8 Then Exit Function 'check we got header
lngTest = InStr(lngPos, S, "IEND®B`,") - lngPos
lngTest = lngTest - 4
If lngTest < 1 Then
Debug.Print "MNG No iEnd"
Exit Function
End If
ReDim bData(lngTest - 1)
ReDim bOut((((pIH.Width * pIH.Height) * 1.01) + 12))
Call CopyMemory(bData(0), ByVal Mid$(S, lngPos, lngTest), lngTest)
lngPos = lngPos + lngTest + 4
Call uncompress(bOut(0), UBound(bOut) + 1, bData(0), lngTest)
If pIH.Bits = 8 Then
Call ClipLines(bOut(), pIH.Width, pIH.Height)
ElseIf pIH.Bits = 4 Then
Call ClipLines(bOut(), pIH.Width / 2, pIH.Height)
End If
Call SortPNGPallet8(bPal(), bBackCol, lngBackCol)
ReDim Preserve bPal(UBound(bPal) + Len(pBM))
pBM.biSize = Len(pBM)
pBM.biPlanes = 1
pBM.biBitCount = pIH.Bits
pBM.biWidth = pIH.Width
pBM.biHeight = pIH.Height
'//Move pallet up 40 spaces
Call CopyMemory(bPal(Len(pBM)), bPal(0), 1024)
'//add info header
Call CopyMemory(bPal(0), pBM, Len(pBM))
'//Create the diBitmap
BitMapCount = BitMapCount + 1
ReDim Preserve BitMap(BitMapCount)
BitMap(BitMapCount).Width = pBM.biWidth
BitMap(BitMapCount).Height = pBM.biHeight
BitMap(BitMapCount).OffX = 0
BitMap(BitMapCount).OffY = 0
lngTest = GetDC(0)
BitMap(BitMapCount).Handle = CreateDIBitmap(lngTest, pBM, &H4, bOut(0), bPal(0), &H0)
Call DeleteDC(lngTest)
ElseIf lngTest = &H444E4549 Then 'IEND (end of image data)
lngPos = lngPos + 8
ElseIf lngTest = &H49464544 Then 'DEFI
If BitMapCount >= 0 Then
Call CopyMemory(BitMap(BitMapCount).OffX, ByVal Mid$(S, lngPos + 4, 4), 4)
Call CopyMemory(BitMap(BitMapCount).OffY, ByVal Mid$(S, lngPos + 8, 4), 4)
BitMap(BitMapCount).OffX = lngReverse(BitMap(BitMapCount).OffX)
BitMap(BitMapCount).OffY = lngReverse(BitMap(BitMapCount).OffY)
End If
lngPos = lngPos + 20
ElseIf lngTest = &H444E454D Then 'MEND (end of MNG)
lngPos = lngPos + 8
LoadMNG = (BitMapCount >= 0)
Exit Function
Else
Debug.Print "Unknown MNG Header: " & MakeDWORD(lngTest)
Exit Function
End If
GoTo LoadMNGNewHeader
End Function
Private Function lngReverse(ByVal lngValue As Long) As Long
Dim S As String * 4
Call CopyMemory(ByVal S, lngValue, 4)
S = StrReverse(S)
Call CopyMemory(lngReverse, ByVal S, 4)
End Function
Private Sub SortPNGPallet8(ByRef bPallet() As Byte, ByVal bBackGround As Boolean, _
ByVal lngBackColor As Long)
Dim i As Integer
Dim i2 As Integer
Dim T(255) As RGBTriple
Dim Q(255) As RGBQUAD
Dim lngTest As Long
Dim bTest As Boolean
ReDim Preserve bPallet(1023)
Call CopyMemory(T(0), bPallet(0), 768)
For i = 0 To 255
Q(i).rgbRed = T(i).rgbBlue
Q(i).rgbGreen = T(i).rgbGreen
Q(i).rgbBlue = T(i).rgbRed
Next i
If bBackGround And (Not lngBackColor = 0) Then
For i = 0 To 255
Call CopyMemory(lngTest, Q(i), 3)
If lngTest = lngBackColor Then
If bTest = False Then
bTest = True
Else
lngTest = 0
Call CopyMemory(Q(i), lngTest, 4)
End If
End If
Next i
End If
Call CopyMemory(bPallet(0), Q(0), 1024)
End Sub
Private Sub ClipLines(ByRef bData() As Byte, ByVal lngBPL As Long, ByVal lngHeight As Long)
Dim i As Long
Dim lngPos As Long
Dim bOut() As Byte
Dim lngWidth As Long
lngWidth = lngBPL
If Not (lngWidth Mod 4) = 0 Then
lngWidth = lngWidth + (4 - (lngWidth Mod 4))
End If
ReDim bOut(lngWidth - 1, lngHeight - 1)
ReDim Preserve bData((lngWidth * lngHeight) - 1)
For i = (lngHeight - 1) To 0 Step -1
Call CopyMemory(bOut(0, i), bData(lngPos + 1), lngBPL)
lngPos = lngPos + lngBPL + 1
Next i
ReDim bData((lngWidth * lngHeight) - 1)
Call CopyMemory(bData(0), bOut(0, 0), (lngWidth * lngHeight))
End Sub
it's not like the format to an mng file is secret, or anything. you can quit guessing, and just use google.
Quote from: betawarz on March 22, 2008, 05:19 AM
it's not like the format to an mng file is secret, or anything. you can quit guessing, and just use google.
Yeah.. Ofc its not a secret, i never said it was :)
But i persionaly would rather try figger this stuff out rather than useing google, it makes good practiss and cures bordom.
Cant always rely on somthing like google to answer every question you have, what about when it cant answer it? :P
Just thought by shareing this, it might help some people with adding MNG support to their bots, is all.
Quote from: Ringo on March 22, 2008, 04:35 PM
Quote from: betawarz on March 22, 2008, 05:19 AM
it's not like the format to an mng file is secret, or anything. you can quit guessing, and just use google.
Yeah.. Ofc its not a secret, i never said it was :)
But i persionaly would rather try figger this stuff out rather than useing google, it makes good practiss and cures bordom.
Cant always rely on somthing like google to answer every question you have, what about when it cant answer it? :P
Just thought by shareing this, it might help some people with adding MNG support to their bots, is all.
lol easy, when google cant answer your question then you try to figure it out yourself, if you have any troubles then you ask here :).
Quote from: AoD-Archangel on March 22, 2008, 11:59 PM
lol easy, when google cant answer your question then you try to figure it out yourself, if you have any troubles then you ask here :).
Well that might be yours and warez way of doing things, but i persionaly like to challenge my self :)
I didnt do this so I could load MNG's, nore so I could have mng support in a bot, I did it just to pass the time and challenge my self. As stated above, it makes good practiss.
People here probly have more use for this than I will, hence why I posted it.
Can we keep on subject, or can a mod trash every reply so far?