# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  [vb6] Class to make Image Controls Support PNG, TIF,  GIF Animation

## LaVolpe

*THIS PROJECT NO LONGER SUPPORTED
SUPERSEDED BY A DIFFERENT PROJECT, SIMILAR LOGIC*

We all know VB is old and doesn't support many of the common image formats, specifically: TIF, PNG and alpha-blended icons. The attached class enables VB to support those formats and a bit more. There is no usercontrol involved. Just a bit of subclassing (IDE safe) to enable the images to be rendered with APIs that do support the alpha channel: AlphaBlend and DrawIconEx. The class is not limited to just Image controls, can be applied to most (if not all) of VB's picture, icon, and other image properties. 

Image formats supported. The 'includes' below are in addition to what VB supports: 
:: BMP. Includes 32bpp alpha and/or premultiplied. Includes those created with v4 & v5 of the bitmap info header
:: GIF. Includes methods to navigate frames
:: JPG. Includes CMYK color space
:: ICO,CUR. Includes 32bpp alpha and/or PNG-encoded Vista-type icons
:: WMF. Includes non-placeable
:: EMF
:: PNG
:: TIF. Both single page & multi-page. Supported compression schemes depend on version of GDI+ installed
See post #8 below for tips regarding design-view usage

*Important to remember*. This is still a VB stdPicture object. All of its properties and methods are supported, including Width, Height, Type, Render, Handle, etc.

The class methods/properties can be categorized as follows:

I. VB Replacement Methods
- LoadPicture: Unicode supported. Has options to keep original image format when loading. This enables you to navigate animated GIF frames and multipage TIFs. Cached data allows you to save that original data to file
- SavePicture: Unicode supported. Has options to always save to bitmap or save original format if it exists
- PictureType: Returns the original image format, i.e., GIF, PNG, TIF, etc, if that format exists
note: LoadPicture & SavePicture both accept byte array as source/destination medium

II. Management Methods
- IsManaged: Return whether the class is rendering the image or VB is
- UnManage: Simply unsubclasses a managed image
- HasOriginalFormat: Return whether or not any Picture is caching original image format data

III. Navigation Methods
- SubImageCount: Returns the number of frames/pages contained within the managed image
- SubImageIndex: Returns the current frame/page displayed by the managed image
- SubImage: Returns a stdPicture object of the requested frame/page
- GetGIFAnimationInfo: Returns an array containing loop count and each frame's display interval for animated GIFs

Quickly. How does this class do it?

1. It creates thunks to subclass the VB picture objects. The source code of the thunks are provided in the package. These thunks draw the image using the APIs AlphaBlend or DrawIconEx. For you ASM gurus out there: I'm a noob with ASM. I'm sure others can rewrite that ASM code to be more efficient.

2. To support AlphaBlend API, images may be converted to a 32bit bitmap having the pixel data premultiplied against the alpha channel. These bitmaps will never display correctly outside of the class. Therefore the class' SavePicture function allows you to create a copy of the bitmap that can be displayed outside of the class. This copy can be passed to the clipboard and/or drag/drop methods of your project.

3. GDI+ is relied upon to parse/load TIF and PNG. It is also used to support JPG in CMYK format, non-placeable WMFs and multi-frame GIF rendering. GDI+ is a requirement for the class. If it fails to load or thunks fail to be created, the class will silently fall back to standard VB methods and VB restrictions.

The transparency displayed by the image control is not faked. It is true transparency and the attached test project should easily prove that. Important to understand. TIF and PNG support is not available at design-time. This is because the class code isn't activated until run-time. Some motivated individuals out there could easily create a windowless usercontrol that hosts an image control (and this class) that could support all formats at design-time. Just a thought and subtle prod.

The class can be expanded by those willing to put in the effort. Ideas would be to incorporate GDI+ high quality scaling, conversion from one image format to another, image effects like rotation, blur, and more. Other image formats can easily be supported from outside the class. If you can parse/render that new format to a 32bpp bitmap, then you can use the class' LoadPicture to display that image. Have fun.
*Tip*: If modifying the thunks or code, recommend identifying these new versions using a different key. Throughout the code, you can find a couple instances of this text: IPIC+Thunker. Change those instances to reflect something else, i.e., "IPIC+Thunker.v2" so you can distinguish between your versions.

When compiled, VB can behave differently vs when uncompiled. Some differences are subtle, others are not. Here's one that is key for animating GIFs. In the test project posted below, the animation works because VB caches the GIF format for the GIF that was loaded into Image1 during design-time. During run-time that info is still cached by VB so the class can extract the entire GIF. But when you compile the project, the GIF no longer animates. Why? Well, when compiled, the GIF information is lost. VB no longer caches it. This can be proven in a simple test project. Add an image control and button. Add a GIF or JPG to that image control. Add the following code behind the button. Click the button when project is running compiled and uncompiled. Different results. The workaround is simply to save GIFs you want to animate in a resource file and load the GIF from that.


```
Dim IPic As IPicture
Set IPic = Image1.Picture
MsgBox CStr(IPic.KeepOriginalFormat)
```

*Known Limitations*:
1) VB-like scaling is in play. Did not spend the time to incorporate high-quality GDI+ scaling
2) Do not send any Picture object to the class if it was retrieved via Clipboard.GetData. Why? Well, on 32 bit screen displays, the alpha channel can be filled with garbage. This garbage will likely be misinterpreted as transparency information
3) If expecting to use the .Render method of the picture object (icons only), do not keep the icon picture object returned from this class as Managed. After loading the icon, call UnManage. See Post #37 for more

Design-time vs. Run-time screenshot

----------


## dilettante

Looks like a nice refinement of earlier concepts.

BTW:  Does "true" transparency include something I haven't seen such NARGB where "n" is the refractive index so that a magnifying glass magnifies what it gets composited on top of?  :Wink:

----------


## LaVolpe

> Looks like a nice refinement of earlier concepts.


Yep, by placing the drawing & subclassing in the thunk, no chance of END crashing the clients because no direct callbacks from the thunk to the class exists. Also, by using what I call a management window that is placed on the hidden VB owner (both when compiled/uncompiled), other crash-related issues are resolved. That management window can determine if IDE is paused/stopped and forego any notifications to the class. GDI+ instance lives in that window & when window is destroyed when thread is destroyed, that window shuts down GDI+ gracefully, another crash-related annoyance avoided. Unfortunately, this requires stuff to reside in memory that VB doesn't know about so VB can't automatically unload it and that means lots of 'management' code contained in the class; some of which is ever only executed once. The huge plus side is IDE-safety. Pre-declaring the class helps prevent premature/accidental class unloading.

Regarding GDI+ and the magnifier, there is a KB article here. Honestly don't know, haven't tested, whether any issues exist with premultiplied bitmaps. The workaround in the class uses a true DIB vs. GDI+ image object which should remove the GDI+ issue. Icons & metafiles don't use premultiplied bitmaps, all other formats do if any transparency is present.

Edited: Just tried it and, as suspected, no issues.

----------


## LaVolpe

As you folks play with this, you are likely to find something that isn't quite right. I'll patch those as they are identified. 

I found two that will be corrected on the next upload:

1) When GIF loaded in design-time and the project is compiled to an exe, StdPictureEx.LoadPicture(thatImage) will produce the GIF as a bitmap with the transparent color visible, not transparent. This is because VB is not caching the original GIF information & is extracting the image by its bitmap handle which produces the result. The patch is rather simple. Test for that scenario and simply return the passed image as the function's return result.

2) When calling StdPictureEx.LoadPicture and passing an existing picture object, you'll get the entire image format whether or not the KeepOriginalFormat parameter is set to False. This was an oversight by me. The logic was intended, and will be corrected, to only capture the current image if KeepOriginalFormat is passed as False. Broken logic only applicable to multi-frame/page GIF/TIF.

Both above issues are rather minor in scope. Will upload patch after considering any near-future 'bugs' you guys/gals post or after I've played with the class a bit more to see if I can find any other oddities.

----------


## georgekar

1. How can we draw a png in a picture box, with transparency but in X,Y position in a given width and height (so to draw at an area in picture box) (I would like to have Png in M2000 Interpreter).

2. I found that Image control have a flickering if we place another transparent image control. The same is happen if we replace image refresh with form refresh.

3. When I change one image (the empty one) with a picturebox then we have trancparency to that box, not to the form. So we can't make pictureboxes picture as transparent, only image.

BTW your code is excellent, without any problem in IDE.

----------


## LaVolpe

> 1. How can we draw a png in a picture box, with transparency but in X,Y position in a given width and height (so to draw at an area in picture box) (I would like to have Png in M2000 Interpreter).


