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

Siekmanski

  • Member
  • *****
  • Posts: 2453
Bare Minimum GDIplus code to save different Image Types.
« on: April 21, 2020, 06:08:10 AM »
Bare Minimum GDIplus code to save different Image Types.
Without the GDIplus helper functions to make the code smaller and easier to read.
Only 1 CLSID guid is used for all the Image Types.

Includes the JPEG Quality Encoder, so creating very small .jpg files is easy.
Creative coders use backward thinking techniques as a strategy.

Vortex

  • Member
  • *****
  • Posts: 2456
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #1 on: April 21, 2020, 06:17:52 AM »
Hi Siekmanski,

Your example works fine on Windows XP :thumbsup:

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #2 on: April 21, 2020, 06:31:03 AM »
Cool, thanks.  :thumbsup:
Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7796
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #3 on: April 21, 2020, 09:34:10 AM »
Gratsie, works fine on my Win10 64. This will be very useful.  :thumbsup:
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7796
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #4 on: April 21, 2020, 07:07:00 PM »
Marinus,

I have a question for you, using your earlier work I got a number of file formats working but had an unusual result saving the image as a bitmap. The source bitmap was a bit over 500k, when saved it was over 800k, the only compression method I have every understood with bitmaps was RLE encodings, do you have any grasp of why the result is so much bigger ?
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #5 on: April 21, 2020, 09:45:23 PM »
These 6 types of ".BMP" Bitmaps are the most commonly used. ( there are more exotic types... )

ARGB  = 32 bits per pixel ( includes the alpha channel )
RGB   = 24 bits per pixel
RGB16 = 16 bits per pixel
RGB8  =  8 bits per pixel ( pixels are indexed using a 256 color map )
RGB4  =  4 bits per pixel ( pixels are indexed using a 16 color map )
RGB1  =  1 bits per pixel ( pixels are indexed using a 2 color map )

There are 2 types of storing, Raw uncompressed and RLE encoded.
RLE encoded, depends on the repeating patterns of equal valued pixels, therefore will vary in size.

So, I think the 800K is raw ARGB and the 500K RLE encoded.

It's possible to create those different types of BMP bitmaps with GDIplus.
Look for:

    invoke  GdipBitmapLockBits,pImage,NULL,ImageLockModeRead,PixelFormat32bppARGB,offset GDIplusBitmapData

    invoke  GdipCreateBitmapFromScan0,I_Width,I_Height,pLockedRect.Pitch,PixelFormat32bppRGB,pLockedRect.pBits,addr pImage


ImageLockModeRead               equ 1
ImageLockModeWrite              equ 2
ImageLockModeReadWrite          equ 3
ImageLockModeUserInputBuf       equ 4

PixelFormat1bppIndexed          equ 30101h
PixelFormat4bppIndexed          equ 30402h
PixelFormat8bppIndexed          equ 30803h
PixelFormat16bppGreyScale       equ 101004h
PixelFormat16bppRGB555          equ 21005h
PixelFormat16bppRGB565          equ 21006h
PixelFormat16bppARGB1555        equ 61007h
PixelFormat24bppRGB             equ 21808h
PixelFormat32bppRGB             equ 22009h
PixelFormat32bppARGB            equ 26200Ah
PixelFormat32bppPARGB           equ 0E200Bh

EDIT:
Useful functions for indexed color types (256 or less colors)

GdipInitializePalette
GdipBitmapConvertFormat
« Last Edit: April 21, 2020, 11:38:55 PM by Siekmanski »
Creative coders use backward thinking techniques as a strategy.

wind

  • Regular Member
  • *
  • Posts: 12
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #6 on: April 24, 2020, 05:26:48 PM »
great, works on my win10 64 too.  :thumbsup:

only 1 small issues, have to comment out line 19-24, or else get an assembling error:

Code: [Select]
C:\Temp\GDIplusSmall\GDIplusSmall.asm(20) : error A2163: non-benign structure redefinition: incorrect initializers : GdiplusStartupInput
C:\Temp\GDIplusSmall\GDIplusSmall.asm(21) : error A2163: non-benign structure redefinition: incorrect initializers : GdiplusStartupInput
C:\Temp\GDIplusSmall\GDIplusSmall.asm(22) : error A2163: non-benign structure redefinition: incorrect initializers : GdiplusStartupInput
C:\Temp\GDIplusSmall\GDIplusSmall.asm(23) : error A2163: non-benign structure redefinition: incorrect initializers : GdiplusStartupInput

windows.inc already has the structure defined.

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #7 on: April 24, 2020, 06:37:18 PM »
Yes, you are right.  :thumbsup:
Didn't know some GdiPlus includes were added in the latest 2012 windows.inc ( Shouldn't they belong in gdiplus.inc ?)
My windows.inc is an older version, time to update I guess. :tongue:
Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7796
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #8 on: April 24, 2020, 07:00:51 PM »
I can get JPG code to work but I have not discovered how to set the last argument for "GdipSaveImageToFile". The range of reference material I can access has conflicting definitions of what is a structure. It currently converts a 6 meg bitmap to a JPG 137194 bytes and nothing I have tried changes the compression level. Using a image app that I have had for years, if I set the compression rate to about 75% I get a JPG of near identical size ad the GDIP conversion looks fine with no visible deterioration.


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

    encoder STRUCT QWORD
      count dq ?
      pguid dq ?
      dwtyp dq ?
      nmval dq ?
    encoder ENDS

 ;     typedef struct EncoderParameters {
 ;       GUID Guid:
 ;       ULONG NumberOfValues;
 ;       ULONG Type;
 ;       void * Value;
 ;     };

 ;     typedef struct EncoderParameters {
 ;        UINT Count;
 ;        EncoderParameter Parameter|i|;
 ;     };




 ;    Jose Roca
 ;    eps.count = 1
 ;    eps.Parameter(0).pGuid = $EncoderQuality
 ;    eps.Parameter(0).dwType = %EncoderParameterValueTypeLong
 ;    eps.Parameter(0).NumberOfValues = 1

  .data
    CLSID_ImageType GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
    CLSID_EncoderQuality GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>

   .code

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

SaveJpgQualityImage proc hBMP:QWORD,pFilename:QWORD,Image_Quality:QWORD

    LOCAL hGdip :QWORD
    LOCAL pTemp :QWORD
    LOCAL pqual :QWORD
    LOCAL ptype :QWORD
    LOCAL penc  :QWORD
    LOCAL encd  :encoder

    mov ptype, ptr$(CLSID_ImageType)
    mov pqual, ptr$(CLSID_EncoderQuality)

    mov encd.count, 1                   ; count
    mrm encd.pguid, pqual               ; pointer to CLSID_EncoderQuality
    mrm encd.dwtyp, 75                   ; EncoderParameterValueTypeLong
    mov encd.nmval, 1                   ; NumberOfValues

    mov penc, ptr$(encd.count)

    invoke GdipCreateBitmapFromHBITMAP,hBMP,0,ptr$(hGdip)       ; OK
    invoke GdipCloneImage,hGdip,ptr$(pTemp)                     ; OK
    test rax, rax
    jnz  Done

    invoke GdipSaveImageToFile,pTemp,L(pFilename),ptype,penc    ; last arg does not work

    invoke GdipDisposeImage,pTemp                               ; release image copy
    invoke GdipDisposeImage,hGdip

  Done:
    ret

SaveJpgQualityImage endp

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


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

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #9 on: April 24, 2020, 08:00:53 PM »
The order of the struct is this,

    LOCAL iqual :QWORD


    encoder STRUCT QWORD
      count dq ? ; = 1
      pguid dq ? ; = pointer to CLSID_EncoderQuality
      nmval dq ? ; = 1  NumberOfValues
      dwtyp dq ? ; = 4  (EncoderParameterValueTypeLong)
      QualityLevel dq ? ; pointer to Image_Quality
    encoder ENDS

    mov iqual, ptr$(Image_Quality) ; 75 in your example

    mov encd.count, 1                   ; count
    mrm encd.pguid, pqual               ; pointer to CLSID_EncoderQuality
    mov encd.nmval, 1                   ; NumberOfValues
    mov encd.dwtyp, 4                   ; EncoderParameterValueTypeLong
    mrm encd.QualityLevel, iqual        ; JPG QualityLevel

