include gdiplus.inc
includelib gdiplus.lib
GdiplusStartupInput STRUCT
GdiplusVersion DWORD ?
DebugEventCallback DWORD ?
SuppressBackgroundThread DWORD ?
SuppressExternalCodecs DWORD ?
GdiplusStartupInput ENDS
ImageCodecInfo STRUCT
ClassID CLSID <>
FormatID GUID <>
CodecName DWORD ?
DllName DWORD ?
FormatDescription DWORD ?
FilenameExtension DWORD ?
MimeType DWORD ?
Flags DWORD ?
Version DWORD ?
SigCount DWORD ?
SigSize DWORD ?
SigPattern DWORD ?
SigMask DWORD ?
ImageCodecInfo ENDS
.data
ALIGN 16
Sprite_Simd_Mask_RVB_unpck_1 DWORD 000010001h,000000001h
Sprite_Simd_Mask_RVB_unpck_255 DWORD 000FF00FFh,0000000FFh
.data?
token dd 0
image dd 0
numEncoders dd 0
sizeImageCodecInfo dd 0
pImageCodecInfo dd 0
gdipsi GdiplusStartupInput <1> ; version must be 1
.code
fPicSaveToFileAsJpg proc uses esi edi lpFileName:dword
LOCAL str_len,tmp_esi:dword
LOCAL buff[256],buff2[256],buff3[256]:dword
invoke exist,lpFileName
.if eax==0
invoke MessageBox,0,0,0,0
ret
.endif
invoke GdiplusStartup, ADDR token, ADDR gdipsi, NULL
invoke MultiByteToWideChar,0,0,lpFileName,-1,addr buff3,1024
invoke GdipLoadImageFromFile, addr buff3, ADDR image
invoke GdipGetImageEncodersSize, ADDR numEncoders,ADDR sizeImageCodecInfo
invoke mAlloc,sizeImageCodecInfo
.if eax==0
invoke MessageBox,0,CADD("Not Enough memory/Memori tidak cukup"),0,0
jmp error
.endif
mov pImageCodecInfo,eax
invoke GdipGetImageEncoders, numEncoders, sizeImageCodecInfo,pImageCodecInfo
invoke lstrlen,lpFileName
mov str_len,eax
sub str_len,2
invoke lstrcpyn,addr buff,lpFileName,str_len
invoke lstrcat,addr buff,CADD("jpg")
assume esi:ptr ImageCodecInfo
mov ecx,numEncoders
mov esi,pImageCodecInfo
@@:
push ecx
push esi
mov tmp_esi,esi
invoke WideCharToMultiByte,0,0,[esi].MimeType,-1,addr buff2,1024,0,0
invoke lstrcmp,addr buff2,CADD("image/jpeg")
.if eax==0
invoke MultiByteToWideChar,0,0,addr buff,-1,addr buff3,1024
invoke GdipSaveImageToFile, image, addr buff3,tmp_esi, NULL
pop esi
pop ecx
jmp error
.endif
pop esi
add esi, SIZEOF ImageCodecInfo
pop ecx
dec ecx
jnz @b
assume esi:nothing
error:
invoke GdipDisposeImage, image
invoke GdiplusShutdown, token
invoke GlobalFree,pImageCodecInfo
ret
fPicSaveToFileAsJpg endp
It worked before, but now when it hit invoke GdipLoadImageFromFile, addr buff3, ADDR image it crashed. Why?
How I used to use
invoke fPicSaveToFileAsJpg,CADD("Test.bmp")
It will generate a file named test.jpg
MultiByteToWideChar just preceeding GdipLoadImageFromFile.
A simple mistake, you must have copied and pasted the code, because you do the same thing later.
Just out of curiosity, what's the advantage of doing this in Assembly? You'd have better type checking, and the code for checking return values would be easier in C++.
Quote from: baltoro on June 11, 2010, 12:03:51 AM
Just out of curiosity, what's the advantage of doing this in Assembly? You'd have better type checking, and the code for checking return values would be easier in C++.
Because that is the only thing I can do.
So MultiByteToWideChar is the main problem?
If you read the documentation: MultiByteToWideChar (http://msdn.microsoft.com/en-us/library/dd319072(VS.85).aspx), that last parameter, if it is not the size of your buffer, the function fails. Hell, I've done it myself numerous times. That's why checking return values before you make the graphics call will keep you from crashing.
Lately, I have been using DirectX, and so, am unfamiliar with the GDI+ APIs.
So the problem is not on GdipLoadImageFromFile?
That is weird, it worked before.
Vortex, I need you here :green
Why is Vortex?
Onan,
Your code works for me under Windows 2000. I did change mAlloc to crt_malloc and GlobalFree to crt_free, because I didn't have time to determine what mAlloc is. When GdipLoadImageFromFile "crashes" what is the return status? See the Status enumeration here (http://com.it-berater.org/gdiplus/noframes/GdiPlus_enumerations.htm).
Hi,
I'm not completely sure why you are searching the codecs for JPEG, it is always found on Windows and has a fixed GUID:
From GdiPlusImaging.h in the GoAsm header project:
#define GUID_ImageEncoderBMP <0x557cf400, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderJPEG <0x557cf401, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderGIF <0x557cf402, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderEMF <0x557cf403, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderWMF <0x557cf404, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderTIFF <0x557cf405, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderPNG <0x557cf406, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
#define GUID_ImageEncoderICON <0x557cf407, 0x1a04, 0x11d3, <0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e>>
You'll find that your proggy is working hard just to yield a predefined GUID.
Edgar
Quote from: MichaelW on June 11, 2010, 07:21:42 AM
Onan,
Your code works for me under Windows 2000. I did change mAlloc to crt_malloc and GlobalFree to crt_free, because I didn't have time to determine what mAlloc is. When GdipLoadImageFromFile "crashes" what is the return status? See the Status enumeration here (http://com.it-berater.org/gdiplus/noframes/GdiPlus_enumerations.htm).
There is no return status, because it doesnot execute GdipLoadImageFromFile completely. On the debugger the ebx point to 0 memory address and it tried to access the value from it. Since the debugger stopped on 0x07000000 address or something I guess the dll tried to access a wrong address. Dont know what is my mistake. Can you upload your .lib file for the Win2000?
Okay I got the return code from the GDIStart function
Quote
UnsupportedGdiplusVersion = 17,
What should I do?
Okay I got where is my mistake is, craps. :red
Quote
fPicSaveToFileAsJpg proc uses esi edi lpFileName:dword
LOCAL str_len,tmp_esi:dword
LOCAL buff[256],buff2[256]
LOCAL buff3[256]:dword
invoke exist,lpFileName
.if eax==0
invoke MessageBox,0,0,0,0
ret
.endif
invoke MultiByteToWideChar,0,0,lpFileName,-1,addr buff3,512
mov eax,OFFSET gdipsi
mov GdiplusStartupInput.GdiplusVersion[eax],1
invoke GdiplusStartup, ADDR token, eax, NULL
; mov ecx,eax
; invoke dw2a,ecx,addr buff
; invoke MessageBox,0,addr buff,0,0
; ret
invoke GdipLoadImageFromFile, addr buff3, ADDR image
invoke GdipGetImageEncodersSize, ADDR numEncoders,ADDR sizeImageCodecInfo
invoke mAlloc,sizeImageCodecInfo
.if eax==0
invoke MessageBox,0,CADD("Not Enough memory/Memori tidak cukup"),0,0
jmp error
.endif
mov pImageCodecInfo,eax
invoke GdipGetImageEncoders, numEncoders, sizeImageCodecInfo,pImageCodecInfo
invoke lstrlen,lpFileName
mov str_len,eax
sub str_len,2
invoke lstrcpyn,addr buff,lpFileName,str_len
invoke lstrcat,addr buff,CADD("jpg")
assume esi:ptr ImageCodecInfo
mov ecx,numEncoders
mov esi,pImageCodecInfo
@@:
push ecx
push esi
mov tmp_esi,esi
invoke WideCharToMultiByte,0,0,[esi].MimeType,-1,addr buff2,1024,0,0
invoke lstrcmp,addr buff2,CADD("image/jpeg")
.if eax==0
invoke MultiByteToWideChar,0,0,addr buff,-1,addr buff3,1024
invoke GdipSaveImageToFile, image, addr buff3,tmp_esi, NULL
pop esi
pop ecx
jmp error
.endif
pop esi
add esi, SIZEOF ImageCodecInfo
pop ecx
dec ecx
jnz @b
assume esi:nothing
error:
invoke GdipDisposeImage, image
invoke GdiplusShutdown, token
invoke GlobalFree,pImageCodecInfo
ret
fPicSaveToFileAsJpg endp
...Oops,...
...I need brain surgery,...
...Sorry, Onan.
Thank god Edgar solved the problem.
Now you just have to handle the error paths a little better.<G> My code would be a lot more clean/compact if I could really ignore them.
Onan, does that routine give you the opportunity to set the "quality" and "smoothing" values for JFIF compression ?
Quote from: dedndave on June 11, 2010, 06:25:24 PM
Onan, does that routine give you the opportunity to set the "quality" and "smoothing" values for JFIF compression ?
No, not yet.
Hi Onan,
There really is no need whatsoever to search the codecs, it is a predefined CLSID for JPEG. Also you are doing a lot of unicode conversion which can be painfully slow, the shlwapi API can replace a bit of it. This should work in MASM though I have not tried it:
.CONST
sCLSID_JPEG textequ <{557cf401H, 1a04H, 11d3H,{09aH, 073H, 000H, 000H, 0f8H, 01eH, 0f3H, 02eH}}>
.DATA
CLSIDImageEncoderJPEG GUID sCLSID_JPEG
JPEGExt DB ".",0,"J",0,"P",0,"G",0,0,0
.CODE
; ...
invoke MultiByteToWideChar,0,0,lpFileName,-1,addr buff3,512
; no need to dereference this, it is the first member of the structure
mov DWORD PTR gdipsi, 1
; start GDIPlus
invoke GdiplusStartup, ADDR token, offset gdipsi, NULL
; Load the image
invoke GdipLoadImageFromFile, addr buff3, ADDR image
; rename the extension
invoke PathRenameExtensionW, ADDR buff3,offset JPEGExt
; Save the image
invoke GdipSaveImageToFile, image, addr buff3,offset CLSIDImageEncoderJPEG, NULL
; Free the image
invoke GdipDisposeImage, image
; Release GDIPlus
invoke GdiplusShutdown, token
Hi Onan,
Sorry for being late. OK, here is my code converting a bitmap image in memory to .jpg :
include GDIpBmpToJPG.inc
.data
jpegFile db 'test.jpg',0
ImgType db 'image/jpeg',0
message db 'BMP file converted to JPG',0
caption db 'GDI+ demo',0
ImgQuality dd 90
_EncoderQuality GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,05h,0e7h,0ebh>>
.data?
StartupInfo GdiplusStartupInput <?>
buffer db 32 dup(?)
pImageCodecInfo dd ?
token dd ?
BmpImage dd ?
eps EncoderParameters <?>
.code
start:
mov eax,OFFSET StartupInfo
mov GdiplusStartupInput.GdiplusVersion[eax],1
; must be always seto to 1
invoke GdiplusStartup,ADDR token,ADDR StartupInfo,0
mov eax,OFFSET pImage
mov ecx,BITMAPFILEHEADER.bfOffBits[eax]
lea edx,[eax+ecx] ; points the array of bytes that contains the pixel data
add eax,SIZEOF BITMAPFILEHEADER ; points the BITMAPINFO structure
invoke GdipCreateBitmapFromGdiDib,eax,edx,ADDR BmpImage
; creates a Bitmap object based on a BITMAPINFO structure and
; an array of pixel data
invoke UnicodeStr,ADDR jpegFile,ADDR buffer
mov esi,OFFSET eps
mov EncoderParameters.Count[esi],1
lea ecx,[esi+EncoderParameters.Parameter.pGUID]
invoke MemCopy,ADDR _EncoderQuality,ecx,16 ; copy the GUID ot the correct address
mov EncoderParameters.Parameter.vType[esi],EncoderParameterValueTypeLong
mov EncoderParameters.Parameter.NumberOfValues[esi],1
; we have only one EncoderParameter structure
mov EncoderParameters.Parameter.value[esi],OFFSET ImgQuality
; set the JPEG compression level
invoke GetEncoderClsid,ADDR ImgType,ADDR pImageCodecInfo
; get the class identifier (CLSID) of the encoder
; The encoder can be bmp,jpeg,gif,tiff or png
invoke GdipSaveImageToFile,BmpImage,ADDR buffer,eax,ADDR eps
; save the image file
invoke VirtualFree,pImageCodecInfo,0,MEM_RELEASE
invoke GdipDisposeImage,BmpImage ; release the image
invoke GdiplusShutdown,token ; shutdown the GDI+ system
invoke MessageBox,0,ADDR message,ADDR caption,MB_OK
invoke ExitProcess,0
GetEncoderClsid PROC USES ebx edi sMimeType:DWORD,pMem:DWORD
LOCAL numEncoders:DWORD
LOCAL nSize:DWORD
LOCAL _buffer[32]:BYTE
invoke GdipGetImageEncodersSize,ADDR numEncoders,ADDR nSize
invoke VirtualAlloc,0,nSize,MEM_COMMIT,PAGE_READWRITE
mov edi,eax
mov eax,pMem ; = pImageCodecInfo
mov DWORD PTR [eax],edi
invoke GdipGetImageEncoders,numEncoders,nSize,edi
invoke UnicodeStr,sMimeType,ADDR _buffer
mov ebx,numEncoders
@@:
invoke StrCmpW,ADDR _buffer,ImageCodecInfo.MimeType[edi]
test eax,eax
jnz @f
sub ebx,1
add edi,SIZEOF ImageCodecInfo
jmp @b
@@:
mov eax,edi ; = lea eax,[edi+ImageCodecInfo.Clsid]
ret
GetEncoderClsid ENDP
END start
Hi Onan,
My code can set the convertion quality of the .jpg but donkey's code is preferable as it's easier to maintain and use. Coding the codecs part of my application was not an easy task.
Hi Vortex,
Nice code, have you thought about allowing for a change in color depth as well ? I do it in some of my GDIP proggies. Here's the appropriate code segment for changing it (in GoAsm syntax - sorry). Allowable values for bitdepth are 8, 16,24, 32, 48 and 64 as well as -1 for no conversion, some depths are only available to bitmaps see the comments for details.
// The largest palette size is 1024 bytes so we allocate that
invoke GdipAlloc,1024 + SIZEOF ColorPalette
mov [pPal],eax
cmp D[bitdepth],-1
je >>.NoConvert
cmp D[bitdepth],8
jne >
// Since this is an indexed type use a large palette to find the closest colors
// it will be reduced to 256 colors by the conversion function
mov D[PalType],PaletteTypeFixedHalftone27
mov D[ImgFmt],PixelFormat8bppIndexed
jmp >>.Convert
:
cmp D[bitdepth],24
jne >
mov D[PalType],PaletteTypeFixedHalftone27
mov D[ImgFmt],PixelFormat24bppRGB
jmp >>.Convert
:
cmp D[bitdepth],32
jne >
mov D[PalType],PaletteTypeFixedHalftone256
mov D[ImgFmt],PixelFormat32bppRGB
jmp >>.Convert
:
cmp D[format],GDIP_IMAGE_BMP
jne >>.NoConvert
// Bitmap only color depths
cmp D[bitdepth],16
jne >
// There is no standard 16Bpp palette so we use a 24 Bpp. It's not optimal but easier.
mov D[PalType],PaletteTypeFixedHalftone27
mov D[ImgFmt],PixelFormat16bppRGB555
jmp >>.Convert
:
cmp D[bitdepth],48
jne >
mov D[PalType],PaletteTypeFixedHalftone256
mov D[ImgFmt],PixelFormat48bppRGB
:
cmp D[bitdepth],64
jne >.NoConvert
mov D[PalType],PaletteTypeFixedHalftone256
mov D[ImgFmt],PixelFormat64bppARGB
.Convert
invoke GdipInitializePalette,[pPal], [PalType], 0, TRUE, NULL
invoke GdipBitmapConvertFormat,[pImage],[ImgFmt], DitherTypeNone,[PalType],[pPal],50.0
.NoConvert
The PalletteType enumeration is found in GdiplusPixelFormats.h of the header project include gdiplus.h. This segment requires GDIP version 1.1.
Edgar
Hi donkey,
Thanks for your code. The color depth is another important parameter and GDI+ can set this value. No problem with the syntax, it's fine :U
Well I guess I will put all of this into one function named "fSaveFileAs".
It can save from one format to another format.