You can extract the code in my project or do a quick search on this site. That question has been asked so many times. The process is simple enough: 1) load PNG using GDI+ or other DLL, 2) simply draw it at the X,Y coordinates using that DLL's render functions

Edited: Here is one such thread on this forum and there are more.




> 2. I found that Image control have a flickering if we place another transparent image control. The same is happen if we replace image refresh with form refresh.


That doesn't surprise me. VB redraws a windowless control by erasing what's behind it and redrawing the 'dirty' area. This is all done on the visible hDC, not in a backbuffer.  With overlapped controls, the drawing is snowball effect down to the lowest ZOrder control.

Edited: This can be corrected but would require rewriting part of the thunk that renders the image. The idea would be to create a 2nd DC and a bitmap equal to the size being rendered. Then BitBlt from the visible DC (passed to the thunk) to the 2nd DC. Thunk would render to that 2nd DC, then BitBlt the 2nd DC to the visible DC. Psuedo code would look like the following. Performance hit would be incurred with creation/destruction of temp DC and DIB, but flicker free rendering would be the result


```
Create New DIB of size: destWidth, destHeight
Create new DC and select New DIB into new DC
BitBlt from passed DC to new DC
Render to new DC
BitBlt from new DC to passed DC
Unselect new DIB from New DC. Destroy both new DIB & new DC
```




> 3. When I change one image (the empty one) with a picturebox then we have trancparency to that box, not to the form. So we can't make pictureboxes picture as transparent, only image.


The image control is a windowless control, the picturebox is not. Windowless controls draw directly on their container. Windowed controls have their own canvas. There is no easy way to make a picturebox transparent. Here is one method that 'fakes' transparency for a picturebox

----------


## LaVolpe

Updated 7 Nov 2015.

- Addresses minor bugs reported in post #4 above.

- Added HasOriginalFormat read-only property

- Reworked SavePicture method to allow saving to array also

- LoadPicture method logic revisited and modified. In short....

:: The return result is always a new Picture object, even if you pass the method an existing Picture object as the source image. Only exception here is that if the method fails, then the same source will be returned as the method result

:: If passing a managed GIF/TIF and not keeping original format, current frame/page extracted & sent to new Picture object else entire image is processed & returned

----------


## LaVolpe

Probably final enhancement made. Will support any bug-reports for quite awhile. 

Project in post #1 updated to include support for non-placeable WMFs. These are rare, but VB does not support them natively. Minor code tweaks added for improved sanity checks but nothing major.

Define non-placeable? Size-less WMF. It has no dimensions, thus no proportions. These types of WMFs are always machine/device-dependent. In other words, they are designed for a specific aspect ratio. Without knowing that ratio, applications must make their own decisions. Most apps will react by defining its dimensions to the size of the screen or some other arbitrary values. This class uses a 256x256 max size scaled proportionally to the current monitor size. In other words, rectangular and not exceeding 256x256.  The aspect-ratio decision is a guess and that decision determines whether the image looks or feels right. Is it better to guess square vs rectangle? Rhetorical question. A better & far more difficult solution would be to analyze the WMF internally and determine the extent of each drawing command; thereby having a better guesstimate of its true aspect-ratio. A lot of effort for what is basically a dead technology. 

Tip: If you want to create sample non-placeable WMFs, simply save to a new file, the old file, starting from byte #23. Placeable WMFs have a 22-byte header placed in front of the WMF. Remove this header & it's now non-placeable

Edited: And a note about using PNG,TIFF, etc and design-view. Recommend the following

1. As mentioned earlier, support for these image types, and any other non-VB supported image type, are not available during design-time, only run-time

2. Since you know the size of the image beforehand, size the target control appropriately and that control is simply a placeholder during design-time. Load the image via the resource file during Form_Load. If you don't know the image size, right clicking on it's file and selecting properties should give that information in one of the property page tabs.

3. I would not recommend creating 32bit bitmaps from PNG/TIF (as is done in the sample project) just so you can see the image during design-view. 32bit bitmaps can be fairly large, storage-wise. Makes a bit more sense to abide by previous paragraph. 

4. Also mentioned in post #1, if you are wanting to access individual sub-images from any multi-image format, place those in a resource file and load them from there. It's one way to guarantee the sub-images are available after your project is compiled to an exe. Sub-image formats can include TIF, GIF, Icons. Would not recommend loading GIFs in design-view if you want to animate them after project is compiled. You'll have to load GIFs from the resource file (or disk) to animate. No point in storing the GIF twice, once in the image control & once in the resource file.

5. Since TIF support is tied to the version of GDI+ installed on the system, would recommend converting your TIF images using whatever is at your disposal to a new TIF file, storing the pages with LZW compression scheme. That scheme is supported among all versions of GDI+. If the TIF can be loaded using v1.0 of GDI+, you should be good to go. Vista and XP require manifests to use GDI+ v1.1 and lower than XP can only use v1.0. Windows 7 and above always uses v1.1 without a need to manifest, though a manifest can request v1.0 prior to Windows 8. If your project will only run on Win7 or better, then this recommendation can be considered overkill.

----------


## LucasMKG

LaVolpe,
- this is the pinnacle of image control..well..thats my opinion. (PNG and animated Gifs.. in one control...i like it...this was what i was looking for while I was spitting VBA code)

- Keith, is it possible for your control  to replace imagelist? I can see that image sizes may vary...but is it possible to set listview; small, large, column and group header...or will it require more twikking to be listview friendly.

LucasMKG
?

----------


## Jonney

> LaVolpe,
> - this is the pinnacle of image control..well..thats my opinion. (PNG and animated Gifs.. in one control...i like it...this was what i was looking for while I was spitting VBA code)
> 
> - Keith, is it possible for your control  to replace imagelist? I can see that image sizes may vary...but is it possible to set listview; small, large, column and group header...or will it require more twikking to be listview friendly.
> 
> LucasMKG
> ?


He have developed an ImageList on PSC. 



> Title: LaVolpe ImageList II (8Jan08)
> Description: Updated, faster renderings. Based on of my c32bppDIB suite, supports pngs, xp/vista icons, and other common graphics. One control that supports multiple imagelists (same/different sizes). THIS IS NOT TRULY DESIGNED to be added uncompiled to a project, rather it is designed to be a stand-alone OCX. The property page is optional if you just need a runtime-only imagelist. 15Nov07:: Added optional image compression if GDI+/zLib not on O/S, added optional image key/tag compression, added multi-select browse, multi-file drag&amp;drop, multi-file Copy&amp;Paste abilities, added image re-ordering via dragging, added importing from VB imagelists, and fixed some minor bugs. 23Nov07: Masks were not kept with multi-selected files, added several more properties/methods, more examples. 3Dec07: Overhauled class structure, fixed minor errors with property page, added more to the RTF file. Barring bugs, moving on to something new. 8Jan08: Significantly faster rendering from the imagelist. GDI+ would process entire DIB at times to render one image. When imagelist is massive size, slow down noticable. Using a rendering-only DIB can improve drawing multiple images over 30x faster for large lists.
> Open with Group1.vbg. See Usage_ImageList.RTF file for overview.
> This file came from Planet-Source-Code.com...the home millions of lines of source code
> You can view comments on this code/and or vote on it at: http://www.Planet-Source-Code.com/vb...69621&lngWId=1

----------


## LaVolpe

@Jonney. I don't support that old project any longer. Also note that the project is not a common controls image list replacement because you cannot assign it to controls like listview, treeview, etc. And if I were to rewrite that (not interested), I've learned much more since then. It could be made simpler and more efficient via GDI+

@Lucas. Haven't really looked much at the common controls replacement project here in the code bank. Does Krool's project do what you are after?

----------


## LucasMKG

LaVolpe,
Krool hands are full, i don't wanna bother him...let him perfect those controls.

- GDI+, sounds good. all that's needed is an imagelist that support PNGs and GIF Animation:: this project is close enough..if only it could be fine-tuned.

- Jonney, i've been following LaVolpe from a distance since 2005...and admire his vb6 image brain...if he could consolidate his image encyclopedia into one control (preferrably imagelist).


LucasMKG
.

----------


## LaVolpe

Well the issue with an imagelist is a bit complex. The API created ones can support 32bpp bitmaps, manifest may be required, if I recall correctly. Theoretically, one can use the code in my class to pass a managed picture object to the imagelist, for copying from/appending, which it could then render on a listivew, etc. Likewise, someone could use their own code to convert PNG/TIF to 32bpp, or even icon maybe, and add that to imagelist dynamically. This would be cumbersome since you are talking about run-time population of the imagelist, images likely stored in resource file or resource-only dll. The imagelist ocx is more limited. When assigned to a control, the imagelist uses its own internal drawing methods. This class subclasses the stdPicture object. Comparing apples to oranges. vbAccelerator has a project that can force a listview & other controls, via subclassing, to use API-created imagelists.

