Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1449

Custom GDI+ PNG Writer v2.0

$
0
0
Completely revamped, comments below reflect the new version. Backup your previous version if you still want it.

If you use GDI+ to write PNGs, one of the shortcomings is that many chunks/properties/tags are not written even if they exist in the PNG before saving it. Another issue many have is that GDI+ automatically adds a gAMA chunk whether you want it or not, whether you feel it is the correct value or not.

This class is meant as a stop-gap for adding chunks and removing chunks after the image is saved by GDI+ but before it is written to disk or to a byte array. I have added lots of comments throughout the class

Caveats:

1. The class does not provide compression for chunks that can include compressed data. Those chunks are IDAT (pixel data), iCCP (ICM profile), zTxt (compressed text only), iTXt (UTF8 text where compression is optional).

2. All but a few standard PNG chunks are now coded as separate functions, which makes it easier for those not totally familiar with the PNG layout to add chunks during PNG creation. There is also a generic function for adding any chunk one would want. The chunks not directly coded for are:

IDAT: pixel data. GDI+ creates these
sBIT: significant bits and pertains specifically to how the pixel data is interpreted
bKGD: suggested background color for rendering transparency on. This is specific to the bit depth of the written image
hIST: palette histogram, again specific to the written pixel data
tRNS: color to be transparent within the image. This is specific to the bit depth of the written image
sPLT: suggested palette. This is specific to the bit depth of the written image
pHYs: typically used to describe non-square pixel dimensions

The class is a text file uploaded here. Simply rename it as .cls once downloaded. Here's a really short example of usage...

- Assumption is GDI+ is loaded and running before you call any class methods.
Code:

Private Sub Command1_Click()
    Dim hImage As Long, c As IPngWriter
    GdipLoadImageFromFile StrPtr("C:\Test Images\LaVolpe.png"), hImage
    If hImage Then
        Set c = New IPngWriter

        ' example of adding a tEXt chunk
        c.AddChunk_tEXt keySoftware, "Custom PNGWriter Class", BeforeIEND
       
        ' example of removing the gAMA, cHRM & sRGB chunks
        c.WritePngToFile hImage, "D:\Users\LaVolpe\Desktop\Test.PNG", CHUNK_gAMA, CHUNK_sRGB, CHUNK_cHRM

        Set c = Nothing
        GdipDisposeImage hImage
    Else
        MsgBox "Failed to load that image"
    End If
End Sub

Should you want a short routine to review the chunks that exist in any valid PNG file, you can use the following.
Code:

Private Sub pvReadPngChunks(FileName As String)

    Dim fnr As Integer, lName As Long, lSize As Long
    Dim sName As String * 4&
    Dim lPtr As Long, lMax As Long
    Dim lPrevName As Long, bFailed As Boolean
   
    On Error Resume Next
    fnr = FreeFile()
    Open FileName For Binary Access Read As #fnr
    If Err Then
        MsgBox Err.Description, vbExclamation + vbOKOnly, "Error"
        Exit Sub
    End If
    On Error GoTo 0
    lMax = LOF(fnr)
   
    If lMax < 46& Then
        bFailed = True: GoTo ExitRoutine
    Else
        Get #fnr, 1, lName
        If lName <> 1196314761 Then bFailed = True: GoTo ExitRoutine
        Get #fnr, , lName
        If lName <> 169478669 Then bFailed = True: GoTo ExitRoutine
        Debug.Print "Processing "; FileName;
        lPtr = 9
        Do Until lPtr + 8& > lMax
            Get #fnr, lPtr, lSize: lSize = pvReverseLong(lSize)
            Get #fnr, , lName
            Mid$(sName, 4, 1) = Chr$(((lName And &HFF000000) \ &H1000000) And &HFF)
            Mid$(sName, 3, 1) = Chr$((lName And &HFF0000) \ &H10000)
            Mid$(sName, 2, 1) = Chr$((lName And &HFF00&) \ &H100)
            Mid$(sName, 1, 1) = Chr$(lName And &HFF)
            If lName = lPrevName Then
                Debug.Print ","; sName; "("; CStr(lSize); ")";
            Else
                lPrevName = lName
                Debug.Print vbCrLf; sName; "("; CStr(lSize); ")";
            End If
            lPtr = lPtr + 12& + lSize
        Loop
    End If
    Debug.Print vbCrLf; "Done..."
   
ExitRoutine:
    Close #fnr
    If bFailed Then MsgBox "Failed to process that file. Sure it was a PNG?", vbQuestion + vbOKOnly
End Sub

Private Function pvReverseLong(ByVal inLong As Long) As Long

    ' fast function to reverse a long value from big endian to little endian
    ' PNG files contain reversed longs, as do ID3 v3,4 tags
    pvReverseLong = _
      (((inLong And &HFF000000) \ &H1000000) And &HFF&) Or _
      ((inLong And &HFF0000) \ &H100&) Or _
      ((inLong And &HFF00&) * &H100&) Or _
      ((inLong And &H7F&) * &H1000000)
    If (inLong And &H80&) Then pvReverseLong = pvReverseLong Or &H80000000
End Function

And a short example using the above code follows. Note. For simplicity, I used VB's File I/O functions in above code. You may want to use APIs for unicode support
Code:

    Call pvReadPngChunks("C:\Test Images\LaVolpe.PNG")
Edited: Link to PNG format specifications

New version uploaded
Attached Files

Viewing all articles
Browse latest Browse all 1449

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>