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.
RichMasm uses these for loading and displaying images, e.g. the car in the attached source:
GdipLoadImageFromFile
GdipGetImageDimension
GdipDrawImageRectI
GdipDisposeImage
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
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:
Hi Hutch,
The functions from masm32.lib :
BitmapFromFile
BitmapFromMemory
BitmapFromPicture
BitmapFromResource
Attached is a similar example based on the OleLoadPicture and CopyImage API functions.
Thanks Erol, these will be very useful, all I have to do is convert it to 64 bit. :biggrin:
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,
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
GdipCreateBitmapFromFile (https://msdn.microsoft.com/en-us/library/windows/desktop/ms533971%28v=vs.85%29.aspx) 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.
Thanks for that, I was not having much luck with the data I could find.
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
Just as side note: to destroy the GDI+ Bitmap you must use GdipDisposeImage (could be done after creating the hBitmap)
regards
Interestingly enough it has been worth the effort, assembled size dropped from 65k to 39k by changing from a bitmap to a png file.
This is just an x64bit example
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
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.
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 ?
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.
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.
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
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
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.
Nice :t
I have one problem with building, though:
mov wc.lpfnWndProc,rax ; trashes return address with HJWasm; OK with AsmC/code]
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.
Hi Jochen,
Thanks. The window template is taken from the code \HJWasm\Samples\Win64_3.asm Is your WinMain procedure constructed differently?
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 (http://undoc.airesoft.co.uk/shlwapi.dll/SHCreateStreamOnModuleResourceW.php) 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.
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
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.
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.
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 :(
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
Yes, that makes sense, thanks for the confirmation.
hutch--
Full Example: Crystal ball from resource without creating a temp file:crystal-ball.zip (http://www.mediafire.com/file/rdg27w6av59k7d9/crystal-ball.zip) * It could not be included in the attachments because it is larger than 512
I downloaded it, its very well done. :t
I took another road, work in progress (still severe memory leaks) but so far it looks OK, i.e. the exe runs on XP and Win7-64 alike. Grateful for tests with other OS versions :icon14:
It requires a hilarious workaround, though, to remain compatible with XP:
QuotePrior to Windows Vista, SHCreateMemStream was not included in the public Shlwapi.h file, nor was it exported by name from Shlwapi.dll. To use it on earlier systems, you must call it directly from the Shlwapi.dll file as ordinal 12.
The original bmp file is 922270 bytes, the exe has 270kB.
Just a thought, though have you looked on... SourceForge for the FreeImage library?
GuruSR.
Hi,
Quote from: jj2007 on October 20, 2016, 01:18:43 PM
I took another road, work in progress (still severe memory leaks) but so far it looks OK, i.e. the exe runs on XP and Win7-64 alike. Grateful for tests with other OS versions
Windows XP and 8.1. There was an error message in the console
output on XP. Output attached. Didn't like my P-III as always. <g>
Regards,
Steve N.
Hi JJ!! 7-32
Not skat.zip for recompilation.
I am actually happy enough with the technique of storing the PNG file as a resource, writing it as a file then loading the file with the GDI+ function. I have used the technique in the past and what makes it viable is the OS disk cache and the time lag between writing the file, reading it with the GDI+function then deleting the file is so short that it never reaches the disk, its a purely in memory operation that is easily fast enough.
Quote from: GuruSR on October 20, 2016, 02:03:07 PM
Just a thought, though have you looked on... SourceForge for the FreeImage library?
A DLL with 5.5MB :dazzled:
I feel already ashamed for the 20k overhead of a MasmBasic hello world :redface:
@HSE+FORTRANS: Thanks for testing. I have to investigate into the SelectObject failure. Does the image show properly?
@Hutch: Correct but still, if you can avoid writing to a file, it would be even better. Zipping a BMP has about the same compression as converting to PNG, sometimes more, sometimes less. So unzipping to bmp in memory is a valid option imho. However, GdipCreateBitmapFromStream as used by mabdelouahab seems even better - compliments :t
@mabdelouahab: How (or rather: why) does this line work?
.invoke GdipCreateBitmapFromScan0,rect.right,rect.bottom,0,PixelFormat32bppPARGB,
0,&bitmap
According to the docs, the
0 should be a pointer to the pixels ::)
Hi,
Quote from: jj2007 on October 21, 2016, 02:10:13 AM
@HSE+FORTRANS: Thanks for testing. I have to investigate into the SelectObject failure. Does the image show properly?
I think so. On the XP machine there was a lot of flickering when
the window was moved. But it is a slower machine.
HTH,
Steve N.
JJ : image look good. I can't see As column. It's no posible to increase window size beyond Kings.
Thanks. The images show, so it's working in principle. I've spent some time on a weird problem:
if 1 ; ##### no leak but bmp doesn't work, and jpg fails often #####
push rv(SizeofResource, 0, esi)
push rv(LoadResource, 0, esi) ; src
mov hGlobal, rv(GlobalAlloc, GMEM_MOVEABLE, stack[4]) ; dest
invoke GlobalLock, eax
push eax
call MbCopy ; invoke MbCopy, dest, src, bytes
invoke GlobalUnlock, hGlobal
invoke CreateStreamOnHGlobal, hGlobal, 0, addr iStream
invoke GdipCreateBitmapFromStream, iStream, ebx ; addr hBitmap; GdipBitmapLockBits?
invoke GlobalFree, hGlobal
else ; ##### bmp works, but leaks approx size of bitmaps #####
push rv(SizeofResource, 0, esi) ; invoke ShlWapi:SHCreateMemStream, pBuffer, bytes
push rv(LoadResource, 0, esi)
call GdiSI.jjMemStream ; get *IStream; use global GdiSI because ebx points to bitmap
invoke GdipCreateBitmapFromStream, eax, ebx ; addr hBitmap; GdipBitmapLockBits
endif
The upper version works fine with png, and it doesn't leak, but it fails always with bmp and sometimes with jpg.
The lower version works great, but to prevent leaking, the stream needs to be released in the right moment (MS Social (https://social.msdn.microsoft.com/Forums/sqlserver/en-US/d603d347-53ec-4b82-be4b-7581a485d61c/gdiplus-lock-the-file-not-able-to-delete?forum=vclanguage)):
QuoteUnfortunately GDI+ does tend to hold files open for images for the duration of the image. This is because GDI+ lazy decompresses the image (i.e. it doesn't actually decompress the image until the DrawImage call), which means it keeps your stream open.
There is a related post by Siekmanski (http://masm32.com/board/index.php?topic=3124.msg32367#msg32367) that inspired me :t
iStream pointer must be empty before use.
I use some kind of a SAFE_RELEASE,
.if iStream != 0
mov ecx,iStream
push ecx
mov eax,[ecx]
call DWORD PTR [eax + 8] ; IUnknown::Release
mov iStream,0
.endif
.
Yes, that did the trick. Just tested it on my XP VM, and noticed that in one occasion, SelectObject oldpen returns 0, which was flagged by my debugging routine. GetLastError reveals that there was no error, though (meaning XP has initially no pen selected). So I got curious and found this in Raymond Chen's What are the dire consequences of not selecting objects out of my DC? (https://blogs.msdn.microsoft.com/oldnewthing/20130306-00/?p=5043):
QuoteGDI will sometimes (not always) lie and say, "Sure, I deleted your object." It didn't actually delete it, because it's still selected into a DC, but it also ties a string around its finger, and when the object is finally deselected, GDI will say, "Oh, wait, I was supposed to delete this object," and perform the deletion.
::)
Here is my GdipCreateBitmapFromStream example. It displays a PNG image loaded from file into memory :
.IF uMsg==WM_CREATE
invoke GlobalAlloc,GMEM_MOVEABLE,PNG_IMG_SIZE
mov hMem,eax
invoke GlobalLock,eax
mov pMem,eax
invoke ReadFileToMem,ADDR filename,eax
mov eax,OFFSET StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax],1
mov GdiplusStatus,eax
invoke GdiplusStartup,ADDR token,ADDR StartupInfo,0
invoke CreateStreamOnHGlobal,hMem,FALSE,ADDR pStream
invoke GdipCreateBitmapFromStream,pStream,ADDR BmpImage
invoke GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0
Quote from: Vortex
Here is my GdipCreateBitmapFromStream example. It displays a PNG image loaded from file into memory
Works fine Windows 10 Home Ed.