Edited: @Lucas, I don't expect any imagelist to support GIF animation natively. The imagelist is basically a strip of images or individual bitmaps. If they accept a GIF file, I can only expect that the 1st frame would be used/rendered. Otherwise, one would need to store each frame separately within the imagelist. The imagelist is not a collection of stdPicture objects.

----------


## Jonney

I seldom used ImageList. My recent projects  used collection Images & Image, Image class hold hgdipImage and hAttributes. 
Your pioneering GDI+ projects help me a lot, now I can use GDI+ doing most things I want. Thank you Sir.

----------


## Jonney

I just download the new attachment. I saw lots of similar code in AlphaImage.

I walk around some codes (work for Alpha PNG), I will try to integrate into this class for testing:



```

Friend Function LoadImage(ByVal sFileName As String) As Boolean
    
    m_lImageWidth = 0
    m_lImageHeight = 0
    
    If Len(sFileName) = 0 Then Exit Function
    If DoesFileExist(sFileName) Then
        If m_bIsGDIPlusAvilable Then
           Dim Img As Long
           GdipLoadImageFromFile StrPtr(sFileName), Img
           If Img Then
              'Retrieve the image's format
              m_lImageType = GetImageType(Img)
              GdipGetImageWidth Img, m_lImageWidth
              GdipGetImageHeight Img, m_lImageHeight
              Set m_objPicture = GetPictureFromGDIpImg(Img, vbWhite)
              GdipDisposeImage Img
              LoadImage = True
           End If
       End If
    End If
    
End Function

Private Function GetPictureFromGDIpImg(Img As Long, Optional ByVal BackColor As Long = -1) As StdPicture
Dim I As Long, hDIB As Long, Rct As RECTL, BMSrc As BitmapData, BI&(9), pDst As Long
Const PixelFormat32bppPARGB& = &HE200B

  'If BackColor <> -1 Then 'if we are interested in just a "normal colored StdPic" (without AlphaChannel), then the following single Line is enough
    'GdipCreateHBITMAPFromBitmap Img, hDIB, BackColor
  
  'Else 'but this is "what you came for" (the extraction of the Premultiplied 32bit-Alpha-Data from the Png)
    GdipGetImageWidth Img, Rct.Width
    GdipGetImageHeight Img, Rct.Height
    GdipBitmapLockBits Img, Rct, 1, PixelFormat32bppPARGB, BMSrc
  
    If BMSrc.scan0 Then
      BI(0) = 40
      BI(1) = BMSrc.Width
      BI(2) = -BMSrc.Height
      BI(3) = 32 * 65536 + 1 '32bpp
      
      hDIB = CreateDIBSection(0, BI(0), 0, pDst, 0, 0) 'we create a DIB-section(Handle) with the approppriate 32bpp-size-allocation
      If hDIB Then 'if successful, then we copy over into pDst (and we keep in mind, that Stride can be longer than Width*4Bytes)
        For I = 0 To Rct.Height - 1
          MemCopy ByVal pDst + I * Rct.Width * 4, ByVal BMSrc.scan0 + I * Abs(BMSrc.stride), Rct.Width * 4
        Next I
      End If
      
      GdipBitmapUnlockBits Img, BMSrc
    End If
  'End If
  
  If hDIB Then Set GetPictureFromGDIpImg = GetPictureFromHdl(hDIB)
  
End Function

Private Function GetPictureFromHdl(ByVal hBmp As Long) As StdPicture
Dim IID_IDispatch(0 To 3) As Long, PicDesc(0 To 4) As Long
  IID_IDispatch(0) = &H20400: IID_IDispatch(2) = &HC0: IID_IDispatch(3) = &H46000000

  PicDesc(0) = 20
  PicDesc(1) = vbPicTypeBitmap
  PicDesc(2) = hBmp

  OleCreatePictureIndirect PicDesc(0), IID_IDispatch(0), 1, GetPictureFromHdl
End Function

Public Sub AlphaRenderTo(ByVal hDC As Long, Optional ByVal X As Long, Optional ByVal Y As Long, _
                                                           Optional ByVal dx As Long, Optional ByVal dy As Long, _
                                                           Optional ByVal xSrc As Long, Optional ByVal ySrc As Long, _
                                                           Optional ByVal wSrc As Long, Optional ByVal hSrc As Long, _
                                                           Optional ByVal GlobalAlpha As Double = 1)
  If wSrc = 0 Then wSrc = m_lImageWidth
  If hSrc = 0 Then hSrc = m_lImageHeight
 
  If m_objPicture.Handle Then OldBM = SelectObject(mhDC, m_objPicture.Handle)
  GdiAlphaBlend hDC, X, Y, dx, dy, mhDC, xSrc, ySrc, wSrc, hSrc, 2 ^ 24 + &HFF0000 * GlobalAlpha
  If OldBM Then SelectObject mhDC, OldBM
End Sub
```

----------


## LaVolpe

Yes, you can load an image via GDI+ and render it to a 32bpp DIB to use AlphaBlend to render that to a DC. Couple things:

1) If using GDI+ to load the image and using stdPicture to just cache the image, not sure of the benefits? Why not just use GDI+ to render, you would have the option of rendering at different scales using different interpolation/quality. Once the DIB is created, AlphaBlend's scaling is just as bad as VB. In the class I wrote (post #1), AlphaBlend is used in the assembly thunk. To use GDI+ for scaling would require re-writing that thunk.

2) You really do not need a loop to transfer the LockBits to DIB. One call is all that's needed
MemCopy ByVal pDst, ByVal BMSrc.scan0, Rct.Height * Rct.Width * 4

Caution: LockBits can't be used with metafiles. Crash will result. So, maybe a more generic approach is to render the GDI+ image to the 32bpp DIB instead of transferring the bits. Just a thought.

Edited & FYI: GDI+ rendering to a DIB where all bits are set to zero is identical to creating a premultiplied bitmap.

----------


## Jonney

> Yes, you can load an image via GDI+ and render it to a 32bpp DIB to use AlphaBlend to render that to a DC. Couple things:
> 
> 1) If using GDI+ to load the image and using stdPicture to just cache the image, not sure of the benefits? Why not just use GDI+ to render, you would have the option of rendering at different scales using different interpolation/quality.


It is weird. I know it. It is because of back compatibility. The old project has an Image Class and Images Collection. The Images Collection has two functions: 


```
Public Function Add(ByVal FileName As String, ByVal Key As String)
   '...
   End Function 
  Public Function AddPicture(Picture As StdPicture, ByVal Key As String)
  '...
  End Function
```

The Image Class has a Function:


```
Public Property Get Picture() As StdPicture

    Set Picture = m_objPicture

End Property
```

I just don't want to break the old structure but want for more Image formats support.

----------


## Jonney

> You really do not need a loop to transfer the LockBits to DIB. One call is all that's needed
> MemCopy ByVal pDst, ByVal BMSrc.scan0, Rct.Height * Rct.Width * 4


Yes, it works.



> Caution: LockBits can't be used with metafiles. Crash will result. So, maybe a more generic approach is to render the GDI+ image to the 32bpp DIB instead of transferring the bits. Just a thought.


Yes, it crash if we load EMF/WMF file.

