cGIFparser.cls
上传用户:jnjasmy
上传日期:2015-01-04
资源大小:637k
文件大小:16k
源码类别:

GDI/图象编程

开发平台:

Visual Basic

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "cGIFparser"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15. ' No APIs are declared public. This is to prevent possibly, differently
  16. ' declared APIs, or different versions of the same API, from conflciting
  17. ' with any APIs you declared in your project. Same rule for UDTs.
  18. ' used to extract data from a converted GIF
  19. Private Declare Function GetGDIObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
  20. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
  21. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
  22. Private Type SafeArrayBound
  23.     cElements As Long
  24.     lLbound As Long
  25. End Type
  26. Private Type SafeArray
  27.     cDims As Integer
  28.     fFeatures As Integer
  29.     cbElements As Long
  30.     cLocks As Long
  31.     pvData As Long
  32.     rgSABound(0 To 1) As SafeArrayBound ' reusable UDT for 1 & 2 dim arrays
  33. End Type
  34. Private Type BITMAP
  35.     bmType As Long
  36.     bmWidth As Long
  37.     bmHeight As Long
  38.     bmWidthBytes As Long
  39.     bmPlanes As Integer
  40.     bmBitsPixel As Integer
  41.     bmBits As Long
  42. End Type
  43. Private c_GIFdata() As Byte     ' source bytes (mapped array, never initialized)
  44. Private c_GIFbytes() As Byte    ' 1st frame from source bytes
  45. Public Function LoadStream(inStream() As Byte, cHost As c32bppDIB, _
  46.                     Optional ByVal streamOffset As Long, Optional ByVal streamLength As Long) As Boolean
  47.     ' Parameters:
  48.     ' insSream() :: a byte array containing a GIF
  49.     ' cHost :: an initialized c32bppDIB
  50.     ' streamOffset :: array position for 1st byte in the stream
  51.     ' streamLength :: size of stream that contains the image
  52.     '   - If zero, then size is UBound(inStream)-streamOffset+1
  53.     
  54.     ' IMPORTANT: the array offset & length are not checked in this class.
  55.     '   They were checked before this class was called. If this class is to
  56.     '   be pulled out and put in another project, ensure you include the
  57.     '   validation shown in c32bppDIB.LoadPicture_Stream
  58.     
  59.     Dim tTSA As SafeArray
  60.     ' overlay our module level array onto the passed array
  61.     With tTSA
  62.         .cbElements = 1         ' byte array
  63.         .cDims = 1              ' 1 dimensional
  64.         .pvData = VarPtr(inStream(streamOffset))
  65.         .rgSABound(0).cElements = streamLength
  66.     End With
  67.     CopyMemory ByVal VarPtrArray(c_GIFdata), VarPtr(tTSA), 4& ' apply overlay
  68.     
  69.     ' call routine to parse the GIF & convert it to 32bpp
  70.     LoadStream = ParseGIF(cHost)
  71.     CopyMemory ByVal VarPtrArray(c_GIFdata), 0&, 4&    ' remove overlay
  72.     
  73. End Function
  74. Private Function ParseGIF(cHost As c32bppDIB) As Boolean
  75.     On Error Resume Next
  76.     ' a modified routine from some of my other GIF postings
  77.     ' This version is scaled back and only extracts first frame
  78.     
  79.     ' This routine has one limitation. Some rare GIFs do not follow the
  80.     ' standards and when those are encountered, the routine will return
  81.     ' True to prevent GIF from being sent to other parsers.  This is
  82.     ' important because the BMP parser sends the stream to an API
  83.     ' to convert an unknown image to a stdPicture. If the GIF stream
  84.     ' isn't formatted within standards that API hangs the application.
  85.     
  86.     Dim gLong As Long
  87.     Dim aPointer As Long
  88.     Dim gHeaderLen As Long
  89.     Dim g87aStart As Long, g87aStop As Long
  90.     Dim g89aStart As Long, g89aStop As Long
  91.     
  92.     ' transparency flags and variables use to tweak GIF
  93.     Dim transUsed As Byte, TransIndex As Long
  94.     Dim aLocalTbl As Long, gColorsUsed As Long
  95.     Dim uniquePalette(0 To 767) As Byte
  96.     Dim p As Long
  97.     
  98.     On Error GoTo ExitReadRoutine
  99.     
  100.     ' read signature
  101.     ReDim c_GIFbytes(0 To 5)
  102.     CopyMemory c_GIFbytes(0), c_GIFdata(0), 6&
  103.     Select Case LCase(StrConv(c_GIFbytes, vbUnicode))
  104.         Case "gif89a", "gif87a"
  105.         Case Else
  106.             Exit Function
  107.     End Select
  108.         
  109.     ' skip to the global color table information
  110.     If (c_GIFdata(10) And 128) = 128 Then ' color table used? If so, skip it
  111.         gColorsUsed = 2& ^ (1& + (c_GIFdata(10) And &H7)) ' count colors
  112.         gHeaderLen = gColorsUsed * 3& + 13&
  113.     Else 'no global color table; probably uses local color tables
  114.         gHeaderLen = 13&
  115.     End If
  116.     aPointer = gHeaderLen
  117.     
  118.     Do
  119.         Select Case c_GIFdata(aPointer)    ' read a single byte
  120.         Case 0  ' block terminators
  121.             aPointer = aPointer + 1&
  122.             
  123.         Case 33 'Extension Introducer
  124.             aPointer = aPointer + 1&
  125.             
  126.             Select Case c_GIFdata(aPointer) ' read the extension type
  127.             
  128.             Case 255    ' application extension
  129.                 ' Get the length of extension: will always be 11
  130.                 aPointer = aPointer + c_GIFdata(aPointer + 1&) + 2&
  131.                 Call SkipGifBlock(aPointer)
  132.                 
  133.             Case 249    ' Graphic Control Label
  134.                         ' (description of frame & is an optional block) 8 bytes
  135.                 transUsed = (c_GIFdata(aPointer + 2&) And 1&)
  136.                 If transUsed = 1& Then ' has transparency?
  137.                     TransIndex = c_GIFdata(aPointer + 5&) ' cache transparency index
  138.                 End If
  139.                 g89aStart = aPointer - 1&    ' location where 89a block starts
  140.                 aPointer = aPointer + 7&     ' move to end of block
  141.                 
  142.             Case Else   ' Comment block, plain text extension, or Unknown extension
  143.                 aPointer = aPointer + 1&
  144.                 Call SkipGifBlock(aPointer)
  145.             End Select
  146.                 
  147.         Case 44 ' Image Descriptor (image dimensions & color table)
  148.                 ' mark position where image description starts
  149.             g87aStart = aPointer
  150.             aPointer = aPointer + 9& ' image data starts 10 bytes after header
  151.             ' next byte indicates if local color table used
  152.             If (c_GIFdata(aPointer) And 128) = 128 Then   ' local color table used?
  153.                 gColorsUsed = 2& ^ (1& + (c_GIFdata(aPointer) And &H7)) ' count colors
  154.                 aPointer = aPointer + gColorsUsed * 3&
  155.                 aLocalTbl = 1&  ' flag indicating colors from local table vs global table
  156.             End If
  157.             aPointer = aPointer + 2& ' move to position of first data block
  158.             Call SkipGifBlock(aPointer)
  159.                 
  160.             g87aStop = aPointer - 1&    ' this is where the data ends
  161.             If g87aStop - g87aStart < 3& Then Exit Function ' invalid frame
  162.             Exit Do
  163.             
  164.         Case Else
  165.             ' shouldn't happen; abort with what we have
  166.             Exit Function
  167.         End Select
  168.     Loop
  169.     
  170.     If Not (g87aStart = 0& Or gColorsUsed = 0&) Then ' we have a valid gif frame
  171.     
  172.         ' rebuild the GIF file to include only the 1st frame read
  173.         If g89aStart > 0 Then   ' gif is 89a format
  174.             ' resize array, copy header info & gif89a info
  175.             ReDim c_GIFbytes(0 To gHeaderLen + (g87aStop - g87aStart + 1&) + 8&)
  176.             CopyMemory c_GIFbytes(0), c_GIFdata(0), gHeaderLen
  177.             CopyMemory c_GIFbytes(gHeaderLen), c_GIFdata(g89aStart), 8&
  178.             aPointer = gHeaderLen + 8&  ' adjust pointer for gif87a info
  179.         Else
  180.             ' resize array and copy header info only
  181.             ReDim c_GIFbytes(0 To gHeaderLen + (g87aStop - g87aStart + 1&))
  182.             CopyMemory c_GIFbytes(0), c_GIFdata(0), gHeaderLen
  183.             aPointer = gHeaderLen
  184.         End If
  185.         ' now copy the gif 87a info
  186.         CopyMemory c_GIFbytes(aPointer), c_GIFdata(g87aStart), g87aStop - g87aStart + 1&
  187.         c_GIFbytes(UBound(c_GIFbytes)) = 59 ' trailer/end of file
  188.     
  189.         ' fix up the left/top & width/height of overall frame
  190.         CopyMemory c_GIFbytes(aPointer + 1), 0&, 4& ' make frame left/top zero
  191.         CopyMemory c_GIFbytes(6), c_GIFbytes(aPointer + 5), 4& ' make window & frame size same
  192.     
  193.         If transUsed = 1& Then
  194.             ' Fix up the color table/indexes for images with transparency
  195.             ' Why? Instead of parsing/decompressing the GIF, we will allow an API to do it for us.
  196.             ' But that API can re-index the GIF which means we may lose track of the transparency
  197.             ' color/index.  This happens whenever any color in the GIF's palette is duplicated.
  198.             ' To prevent this from occuring, we simply replace the GIF's palette with another
  199.             ' palette of non-duplicated entries.
  200.             ' BTW: This way of creating GIF is still much faster than parsing the GIF by hand
  201.             If aLocalTbl = 1& Then ' local color table else global
  202.                 ' local color table starts 10 bytes after the gif87a block
  203.                 aPointer = gHeaderLen + 10&  ' location of table within single frame array
  204.                 aLocalTbl = g87aStart + 10&  ' location of table within souce array
  205.                 ' offset single frame array when gif89a structure is used
  206.                 If Not g89aStart = 0& Then aPointer = aPointer + 8&
  207.             Else
  208.                 aPointer = 13&        ' global table location
  209.                 aLocalTbl = 13&       ' same in both arrays
  210.             End If
  211.             For p = 1& To gColorsUsed - 1&  ' create non-duplicating color palette
  212.                 gLong = p * 3&
  213.                 uniquePalette(gLong) = p
  214.                 uniquePalette(gLong + 1) = p
  215.                 uniquePalette(gLong + 2) = p
  216.             Next
  217.             ' replace the old palette with the new one
  218.             CopyMemory c_GIFbytes(aPointer), uniquePalette(0), gColorsUsed * 3&
  219.             Erase uniquePalette()
  220.         Else
  221.             TransIndex = -1&
  222.         End If
  223.         
  224.         ' all done parsing the GIF file, send it to routine to convert it to a 32bpp
  225.         ParseGIF = ConvertGIFto32bpp(TransIndex, aLocalTbl, cHost)
  226.     
  227.     End If
  228.     
  229. ExitReadRoutine:
  230. If Err Then
  231.     Err.Clear           ' this is a GIF format, but the format is invalid
  232.     cHost.DestroyDIB    ' something is wrong; don't allow it to continue
  233.     ParseGIF = True     ' to other parsers
  234. End If
  235. End Function
  236. Private Sub SkipGifBlock(ByRef Ptr As Long)
  237.     ' Routine skips a block of data within the GIF file
  238.     Dim curByte As Byte
  239.     curByte = c_GIFdata(Ptr)
  240.     Do While Not curByte = 0
  241.         Ptr = Ptr + 1& + curByte
  242.         curByte = c_GIFdata(Ptr)
  243.     Loop
  244.     Ptr = Ptr + 1&
  245. End Sub
  246. Private Function ConvertGIFto32bpp(TransIndex As Long, tblOffset As Long, cHost As c32bppDIB) As Boolean
  247.     ' Function converts GIF to a standard picture and then premultiplies RGB values based on the
  248.     ' GIFs transparent index, if applicable.
  249.     ' Note: The c_GIFbytes array was already processed/filled in the ParseGIF function
  250.     
  251.     Dim tPic As StdPicture, tBMP As BITMAP
  252.     
  253.     ' used for parsing a transparent gif
  254.     Dim X As Long, Y As Long, m As Long, dX As Long, Index As Long
  255.     Dim gSA As SafeArray, dSA As SafeArray
  256.     Dim Pow2(0 To 8) As Long, dibBytes() As Byte
  257.     Dim maskShift As Long, maskAND As Long
  258.     Dim hostDC As Long
  259.     
  260.     ' first: have API create a stdPicture for us
  261.     Set tPic = iparseArrayToPicture(c_GIFbytes, 0&, 1& + UBound(c_GIFbytes))
  262.     Erase c_GIFbytes
  263.     If Not tPic Is Nothing Then
  264.         
  265.         ' a VB stdPicture is a DIB, therefore it has a handle to the DIB bits; get it
  266.         GetGDIObject tPic.Handle, Len(tBMP), tBMP
  267.         If Not tBMP.bmBits = 0& Then
  268.         
  269.             ' have host create application's 32bpp DIB
  270.             cHost.InitializeDIB tBMP.bmWidth, tBMP.bmHeight
  271.             
  272.             ' we only need to parse the palette & indexes if transparency is used
  273.             If TransIndex = -1& Then                ' opaque GIF
  274.                 ' render GIF to our DIB DC, then ensure all alpha bytes are 255
  275.                 hostDC = cHost.LoadDIBinDC(True)
  276.                 tPic.Render hostDC + 0&, 0&, 0&, tBMP.bmWidth + 0&, tBMP.bmHeight + 0&, _
  277.                     0&, tPic.Height, tPic.Width, -tPic.Height, ByVal 0&
  278.                 cHost.LoadDIBinDC False
  279.                 With dSA
  280.                     .cbElements = 1
  281.                     .cDims = 2
  282.                     .pvData = cHost.BitsPointer
  283.                     .rgSABound(0).cElements = cHost.Height
  284.                     .rgSABound(1).cElements = cHost.scanWidth
  285.                 End With
  286.                 CopyMemory ByVal VarPtrArray(dibBytes), VarPtr(dSA), 4&
  287.                 iparseValidateAlphaChannel dibBytes(), True, False, -1&
  288.                 CopyMemory ByVal VarPtrArray(dibBytes), 0&, 4&
  289.                 cHost.Alpha = False
  290.                 
  291.             Else
  292.                 ' next: getting ready to parse the paletted stdPic
  293.                 Pow2(0) = 1&
  294.                 For X = 1& To tBMP.bmBitsPixel   ' power of 2 array
  295.                     Pow2(X) = Pow2(X - 1&) * 2&
  296.                 Next
  297.                 maskAND = Pow2(tBMP.bmBitsPixel) - 1& ' AND mask for stdPic indexes
  298.                 ' we need to overlay arrays onto the GIF and the host's DIB pointers
  299.                 With gSA
  300.                     .cbElements = 1
  301.                     .cDims = 2
  302.                     .pvData = tBMP.bmBits
  303.                     .rgSABound(0).cElements = tBMP.bmHeight
  304.                     .rgSABound(1).cElements = iparseByteAlignOnWord(tBMP.bmBitsPixel, tBMP.bmWidth)
  305.                 End With
  306.                 With dSA
  307.                     .cbElements = 1
  308.                     .cDims = 2
  309.                     .pvData = cHost.BitsPointer
  310.                     .rgSABound(0).cElements = cHost.Height
  311.                     .rgSABound(1).cElements = cHost.scanWidth
  312.                 End With
  313.                 CopyMemory ByVal VarPtrArray(c_GIFbytes), VarPtr(gSA), 4&
  314.                 CopyMemory ByVal VarPtrArray(dibBytes), VarPtr(dSA), 4&
  315.                 
  316.                 ' last: start parsing stdPic's paletted DIB
  317.                 For Y = 0& To tBMP.bmHeight - 1&
  318.                     dX = 0&: m = 0&   ' reset dX=host DIB's X & M=stdPic DIB's X
  319.                     maskShift = 8& - tBMP.bmBitsPixel    ' 1st bit to process
  320.                     
  321.                     ' note: do not loop thru using gif ScanWidth. If the GIF
  322.                     ' width is not DWORD ligned , you will overflow the target
  323.                     ' DIB width and eventually write to uninitialized memory
  324.                     For X = 1& To tBMP.bmWidth&
  325.                         ' get the palette index by shifting bits
  326.                         Index = ((c_GIFbytes(m, Y)  Pow2(maskShift)) And maskAND)
  327.                         
  328.                         If Not Index = TransIndex Then  ' 100% opaque else 100% transparent
  329.                             Index = Index * 3& + tblOffset
  330.                             dibBytes(dX, Y) = c_GIFdata(Index + 2&)     ' make BGR vs RGB
  331.                             dibBytes(dX + 1, Y) = c_GIFdata(Index + 1&)
  332.                             dibBytes(dX + 2, Y) = c_GIFdata(Index)
  333.                             dibBytes(dX + 3, Y) = 255
  334.                         End If
  335.                         
  336.                         ' adjust for parsing/shifting the next index
  337.                         If maskShift = 0& Then
  338.                             maskShift = 8& - tBMP.bmBitsPixel ' start new byte
  339.                             m = m + 1&                        ' next GIF byte
  340.                         Else
  341.                             maskShift = maskShift - tBMP.bmBitsPixel ' adjust
  342.                         End If
  343.                         dX = dX + 4&                          ' next Host pixel
  344.                     Next
  345.                 Next
  346.                 ' done, remove overlays
  347.                 CopyMemory ByVal VarPtrArray(c_GIFbytes), 0&, 4&
  348.                 CopyMemory ByVal VarPtrArray(dibBytes), 0&, 4&
  349.                 cHost.Alpha = True
  350.             End If
  351.             cHost.ImageType = imgGIF
  352.             ConvertGIFto32bpp = True
  353.         End If
  354.     End If
  355. End Function