News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Has anyone done any work in GDI+ recently ?

Started by hutch--, October 16, 2016, 10:13:35 AM

Previous topic - Next topic

hutch--

I remember in the distant past that there was a technique for storing JPG or PNG files as resources which could then be loaded in BMP format for display. Storing the JPG or PNG files as resources or any of the alternatives is simple enough but what I forget is the technique to load them as bitmaps so you can use them for toolbars, background images and similar. I don't know if GDI+ handles RGB/A format images.

The main thing I am chasing is image size reduction as any reasonable sized bitmap starts to get very large and you can end up with an application that may be 50 or 60 kilobytes that blows out over a megabyte due to image data.

jj2007

RichMasm uses these for loading and displaying images, e.g. the car in the attached source:
GdipLoadImageFromFile
GdipGetImageDimension
GdipDrawImageRectI
GdipDisposeImage

fearless

Can store the resource (png/jpeg) as RT_RCDATA (RCDATA)

then use something similar to chris vega's load png from resource (had to search for this code fragment):


include gdiplus.inc

IFNDEF GDIPLUS_LOADBITMAPFROMRESOURCE_INC
GDIPLUS_LOADBITMAPFROMRESOURCE_INC equ <1>

;
; Load JPG/PNG from resource using GDI+
;   Actually, this function can load any image format supported by GDI+
;
; by: Chris Vega
;

gdiplusLoadBitmapFromResource proc hInst:HMODULE, lpName:LPSTR, lpType:LPSTR, pBitmapFromStream:DWORD

        local rcRes:HRSRC
        local hResData:HRSRC
        local pResData:HANDLE
        local sizeOfRes:DWORD
        local hbuffer:HANDLE
        local pbuffer:DWORD
        local pIStream:DWORD
        local hIStream:DWORD

        ; ------------------------------------------------------------------
        ; STEP 1: Find the resource
        ; ------------------------------------------------------------------
        invoke  FindResource, hInst, lpName, lpType
        or              eax, eax
        jnz             @f
        jmp             gdiplusLoadBitmapFromResource@Close
@@:     mov             rcRes, eax
       
        ; ------------------------------------------------------------------
        ; STEP 2: Load the resource
        ; ------------------------------------------------------------------
        invoke  LoadResource, hInst, rcRes
        or              eax, eax
        jnz             @f
        ret             ; Resource was not loaded
@@:     mov             hResData, eax

        ; ------------------------------------------------------------------
        ; STEP 3: Create a stream to contain our loaded resource
        ; ------------------------------------------------------------------
        invoke  SizeofResource, hInst, rcRes
        or              eax, eax
        jnz             @f
        jmp             gdiplusLoadBitmapFromResource@Close
@@:     mov             sizeOfRes, eax
       
        invoke  LockResource, hResData
        or              eax, eax
        jnz             @f
        jmp             gdiplusLoadBitmapFromResource@Close
@@:     mov             pResData, eax

        invoke  GlobalAlloc, GMEM_MOVEABLE, sizeOfRes
        or              eax, eax
        jnz             @f
        jmp             gdiplusLoadBitmapFromResource@Close
@@:     mov             hbuffer, eax

        invoke  GlobalLock, hbuffer
        mov             pbuffer, eax
       
        invoke  RtlMoveMemory, pbuffer, hResData, sizeOfRes
        invoke  CreateStreamOnHGlobal, pbuffer, FALSE, addr pIStream
        or              eax, eax
        jz              @f
        jmp             gdiplusLoadBitmapFromResource@Close
@@:     

        ; ------------------------------------------------------------------
        ; STEP 4: Create an image object from stream
        ; ------------------------------------------------------------------
        invoke  GdipCreateBitmapFromStream, pIStream, pBitmapFromStream
       
        ; ------------------------------------------------------------------
        ; STEP 5: Free all used locks and resources
        ; ------------------------------------------------------------------
        invoke  GetHGlobalFromStream, pIStream, addr hIStream
        ;invoke  GlobalFree, hIStream ; fearless 2016 - dont uncomment as stream has to be open for lifetime of image or corruption can occur.
        invoke  GlobalUnlock, hbuffer
        invoke  GlobalFree, hbuffer
       
