Theos PowerBasic Museum 2017

Webmaster: José Roca (PBWIN 10+/PBCC 6+) (SDK Forum) => Discussion => Topic started by: Dan Gin zel on March 23, 2013, 09:20:25 AM

Title: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 23, 2013, 09:20:25 AM
Briefly, I have been trying to figure out how to load a PNG from my embedded resources in hopes of taking advantage of transparency in my images.

I have no issue embedding a BMP in my resources and using GdipCreateBitmapFromStream to load it.

I can load a PNG from a file but I would really rather not have keep a number of PNG files attached to my EXE when embedding with #RESOURCE would be so much cleaner.

Any input that might point me in the right direction would be most appreciated.

Cheers!

Dan

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 23, 2013, 10:18:29 AM
Use GdipCreateBitmapFromResource.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 23, 2013, 04:06:45 PM
As soon as I saw your response, I realize what I had typed (at 2 AM.)

Yes, I am using GdipCreateBitmapFromResource 

Is there a special way I am supposed to put the PNG file in the resources ? Right now, I'm using

     #RESOURCE BITMAP, 15, "images\options.bmp"
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 23, 2013, 04:53:25 PM
DATA
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 23, 2013, 06:47:57 PM
Use RCDATA instead of BITMAP.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 23, 2013, 08:32:09 PM
I did try that earlier. Just tried it again without success.

     #RESOURCE RCDATA, 15, "images\options2.png"

and

               ResourceName = UCODE$("#15")
               Result = GdipCreateBitmapFromResource(hModule, ResourceName, pGDIPIcon)

The EXE is increasing in size appropriately so I believe the PNG is being included.

I believe my problem is how I am referencing the resource or perhaps there being another step. I have tried using the BMP version as RCDATA and it doesn't work, either.

I shall continue to research...
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 23, 2013, 08:59:08 PM
It is much more complex than that.

You will have to use GdipCreateBitmapFromStream, at least it is what i am doing in GDImage.

...
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 23, 2013, 09:08:11 PM
Several things point to using streams but I am not sure I understand what a "stream" is.

Patrice, can you offer a one or two sentence description of what a stream is from your practical experience?

My guess is that it is a collection of data elements combined sequentially.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 23, 2013, 09:52:41 PM
The problem is what kind of handle would you retrieve a GDI32 bitmap or a GDIPLUS image?

a stream is a flow of data of any type (audio, image, video, etc.), suitable for internet streaming or resource streaming from a DLL.

...
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 24, 2013, 12:08:39 AM
I need to learn about streaming audio. This going me on my to-do list shortly.

I found a function LoadImageFromResource that takes care of everything. (Source: http://www.powerbasic.com/support/pbforums/showthread.php?t=24563 (http://www.powerbasic.com/support/pbforums/showthread.php?t=24563))

Thanks for y'alls help. The input helped me get the pieces working together.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 24, 2013, 06:56:37 AM
This is wrong:

ResourceName = UCODE$("#15")

Give the resource a name in the .rc file, e.g.

PngResource RCDATA "MyImage.png"

and the use

ResourceName = UCODE$("PngResource")
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 24, 2013, 10:07:06 AM
Because i am in a good day  8)

Image handle
FUNCTION ZI_ImageFromResource ALIAS "ZI_ImageFromResource" (BYVAL nInstance AS LONG, zName AS ASCIIZ) EXPORT AS LONG
    LOCAL hResource     AS LONG
    LOCAL imageSize     AS LONG
    LOCAL pResourceData AS DWORD
    LOCAL MemBuffer     AS DWORD
    LOCAL pBuffer       AS DWORD
    LOCAL pStream       AS DWORD PTR
    LOCAL hImage        AS LONG

    hResource = FindResource(nInstance, zName, BYVAL %RT_RCDATA)
    IF hResource THEN
       imageSize = SizeofResource(nInstance, hResource)
       IF imageSize THEN
          pResourceData = LockResource(LoadResource(nInstance, hResource))
          IF pResourceData THEN
             MemBuffer = GlobalAlloc(%GMEM_MOVEABLE OR %GMEM_NODISCARD, imageSize)
             IF MemBuffer THEN
                pBuffer = GlobalLock(MemBuffer)
                IF pBuffer THEN
                   CALL MoveMemory(BYVAL pBuffer, BYVAL pResourceData, imageSize)
                   IF CreateStreamOnHGlobal(MemBuffer, %FALSE, pStream) = 0 THEN
                      IF GdipCreateBitmapFromStream(pStream, hImage) = 0 THEN
                         FUNCTION = hImage
                      END IF
                      CALL IUnknown_Release(pStream)
                   END IF
                END IF
                CALL GlobalUnlock(MemBuffer)
             END IF
             CALL GlobalFree(MemBuffer)
          END IF
       END IF
    END IF
