Author Topic: Bare Minimum GDIplus code to save different Image Types.  (Read 1675 times)

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #15 on: April 25, 2020, 02:41:19 AM »
This also works in 64-bit.

; build: asmc64 -ws -pe -Zp8 test.asm
include windows.inc
include gdiplus.inc
include tchar.inc

    .data
    StartupInfo GdiplusStartupInput <1, 0, 0, 0>
    ImageFormatBMP GUID <0557CF400h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>

    .code

wmain proc argc:int_t, argv:wstring_t

    .if ( argc == 3 )

        .new token:ULONG_PTR       

        .ifd !GdiplusStartup(&token, &StartupInfo, 0)

            .new BmpImage:ptr GpBitmap

            mov rcx,argv
            .ifd !GdipCreateBitmapFromFile([rcx+size_t], &BmpImage)

                mov rcx,argv
                GdipSaveImageToFile(BmpImage, [rcx+size_t*2], &ImageFormatBMP, NULL)
                GdipDisposeImage(BmpImage)
            .endif
            GdiplusShutdown(token)
        .endif
    .endif
    xor eax,eax
    ret

wmain endp

    end _tstart

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #16 on: April 25, 2020, 03:51:57 AM »
I think the JPG quality encoder parameters for 64 bit are organized in an other way as in 32 bit code?
Creative coders use backward thinking techniques as a strategy.

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #17 on: April 25, 2020, 06:23:08 AM »
There's a sample here:
https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-setting-jpeg-compression-level-use

The ImageCodecInfo struct is off by 8 bytes, not sure why (alignment perhaps).
Code: [Select]
include windows.inc
include gdiplus.inc
include stdio.inc
include tchar.inc

    .data
    EncoderQuality IID { 0x1d5be4b5, 0xfa4a, 0x452d, {0x9c, 0xdd, 0x5d, 0xb3, 0x51, 0x05, 0xe7, 0xeb } }
    gdiplusStartupInput GdiplusStartupInput { 1, 0, 0, 0 }

    .code

GetEncoderClsid proc uses rsi rdi rbx format:wstring_t, pClsid:ptr CLSID

  local num:UINT  ; number of image encoders
  local size:UINT ; size of the image encoder array in bytes

    xor edi,edi
    mov num,edi
    mov size,edi

    GdipGetImageEncodersSize(&num, &size)
    .return -1 .if ( size == 0 )
   
    mov rdi,malloc(size)
    .return -1 .if ( rax == NULL ) ;; Failure

    GdipGetImageEncoders(num, size, rdi)

    .for ( esi = 0: esi < num: ++esi )
   
        imul ebx,esi,ImageCodecInfo+8
       
        .if ( wcscmp( [rdi+rbx].ImageCodecInfo.MimeType, format ) == 0 )
     
            mov rdx,pClsid
            mov oword ptr [rdx],[rdi+rbx].ImageCodecInfo.Clsid

            free(rdi)
            .return esi ;; Success
        .endif
    .endf

   free(rdi)
   .return -1 ;; Failure

GetEncoderClsid endp

