Author Topic: Has anyone done any work in GDI+ recently ?  (Read 8447 times)

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Has anyone done any work in GDI+ recently ?
« on: October 16, 2016, 10:13:35 AM »
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.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

jj2007

  • Member
  • *****
  • Posts: 8832
  • Assembler is fun ;-)
    • MasmBasic
Re: Has anyone done any work in GDI+ recently ?
« Reply #1 on: October 16, 2016, 11:33:44 AM »
RichMasm uses these for loading and displaying images, e.g. the car in the attached source:
Code: [Select]
GdipLoadImageFromFile
GdipGetImageDimension
GdipDrawImageRectI
GdipDisposeImage

fearless

  • Member
  • ***
  • Posts: 362
    • LetTheLightIn
Re: Has anyone done any work in GDI+ recently ?
« Reply #2 on: October 16, 2016, 12:06:15 PM »
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):


Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
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

fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

www.LetTheLight.in  My Github  Twitter

Siekmanski

  • Member
  • *****
  • Posts: 1684
Re: Has anyone done any work in GDI+ recently ?
« Reply #3 on: October 16, 2016, 04:07:51 PM »
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

Code: [Select]
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:
« Last Edit: October 16, 2016, 05:31:32 PM by Siekmanski »
Creative coders use backward thinking techniques as a strategy.

Vortex

  • Member
  • *****
  • Posts: 1864
Re: Has anyone done any work in GDI+ recently ?
« Reply #4 on: October 16, 2016, 09:07:36 PM »
Hi Hutch,

The functions from masm32.lib :

Code: [Select]
BitmapFromFile
BitmapFromMemory
BitmapFromPicture
BitmapFromResource

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

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Has anyone done any work in GDI+ recently ?
« Reply #5 on: October 16, 2016, 09:45:15 PM »
Thanks Erol, these will be very useful, all I have to do is convert it to 64 bit.  :biggrin:
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Has anyone done any work in GDI+ recently ?
« Reply #6 on: October 17, 2016, 01:08:14 AM »
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 ?
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

jcfuller

  • Member
  • **
  • Posts: 172
Re: Has anyone done any work in GDI+ recently ?
« Reply #7 on: October 17, 2016, 01:15:38 AM »
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

  • Member
  • *****
  • Posts: 1473
  • The base type of a type is the type itself
    • SmplMath macros
Re: Has anyone done any work in GDI+ recently ?
« Reply #8 on: October 17, 2016, 04:43:36 AM »
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--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Has anyone done any work in GDI+ recently ?
« Reply #9 on: October 17, 2016, 05:21:43 AM »
Thanks for that, I was not having much luck with the data I could find.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Has anyone done any work in GDI+ recently ?
« Reply #10 on: October 17, 2016, 05:57:51 AM »
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
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

qWord

  • Member
  • *****
  • Posts: 1473
  • The base type of a type is the type itself
    • SmplMath macros
Re: Has anyone done any work in GDI+ recently ?
« Reply #11 on: October 17, 2016, 07:28:37 AM »
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--

  • Administrator
  • Member
  • ******
  • Posts: 5899
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Has anyone done any work in GDI+ recently ?
« Reply #12 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.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

mabdelouahab

  • Member
  • ***
  • Posts: 397
Re: Has anyone done any work in GDI+ recently ?
« Reply #13 on: October 17, 2016, 04:00:07 PM »
This is just an x64bit example

jj2007

  • Member
  • *****
  • Posts: 8832
  • Assembler is fun ;-)
    • MasmBasic
Re: Has anyone done any work in GDI+ recently ?
« Reply #14 on: October 17, 2016, 06:49:16 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