END FUNCTION


bitmap handle
FUNCTION ZI_LoadFromResource ALIAS "ZI_LoadFromResource" (BYVAL hWnd AS LONG, zName AS ASCIIZ) EXPORT AS LONG
    LOCAL hResource     AS LONG
    LOCAL imageSize     AS LONG
    LOCAL pResourceData AS DWORD
    LOCAL MemBuffer     AS DWORD
    LOCAL pBuffer       AS DWORD
    LOCAL pStream       AS DWORD PTR
    LOCAL hImage        AS LONG
    LOCAL hbmReturn     AS LONG

    hResource = FindResource(zInstance, zName, BYVAL %RT_RCDATA)
    IF hResource THEN
       imageSize = SizeofResource(zInstance, hResource)
       IF imageSize THEN
          pResourceData = LockResource(LoadResource(zInstance, hResource))
          IF pResourceData THEN
             MemBuffer = GlobalAlloc(%GMEM_MOVEABLE OR %GMEM_NODISCARD, imageSize)
             IF MemBuffer THEN
                pBuffer = GlobalLock(MemBuffer)
                IF pBuffer THEN
                   CALL MoveMemory(BYVAL pBuffer, BYVAL pResourceData, imageSize)
                   IF CreateStreamOnHGlobal(MemBuffer, %FALSE, pStream) = 0 THEN
                      IF GdipCreateBitmapFromStream(pStream, hImage) = 0 THEN
                         IF hWnd THEN
                            IF zSetGdipImageHandle(hWnd, hImage) THEN CALL ZI_UpdateWindow(hWnd, %TRUE)
                         ELSE ' Return a standard bitmap
                            CALL GdipCreateHBITMAPFromBitmap(hImage, hbmReturn, background&)
                            CALL GdipDisposeImage(hImage) ' Delete image
                            FUNCTION = hbmReturn
                         END IF
                      END IF
                      CALL IUnknown_Release(pStream)
                   END IF
                END IF
                CALL GlobalUnlock(MemBuffer)
             END IF
             CALL GlobalFree(MemBuffer)
          END IF
       END IF
    END IF
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 24, 2013, 10:47:27 AM
Ah, yes. I had forgot that GdipCreateBitmapFromResource only works with bitmaps. Looks like after some months of inactivity I'm beginning to forget programming.

In my graphic control I'm using:


' ========================================================================================
' Loads an image from a resource file using GDI+
' Parameters:
' * hwnd = Control's window handle
' * hInstance = Instance handle
' * wszResourceName = Name of the resource image.
' Return value:
' * %StatusOk or an error code.
' ========================================================================================
FUNCTION GdipImageCtx_LoadImageFromResource (BYVAL hwnd AS DWORD, BYVAL hInstance AS DWORD, BYREF wszResourceName AS WSTRINGZ) AS LONG

   LOCAL hStatus AS LONG                   ' // Result code
   LOCAL hDc AS DWORD                      ' // Device context handle
   LOCAL pGraphics AS DWORD                ' // Graphocs object pointer
   LOCAL hResource AS DWORD                ' // Resource handle
   LOCAL pResourceData AS DWORD            ' // Pointer to the resoruce data
   LOCAL hGlobal AS DWORD                  ' // Global memory handle
   LOCAL pGlobalBuffer AS DWORD            ' // Pointer to global memory buffer
   LOCAL pImageStream AS IStream           ' // IStream interface pointer
   LOCAL hBgBrush AS DWORD                 ' // Background brush
   LOCAL imageSize AS DWORD                ' // Image size
   LOCAL rc AS RECT                        ' // Image bounds
   LOCAL rcFill AS RECT                    ' // Fill area
   LOCAL pData AS GDIP_IMAGECTXDATA PTR    ' // Pointer to the control data

   ' // Checks the validity of the parameters
   IF hwnd = %NULL THEN FUNCTION = %E_POINTER : EXIT FUNCTION
   IF LEN(wszResourceName) = 0 THEN FUNCTION = %E_INVALIDARG : EXIT FUNCTION

   ' // Gets a Pointer to the control data
   pData = GetWindowLong(hwnd, 0)
   IF pData = %NULL THEN FUNCTION = %E_POINTER : EXIT FUNCTION

   ' // Dispose a previous instance of the Image object, if any
   IF @pData.m_pImage THEN
      GdipDisposeImage(@pData.m_pImage)
      @pData.m_pImage = 0
   END IF

   ' // Find the resource and lock it
   hResource = FindResourceW(hInstance, wszResourceName, BYVAL %RT_RCDATA)
   IF hResource = %NULL THEN FUNCTION = %E_INVALIDARG : EXIT FUNCTION
   imageSize = SizeofResource(hInstance, hResource)
   IF imageSize = 0 THEN FUNCTION = %E_INVALIDARG : EXIT FUNCTION
   pResourceData = LockResource(LoadResource(hInstance, hResource))
   IF pResourceData = %NULL THEN FUNCTION = %E_INVALIDARG : EXIT FUNCTION
   ' // Allocate memory to hold the image
   hGlobal = GlobalAlloc(%GMEM_MOVEABLE, imageSize)
   IF hGlobal THEN
      ' // Lock the memory
      pGlobalBuffer = GlobalLock(hGlobal)
      IF pGlobalBuffer THEN
         ' // Copy the image from the resource file to global memory
         CopyMemory pGlobalBuffer, pResourceData, imageSize
         ' // Create an stream in global memory
         IF CreateStreamOnHGlobal(hGlobal, %FALSE, pImageStream) = %S_OK THEN
            ' // Create a bitmap from the data contained in the stream
            hStatus = GdipCreateBitmapFromStream(pImageStream, @pData.m_pImage)
            IF hStatus = %StatusOk THEN
               ' // Gets the device context handle
               hDc = GetDc(hwnd)
               ' // Creates a graphics object from it
               IF hDc THEN hStatus = GdipCreateFromHDC(hDc, pGraphics)
               ' // Draws the image (required to keep it in memory, since we are
               ' // going to unlock and free the resource)
               IF pGraphics THEN hStatus = GdipDrawImageI(pGraphics, @pData.m_pImage, 0, 0)
               ' // Deletes the graphics object
               IF pGraphics THEN GdipDeleteGraphics(pGraphics)
               ' // Releases the device context handle
               IF hDc THEN DeleteDc hDc
            END IF
            pImageStream = NOTHING
         END IF
         ' // Unlock the memory
         GlobalUnlock pGlobalBuffer
      END IF
      ' // Free the memory
      GlobalFree hGlobal
   END IF

   ' // Erases the window's client area
   hBgBrush = CreateSolidBrush(@pData.m_BkColor)
   IF hBgBrush THEN
      hDc = GetDc(hwnd)
      IF hDc THEN
         GetClientRect hwnd, rcFill
         FillRect hDc, rcFill, hBgBrush
         DeleteDc hDc
      END IF
      DeleteObject hBgBrush
   END IF

   ' // Redraws the control
   InvalidateRect hwnd, BYVAL %NULL, 0
   UpdateWindow hwnd

   FUNCTION = hStatus

END FUNCTION
' ========================================================================================

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: John Spikowski on March 24, 2013, 04:06:04 PM
QuoteLooks like after some months of inactivity I'm beginning to forget programming.

I have the cure!

We could sure use your help with COM integration with the Windows version of ScriptBasic. Charles has done an incredible job with DLLC (FFI extension module) to access APIs / COM dynamically at runtime. If you would consider being our mentor and keep us from straying off the path, it would be much appreciated.

All your work is priceless no matter what direction PB ends up going in.

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Dan Gin zel on March 24, 2013, 04:56:00 PM
I will review these routines in detail shortly.

As usual, my deepest appreciation!