wmain proc argc:int_t, argv:wstring_t

 ;; Initialize GDI+.
 local gdiplusToken:ULONG_PTR
 local encoderClsid:CLSID
 local encoderParameters:EncoderParameters
 local quality:ULONG
 local Image:ptr GpBitmap
   
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL)

    ;; Get an image from the disk.
    GdipCreateBitmapFromFile(L"Shapes.bmp", &Image)

    ;; Get the CLSID of the JPEG encoder.
    GetEncoderClsid(L"image/jpeg", &encoderClsid)

    ;; Before we call Image::Save, we must initialize an
    ;; EncoderParameters object. The EncoderParameters object
    ;; has an array of EncoderParameter objects. In this
    ;; case, there is only one EncoderParameter object in the array.
    ;; The one EncoderParameter object has an array of values.
    ;; In this case, there is only one value (of type ULONG)
    ;; in the array. We will let this value vary from 0 to 100.

    mov encoderParameters.Count,1
    mov encoderParameters.Parameter.Guid,EncoderQuality
    mov encoderParameters.Parameter.Type,EncoderParameterValueTypeLong
    mov encoderParameters.Parameter.NumberOfValues,1

    ;; Save the image as a JPEG with quality level 0.
    mov quality,0
    mov encoderParameters.Parameter.Value,&quality
    GdipSaveImageToFile(Image, L"Shapes001.jpg", &encoderClsid, &encoderParameters)
    .if (eax == 0)
        wprintf(L"%s saved successfully.\n", L"Shapes001.jpg")
    .else
        wprintf(L"%d  Attempt to save %s failed.\n", eax, L"Shapes001.jpg")
    .endif

    ;; Save the image as a JPEG with quality level 50.
    mov quality,50
    mov encoderParameters.Parameter.Value,&quality   
    GdipSaveImageToFile(Image, L"Shapes050.jpg", &encoderClsid, &encoderParameters)
    .if (eax == 0)
        wprintf(L"%s saved successfully.\n", L"Shapes050.jpg");
    .else
        wprintf(L"%d  Attempt to save %s failed.\n", eax, L"Shapes050.jpg");
    .endif

    ;; Save the image as a JPEG with quality level 100.
    mov quality,100
    mov encoderParameters.Parameter.Value,&quality   
    GdipSaveImageToFile(Image, L"Shapes100.jpg", &encoderClsid, &encoderParameters)
    .if (eax == 0)
        wprintf(L"%s saved successfully.\n", L"Shapes100.jpg");
    .else
        wprintf(L"%d  Attempt to save %s failed.\n", eax, L"Shapes100.jpg");
    .endif

    GdipDisposeImage(Image)
    GdiplusShutdown(gdiplusToken)
    .return 0;

wmain endp

    end _tstart

The input file is 480056 (WomanRGB.bmp from WomanRGB.jpg)
Shapes001.jpg is 3759
Shapes050.jpg is 18945
Shapes100.jpg is 150099

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #18 on: April 25, 2020, 07:37:38 PM »
This is my first work in 64 bit coding.  :biggrin:
Did the same direct approach as in my 32 bit GdiPlus examples but it failed.
Don't know if the EncoderParameters object is different in 64 bit.
I'm not sure if I did the addressing of the pointer to the JpgQualityLevel parameter value correctly....

   mov   JpgQualityLevel,10
   lea   eax,JpgQualityLevel
   mov   Qptr,eax      

It saves a jpg file but the Encoder Quality has no effect.
Tried all kind of alignments for the EncoderParameters object.

@nidud
Could you make a memory dump of the ( filled ) EncoderParameters object of your working example?
So I can compare the values and alignments.
Creative coders use backward thinking techniques as a strategy.

nidud

  • Member
  • *****
  • Posts: 1980
    • https://github.com/nidud/asmc
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #19 on: April 25, 2020, 08:13:29 PM »

EncoderParameter    STRUC
Guid                GUID <>
NumberOfValues      ULONG ?
Type                ULONG ?
Value               PVOID ?
EncoderParameter    ENDS

EncoderParameters   STRUC
Count               UINT ?
Parameter           EncoderParameter <>
EncoderParameters   ENDS

from the list file:

EncoderParameter . . . . . . . .              20 (8)
  Guid . . . . . . . . . . . . .               0   GUID
  NumberOfValues . . . . . . . .              10   ULONG
  Type . . . . . . . . . . . . .              14   ULONG
  Value  . . . . . . . . . . . .              18   PVOID
EncoderParameters  . . . . . . .              28 (8)
  Count  . . . . . . . . . . . .               0   UINT
  Parameter  . . . . . . . . . .               8   EncoderParameter

Note that UINT/ULONG is DWORD and PVOID is QWORD.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7530
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #20 on: April 25, 2020, 08:28:17 PM »
I tried a whole collection of variations on the JPG code in 64 bit but could not get the extra parameters to work. This is the library format that I got to work but no extra parameters for the last argument,

    invoke GdipSaveImageToFile,hGdip,L(filename),pCLSID,NULL

Algo

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

SaveAsJPGx proc bmHandle:QWORD,filename:QWORD

    LOCAL hGdip  :QWORD
    LOCAL pCLSID :QWORD

  .data
    CLSID_ImageType1 GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
  .code

    mov pCLSID, ptr$(CLSID_ImageType1)

    invoke GdipCreateBitmapFromHBITMAP,bmHandle,0,ptr$(hGdip)
    invoke GdipSaveImageToFile,hGdip,L(filename),pCLSID,NULL
    invoke GdipDisposeImage,hGdip

    ret