In your example you don't need to clone the image -> GdipCloneImage


Did not test it, hope it works.  :biggrin:
Creative coders use backward thinking techniques as a strategy.

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #10 on: April 24, 2020, 08:26:36 PM »
Bare Minimum GDIplus code to save different Image Types and PixelFormat conversions.

Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7796
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #11 on: April 24, 2020, 09:13:01 PM »
I modified the algo to this form but it still won't change the compression ratio. I wondered if I am using the correct GUID

 CLSID_EncoderQuality GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>



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

    encoder STRUCT QWORD
      count dq ?            ; = 1
      pguid dq ?            ; = pointer to CLSID_EncoderQuality
      nmval dq ?            ; = 1  NumberOfValues
      dwtyp dq ?            ; = 4  (EncoderParameterValueTypeLong)
      qlevl dq ?            ; = pointer to Image_Quality
    encoder ENDS

  .data
    CLSID_ImageType GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
    CLSID_EncoderQuality GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>

   .code

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

SaveJpgQualityImage proc hBMP:QWORD,pFilename:QWORD,Image_Quality:QWORD

    LOCAL hGdip :QWORD
    LOCAL pTemp :QWORD
    LOCAL pqual :QWORD
    LOCAL ptype :QWORD
    LOCAL penc  :QWORD
    LOCAL iqual :QWORD
    LOCAL encd  :encoder

    mov ptype, ptr$(CLSID_ImageType)
    mov pqual, ptr$(CLSID_EncoderQuality)

    mov iqual, ptr$(Image_Quality)

    mov encd.count, 1                   ; count
    mrm encd.pguid, pqual               ; pointer to CLSID_EncoderQuality
    mov encd.nmval, 1                   ; NumberOfValues
    mov encd.dwtyp, 4                   ; EncoderParameterValueTypeLong
    mrm encd.qlevl, iqual               ; JPG qlevl

    mov penc, ptr$(encd.count)

    invoke GdipCreateBitmapFromHBITMAP,hBMP,0,ptr$(hGdip)       ; OK
    invoke GdipSaveImageToFile,hGdip,L(pFilename),ptype,penc    ; last arg does not work
    invoke GdipDisposeImage,hGdip

    ret