Dan
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 25, 2013, 08:09:15 AM
Yes for GDI+

bitmap = gdiplus image
while
HBITMAP = GDI32 bitmap

The case makes all the difference! (remember GDIPLUS is written in plain C that is a very case sensitive language).

This is why i am using the word image in my code for GDIPLUS handle, and bitmap for GDI32 handle, then i can quickly determine which one i am refering to, when reading back the source code.

I had also to write my own low level API in GDImage to convert from image to bitmap, and from bitmap to image.
Because GdipCreateHBITMAPFromBitmap and GdipCreateBitmapFromHBITMAP won't work with PNG using transparency nor with DWM.

...
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 25, 2013, 11:11:28 AM
I have an small problem with that. I wrote some routines to make icons from .png files (see one of them below) to allow to use .png icons in toolbars. Works fine excepting that the grayed ones, made using a light gray tone, are rendered darker than they should.


' ========================================================================================
' Loads an image from a resource using GDI+, converts it to an icon and returns the icon handle.
' Parameter:
' - hInstance  = [in] Handle to the instance that contains the resource.
' - bstrImage  = [in] Name of the image in the resource file (.RES). If the image resource uses
'                an integral identifier, bstrImage should begin with a number symbol (#)
'                followed by the identifier in an ASCII format, e.g., "#998". Otherwise,
'                use the text identifier name for the image. Only images embedded as raw data
'                (type RCDATA) are valid. These must be icons in format .png, .jpg, .gif, .tiff.
' Return Value:
'   If the function succeeds, the return value is the handle of the created icon.
'   If the function fails, the return value is NULL.
'   Call GetLasrError to retrieve the error code.
' ========================================================================================
FUNCTION GdiPlusCreateHICONFromResource (BYVAL hInstance AS DWORD, BYVAL bstrImage AS WSTRING) AS DWORD

   LOCAL hStatus AS LONG                        ' // Status
   LOCAL token AS DWORD                         ' // Token to shutdown GDI+
   LOCAL StartupInput AS GdiplusStartupInput    ' // Structure to initialize GDI+
   LOCAL pImage AS DWORD                        ' // Image handle
   LOCAL hIcon AS DWORD                         ' // Icon handle
   LOCAL hResource     AS DWORD                 ' // Resource handle
   LOCAL pResourceData AS DWORD                 ' // Pointer to the resoruce data
   LOCAL hGlobal       AS DWORD                 ' // Global memory handle
   LOCAL pGlobalBuffer AS DWORD                 ' // Pointer to global memory buffer
   LOCAL pImageStream  AS IStream               ' // IStream interface pointer
   LOCAL imageSize     AS DWORD                 ' // Image size
   LOCAL wID AS WORD
   LOCAL dwID AS DWORD

   IF hInstance = 0 THEN EXIT FUNCTION

   StartupInput.GdiplusVersion = 1
   hStatus = GdiplusStartup(token, StartupInput, BYVAL %NULL)
   IF hStatus <> %S_OK THEN EXIT FUNCTION

   ' // Find the resource and lock it
   IF LEFT$(bstrImage, 1) = "#" THEN
      wID = VAL(MID$(bstrImage, 2))
      dwID = MAK(DWORD, wID, 0)
      hResource = FindResourceW(hInstance, BYVAL dwID, BYVAL %RT_RCDATA)
   ELSE
      hResource = FindResourceW(hInstance, BYCOPY bstrImage, BYVAL %RT_RCDATA)
   END IF
   IF hResource = %NULL THEN SetLastError(%E_INVALIDARG) : GOTO LExit
   imageSize = SizeofResource(hInstance, hResource)
   IF imageSize = 0 THEN SetLastError(%E_INVALIDARG) : GOTO LExit
   pResourceData = LockResource(LoadResource(hInstance, hResource))
   IF pResourceData = %NULL THEN SetLastError(%E_INVALIDARG) : GOTO LExit
   ' // Allocate memory to hold the image
   hGlobal = GlobalAlloc(%GMEM_MOVEABLE, imageSize)
   IF hGlobal THEN
      ' // Lock the memory
      pGlobalBuffer = GlobalLock(hGlobal)
      IF pGlobalBuffer THEN
         ' // Copy the image from the resource file to global memory
         CopyMemory pGlobalBuffer, pResourceData, imageSize
         ' // Create an stream in global memory
         IF CreateStreamOnHGlobal(hGlobal, %FALSE, pImageStream) = %S_OK THEN
            ' // Create a bitmap from the data contained in the stream
            hStatus = GdipCreateBitmapFromStream(pImageStream, pImage)
            IF hStatus = %StatusOk THEN
               IF pImage THEN
                  hStatus = GdipCreateHICONFromBitmap(pImage, hIcon)
                  GdipDisposeImage pImage
               END IF
            END IF
            pImageStream = NOTHING
         END IF
         ' // Unlock the memory
         GlobalUnlock pGlobalBuffer
      END IF
      ' // Free the memory
      GlobalFree hGlobal
   END IF