SaveAsJPGx endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #21 on: April 25, 2020, 09:20:03 PM »
nidud, thank you very much for the list dump.  :thumbsup:
It made things clear to me and now I did succeed to do the direct approach in 64 bit as well.

@Hutch, problem solved.  :biggrin:

EDIT: source code update

Code: [Select]
    include \masm32\include64\masm64rt.inc

.data?
pImage                  dq ?
JpgQualityLevel         dd ?
FilenameW               dw  MAX_PATH dup (?)

.data
CLSID_ImageType         GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>> ; JPG image type
JPG_EncoderParameters   dq 1    ; Number of parameters in this structure
CLSID_EncoderQuality    GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>
                        dd 1    ; Number of the parameter values
                        dd 4    ; ValueTypeLong
                        dq offset JpgQualityLevel

szWomanImage            db "Woman.bmp",0
szWoman                 db "Woman.jpg",0
   
.code
entry_point proc

    conout lf,"  64bit GDIplus Jpg Quality Encoder",lf,lf

    GdiPlusBegin                    ; initialise GDIPlus

    invoke  MultiByteToWideChar,CP_ACP,0,addr szWomanImage,-1,addr FilenameW,MAX_PATH-1
    invoke  GdipCreateBitmapFromFile,addr FilenameW,addr pImage
    test    rax,rax
    jnz     Done
   
    mov     JpgQualityLevel,50      ; 0 to 100

    invoke  MultiByteToWideChar,CP_ACP,0,addr szWoman,-1,addr FilenameW,MAX_PATH-1
    invoke  GdipSaveImageToFile,pImage,addr FilenameW,addr CLSID_ImageType,addr JPG_EncoderParameters

    invoke  GdipDisposeImage,pImage
Done:

    waitkey "  Press any key to continue ..."

    GdiPlusEnd                      ; GdiPlus cleanup

    invoke  ExitProcess,0
    ret

entry_point endp

end
Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7530
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #22 on: April 25, 2020, 09:55:59 PM »
Great, it works well, I have tested it from 10% to 75% and the results are perfect.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7530
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #23 on: April 25, 2020, 11:54:55 PM »
Made the code into module format, works fine.

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

SaveImgAsJpg proc BmpHndl:QWORD,filename:QWORD,quality:QWORD

    LOCAL pImage :QWORD

  ; ------------------------------------------------------------------------

  .data?
    QualityLevel@@@@@@@@   dd ?    ; mangle to avoid accidental duplicates

  .data
    CLSID_ImageType1 GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h, \
                           000h,0F8h,01Eh,0F3h,02Eh>> ; JPG image type

    JPG_EncoderParameters  dd 1    ; Number of parameters in this structure
                           dd 0    ; alignment !
    CLSID_EncoderQuality   GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh, \
                                 05dh,0b3h,051h,005h,0e7h,0ebh>>
                           dd 1    ; Number of the parameter values
                           dd 4    ; ValueTypeLong
    JpgQualityParameterPTR dq offset QualityLevel@@@@@@@@

  .code

  ; ------------------------------------------------------------------------

    cmp quality, 100
    ja default
    jmp next

  default:
    mov quality, 75                                             ; default for quality error

  next:
    rcall GdipCreateBitmapFromHBITMAP,BmpHndl,0,ptr$(pImage)    ; convert BMP handle
    mov rax, quality                                            ; set the quality level
    mov QualityLevel@@@@@@@@, eax                               ; 100 = highest, 0 = lowest
    invoke GdipSaveImageToFile,pImage,L(filename), \
           ADDR CLSID_ImageType1,ADDR JPG_EncoderParameters     ; write JPG image to file
    rcall GdipDisposeImage,pImage                               ; delete GDIP handle

    ret

SaveImgAsJpg endp

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


I should mangle all of the names to ensure no accidents.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #24 on: April 26, 2020, 12:32:17 AM »
Cool.  :cool:

Finally started exploring 64bit coding.
Working my way thru MACROS and addressing modes, still have a lot to learn...

Made my first Routine in 64bit.

Code: [Select]
    include \masm32\include64\masm64rt.inc

.const
Image_BMP       equ 0
Image_JPG       equ 1
Image_GIF       equ 2
Image_TIF       equ 5
Image_PNG       equ 6