gdiplusLoadBitmapFromResource@Close:
        ret
gdiplusLoadBitmapFromResource endp



GdipCreateHBITMAPFromBitmap can be used to convert to normal GDI HBITMAP bitmap format from the pBitmap GDI+ bitmap (pBitmapFromStream)

When using the above code, notice I've commented out the GlobalFree of the hIStream handle and added the comment, plus function should return hIStream in eax for later proper release.

Once finished with image handles, they can be disposed of with the DeleteObject (Gdi) and GdiDisposeImage (Gdi+) and the stream handle freed with the IStream release method:

Which requires the following structures if not already defined/included:

UNKNOWN STRUCT
   QueryInterface   DWORD ?
   AddRef           DWORD ?
   Release          DWORD ?
UNKNOWN ENDS

IStream STRUCT
IUnknown            UNKNOWN <>
Read                DWORD ?
Write               DWORD ?
Seek                DWORD ?
SetSize             DWORD ?
CopyTo              DWORD ?
Commit              DWORD ?
Revert              DWORD ?
LockRegion          DWORD ?
UnlockRegion        DWORD ?
Stat                DWORD ?
Clone               DWORD ?
IStream ENDS
ENDIF



And releasing the stream is something like this:

mov eax, hIStream
push eax
mov eax,DWORD PTR [eax]
Call IStream.IUnknown.Release[eax]; release the stream handle