The above code (@#15) is just for testing purpose. In the real production, I am hesitated to use such technique including the advanced StdPictureEx class. I just use simple GDI+ for graphical stuff. The reason is that it hard for me to manage and maintenance the codes. Another reason .NET also doesn't support all Formats.

----------


## Jonney

```
           Set m_objPicture = StdPictureEx.LoadPicture(sFileName)
           If Not m_objPicture Is Nothing Then
              LoadImage = True
              Dim Bmp As BITMAP
              GetObject Picture.Handle, Len(Bmp), Bmp
              m_lImageWidth = Bmp.bmWidth
              m_lImageHeight = Bmp.bmHeight
           End If
```



```
Public Sub AlphaRenderTo(ByVal hDC As Long, Optional ByVal X As Long, Optional ByVal Y As Long, _
                                                           Optional ByVal dx As Long, Optional ByVal dy As Long, _
                                                           Optional ByVal xSrc As Long, Optional ByVal ySrc As Long, _
                                                           Optional ByVal wSrc As Long, Optional ByVal hSrc As Long, _
                                                           Optional ByVal GlobalAlpha As Double = 1)
  If wSrc = 0 Then wSrc = m_lImageWidth
  If hSrc = 0 Then hSrc = m_lImageHeight
 
  If m_objPicture.Handle Then OldBM = SelectObject(mhDC, m_objPicture.Handle)
  GdiAlphaBlend hDC, X, Y, dx, dy, mhDC, xSrc, ySrc, wSrc, hSrc, 2 ^ 24 + &HFF0000 * GlobalAlpha
  If OldBM Then SelectObject mhDC, OldBM
End Sub

Private Sub Class_Initialize()
   mhDC = CreateCompatibleDC(0)
End Sub
```

OK, I am testing the StdPictureEx.LoadPicture function, I expect it is smart as AlphaImage. But there's limits due to different design?

Using StdPictureEx.cls loading File path:
1. I don't see the exposed Public Property of Image Size/Image Type...
2. I load  WMF/EMF/Color Management Jpg/ico, I see nothing.
3. If the file is gif with single frame, StdPictureEx.LoadPicture(sFileName) doesn't return the first frame. I guess I have to use SubImage for my case (above code)?

Edited: For 3, I use BitBlt, I see the single frame gif:


```
BitBlt hDC, X, Y, dx, dy, mhDC, xSrc, ySrc, vbSrcCopy
'GdiAlphaBlend hDC, X, Y, dx, dy, mhDC, xSrc, ySrc, wSrc, hSrc, 2 ^ 24 + &HFF0000 * GlobalAlpha
```

But lose Alpha features if use BitBlt.

----------


## LaVolpe

I am assuming that m_objPicture is declared as StdPicture?

1. Class doesn't need to expose dimensions. It's part of m_objPicture: .Width & .Height properties

2. WMF/EMF/ICO doesn't support ICM. I'm a bit confused there. I have no problems loading those file types.
- JPG color management. ICM is not supported, but could be added with a little effort.

3. I loaded a single-frame GIF and had no issues.




> I expect it is smart as AlphaImage


Bad assumption  :Wink:  
This project is a quick and easy way to support TIF, PNG, animated GIF using the VB Image control. It is not a mini-version of my AlphaImage control. I am playing with the idea of being able to assign an AlphaImage control image to a VB picture object. But this project is not that

----------


## Jonney

> I am assuming that m_objPicture is declared as StdPicture?


Good guess. :Smilie: 




> 1. Class doesn't need to expose dimensions. It's part of m_objPicture: .Width & .Height properties


We prefer pixels unit. :Big Grin: 




> 2. WMF/EMF/ICO doesn't support ICM. I'm a bit confused there. I have no problems loading those file types.
> - JPG color management. ICM is not supported, but could be added with a little effort.


I means for my case, I didn't see the graphic with BitBlt or GdiAlphaBlend.




> 3. I loaded a single-frame GIF and had no issues.[/CODE]
> Refer my edited #19. I have to use BitBlt.
> 
> [CODE]This project is a quick and easy way to support TIF, PNG, animated GIF using the VB Image control. It is not a mini-version of my AlphaImage control. I am playing with the idea of being able to assign an AlphaImage control image to a VB picture object. But this project is not that


I know it,Sir.
We need a better smart Function than GDI+ GdipLoadImageFromFile API which supports more formats BMPs/PNG/TIF/GIF/...

Something like:
hImage = Class1.LoadImageFile(FileName)  vs GdipLoadImageFromFile StrPtr(sFileName), hImage

then I can use GDI+ render functions (GdipDrawImage/GdipDrawImageRect/GdipDrawImageRectRectI) to draw hImage on any DC.

----------


## LaVolpe

Ok, for this project I've added lots of comments in the top of the class. But let me go over a couple things to help clear it up.

1. I did not add any enhancements to VB's Image control except for these:
-- can load alpha blended bitmaps, can load bitmaps with v4/v5 of the bitmap info header
-- can load CMYK JPGs
-- can load non-placeable WMFs
-- can load XP-style & Vista-style icons. can choose more than just the 1st icon if more than 1 exists
-- can load PNG, TIF
-- can animate GIF
-- unicode file support
Consider the class as an extension of VB's Image control to load more image types, not much more. The returned object is still a stdPicture.

2. The StdPictureEx.LoadPicture method will not create a 32bpp DIB if it does not need to. WMF/EMF are never 32bpp bitmaps unless the optional RequiredFormat parameter is passed as vbPicTypeBitmap.

3. To know if the bitmap is 32bpp premultiplied or not, query the class' IsManaged property and the stdPicture's .Type must be bitmap, not icon or metafile. If .Type = vbPicTypeBitmap and IsManaged = True then premultiplied 32bpp.




> Refer my edited #19. I have to use BitBlt.


Using BitBlt will always lose transparency. BitBlt doesn't support it, AlphaBlend does. Even if you didn't use my class, you would not get transparency if you tried to use BitBlt on a stdPicture.handle when the stdPicture contained a VB-loaded GIF. Trying to use AlphaBlend on a VB-loaded image is useless, unless you know for a fact that it is premultiplied 32bpp.

----------


## LaVolpe

Oh. Jonney. I should've mentioned from the start. You can still use the stdPicture.Render method and will do the alpha blending as expected. This works with images loaded from my class or loading from VB.

----------


## Jonney

> Oh. Jonney. I should've mentioned from the start. You can still use the stdPicture.Render method and will do the alpha blending as expected. This works with images loaded from my class or loading from VB.


Very good catch!

btw, I read {stdPicture Render Usage} in your signature. But I can't get .Render work, it always report "Type Miss Match" error.

I just need render at my own position,how to do that?



```
Public Sub Render( _
       ByVal hdc As Long, Optional ByVal dx As Long, Optional ByVal dy As Long, _
       Optional ByVal dW As Long, Optional ByVal dH As Long, _
       Optional ByVal xSrc As Long, Optional ByVal ySrc As Long, _
       Optional ByVal wSrc As Long, Optional ByVal hSrc As Long) 
                                 
   With m_objPicture
      .Render  
   End With
End Function
```

----------


## LaVolpe

> I just need render at my own position,how to do that?


Simple rules of thumb. 
-- If using variables, surround them with parentheses, i.e., (x), (y)
-- You can use numbers as is, i.e., 0, 256
-- The destination coordinates and dimensions are in pixels (scale your destination parameters to pixels)
-- The source coordinates and dimensions are in himetrics (scale your source parameters to himetric)
Remember to use the negative height as shown in that other post of mine

Edited: Better yet, and forgot about this until I re-read my last post in that other project. Declare all coordinate & destination variables as Single. Then the only parameter that may cause type mismatch would be a variable holding the destination DC. In that case, surround that variable with parentheses. Scaling to correct scalemode still needed.

----------


## Jonney

> Simple rules of thumb. 
> -- If using variables, surround them with parentheses, i.e., (x), (y)
> -- You can use numbers as is, i.e., 0, 256
> -- The destination coordinates and dimensions are in pixels (scale your destination parameters to pixels)
> -- The source coordinates and dimensions are in himetrics (scale your source parameters to himetric)
> Remember to use the negative height as shown in that other post of mine
> 
> Edited: Better yet, and forgot about this until I re-read my last post in that other project. Declare all coordinate & destination variables as Single. Then the only parameter that may cause type mismatch would be a variable holding the destination DC. In that case, surround that variable with parentheses. Scaling to correct scalemode still needed.


Magic parentheses!!!



> -- If using variables, surround them with parentheses, i.e., (x), (y)

----------


## Jonney

Give up.
IPicture.Render hardly do partial painting at vertical direction. But Horizontal can be partial. Am I right?



```
Public Function Render(ByVal hdc As Long, _
                             Optional ByVal destX As Single, Optional ByVal destY As Single, _
                             Optional ByVal destWidth As Single, Optional ByVal destHeight As Single, _
                             Optional ByVal srcX As Single, Optional ByVal srcY As Single, _
                             Optional ByVal srcWidth As Single, Optional ByVal srcHeight As Single) As Boolean
  '-- If using variables, surround them with parentheses, i.e., (x), (y)
  '-- You can use numbers as is, i.e., 0, 256
  '-- The destination coordinates and dimensions are in pixels (scale your destination parameters to pixels)
  '-- The source coordinates and dimensions are in himetrics (scale your source parameters to himetric)
  'Remember to use the negative height as shown in that other post of mine
   If srcWidth = 0 Then
      srcWidth = m_objPicture.Width
   Else
      srcWidth = srcWidth * 2540& * Screen.TwipsPerPixelX / 1440&
   End If
   If srcHeight = 0 Then
      srcHeight = -(m_objPicture.Height)
   Else
      srcHeight = -(srcHeight * 2540& * Screen.TwipsPerPixelY / 1440&)
   End If
   
   With m_objPicture
      '.Render (hdc), (destX), (destY), (destWidth), (destHeight), 0, (.Height - 1&), .Width, -.Height, ByVal 0&
      .Render (hdc), (destX), (destY), (destWidth), (destHeight), _
                      (srcX * 2540& * Screen.TwipsPerPixelX / 1440&), (m_objPicture.Height - 1 - (srcY * 2540& * Screen.TwipsPerPixelY / 1440&)), _
                      (srcWidth), (srcHeight), ByVal 0&
   Debug.Print srcHeight,m_objPicture.Height
   End With
End Function
```

For Example, If srcHeight is -4947.708 (Image Height is 5715), the whole graphic disappear.

Hard to understand why VB6 design .Render should give a negative value.

----------


## LaVolpe

You have to use the actual height along with the srcY for partial renderring

Using pixels for example. Let's say the image was 300 pixels in height and you wanted to render 200 of the 300 
SrcY = ActualHeight - SrcY i.e., SrcY = 300 - 200

I see you have this in a class and are manually converting to/from himetrics. But I'll use ScaleX & ScaleY for easier reading & assume all the parameters are passed in pixels. 


```
.Render (hDC), destX, destY, destWidth, destHeight, _
    srcX, .Height - ScaleY(SrcY, vbPixels, vbHimetric), SrcWidth, -SrcHeight, ByVal 0&
```

To prevent unexpected consequences, I'd recommend validating passed source parameters at a minimum & also ensure destination parameters are within reason. Maybe one of the following proves invalid?
- SrcY + SrcHeight cannot be less than 0 and cannot be > ActualHeight
- SrcX + SrcWidth cannot be less than 0 and cannot be > ActualWidth
- SrcHeight & SrcWidth must be between 0 and actual dimensions

----------


## Jonney

> You have to use the actual height along with the srcY for partial renderring
> 
> Using pixels for example. Let's say the image was 300 pixels in height and you wanted to render 200 of the 300 
> SrcY = ActualHeight - SrcY i.e., SrcY = 300 - 200
> 
> I see you have this in a class and are manually converting to/from himetrics. But I'll use ScaleX & ScaleY for easier reading & assume all the parameters are passed in pixels. 
> 
> 
> ```
> ...


The above code is the same with #27. 
I want to Render stdPicture on a Grid Cell, the Cell has 16 types of alignments, from what I tested, implement Vertical partial paint has difficulties. For example, I want to paint "RightBottom"(or LeftBottom or CenterBottom), With Cell height reducing, the picture should moving up as well, until Top of picture is covered. With the above code, if the cell height is smaller than picture actual height, the picture just disappear. I have tried many combinations, but still hard to do so,though my GDI+ render code works for all scenarios.



```
Public Enum AlignmentEnum

    GeneralGeneral = 0 'Horizontal Default; Vertical Default. 'Left Center
    GeneralTop = 1 'Horizontal Default; Vertical Top.         'Left Top
    GeneralCenter = 2 'Horizontal Default; Vertical Center.   'Left Center
    GeneralBottom = 3 'Horizontal Default; Vertical Bottom.   'Left Bottom
    LeftGeneral = 4 'Horizontal Left; Vertical Default.       'Left Center
    LeftTop = 5 'Horizontal Left; Vertical Top.               'Left Top
    LeftCenter = 6 'Horizontal Left; Vertical Center.         'Left Center
    LeftBottom = 7 'Horizontal Left; Vertical Bottom.         'Left Bottom
    CenterGeneral = 8 ' Horizontal Center; Vertical Default.  'Center Center
    CenterTop = 9 'Horizontal Center; Vertical Top.           'Center Top
    CenterCenter = 10 'Horizontal Center; Vertical Center.    'Center Center
    CenterBottom = 11 ' Horizontal Center; Vertical Bottom.   'Center Bottom
    RightGeneral = 12 ' Horizontal Right; Vertical Default.   'Right Center
    RightTop = 13 'Horizontal Right; Vertical Top.            'Right Top
    RightCenter = 14 'Horizontal Right; Vertical Center.      'Right Center
    RightBottom = 15 'Horizontal Right; Vertical Bottom.      'Right Bottom

End Enum
```



```
Friend Function Render(ByVal hdc As Long, _
                             Optional ByVal DestX As Long, Optional ByVal DestY As Long, _
                             Optional ByVal DestWidth As Long, Optional ByVal DestHeight As Long, _
                             Optional ByVal srcX As Long, Optional ByVal srcY As Long, _
                             Optional ByVal srcWidth As Long, Optional ByVal srcHeight As Long) As Boolean
                             
   Render = gdipStretchPicture(hdc, DestX, DestY, DestWidth, DestHeight, m_hImage, srcX, srcY, srcWidth, srcHeight)
   
End Function

Public Function gdipStretchPicture(ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal Width As Long, ByVal Height As Long, hImage As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal srcWidth As Long, ByVal srcHeight As Long, Optional ByVal gdipAttributes As Long, Optional ByVal srcUnit As GpUnit = UnitPixel) As Boolean
   
   Dim lR As Long
   lR = -1
   If hImage <> 0 Then
      Dim hGraphics As Long
      lR = GdipCreateFromHDC(hdc, hGraphics)
      lR = GdipDrawImageRectRectI(hGraphics, hImage, x, y, Width, Height, srcX, srcY, srcWidth, srcHeight, srcUnit, gdipAttributes)
      GdipReleaseDC hGraphics, hdc
      GdipDeleteGraphics hGraphics
   End If
   
   If lR = 0 Then gdipStretchPicture = True
   
End Function
```

----------


## LaVolpe

Jonney. You discovered a miscalculation in my ASM thunk. Took awhile to find the problem.

1. Replace your pvCreateThunks routine with this one.


```
 code removed, project updated. See post #37 for more info
```

2. Save the changes and close VB completely. Re-open the project and try again. Reason to close it is that if the thunk was originally created, the changes you made will not take effect because the project knows the thunk is in virtual memory & won't overwrite it. By closing VB completely, you release the virtual memory.

Let me know if it works as expected. I'll update this project once I know it works for you. Sorry about your frustration

----------


## Jonney

> Let me know if it works as expected. I'll update this project once I know it works for you. Sorry about your frustration


It is OK now. Very good,Very important fixes!  :Smilie:  
Thank you.

----------


## Jonney

So advanced class. I have no idea how you can come up with this. For education, Please explain the principle.

We all knew, even VBAccelerator's Steve is not the pioneer using ASM Thunk Subclassser, that is why he widely used annoying SSubTmr6.dll in his projects, Vlad, who is the first one using Thunk (HookMenu), then Paul wrote built-in Self-subclassing Class then you renovated to cSelfSubHookCallback class and your further practice makes perfect and go beyond all of them.

If you got time, you can write a book...about Subclasser and GDI/DIB/GDI+ Graphic stuff.

----------


## Jonney

> Image formats supported. The 'includes' below are in addition to what VB supports: 
> :: BMP. Includes 32bpp alpha and/or premultiplied. Includes those created with v4 & v5 of the bitmap info header
> :: GIF. Includes methods to navigate frames
> :: JPG. Includes CMYK color space
> :: ICO,CUR. Includes 32bpp alpha and/or PNG-encoded Vista-type icons
> :: WMF. Includes non-placeable
> :: EMF
> :: PNG
> :: TIF. Both single page & multi-page. Supported compression schemes depend on version of GDI+ installed


This class is ready to drop-in the existing VB6 project for image control to support those formats . But what about reversing this class from "ImageSource As Variant" or PictureBox.Picture to GDI+ hImage so that we can solely use GDI+ instead of old GDI? I know I can use your GDI+ class, but it hasn't updated for a while.

Something like:
Public Function LoadPicture(Optional ImageSource As Variant, _
                            Optional ByVal IconSize As LoadPictureSizeConstantsEx = lpsDefault, _
                            Optional ByVal IconColorDepth As Long, _
                            Optional ByVal DesiredIconCx As Long, _
                            Optional ByVal DesiredIconCy As Long, _
                            Optional ByVal KeepOriginalFormat As Boolean = False, _
                            Optional ByVal RequiredFormat As PictureTypeConstants = vbPicTypeNone) As Long

The output is GDI+ hImage. I think you understand what I means.

----------


## LaVolpe

> This class is ready to drop-in the existing VB6 project for image control to support those formats . But what about reversing this class from "ImageSource As Variant" or PictureBox.Picture to GDI+ hImage so that we can solely use GDI+ instead of old GDI?


Jonney, you could but the logic would need to be revised.

Currently: Thunk intercepts StdPicture.Render method and uses AlphaBlend (bitmaps), DrawIconEx (icons) for rendering. The class does not subclass WMF/EMF nor does it subclass any bitmap without transparency (GIF/JPG/TIF/PNG are considered bitmaps here). The only time the subclass thunk talks to the class is when a subclassed picture is completely released, i.e., set to Nothing.

To use GDI+ with user-options (attributes, effects, rotation, etc). First, a GDI bitmap will always be needed because StdPicture is a wrapper around a GDI bitmap. GDI+ can also wrap the same bitmap. You will need both a GDI bitmap & GDI+ hImage object. The thunk would need to know how to cross-reference the StdPicture.Handle to the associated GDI+ hImage handle. I think a simple method is for a class to create the GDI+ hImage and build a lookup table to cross-reference that hImage to StdPicture.Handle and vice versa. The class would also store user-defined options related to that hImage: Attributes, Effects, Rotation, Mirroring, etc, etc, or call back to a user via a raised event. When the thunk gets a .Render call, the thunk would call back to the class and pass all the .Render parameters plus the StdPicture handle. The class would then lookup the hImage for the passed handle and render to the passed DC or pass that info to the user via a raised event.

What I described above could turn a VB Image control into something like my AlphaImage control. Because subclassing is involved, the callbacks likely will not be IDE safe if a user hits END while subclassed picture is displayed. This would be a nice project for someone to develop if they choose to. I am sure there will be other obstacles encountered during the development.

Edited: If you are solely talking about converting the return result of the class LoadPicture method to GDI+ image. All the code is in the class right now. The class is already using GDI+ to load GIF, TIF, PNG, non-placeable WMF, CMYK JPG, and v4/v5 bitmap headers. Alphablended bitmaps are processed as DIBs and icons/cursors can be too. DIBs can be easily used in GDI+ to create images. GDI+ has no issues that I know of when loading metafiles

----------


## LaVolpe

Jonney. P.S. Using Render on a StdPictureEx-created picture that contains an icon will only render actual size of icon. 

This is something I didn't really consider since icons are generally rendered by VB actual size only. However, stdPicture.Render, PaintPicture, etc can render a portion of the icon or render it at different sizes. To correct this issue, a complete re-thinking of how to handle icons is required because I use DrawIconEx in the thunk. DrawIconEx does allow you to resize the output, but does not allow you to specify a source X,Y for partial renderings. Again, the current limitation is for managed icons only. Will consider solutions.

----------


## LaVolpe

Updated to address a .Render call bug discussed in previous few replies. Changed ASM source also uploaded

1. The thunk was corrected to allow partial rendering via the VB picture object's .Render method. Does not apply to icons.
2. The thunk was corrected to allow icons to be rendered at different sizes, i.e., Image control's Stretch property set to True.
3. Limitation remains... If picture type is icon and class' .IsManaged = True, cannot partially render icons.

*Icons* created from this class will not be capable of rendering them partially via the .Render method of the VB picture object, just entire image. They can be rendered at different sizes. Though VB will allow partial rendering, I decided the fix would require a massive rewrite of the subclassing thunk and not worth it. You do have a couple of options though.

a) After sending an *icon* to this class to load into a picture object, if the IsManaged property returns true and the .Type property returns vbPicTypeIcon, call the UnManage method. That will release the subclassing and keep the picture as an icon if required, i.e., a hIcon handle. Once unmanaged, you can use the .Render method of the picture object since VB will be rendering it now. You will be stuck with VB's limits in displaying 32 bit alpha blended icons.

b) If you know you will not need the picture to be an *icon*, that is, you just want it to be displayed in your project, you can pass the optional RequiredFormat to be vbPicTypeBitmap. This will convert the icon to bitmap, it will not be a hIcon but a hBitmap instead. As a bitmap, you can use the .Render method for partial rendering.

c) If neither of the above prove acceptable, then you can always take control manually. Whatever DC you will be using in the .Render call, create a clipping region on the DC to prevent any drawing outside the region. Render the image in its entirety, remove the clipping region. This would be the exact same result as partial rendering.

----------


## Jonney

> Updated to address a .Render call bug discussed in previous few replies. Changed ASM source also uploaded
> 
> 1. The thunk was corrected to allow partial rendering via the VB picture object's .Render method. Does not apply to icons.
> 2. The thunk was corrected to allow icons to be rendered at different sizes, i.e., Image control's Stretch property set to True.
> 3. Limitation remains... If picture type is icon and class' .IsManaged = True, cannot partially render icons.
> 
> *Icons* created from this class will not be capable of rendering them partially via the .Render method of the VB picture object, just entire image. They can be rendered at different sizes. Though VB will allow partial rendering, I decided the fix would require a massive rewrite of the subclassing thunk and not worth it. You do have a couple of options though.
> 
> a) After sending an *icon* to this class to load into a picture object, if the IsManaged property returns true and the .Type property returns vbPicTypeIcon, call the UnManage method. That will release the subclassing and keep the picture as an icon if required, i.e., a hIcon handle. Once unmanaged, you can use the .Render method of the picture object since VB will be rendering it now. You will be stuck with VB's limits in displaying 32 bit alpha blended icons.
> ...


All 3 methods are accepted! For my case, I will used method 3, Because I used .Rend in class.

----------


## LaVolpe

Jonney, just remember that the limitation only applies to Icons loaded via this class, and only if they are managed. And last but not least, limitation only applies if  you are trying to render just a portion of the icon by calling the picture object's .Render method. An icon loaded by this class will render just fine when it's control is refreshed/repainted.

----------


## baron-

First of all thank you very much for this incredible class. I've been using it for a while but recently encountered two machines where it crashes. Even the sample project included with this class crashes on these systems.

One is a 32bit Vista Ultimate system without SP1 installed. As soon as SP1 is installed, it stops crashing.
The second is a Windows 8.1 64bit tablet system with automatic updates always running. I tested on a different Windows 8.1 machine and it works on that one so there must be something specific on that machine.

I narrowed down the crash to the following line of code in the pvCreateThunks function when it subclasses IPictureDisp IUnknown:Release:
     CopyMemory ByVal pvSafePointerAdd(vIPicDisp, 8&), pvSafePointerAdd(vThunks(1), 14& * 4&), 4&

Unfortunately, I do not understand the code enough yet to debug it further, and although it would make sense that there is a dll problem, I do not know what dlls to search for. So I was wondering if you could save me a lot of time and tell me what to look for next? Thanks.

----------


## baron-

OK I debugged all day, even though my understanding on some of these subjects is weak, and I found a solution. But I would appreciate it if someone here would enlighten me on two things: Whether the solution is solid, and why my findings demonstrated this behaviour.

The problem, within my very limited understanding, is that the IPictureDisp and the IPicture vtables are two different vtables on these problematic machines that I mentioned above, whereas the code thunks both as if they were one. I.e. both the address and the size returned by VirtualQuery are different for these two interfaces.

So, it seemed to me that the solution is to thunk each one separately using their respective addresses/sizes. Since I do not know how to change the ASM code, I hacked the code so that it allocates memory for both thunks in the same place, but I have no idea if my approach is solid. My code is as follows:



```
    '/////////////////////////////////////////////////////////////////////////////////////////////////////
    ' Copy of COM's VTable for IPicture & IPictureDisp
    '/////////////////////////////////////////////////////////////////////////////////////////////////////
    
    Dim MBI2() As Long, addressvIPicDisp As Long
    ReDim MBI(0 To 6)                               ' get memory used by COM for IPicture/IPictureDisp
    ReDim MBI2(0 To 6)                               ' get memory used by COM for IPicture/IPictureDisp
    VirtualQuery ByVal vIPic, MBI(0), 28&           ' MBI() equivalent to MEMORY_BASIC_INFORMATION udt
    VirtualQuery ByVal vIPicDisp, MBI2(0), 28&           ' MBI() equivalent to MEMORY_BASIC_INFORMATION udt
    
    vThunks(2) = VirtualAlloc(0&, MBI(3) + MBI2(3), MEM_COMMIT, PAGE_RWX) ' reserve memory for same size
    If vThunks(2) = 0& Then GoTo ExitRoutine
    addressvIPicDisp = pvSafePointerAdd(vThunks(2), MBI(3))
    
    CopyMemory ByVal vThunks(2), ByVal MBI(0), MBI(3) ' copy VTable memory and we will hack this copy only
    CopyMemory ByVal addressvIPicDisp, ByVal MBI2(0), MBI2(3) ' copy VTable memory and we will hack this copy only
    
    CopyMemory ByVal pvSafePointerAdd(vThunks(1), 12&), vThunks(2), 4& ' update thunk #2 with address of this copy
   
    vIPicDisp = pvSafePointerAdd(addressvIPicDisp, vIPicDisp - MBI2(0))
    ' subclass IPictureDisp IUnknown:Release
    CopyMemory ByVal pvSafePointerAdd(vIPicDisp, 8&), pvSafePointerAdd(vThunks(1), 14& * 4&), 4&
   
    vIPic = pvSafePointerAdd(vThunks(2), vIPic - MBI(0)): Erase MBI()
    ' subclass IPicture IUnknown:Release
    CopyMemory ByVal pvSafePointerAdd(vIPic, 8&), pvSafePointerAdd(vThunks(1), 26& * 4&), 4&
    ' subclass IPicture:Render
    CopyMemory ByVal pvSafePointerAdd(vIPic, 32&), pvSafePointerAdd(vThunks(1), 42& * 4&), 4&
    ' subclass IPicture:GetAttributes
    CopyMemory ByVal pvSafePointerAdd(vIPic, 64&), pvSafePointerAdd(vThunks(1), 31& * 4&), 4&
```

----------


## Black_Storm

hi i hv 3 question

1: how can use of this class for load pictures on layered window(for example transparent form,please send a example projecte used this class, i used alpha image control but i dont want load all of classes and control and ... i want just use a simple class like ur class )

2: how can load apng format with this class(when this calss can suppore png or tiff multipage why can not support apng?
i want load animation png on form(transparent form like as desktop)


3: how can load psd format (for example psd old format with adobe 8 cs for better processing) and i want load each layers on layred window or i want find counts of layers and ....?
or if psd format is hard so no matter i can use tiff format but i need load each layers on separated layered window

(for example i hv a a.psd or a.tiff with 10 layers and i named each layer to open close hover disable or like this and i want load like object and when mouse entered on object hover layer can show or ....) i see like this on some softwares and i want create like that with vb.

----------


## LaVolpe

PSD and APNG are not supported by this class. If you can extract an image in bmp, jpg or tiff format from PSD (common in PSDs I believe), then you can send the bytes to this class. 

Edited: I have no PSD parsing routines and am not interested in supporting PSD. That does not stop anyone from using whatever they want to parse the PSD themselves to extract the image format. Looks like the format is described here

If APNG is a requirement, suggest not using this class. Though, maybe over the next few days, I'll play with the idea. Initial thoughts are parsing the APNG and saving it to multi-page TIFF format and then treating the TIFF much like how this class handles GIF. Just a thought & no promises. 

As far as your first question, let me create a simple example and post a bit later (tonite or tomorrow). You'll need a short routine to convert any image to 32bpp pre-multiplied so that you can use layered window APIs.  Kinda busy with something else at the moment.

----------


## Black_Storm

ok i am waiting to question 1 samples and i hv some sources to work with psd format in vb (special photoshop 8 or 6 format - is different with current PSDfiles created by photoshop versions) i can send a sample using psd by vb but i want u  check it and maybe can convert to a class for use in a sample project and used by your class too. i think it is easy for your because psd sample worked with vb is very simple but my problem is in work with graphic in vb6 and i think it is easy for get counts of layers and ....

----------


## LaVolpe

Here is a simple example using layered windows. I assume you already know how to apply the attributes and create the bitmap required for layered window APIs. If so, the part of this code you will be interested in is Form_Load

1. Add a new form to the test project included the one from the 1st post in this thread. Make form borderless
2. Set your project startup to begin with the new form
3. Copy and paste this into the new form


```
Option Explicit

Private Declare Function UpdateLayeredWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal hdcDst As Long, ByRef pptDst As Any, ByRef pSize As Any, ByVal hdcSrc As Long, ByRef pptSrc As Any, ByVal crKey As Long, ByRef pblend As Long, ByVal dwFlags As Long) As Long
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetLayeredWindowAttributes Lib "user32.dll" (ByVal hWnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function GetDC Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Sub FillMemory Lib "kernel32.dll" Alias "RtlFillMemory" (ByRef Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hDC As Long, ByRef pBitmapInfo As Any, ByVal un As Long, ByRef lplpVoid As Long, ByVal Handle As Long, ByVal dw As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Const WS_EX_LAYERED As Long = &H80000
Private Const GWL_EXSTYLE As Long = -20
Private Const ULW_ALPHA As Long = &H2
Private Const ULW_COLORKEY As Long = &H1
Private Const WM_NCLBUTTONDOWN As Long = &HA1
Private Const HTCAPTION As Long = 2
Private Const AC_SRC_ALPHA As Long = &H1
Private Const AC_SRC_OVER As Long = &H0
Private Const GWL_STYLE As Long = -16
Private Const WS_BORDER As Long = &H800000
Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type Size
    Cx As Long
    Cy As Long
End Type
Private Type BITMAPINFOHEADER        ' GDI structure
    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 m_hDC As Long, m_hBmp As Long, m_DIBptr As Long
Private m_Opacity As Long
Private m_MousePoints As POINTAPI
Private m_Size As Size

Private Sub Form_DblClick()
    Unload Me
End Sub

Private Sub Form_Load()

    Dim tPic As StdPicture
   
    m_MousePoints.x = -1&
    m_Opacity = 255
    
    Set tPic = StdPictureEx.LoadPicture(LoadResData("PNG", "CUSTOM"), , , , , , vbPicTypeBitmap)
    'Set tPic = StdPictureEx.LoadPicture(LoadResData("ICON", "CUSTOM"), lpsCustom, , 256, 256)
    
    m_Size.Cx = ScaleX(tPic.Width, vbHimetric, vbPixels)
    m_Size.Cy = ScaleY(tPic.Height, vbHimetric, vbPixels)
    If pvCreateLWdc = False Then
        Stop
        ' error couldn't create a basic DC & bitmap
    End If
    Me.Move Me.Left, Me.Top, m_Size.Cx * Screen.TwipsPerPixelX, m_Size.Cy * Screen.TwipsPerPixelY
    tPic.Render (m_hDC), 0!, 0!, (m_Size.Cx), (m_Size.Cy), 0!, tPic.Height, tPic.Width, -tPic.Height, ByVal 0&
    
    SetWindowLong Me.hWnd, GWL_EXSTYLE, GetWindowLong(Me.hWnd, GWL_EXSTYLE) Or WS_EX_LAYERED
    Call pvUpdateWindow
    Show
    
End Sub

Private Sub pvUpdateWindow()
    Dim srcPt As POINTAPI
    Dim lBlendFunc As Long
    lBlendFunc = AC_SRC_OVER Or (m_Opacity * &H10000) Or (AC_SRC_ALPHA * &H1000000)
    UpdateLayeredWindow Me.hWnd, 0&, ByVal 0&, m_Size, m_hDC, srcPt, 0&, lBlendFunc, ULW_ALPHA
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
    If Button = vbLeftButton And m_MousePoints.x > -1& Then
        If m_MousePoints.x <> x And m_MousePoints.y <> y Then
            ReleaseCapture
            m_MousePoints.x = -1
            SendMessage Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, ByVal 0&
        End If
    End If
End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    If Button = vbLeftButton Then
        m_MousePoints.x = x
        m_MousePoints.y = y
    End If
End Sub

Private Function pvCreateLWdc() As Boolean
    Dim BHI As BITMAPINFOHEADER
    Dim tDC As Long
    With BHI
        .biBitCount = 32
        .biHeight = -m_Size.Cy
        .biWidth = m_Size.Cx
        .biPlanes = 1
        .biSize = Len(BHI)
    End With
    tDC = GetDC(0)
    m_hDC = CreateCompatibleDC(tDC)
    ReleaseDC 0, tDC
    m_hBmp = CreateDIBSection(m_hDC, BHI, 0&, m_DIBptr, 0&, 0&)
    If m_hBmp <> 0 Then
        m_hBmp = SelectObject(m_hDC, m_hBmp)
        pvCreateLWdc = True
    Else
        DeleteDC m_hDC: m_hDC = 0&
    End If
End Function

Private Sub Form_Unload(Cancel As Integer)
    If m_hBmp <> 0 Then
        DeleteObject SelectObject(m_hDC, m_hBmp)
        DeleteDC m_hDC
    End If
End Sub
```

An example of using an icon is also in the Form_Load, but commented out. The form uses a PNG in this example. GIFs are not working so avoid those for layered window images. 

Avoid hitting STOP while using the class. The sample form can be closed by double clicking on it. It can be dragged around the desktop.

If your goal is to animate GIFs onto layered windows, let me know. I'll see if I can tweak the class to support GIFs to 32bpp premultiplied so you can use it correctly. In any case, animation would require rendering each GIF frame to the layered window bitmap and calling UpdateLayeredWindow. The test project (Form1) included with this class shows how animation is performed and how to navigate the GIF frames.

Icons less than 32bpp don't work either in this case. I'll have to look at the routine that is supposed to return/convert to 32bpp bitmap. I think I coded it to convert to 32bpp bitmap only if necessary vs. always if user wants it that way. Makes sense to me because why subclass the image if not needed. In this case, it may be needed to render 32bpp premultiplied correctly. The icon in the example works because there are 32bpp icons in the resource file icon.

FYI: This is nearly the exact same example I used when describing how to do this with the AlphaImage Control

----------


## Black_Storm

hi i used ur codes but now i hv a problem for use form controls too, i want use this class and form controls too in same time,in this code just used dc.how can merge layered window with form and this class too.for example i want use text boxes or commands or .... on form and images used by this class too(can u send a sampe code or project)

and how can make animation (not gif) just png pictures on layered window or forms or merged both by this class ?

and  any ways to can use sprite sheets png ? u hv any class  or control for it? i can use apng in alpha image control but i want know any way for import animations like sprite sheets in these classes? and make animation on forms or layared window merged with form like games?

----------


## LaVolpe

This thread isn't the place to ask about using controls on layered windows. I'd recommend posting those questions in the VB Forums section. 

For animation and this class: 

1. The class does not animate. It is the responsibility of the user (you) to begin and stop animation with whatever method you want. In the demo provided with this class, you can see one way to animate using a simple timer. The class can pass you the information you need for animation: frame count, duration times, etc.

2. Animating PNGs is not yet supported because this class does not parse APNG files. I mentioned that I will consider adding that capability, but that will take a few days. And even if I provide the capability, it will not be as efficient as the Alpha Image Control. I purposely did not intend this project to be a mini-AlphaImage control. This class was only intended to extend VB's Image control and stdPicture object.

----------


## Black_Storm

i hv a sample for read a special file format (PSD) - i dont want use png or tif or .... .
read pixels strcure is like this :


```
	For y = 0 To FHeight - 1
		For x = 0 To FWidth - 1
			r = ScanR(y * FWidth + x)
			g = ScanG(y * FWidth + x)
			b = ScanB(y * FWidth + x)
			a = ScanA2(y * FWidth + x)
			If a > layers(i).Opacity Then a = layers(i).Opacity
			SetPixelA theHdc, x + FLeft, y + FTop, r, g, b, a
		Next x
		If y Mod 32 = 0 Then FObject.Refresh: DoEvents
	Next y
```

theHdc is same target hdc like form.hdc or picturebox.hdc ( not worked on image box)

1.how can show this data on image box too ?

2.how i can load this array in to ur class?(send a sample project pls)

and my other question :
i can show this array on picture box or form.picture but i want show on layerd window.
when i used like ur sent code  (pvCreateLWdc and pvUpdateWindow and hdc was been set picturebox.hdc or form hdc) not worked good
3.how can show this array on layered window and can click or move dc?

----------


## LaVolpe

The first step is to understand how to create a bitmap from raw bytes.

Suggestion: Post a thread in the classic VB Forums section asking how to transfer PSD bytes to a bitmap. When I and others see that thread, we will reply. Regardless whether you use this class or not, you will want to be able to transfer the bytes to a bitmap. After you understand how to do that, then the following will apply...

If you create the bitmap in premulitplied format, you don't even need this class. You could use AlphaBlend to draw the bitmap directly to m_hDC (pvCreateLWdc ) or any other DC. 

And if you still want to use this class, do not premulitiply the pixels. Then you could pass the bitmap handle to the class' pvHandleToStdPicture function (make it public so you can call it), then pass the returned stdPicture to the class' LoadPicture function.

You have choices, but first thing is first. You need to understand how to transfer raw bytes to a bitmap in the proper format. And that applies whether you use this class or not.

----------


## Black_Storm

hi created this thread : *http://www.vbforums.com/showthread.p...35#post5216435*

i completed my project but i need optimize it and so i asked questions in that thread.
i am waiting to ur answers and thanks.

----------


## xiaoyao

The image control can be transparent and moved, and together with the LABEL control, it supports up and down switching. But if you put this control on the PICTURE, can't display PNG images transparently.


```
Private Sub Command2_Click()
    Set Image1.Picture = StdPictureEx1.LoadPicture(App.Path & "\02AlphaPNG.png")
    Set Image2.Picture = StdPictureEx1.LoadPicture(App.Path & "\02AlphaPNG.png")
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
Image2.Left = Image2.Left + 10
End Sub
```

----------


## wqweto

Btw, it says that this project is no longer maintained and the original author LaVolpe said good bye to these forums a couple of months ago.

If Image1 and Image2 in the snippet above are VB.Image controls then there is no way to make these controls paint alpha transparent StdPictures unless hooking the StdPicture.Render method as has been show before in other threads here.

I know that I will probably regret it but. . . here is a link to a single-file windowless image control that can load and paint 32-bit PNG images with alpha transparency: https://github.com/wqweto/AlphaBlendImage

cheers,
</wqw>

----------


## xiaoyao

how to loadpicture from png only by gdi32.dll?or gdiplus.dll?
not with thunk,need your help,thank you

----------


## xiaoyao

> Btw, it says that this project is no longer maintained and the original author LaVolpe said good bye to these forums a couple of months ago.
> 
> If Image1 and Image2 in the snippet above are VB.Image controls then there is no way to make these controls paint alpha transparent StdPictures unless hooking the StdPicture.Render method as has been show before in other threads here.
> 
> I know that I will probably regret it but. . . here is a link to a single-file windowless image control that can load and paint 32-bit PNG images with alpha transparency: https://github.com/wqweto/AlphaBlendImage
> 
> cheers,
> </wqw>


Set Image1.Picture = StdPictureEx1.LoadPicture(App.Path & "\02AlphaPNG.png")
Set me.Picture = StdPictureEx1.LoadPicture(App.Path & "\02AlphaPNG.png")

it's with Transparent channel，it's very nice

----------


## wqweto

Try Set AlphaBlendImage1.Picture = AlphaBlendImage1.GdipLoadPicture(App.Path & "\02AlphaPNG.png") instead.

There are several helper functions too: GdipLoadPicture, GdipLoadPictureArray, WicLoadPicture and WicLoadPictureArray for loading a transparent StdPicture using GDI+ and WIC respectively and several miscellaneous functions like GdipSetClipboardDib, GdipSetClipboardDibV5 and GdipUpdateLayeredWindow which are self-explanatory what they do.

You have to realize that the first step is to *load* a transparent PNG into a StdPicture (this is not easy) and the second step is to *paint* a transparent StdPicture. You've been looking for transparent paint left and right but the point is you don't even know how to load a 32-bit alpha PNG into an StdPicture.

cheers,
</wqw>

----------


## xiaoyao

> Try Set AlphaBlendImage1.Picture = AlphaBlendImage1.GdipLoadPicture(App.Path & "\02AlphaPNG.png") instead.
> 
> There are several helper functions too: GdipLoadPicture, GdipLoadPictureArray, WicLoadPicture and WicLoadPictureArray for loading a transparent StdPicture using GDI+ and WIC respectively and several miscellaneous functions like GdipSetClipboardDib, GdipSetClipboardDibV5 and GdipUpdateLayeredWindow which are self-explanatory what they do.
> 
> You have to realize that the first step is to *load* a transparent PNG into a StdPicture (this is not easy) and the second step is to *paint* a transparent StdPicture. You've been looking for transparent paint left and right but the point is you don't even know how to load a 32-bit alpha PNG into an StdPicture.
> 
> cheers,
> </wqw>


  Call GdipCreateFromHDC(Me.hDC, pGraphics)
     Call GdipLoadImageFromFile(StrConv(c_pngPath, vbUnicode), pImg)
     Call GdipGetImageWidth(pImg, w)
     Call GdipGetImageHeight(pImg, h)
     Call GdipDrawImageRect(pGraphics, pImg, 0, 0, w, h)

i only khnow how to GdipDrawImageRect,but don't khnow set alpha png to StdPicture

Set Image1.Picture = AlphaBlendImage1.GdipLoadPicture(App.Path & "\bbb.png"),THIS code ,The background image is not transparent, it is black

----------


## wqweto

VB.Image cannot display transparent StdPictures so you have to use AlphaBlendImage control. This is the second part of the problem when *painting* transparent StdPictures is not working.

----------


## xiaoyao

image.picture=*
He used this component to make the picture transparent, but how to zoom in?
If you use the control itself to zoom out, it will be jagged and deformed.

----------