.data?
pImage                  dq ?
pImageTemp              dq ?
JpgQualityLevel         dd ?

.data
CLSID_ImageTypes        GUID <0557CF400h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>> ; For all image types
JPG_EncoderParameters   dq 1    ; Number of parameters in this structure
CLSID_EncoderQuality    GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>
                        dd 1    ; Number of the parameter values
                        dd 4    ; ValueTypeLong
                        dq offset JpgQualityLevel
.code

SaveJpgQualityImage proc pGdiPlusImage:QWORD,pFilename:QWORD,Image_Quality:DWORD

    invoke  GdipCloneImage,pGdiPlusImage,addr pImageTemp ; make a copy of the image to work with.

    mov     eax,Image_Quality
    mov     JpgQualityLevel,eax
    mov     byte ptr [CLSID_ImageTypes],Image_JPG
   
    invoke  GdipSaveImageToFile,pImageTemp,L(pFilename),addr CLSID_ImageTypes,addr JPG_EncoderParameters
    invoke  GdipDisposeImage,pImageTemp
    ret
SaveJpgQualityImage endp
   
entry_point proc

    conout lf,"  64bit GDIplus Jpg Quality Encoder",lf,lf

    GdiPlusBegin                    ; initialise GDIPlus

; Load a test Image
    invoke  GdipCreateBitmapFromFile,L("Woman.bmp"),addr pImage
    test    rax,rax
    jnz     Done

    invoke  SaveJpgQualityImage,pImage,"Woman.jpg",50

    invoke  GdipDisposeImage,pImage
Done:

    waitkey "  Press any key to continue ..."

    GdiPlusEnd                      ; GdiPlus cleanup

    invoke  ExitProcess,0
    ret

entry_point endp

end
Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7530
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #25 on: April 26, 2020, 01:18:04 AM »
Once you get a feel for it, its great stuff, lots more registers, massive amounts of memory and without having to keep making prototypes, you can code very fast with practice.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #26 on: April 26, 2020, 03:56:24 AM »
@Hutch
Having fun already, prepare for some questions later.  :biggrin:

Bare Minimum 64 bit GDIplus code to save different Image Types, PixelFormat and colorconversions.
Only 1 GUID used for all the Image Types.
Now you can find a balance between Image quality and file size.

You need to replace the old 64bit "gdiplus.lib" with a newer version.
The lib is included in the Lib folder, it's from the Microsoft SDK Windows v7.1A.

Hope I didn't violate the 64 bit coding rules, else let me know......
Creative coders use backward thinking techniques as a strategy.

Vortex

  • Member
  • *****
  • Posts: 2327
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #27 on: April 26, 2020, 05:40:20 AM »
Hello Siekmanski,

I was able to build your example GDIplus64_color_conversions with Pelle's 64-bit import library. An alternative to SDK Windows v7 :

Code: [Select]
\PellesC\lib\Win64\gdiplus.lib

Siekmanski

  • Member
  • *****
  • Posts: 2314
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #28 on: April 26, 2020, 06:20:21 AM »
Nice that it works.
Still have to find my way in 64 bit coding, maybe the code can be improved?
Creative coders use backward thinking techniques as a strategy.

jj2007

  • Member
  • *****
  • Posts: 10539
  • Assembler is fun ;-)
    • MasmBasic
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #29 on: April 29, 2020, 07:07:49 AM »
A little bit off topic: I am trying (successfully) to implement...

GdipDrawImageRectRectI(*graphics, GpImage *image, INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy, INT srcwidth, INT srcheight, GpUnit srcUnit, GpImageAttributes* imageAttributes, DrawImageAbort callback, VOID * callbackData)

... to zoom into a picture. It works fine. Then I want to save the image using GdipSaveImageToFile

GdipSaveImageToFile(GpImage *image, WCHAR* filename, CLSID* clsidEncoder, EncoderParameters* encoderParams)

That works fine, too, but it saves the original image, not the zoomed one. Does Draw write to the bitmap/the image?

My understanding was that graphics is the equivalent of a DC in "normal" GDI, while image is the bitmap. Since GdipSaveImageToFile saves the bitmap, I expected to see the zoomed version. What's wrong with my logic? And how could I get access to the zoomed (or otherwise modified) image?

Unfortunately, the documentation is lousy :cool: