News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

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--

The win with PNG is its lossless which matters with things like image strips. I produce most PNG files I use with Axialis Icon Workshop so Windows does not get a chance to modify any of the images directly. They do get fed through whatever rendering gdiplus uses but they seem to look OK. The size win is a rgb/a bitmap at 41014 bytes is replaced with a PNG at 14246 bytes with no quality loss.

Siekmanski

One option to reduce png size is cutting out the crystal ball ( it becomes 420 by 420 pixels ) and reducing color count without messing up the aplha values.
I tried this in Paint Shop Pro but it messes up the alpha values.

Hutch,
Can Axialis Icon Workshop cut out pieces of the picture and reduce color count without messing up the alpha values ?
Creative coders use backward thinking techniques as a strategy.

hutch--

 I will have a look but it does not ring a bell, I mainly work in PNG in Icon Workshop and anything you load in can be converted to RGB/A but I have not seen that option. I did a test piece with a 512 square icon and even with the default compression, it ended up about 350k, without the compression it was over 1 meg. The immediate requirement was to get the toolbar bitmap size down which is working OK.

I just checked and the only option is to save the file as a PNG but with no other modifications. Just as size comparisons, I made 2 512 x 512 images, one a PNG, the other a BMP of the identical image, PNG saved at 280280 bytes, BMP saved at 1048630 bytes so the size saving is significant. You can routinely cut out rectangular parts and save them as files but no colour depth adjustments.

Siekmanski

OK, maybe here is a new challenge for us.
Image compression using GDI+ and keeping the info in the file we need such as alpha channels, color reduction to an acceptable loss, cutting a portion out of the original image.

I was thinking of manipulations of the different layers and splitting up the rgb -> yuv to do color reduction on each channel seperatly and choose the best image compression for each channel or other things we can think of.
Combining those to one "small" file wich can be loaded from the resource.
Creative coders use backward thinking techniques as a strategy.

hutch--

As a number of folks have helped out with the question, here is what I have used so far in loading a PNG file instead of a bitmap. The PNG data is stored as a DB sequence and is loaded by the following code.

    invoke png_toolbar                  ; get PNG data address and length
    mov ppng, rax                       ; pointer to png data
    mov lpng, rcx                       ; length of png data
    mrm npng, "tb_@_name.png"           ; temp file name


This is the entry proc where I have put all of the gdiplus code so far.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

entry_point proc

    LOCAL gdii  :GdiplusStartupInput    ; structure for GdiplusStartup
    LOCAL pbmp  :QWORD                  ; address for bitmap
    LOCAL ppng  :QWORD                  ; pointer PNG data
    LOCAL lpng  :QWORD                  ; length PNG data
    LOCAL npng  :QWORD                  ; name for temp PNG file

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

    invoke GdiplusStartup,ADDR token,ADDR gdii,0

    mov hInstance, rv(GetModuleHandle,0)
    mov hCursor,   rv(LoadCursor,0,IDC_ARROW)
    mov sWid,      rv(GetSystemMetrics,SM_CXSCREEN)
    mov sHgt,      rv(GetSystemMetrics,SM_CYSCREEN)
    mov hIcon,     rv(LoadImage,hInstance,10,IMAGE_ICON,48,48,LR_LOADTRANSPARENT)

    invoke png_toolbar                  ; get PNG data address and length
    mov ppng, rax                       ; pointer to png data
    mov lpng, rcx                       ; length of png data
    mrm npng, "tb_@_name.png"           ; temp file name

    invoke save_file,npng,ppng,lpng     ; create the temp file

    invoke GdipCreateBitmapFromFile,L(npng),ADDR pbmp
    invoke GdipCreateHBITMAPFromBitmap,pbmp,ADDR hBitmap,0
    invoke GdipDisposeImage,pbmp

    invoke DeleteFile,npng              ; delete the temp file

  ; ----------------------------------------------------------------
  ; load the rebar background tile stretching it to the rebar height
  ; ----------------------------------------------------------------
    mov tbTile, rv(LoadImage,hInstance,30,IMAGE_BITMAP,sWid,rbht,LR_DEFAULTCOLOR)

    call main

    invoke GdiplusShutdown,token        ; cleanup on exit
    invoke ExitProcess,0
    ret

entry_point endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

Vortex

Attached is a GDI+ example assembled with Poasm 64 and HJWasm 64  The sample project loads a PNG image from file and displays it inside a window.

jj2007

Nice :t

I have one problem with building, though:
    mov    wc.lpfnWndProc,rax ; trashes return address with HJWasm; OK with AsmC/code]

Vortex

This time the image stored in memory is displayed in the window. OleLoadPicture loads the GIF image embedded into the executable and the get_Handle method of the IPicture interface retrieves the handle to the image in memory.

Vortex

Hi Jochen,

Thanks. The window template is taken from the code \HJWasm\Samples\Win64_3.asm  Is your WinMain procedure constructed differently?

adeyblue

If you don't mind being adventurous with undocumented function usage, then you can easily give GDI+ a PNG from a resource without creating a temp file first, using the GdipCreateBitmapFromStream(IStream*, GpBitmap**) function.

SHCreateStreamOnModuleResourceW in shlwapi.dll (Vista+) copies resource bytes into a COM IStream, which you pass to GdipCreateBitmapFromStream to get your GpBitmap and go from there. They moved that function to shcore.dll from Windows 8, but they left a forwarder so GetProcAddress(shlwapi, 628) still works.

mabdelouahab

from a resource

DrawResImage PROC  Graphic__:qword,IdRes:dword,X_:DWORD,Y_:DWORD,W_:DWORD,H_:DWORD
LOCAL _rcRes:qword,_hResData:qword,_sizeOfRes:qword,_hbuffer:qword,_pIStream:qword,_hBitmap:qword

.invoke  FindResource , 0, IdRes, RT_RCDATA
.if rax
mov _rcRes, rax
mov _hResData, _rv(LoadResource , 0, _rcRes)
mov _sizeOfRes, _rv(SizeofResource , 0, _rcRes)

.invoke  CreateStreamOnHGlobal, 0, TRUE, & _pIStream 
.invoke _pIStream.4,_hResData,_sizeOfRes,0
; mov rdx, _hResData
; mov r8, _sizeOfRes
; xor r9, r9
; mov rcx, _pIStream
; mov rax, qword ptr [rcx]
; add rax, 020h
; call qword ptr [rax]
.invoke  GdipCreateBitmapFromStream, _pIStream,& _hBitmap
.if W_
.invoke GdipDrawImageRectI ,Graphic__, _hBitmap,X_, Y_ ,W_,H_
.else
.invoke GdipDrawImage ,Graphic__, _hBitmap,X_, Y_
.endif
.invoke GdipDisposeImage,_hBitmap
.invoke _pIStream.2
; mov rcx, _pIStream
; mov rax, qword ptr [rcx]
; add rax, 010h
; call qword ptr [rax]
mov rax,TRUE
.else
xor rax,rax
.endif
ret
DrawResImage endp

jj2007

Quote from: Vortex on October 19, 2016, 07:34:38 AM
Hi Jochen,

Thanks. The window template is taken from the code \HJWasm\Samples\Win64_3.asm  Is your WinMain procedure constructed differently?

No, Erol. I am actually getting different code from the same identical source, when building a) with HJWasm or b) with AsmC. Knowing how many idiosyncratic assembly options have crept into the Watcom forks, I suspect that there is one that influences the way the frame is built ::)

What actually happens in the HJWasm build (attached) is that mov wc.lpfnWndProc, rax substitutes the return address with the pointer to the WndProc. So on exit the ret lets the code continue in WndProc...

Anyway, this is not a problem with your code but rather with the assemblers.

hutch--

Has anyone had any luck getting this GDI+ function to run on a PNG resource ?

GpStatus WINGDIPAPI GdipCreateBitmapFromResource(
  HINSTANCE hInstance,
  GDIPCONST WCHAR* lpBitmapName,
  GpBitmap** bitmap
);


I can routinely get this code to load a PNG resource by using a temp file but I have been trying to get the direct "GdipCreateBitmapFromResource()" function to work with no success on a PNG RCDATA resource.



In RC file

20 RCDATA "toolbar.png"

In source file.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

BmpFromResource proc resid:QWORD

    LOCAL hRes  :QWORD
    LOCAL pRes  :QWORD
    LOCAL lRes  :QWORD
    LOCAL pbmp  :QWORD
    LOCAL hBmp  :QWORD
    LOCAL npng  :QWORD

    mov hRes, rv(FindResource,0,resid,RT_RCDATA)
    mov pRes, rv(LoadResource,0,hRes)
    mov lRes, rv(SizeofResource,0,hRes)
    mrm npng, "@@@@.@@@"                ; temp file name

    invoke save_file,npng,pRes,lRes     ; create the temp file

    invoke GdipCreateBitmapFromFile,L(npng),ADDR pbmp
    invoke GdipCreateHBITMAPFromBitmap,pbmp,ADDR hBmp,0
    invoke GdipDisposeImage,pbmp

    invoke DeleteFile,npng              ; delete the temp file

    mov rax, hBmp

    ret

BmpFromResource endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

This works fine but I was trying to bypass the temp file using "GdipCreateBitmapFromResource()" but nothing will get a working return value from it.


jj2007

Quote from: hutch-- on October 19, 2016, 11:18:26 PM
Has anyone had any luck getting this GDI+ function to run on a PNG resource ?

I made some tests:       invoke SetLastError, 0
      invoke LoadBitmap, wcx.hInstance, chr$("MYPNG")
      deb 4, "LoadBitmap MYPNG", eax, $Err$()
      invoke SetLastError, 0
      invoke LoadBitmap, wcx.hInstance, chr$("MYBITMAP")
      deb 4, "LoadBitmap MYBITMAP", eax, $Err$()
      invoke SetLastError, 0
      invoke FindResource, wcx.hInstance, chr$("MYPNG"), RT_RCDATA
      deb 4, "FindRes MYPNG", eax, $Err$()
      invoke FindResource, wcx.hInstance, chr$("MYBITMAP"), RT_BITMAP
      deb 4, "FindRes MYBITMAP", eax, $Err$()
      invoke SetLastError, 0
      invoke GdipCreateBitmapFromResource, wcx.hInstance, eax, addr hBitmap
      deb 4, "Bitmap from FindResource", eax, $Err$()
      invoke SetLastError, 0
      invoke GdipCreateBitmapFromResource, wcx.hInstance, wChr$("MYBITMAP"), addr hBitmap
      deb 4, "Bitmap from wChr$()", eax, $Err$()


Output:LoadBitmap MYPNG
eax             0
$Err$()         Impossibile trovare il nome di risorsa specificato nel file immagine.


LoadBitmap MYBITMAP
eax             84219241
$Err$()         Operazione completata.


FindRes MYPNG
eax             4964648
$Err$()         Operazione completata.


FindRes MYBITMAP
eax             4964456
$Err$()         Operazione completata.


Bitmap from FindResource
eax             2
$Err$()         Impossibile trovare il nome di risorsa specificato nel file immagine.


Bitmap from wChr$()
eax             0
$Err$()         Operazione completata.


The main problem seems to be that RCDATA and BITMAP are different animals :(

mabdelouahab

Quote from: hutch-- on October 19, 2016, 11:18:26 PM
I can routinely get this code to load a PNG resource by using a temp file but I have been trying to get the direct "GdipCreateBitmapFromResource()" function to work with no success on a PNG RCDATA resource.
GdipCreateBitmapFromResource work with bitmap resource not with rcdata