Other solution is to use pnglib (http://www.madwizard.org/programming/projects/pnglib) to load pngs (Stored as RCDATA) and convert to bitmap handle - rough example:

invoke PNG_Init, addr pngInfo
; resource must be RCDATA type
invoke PNG_LoadResource, addr pngInfo, hInstance, IMG_SOMEPNG
invoke PNG_Decode, addr pngInfo
invoke PNG_CreateBitmap, addr pngInfo, hWin, PNG_OUTF_AUTO, FALSE
mov hPngBitmap, eax
invoke PNG_Cleanup, addr pngInfo
; do something with bitmap handle now:
Invoke SendDlgItemMessage, hWin, IDC_SOMEBITMAPCONTROL, STM_SETIMAGE, IMAGE_BITMAP, hPngBitmap



Siekmanski

#3
With a call to, "GdipBitmapLockBits" you get access to the picture data

BitmapData struct                         
    dwWidth      dd ?   
    dwHeight     dd ?   
    Stride       dd ?   
    PixelFormat  dd ?   
    Scan0        dd ?   Picture data
    Reserved     dd ?
BitmapData ends

I use this method to copy the picture data to a D3D9 texture surface.
In your case you could copy it to a "CreateDIBSection"

Here is the routine I use to load pictures from resource or from file, includes a function to set the transparency with a colorkey.

From resource:
   invoke  LoadTexture,501,NULL,addr g_pPictureTexture ;GOLDFONTTRANS2.png
   invoke  LoadTexture,501,D3DCOLOR_ARGB(255,255,0,255),addr g_pPictureTexture ;GOLDFONTTRANS2.png -> Pink color is transparent
From file:
   invoke  LoadTexture,TEXT_("Media/GOLDFONTTRANS2.png"),D3DCOLOR_ARGB(255,255,0,255),addr g_pPictureTexture

LoadTexture proc uses ebx esi edi PictureName:DWORD,ColorKey:DWORD,pTexture:DWORD
LOCAL GdiplusToken:dword
LOCAL PictureBuffer:dword
LOCAL RsrcSize:dword
LOCAL hRsrc:dword
LOCAL pStream:dword
LOCAL pImage:dword
LOCAL pLockedRect:D3DLOCKED_RECT
LOCAL ReturnMessage:dword

    mov     ReturnMessage,E_FAIL

    mov     eax,pTexture
    cmp     dword ptr [eax],NULL
    jnz     Exit_LoadTexture
   
    invoke  GdiplusStartup,addr GdiplusToken,addr GdiplusSInput,NULL
    test    eax,eax
    jnz     Exit_LoadTexture

    mov     PictureBuffer,NULL
    mov     pStream,NULL
   
    invoke  FindResource,NULL,PictureName,TEXT_("PICTURE")
    test    eax,eax
    jnz     Picture_In_Resource ; Picture found in the resource ?
   
    ; Try to load Picture from file.

    invoke  MultiByteToWideChar,CP_ACP,0,PictureName,-1,addr FilenameW,MAX_PATH-1
    invoke  GdipCreateBitmapFromFile,addr FilenameW,addr pImage
    test    eax,eax
    jnz     ShutdownGDIplus
    jmp     LockBitmap 

Picture_In_Resource:
    mov     hRsrc,eax
    invoke  SizeofResource,NULL,eax
    mov     RsrcSize,eax

    invoke  GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,eax
    test    eax,eax
    jz      ShutdownGDIplus
    mov     PictureBuffer,eax
    invoke  LoadResource,NULL,hRsrc
    invoke  LockResource,eax
    invoke  RtlMoveMemory,PictureBuffer,eax,RsrcSize
    invoke  CreateStreamOnHGlobal,PictureBuffer,TRUE,addr pStream
    test    eax,eax
    jnz     Close_Gdiplus

    invoke  GdipCreateBitmapFromStream,pStream,addr pImage
    test    eax,eax
    jnz     Close_Gdiplus

LockBitmap:
    invoke  GdipBitmapLockBits,pImage,NULL,ImageLockModeRead,PixelFormat32bppARGB,addr GDIplusBitmapData
    test    eax,eax
    jnz     Close_Image

    coinvoke g_pD3DDevice,IDirect3DDevice9,CreateTexture,GDIplusBitmapData.dwWidth,GDIplusBitmapData.dwHeight,\
                                1,D3DUSAGE_DYNAMIC,D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT,pTexture,NULL
    cmp     eax,D3D_OK
    jne     UnlockBitmap

    coinvoke g_pPictureTexture,IDirect3DTexture9,LockRect,0,addr pLockedRect,NULL,D3DLOCK_DISCARD
    cmp     eax,D3D_OK
    jne     UnlockBitmap
   
    mov     esi,GDIplusBitmapData.Scan0     ; pointer to the bitmap data
    mov     edi,pLockedRect.pBits           ; pointer to the texture data
   
    mov     ecx,GDIplusBitmapData.dwHeight
Height_lp:
    mov     edx,GDIplusBitmapData.dwWidth
    xor     ebx,ebx
Width_lp:
    mov     eax,dword ptr [esi+ebx]
   
    cmp     ColorKey,NULL
    jz      NoColorKey
    or      eax,D3DCOLOR_ARGB(255,0,0,0)
    cmp     ColorKey,eax
    jne     NoColorKey
    xor     eax,eax
NoColorKey:
   
    mov     dword ptr [edi+ebx],eax
    add     ebx,4
    dec     edx
    jnz     Width_lp
    add     esi,GDIplusBitmapData.Stride   
    add     edi,pLockedRect.Pitch
    dec     ecx
    jnz     Height_lp   

    coinvoke g_pPictureTexture,IDirect3DTexture9,UnlockRect,0

    mov     ReturnMessage,D3D_OK

UnlockBitmap:
    invoke  GdipBitmapUnlockBits,pImage,addr GDIplusBitmapData
Close_Image:
    invoke  GdipDisposeImage,pImage
Close_Gdiplus:
    SAFE_RELEASE  pStream
    cmp     PictureBuffer,NULL
    jz      ShutdownGDIplus
    invoke  GlobalFree,PictureBuffer
ShutdownGDIplus:
    invoke  GdiplusShutdown,GdiplusToken
Exit_LoadTexture:
    mov     eax,ReturnMessage
    ret

LoadTexture endp


Example of a GDIplus texture loader:
Creative coders use backward thinking techniques as a strategy.

Vortex

Hi Hutch,

The functions from masm32.lib :

BitmapFromFile
BitmapFromMemory
BitmapFromPicture
BitmapFromResource


Attached is a similar example based on the OleLoadPicture and CopyImage API functions.

hutch--

Thanks Erol, these will be very useful, all I have to do is convert it to 64 bit.  :biggrin:

hutch--

I don't seem to be having much luck with GDIPLUS in 64 bit.

    LOCAL nbuffer[128]:BYTE             ; buffer for unicode string
    LOCAL nPbuf :QWORD                  ; its pointer
    LOCAL token :DWORD                  ; token for GdiplusStartup
    LOCAL gdiin :GdiplusStartupInputX    ; structure for GdiplusStartup
    LOCAL pbmp  :DWORD                  ; address for bitmap

    invoke GdiplusStartup,ADDR token,ADDR gdiin,0

    fn MessageBox,0,LastError$(),"GdiplusStartup",MB_OK

  ; ----------------------------------------
  ; get buffer, convert file name to unicode
  ; ----------------------------------------
    mov nPbuf, ptr$(nbuffer)
    invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,"toolbar.png",-1,nPbuf,64      ; OK

    invoke GdipCreateBitmapFromFile,nPbuf,ADDR pbmp
    mrm hBitmap, rax            ; pbmp

    fn MessageBox,0,str$(hBitmap),str$(pbmp),MB_OK

I am only getting junk returns.

Has anyone done any work in GDIPLUS in 64 bit ?

The gdiplus.dll versions I have in win10 in both system32 and wow64 are both 32 bit versions, the next question is there a 64 bit version ?

jcfuller

Hutch,
Don't know if this is relevant but  I have done some 64bit unicode c++ (VS 2015 community) work . Only enough to know it works.

James

qWord

GdipCreateBitmapFromFile does create objects of type GpBitmap and return a value from the GpStatus enumeration (0 == Success).The second parameter is a  pointer to a pointer to the created GpBitmap (pbmp must be a QWORD).
To get a GDI bitmap from GpBitmap see GdipCreateHBITMAPFromBitmap.
The GdiplusStartupInput structure must be filled before calling GdiplusStartup (all zero except GdiplusVersion=1).

BTW: I've done several x64-GDI+ projects successfully.
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

Thanks for that, I was not having much luck with the data I could find.

hutch--

Gratsie,

Works perfectly. I did not know about the last step.

    LOCAL nbuffer[128]:BYTE             ; buffer for unicode string
    LOCAL nPbuf :QWORD                  ; pointer to unicode buffer
    LOCAL token :QWORD                  ; token for GdiplusStartup
    LOCAL gdiin :GdiplusStartupInput    ; structure for GdiplusStartup
    LOCAL pbmp  :QWORD                  ; address for bitmap
    LOCAL ptrb  :QWORD

    mov gdiin.GdiplusVersion, 1
    mov gdiin.DebugEventCallback, 0
    mov gdiin.SuppressBackgroundThread, 0
    mov gdiin.SuppressExternalCodecs, 0

    invoke GdiplusStartup,ADDR token,ADDR gdiin,0
    mov nPbuf, ptr$(nbuffer)
    invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,"toolbar.png",-1,nPbuf,64      ; OK
    invoke GdipCreateBitmapFromFile,nPbuf,ADDR pbmp
    invoke GdipCreateHBITMAPFromBitmap,pbmp,ADDR hBitmap,0

qWord

Just as side note: to destroy the GDI+ Bitmap you must use GdipDisposeImage (could be done after creating the hBitmap)

regards
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

Interestingly enough it has been worth the effort, assembled size dropped from 65k to 39k by changing from a bitmap to a png file.

mabdelouahab


jj2007

Quote from: hutch-- on October 17, 2016, 02:58:08 PM
Interestingly enough it has been worth the effort, assembled size dropped from 65k to 39k by changing from a bitmap to a png file.

Try as jpg with a high compression; png is a great format, but Windows adds lots of colours via anti-aliasing of text, and that spoils the compression rate of png's.

@mabdelouahab: Lovely :t