LExit:

   GdiplusShutdown token

   FUNCTION = hIcon

END FUNCTION
' ========================================================================================

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 25, 2013, 11:34:48 AM
Yes that's just the visible part of the problem, and one of the reason why i had to write my own API to keep the same aspect than within the original, the problem becomes even worse when you want to render on a DirectDraw surface (DWM) while preserving the progressive transparency.

This problem doesn't exist with D2D, because of the native use of the DirectDraw surface.

...
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 25, 2013, 11:52:46 AM
Maybe I should try the Windows Imaging Component. I used GDI+ for compatibility reasons with the damned XP.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Patrice Terrier on March 25, 2013, 12:02:37 PM
José--

Try with this

//GDImage 3.55 setup alpha channel for PNG image
rAlphaCoef = (@pBits[3] / 255)
@pBits[2] = @pBits[2] / rAlphaCoef
@pBits[1] = @pBits[1] / rAlphaCoef
@pBits[0] = @pBits[0] / rAlphaCoef
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 25, 2013, 12:59:15 PM
Quote from: John Spikowski on March 24, 2013, 04:06:04 PM
QuoteLooks like after some months of inactivity I'm beginning to forget programming.

I have the cure!

We could sure use your help with COM integration with the Windows version of ScriptBasic. Charles has done an incredible job with DLLC (FFI extension module) to access APIs / COM dynamically at runtime. If you would consider being our mentor and keep us from straying off the path, it would be much appreciated.

All your work is priceless no matter what direction PB ends up going in.

Inactive in programming. I have many other things to do.

Maybe you should look at the source code of the PHP language. It is written in C, that Charles understands quite well. I don't use COM automation and scripting languages.
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: John Spikowski on March 26, 2013, 03:58:35 AM
Have you looked at the PHP or Python API interfaces for these scripting languages?   :o

Thanks, but I'm going to take a more direct approach with the functionality DLLC offers with dynamic scripting of APIs at runtime.

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: Charles Pegge on March 26, 2013, 09:24:17 AM
Am I right in thinking all the good stuffs has Iunknown interfaces?. Hooking up SB to Iunknow is pretty simple, so I wonder whether we need to go down the automation route. I think you mentioned Crystal Reports, John.

Charles
Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: José Roca on March 26, 2013, 01:07:25 PM
Currently, Automation is only needed to automate Office and little more (some ActiveX controls written for VB). Since I don't use Office, I don't need Automation, that I dislike, at all. Microsoft has implemented all his new stuff as low-level interfaces. Even the Windows Ribbon was implemented as a low-level component that, contrarily to ActiveX controls, doesn't require the use of an OLE container. Low-level COM is faster and use the same conventions for parameter passing that standard functions. No problem to pass arrays or UDTs, no need for the use of Variants, no need for type libraries, no need to register the components to use them, etc.

Title: Re: Using GdipCreateBitmapFromStream with embedded PNG images
Post by: John Spikowski on March 26, 2013, 03:02:05 PM
Quotethink you mentioned Crystal Reports, John.

Memories of the past it seems. A faded dream when Offices integration was a requirement by your customers. Please strike the automation inquiry as it's not within the scope of the project. (FFI++)

QuoteLow-level COM is faster and use the same conventions for parameter passing that standard functions. No problem to pass arrays or UDTs, no need for the use of Variants, no need for type libraries, no need to register the components to use them, etc.

Music to my ears!!!