SaveJpgQualityImage endp

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

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #12 on: April 24, 2020, 09:32:27 PM »
Maybe this will work ( I'm not experienced in 64bit code )

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

    encoder STRUCT QWORD
      count dq ?            ; = 1
      pguid dq ?            ; = pointer to CLSID_EncoderQuality
      nmval dq ?            ; = 1  NumberOfValues
      dwtyp dq ?            ; = 4  (EncoderParameterValueTypeLong)
      qlevl dq ?            ; = pointer to Image_Quality
    encoder ENDS

  .data
    CLSID_ImageType GUID <0557CF401h,01A04h,011D3h,<09Ah,073h,000h,000h,0F8h,01Eh,0F3h,02Eh>>
    CLSID_EncoderQuality GUID <01d5be4b5h,0fa4ah,0452dh,<09ch,0ddh,05dh,0b3h,051h,005h,0e7h,0ebh>>

   .code

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

SaveJpgQualityImage proc hBMP:QWORD,pFilename:QWORD,Image_Quality:QWORD

    LOCAL hGdip :QWORD
    LOCAL pTemp :QWORD
    LOCAL pqual :QWORD
    LOCAL ptype :QWORD
    LOCAL penc  :QWORD
    LOCAL iqual :QWORD
    LOCAL iJPGQ :QWORD
    LOCAL encd  :encoder

    mov ptype, ptr$(CLSID_ImageType)
    mov pqual, ptr$(CLSID_EncoderQuality)

    mov rax,Image_Quality
    mov iJPGQ,rax
    mov iqual, ptr$(iJPGQ)

    mov encd.count, 1                   ; count
    mrm encd.pguid, pqual               ; pointer to CLSID_EncoderQuality
    mov encd.nmval, 1                   ; NumberOfValues
    mov encd.dwtyp, 4                   ; EncoderParameterValueTypeLong
    mrm encd.qlevl, iqual               ; JPG qlevl

    mov penc, ptr$(encd.count)

    invoke GdipCreateBitmapFromHBITMAP,hBMP,0,ptr$(hGdip)       ; OK
    invoke GdipSaveImageToFile,hGdip,L(pFilename),ptype,penc    ; last arg does not work
    invoke GdipDisposeImage,hGdip

    ret

SaveJpgQualityImage endp

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

Creative coders use backward thinking techniques as a strategy.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7796
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #13 on: April 24, 2020, 10:10:16 PM »
I gave it a blast but no change, maybe the 64 bit version of GDIP is broken somewhere.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

nidud

  • Member
  • *****
  • Posts: 2013
    • https://github.com/nidud/asmc
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #14 on: April 25, 2020, 01:28:23 AM »
I gave it a blast but no change, maybe the 64 bit version of GDIP is broken somewhere.

Tested the version Vortex made in 64-bit and that seems to work. It did however fail in 32-bit for some reason.

Code: [Select]

; build: asmc64 -ws -pe -Zp8 -gui test.asm

include windows.inc
include gdiplus.inc
include tchar.inc

    .data
    hBitmap HANDLE 0

    .code

WndProc proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM

    .switch message

    .case WM_CREATE

        .new token:ULONG_PTR
        .new BmpImage:ptr GpBitmap
        .new StartupInfo:GdiplusStartupInput

        mov StartupInfo.GdiplusVersion,1

        GdiplusStartup(&token, &StartupInfo, 0)
        GdipCreateBitmapFromFile("logo.png", &BmpImage)
        GdipCreateHBITMAPFromBitmap(BmpImage, &hBitmap, 0)
        GdipDisposeImage(BmpImage)
        .endc

    .case WM_PAINT

        .new ps:PAINTSTRUCT
        .new hdc:HDC
        .new hMemDC:HANDLE
        .new bm:BITMAP

        mov hdc,BeginPaint(hWnd, &ps)
        mov hMemDC,CreateCompatibleDC(rax)

        SelectObject(rax, hBitmap)
        GetObject(hBitmap, BITMAP, &bm)
        BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY)
        DeleteDC(hMemDC)
        EndPaint(hWnd, &ps)
        .endc

    .case WM_DESTROY
        DeleteObject(hBitmap)
        PostQuitMessage(0)
        .endc

    .default
        .return DefWindowProc(hWnd, message, wParam, lParam)
    .endsw
    xor eax,eax
    ret

WndProc endp

_tWinMain proc hInstance:HINSTANCE, hPrevInstance:HINSTANCE, lpCmdLine:LPTSTR, nShowCmd:SINT

  local wc:WNDCLASSEX, msg:MSG, hwnd:HANDLE

    xor eax,eax
    mov wc.cbSize,          WNDCLASSEX
    mov wc.style,           CS_HREDRAW or CS_VREDRAW
    mov wc.cbClsExtra,      eax
    mov wc.cbWndExtra,      eax
    mov wc.hInstance,       hInstance
    mov wc.hbrBackground,   COLOR_WINDOW+1
    mov wc.lpszMenuName,    rax
    mov wc.lpfnWndProc,     &WndProc
    mov wc.lpszClassName,   &@CStr("BitmapClass")
    mov wc.hIcon,           LoadIcon(0, IDI_APPLICATION)
    mov wc.hIconSm,         rax
    mov wc.hCursor,         LoadCursor(0, IDC_ARROW)

    .ifd RegisterClassEx(&wc)

        .if CreateWindowEx(0, "BitmapClass", "Bitmap from file", WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT, 290, 90, NULL, NULL, hInstance, 0)

            mov hwnd,rax
            ShowWindow(rax, SW_SHOWNORMAL)
            UpdateWindow(hwnd)

            .while GetMessage(&msg,0,0,0)
                TranslateMessage(&msg)
                DispatchMessage(&msg)
            .endw
            mov rax,msg.wParam
        .endif
    .endif
    ret

_tWinMain endp

    end _tstart