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

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #60 on: September 21, 2020, 06:25:40 AM »
Try PixelFormat32bppARGB, it's 32 bits per pixel, the stride is 4 times the width of the image.
So, there is no padding involved.

Be sure the raw image data in memory is valid, thus in 32 bit ARGB format.
Creative coders use backward thinking techniques as a strategy.

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #61 on: September 21, 2020, 06:44:02 AM »
If it still doesn't work, I can write a simple example tomorrow, if you want?
Creative coders use backward thinking techniques as a strategy.

guga

  • Member
  • *****
  • Posts: 1377
  • Assembly is a state of art.
    • RosAsm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #62 on: September 21, 2020, 08:46:23 AM »
Tks, Marinus. If you have time, please, build a small example.

It´s still don´t working.

The goal is to convert to/from whatever image format is stored on a virtual memory buffer (after it collected the image (or pixel data from RT_BITMAP on this case) from the resource section. )


Note: I´m currently updating RosAsm for the next release and i´m fixing these kind of issues on the resources editor. Currently i implemented it to assemble/disassemble and debug SSE3, SSE4, upgraded the debugger, the disassembler and now i´m fixing some of the old resources features for the next release. Doing this i could then continue working on the google watermark remover more properly because i´ll then have the necessary tools to build SSE4 or SSE3 variations of algos if needed :azn:
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

daydreamer

  • Member
  • *****
  • Posts: 1422
  • building nextdoor
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #63 on: September 21, 2020, 11:36:57 PM »
one simple GDI+ question I have after I seen you can rotate images with a GDI+ function:does GDI+ have caps to load and save .ico files?,I want to save size of icons by load .ico file and rotate it as many times I need to have for example icons pointing at 4 or 8 directions,which also could be useful as cursors
Showcase :
With Masm sdk and 2-3 hours = a windows program :D
Beat that C zealots p:

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #64 on: September 21, 2020, 11:58:59 PM »
Hi guga,

Here is an example to get access to the pixels in VirtualMem and converts it in a Gdiplus Bitmap.
You can now save it in all the different Color and Image formats you want.

If I have time this week, I will write a routine that loads all Image formats from:

- Resource
- File
- Memory
- and raw pixels from virtual memory
Creative coders use backward thinking techniques as a strategy.

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #65 on: September 22, 2020, 12:06:52 AM »
Hi Magnus,

As far as I know you can not save Icons with Gdiplus but, I wrote a program that uses Gdiplus with an ico header in front of a .PNG file.
It works perfect and creates very small Icons.
Creative coders use backward thinking techniques as a strategy.

TouEnMasm

  • Member
  • *****
  • Posts: 1501
    • EditMasm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #66 on: September 22, 2020, 01:15:03 AM »

Icon and GDI plus
perhaps :
Quote
GpStatus WINGDIPAPI GdipCreateBitmapFromHICON(HICON hicon, GpBitmap** bitmap)
GpStatus WINGDIPAPI GdipCreateHICONFromBitmap(GpBitmap* bitmap, HICON* hbmReturn)
Fa is a musical note to play with CL

guga

  • Member
  • *****
  • Posts: 1377
  • Assembly is a state of art.
    • RosAsm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #67 on: September 22, 2020, 01:50:14 AM »
Hi Marinus. Many tks :thumbsup: :thumbsup: :thumbsup:

I´m looking at it right now :)

During this morning i succeeded to save it from the resources section, but i´m not sure if it will save it correctly. I´m take a look at yours and see if it´s easier then the way i found this morning. Your´s is, as usual, simpler and easier to follow :thumbsup:

What i did was create a HBitmap from an image loaded in memory and then using GdipCreateBitmapFromHBITMAP to save it as bitmap and later export to other formats. Tooo overhead, i think :mrgreen: :mrgreen: Your´s seems to be way easier. I`ll give a try on it to see if it works for the 8 bpp  bitmap images as well.

What i did was:

1 - GdipCreateBitmapFromBmpMem
Code: [Select]

;;
    GdipCreateBitmapFromBmpMem
        This function creates a Bitmap object from a BitMapInfo structure existent in RT_BITMAP data type of
        the resources section on a PE file. It can also be used to create a HBitmap directly from
        a bitmap loaded in memory to be used in GdiPlus Apis.

    Parameters:
        InputData(In) - Pointer to the start of a BITMAPINFOHEADER structure of a image. The structure can
                        be either loaded from memory or the one existant inside the resources sections of a PE
                        at the RT_BITMAP type from the RESOURCE_HEADER structure
        pImage(out) -   Pointer to a DWORD variable that receives a pointer to a Bitmap object.

    Return value:

        If the function succeeds, it returns TRUE, and it will stored the Bitmap Object onto pImage parameter.
        If the function fails it returns FALSE

;;

Proc GdipCreateBitmapFromBmpMem::
    Arguments @InputData, @pImage
    Local @rhPalette, @hBmp, @ReturnValue
    Uses ecx, edx

    xor eax eax
    On D@pImage = 0, ExitP
    mov D@ReturnValue &FALSE

    lea eax D@rhPalette | mov D$eax 0
    call CreatehBitmapFromBmpMem D@InputData, eax
    mov D@hBmp eax

    .If eax = 0
        If D@rhPalette <> 0
            call 'GDI32.DeleteObject' D@rhPalette
        End_If
        xor eax eax
        ExitP
    .End_If

    mov eax D@pImage | mov D$eax 0
    call 'gdiplus.GdipCreateBitmapFromHBITMAP' D@hBmp, D@rhPalette, D@pImage
    If eax = &S_OK
        mov D@ReturnValue &TRUE
    End_If

    call 'GDI32.DeleteObject' D@rhPalette
    call 'GDI32.DeleteObject' D@hBmp

    mov eax D@ReturnValue

EndP

2 - CreatehBitmapFromBmpMem

Code: [Select]


;;
    CreatehBitmapFromBmpMem
        This function creates a HBitmap from a BitMapInfo structure existent in RT_BITMAP data type of
        the resources section on a PE file. It can also be used to create a HBitmap directly from
        a bitmap loaded in memory.

    Parameters:
        pbmih(In) - Pointer to the start of a BITMAPINFOHEADER structure of a image. The structure can
                    be either loaded from memory or the one existant inside the resources sections of a PE
                    at the RT_BITMAP type from the RESOURCE_HEADER structure
        rhPalette(out) - Pointer to a variable that will hold the handle to a logical palette

    Return value:

        If the function succeeds, the return value is a handle to the compatible bitmap.
        If the function fails, the return value is NULL.
;;

[BITMAPFILEHEADER.bfTypeDis 0
 BITMAPFILEHEADER.bfSizeDis 2
 BITMAPFILEHEADER.bfReserved1Dis 6
 BITMAPFILEHEADER.bfReserved2Dis 8
 BITMAPFILEHEADER.bfOffBitsDis 10]

[Size_Of_BITMAPFILEHEADER 14] ; always unaligned

[BITMAPINFOHEADER.biSizeDis 0
 BITMAPINFOHEADER.biWidthDis 4
 BITMAPINFOHEADER.biHeightDis 8
 BITMAPINFOHEADER.biPlanesDis 12
 BITMAPINFOHEADER.biBitCountDis 14
 BITMAPINFOHEADER.biCompressionDis 16
 BITMAPINFOHEADER.biSizeImageDis 20
 BITMAPINFOHEADER.biXPelsPerMeterDis 24
 BITMAPINFOHEADER.biYPelsPerMeterDis 28
 BITMAPINFOHEADER.biClrUsedDis 32
 BITMAPINFOHEADER.biClrImportantDis 36]

[Size_Of_BITMAPINFOHEADER 40]


[BITMAPINFO.bmiHeader.biSizeDis 0
 BITMAPINFO.bmiHeader.biWidthDis 4
 BITMAPINFO.bmiHeader.biHeightDis 8
 BITMAPINFO.bmiHeader.biPlanesDis 12
 BITMAPINFO.bmiHeader.biBitCountDis 14
 BITMAPINFO.bmiHeader.biCompressionDis 16
 BITMAPINFO.bmiHeader.biSizeImageDis 20
 BITMAPINFO.bmiHeader.biXPelsPerMeterDis 24
 BITMAPINFO.bmiHeader.biYPelsPerMeterDis 28
 BITMAPINFO.bmiHeader.biClrUsedDis 32
 BITMAPINFO.bmiHeader.biClrImportantDis 36
 BITMAPINFO.bmiColors.rgbBlueDis 40
 BITMAPINFO.bmiColors.rgbGreenDis 41
 BITMAPINFO.bmiColors.rgbRedDis 42
 BITMAPINFO.bmiColors.rgbReservedDis 43]

[Size_of_BITMAPINFO 44]

[BITMAPCOREHEADER.bcSizeDis 0
 BITMAPCOREHEADER.bcWidthDis 4
 BITMAPCOREHEADER.bcHeightDis 6
 BITMAPCOREHEADER.bcPlanesDis 8
 BITMAPCOREHEADER.bcBitCountDis 10]

[Size_Of_BITMAPCOREHEADER 12]


; https://cpp.hotexamples.com/pt/site/file?hash=0x87ae29357002cdaaf6145c64b282397a0fd7de4a38023f9193fd5f26f6072cc8
Proc CreatehBitmapFromBmpMem::
    Arguments @pbmih, @rhPalette
    Local @hDC, @iNumColors, @hBitmapFinal
    Uses ecx, edx

    call 'USER32.GetDC' &NULL
    mov D@hDC eax

    mov D@iNumColors 0

    lea eax D@iNumColors
    call CreateDIBPalette D@pbmih, eax
    mov edx D@rhPalette
    mov D$edx eax
    If eax <> 0
        call 'GDI32.SelectPalette' D@hDC, eax, &FALSE
        call 'GDI32.RealizePalette' D@hDC
    End_If

    mov ecx D@pbmih | mov edx D@pbmih
    add edx D$ecx+BITMAPINFOHEADER.biSizeDis
    mov eax D@iNumColors | lea ecx D$edx+eax*4
    call 'GDI32.CreateDIBitmap' D@hDC, D@pbmih, &CBM_INIT, ecx, D@pbmih, &DIB_RGB_COLORS
    mov D@hBitmapFinal eax
    call 'USER32.ReleaseDC' &NULL, D@hDC
    mov eax D@hBitmapFinal

EndP

3 -  CreateDIBPalette
Code: [Select]

;;
    CreateDIBPalette
        This function creates a logical Palette from the BitmapInfo Header of a bitmap file.

        lpbmi(in) - Pointer to the start of a BITMAPINFOHEADER structure of a image. The structure can
                    be either loaded from memory or the one existant inside the resources sections of a PE
                    at the RT_BITMAP type from the RESOURCE_HEADER structure
        riNumColors(out) - Pointer to a variable that will hold the total amount of color Indexes ued on the
                           generated palette

    Return value:

        If the function succeeds, the return value is a handle to the logical palette.
        If the function fails, the return value is NULL.

;;

[LOGPALETTE.palVersionDis 0
 LOGPALETTE.palNumEntriesDis 2
 LOGPALETTE.palPalEntry.peRedDis 4
 LOGPALETTE.palPalEntry.peGreenDis 5
 LOGPALETTE.palPalEntry.peBlueDis 6
 LOGPALETTE.palPalEntry.peFlagsDis 7]

[Size_Of_LOGPALETTE 8]

Proc CreateDIBPalette::
    Arguments @lpbmi, @riNumColors
    Local @NumColors, @lpPal, @TmpMem, @hPal
    Uses esi, edi, ecx, edx

    mov esi D@lpbmi
    movzx ecx W$esi+BITMAPINFOHEADER.biBitCountDis

    ; pick number of colors
    xor eax eax ; No palette needed for 24 BPP DIB
    If ecx <= 8 ; we only need for 8 bpp or less
        mov eax 1 | shl eax cl
    Else_If D$esi+BITMAPINFOHEADER.biClrUsedDis > 0
        mov eax D$esi+BITMAPINFOHEADER.biClrUsedDis
    End_If
    mov ecx D@riNumColors
    mov D$ecx eax
    On eax = 0, ExitP ; No palette if no colors
    mov D@NumColors eax

    ; Build palette from bitmap info

    ; get the total size of the pallette
    mov ecx D@NumColors
    lea edx D$ecx*4+4 ; sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * (riNumColors-1)) = 8 + 4*(NumColors-1) = 8 + 4*NumColors - 4 => 4*(1+NumColors)
    mov D@TmpMem 0 | lea eax D@TmpMem
    call 'RosMem.VMemAlloc' eax, edx
    mov D@lpPal eax
    mov edi eax

    mov W$edi+LOGPALETTE.palVersionDis 0300
    mov eax D@NumColors | mov W$edi+LOGPALETTE.palNumEntriesDis ax

    xor ecx ecx
    Do
        mov al B$esi+ecx*4+BITMAPINFO.bmiColors.rgbRedDis | mov B$edi+ecx*4+LOGPALETTE.palPalEntry.peRedDis al
        mov al B$esi+ecx*4+BITMAPINFO.bmiColors.rgbGreenDis | mov B$edi+ecx*4+LOGPALETTE.palPalEntry.peGreenDis al
        mov al B$esi+ecx*4+BITMAPINFO.bmiColors.rgbBlueDis | mov B$edi+ecx*4+LOGPALETTE.palPalEntry.peBlueDis al
        mov B$edi+ecx*4+LOGPALETTE.palPalEntry.peFlagsDis 0
        inc ecx
    Loop_Until ecx >= D@NumColors

    call 'GDI32.CreatePalette' D@lpPal
    mov D@hPal eax
    call 'RosMem.VMemFree' D@lpPal ; release the allocated pallette
    mov eax D@hPal

EndP



It works like:


    mov eax D@pRsrcList
    mov ecx D$eax+RosAsm_Rsrc_Data.PtrDis | mov D@InputData ecx

    lea eax D@pImage | mov D$eax 0
    call 'FastCRT.GdipCreateBitmapFromBmpMem' D@InputData, eax

    call SaveNonIndexedImage D@pImage, OtherSaveFilter, Image_PNG, PixelFormat32bppRGB


As you see, is way complicated to work then yours (My head will blow...didn´t sleep again) :bgrin: :bgrin:. I´ll  take a look at yours and see if i can make it work with it insetad the huge bloated code i did :)
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

  • Member
  • *****
  • Posts: 1377
  • Assembly is a state of art.
    • RosAsm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #68 on: September 22, 2020, 08:41:51 AM »
Hi Marinus

Im testing the different output formats for bitmap. 2 of them seems not to be working

PIXELFORMAT_16BPPGRAYSCALE
and
PIXELFORMAT_1BPPINDEXED

Are not converting to gray or monochrome.


These other formats below i didn´t tested yet:
PIXELFORMAT_48BPPRGB
PIXELFORMAT_64BPPARGB
PIXELFORMAT_64BPPPARGB

All the rest seems to be working fine.

I tried again using your routine on the example, but it only works when i convert the 8bit image through mine routine GdipCreateBitmapFromBmpMem. I didn´t understood completely what is the math related to those conversions of different formats  to point to the start of the pixel data and not the start of a palette, for example (which seems to be the case when dealing with 8bpp or less right ?)

Below is what i´m currently doing to test all the routines :)



And the attached file is the output in  PIXELFORMAT_32BPPRGB


I´ll now try to export in png and later in jpg, heic and emf file.


One question...I know how to decode a webp file using libwebp, but how to make it work directly from gdiplus ?
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #69 on: September 22, 2020, 09:24:08 AM »
Quote
I tried again using your routine on the example, but it only works when i convert the 8bit image through mine routine GdipCreateBitmapFromBmpMem. I didn´t understood completely what is the math related to those conversions of different formats  to point to the start of the pixel data and not the start of a palette, for example (which seems to be the case when dealing with 8bpp or less right ?)

This is what I always do,
First load the image and convert it to 32 bit ARGB -> [ invoke      GdipBitmapLockBits,pImage,NULL,ImageLockModeRead,PixelFormat32bppARGB,offset GDIplusBitmapData ]

Processing the pixels if I need to.

Then save it in the color format I want with the help of GdiPlus. ( as I did in GDIplusMemory.zip )
Never did I use Pixelformats that has more than 8 bit per color element.

PIXELFORMAT_16BPPGRAYSCALE
I have to check the formats with more than 8 bit per color element.
I wonder how you project more than 256 pure gray colors to the screen.....

PIXELFORMAT_1BPPINDEXED works OK
Function: SaveColorIndexedImage -> set the Color_Count to 2

Quote
One question...I know how to decode a webp file using libwebp, but how to make it work directly from gdiplus ?

Don't know.
You could try to save the raw ARGB data to memory ( if libwebp allows you to do that ) and use GdipCreateBitmapFromScan0.
Creative coders use backward thinking techniques as a strategy.

guga

  • Member
  • *****
  • Posts: 1377
  • Assembly is a state of art.
    • RosAsm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #70 on: September 22, 2020, 02:03:11 PM »
Quote
I tried again using your routine on the example, but it only works when i convert the 8bit image through mine routine GdipCreateBitmapFromBmpMem. I didn´t understood completely what is the math related to those conversions of different formats  to point to the start of the pixel data and not the start of a palette, for example (which seems to be the case when dealing with 8bpp or less right ?)

This is what I always do,
First load the image and convert it to 32 bit ARGB -> [ invoke      GdipBitmapLockBits,pImage,NULL,ImageLockModeRead,PixelFormat32bppARGB,offset GDIplusBitmapData ]

Ok, then you are using it on stream right ? From 2d_image_loader_saver.asm, i see that you locked the data from the resoruce3s section 1st after using global alloc to create a copy oif the image on memory.

This seems to work only for the data located in the RT_RCDATA type of the PE and not from the RT_BITMAP, for example , since RT_BITMAP, the BITMAPFILEHEADER is not present.

I´ll later try testing the routine from images stored with RT_RCDATA type, but after making it work for the RT_BITMAP ones.


But, why 2D_Image_loader_saver seems to work with GdipBitmapLockBits and GDiPlusMemory don´t ?

Can you do a small test ?  Create a file and insert a 8bpp bitmap (or even a lower quality ones) on RT_BITMAP (And not RT_RCDATA) and then copy the data onto a virtualloc buffer (so, without GlobalAlloc or GdipBitmapLockBits etc) and try to make it convert from the generated buffer in memory, so i can try see exactly what i´m doing wrong ?


It seems that there are 2 different kind of files that gdiplus can handle the images. One containing a full image file stored in RT_RCDATA and other containing incomplete bitmaps (without the header) from the RT_BITMAP. Maybe gdiplus handle those kind of files on a different way ?

Processing the pixels if I need to.

Then save it in the color format I want with the help of GdiPlus. ( as I did in GDIplusMemory.zip )
Never did I use Pixelformats that has more than 8 bit per color element.

Ok, but on GDIplusMemory you created the pixels manually and pointed to GdipCreateBitmapFromScan0 the start of the data in the buffer.

How do i located the correct pixels data when a image is not in 32bpp originally and contains a palette ? I mean, like the images i´m testing that does not have the BITMAPFILEHEADER and seems to contains a palette because they are encoded with 256 colors (or less)
 So, how do i use the information from their BITMAPINFOHEADER to  locate the pixel data and correct them to 32 bits when needed ?

Quote
PIXELFORMAT_16BPPGRAYSCALE
I have to check the formats with more than 8 bit per color element.
I wonder how you project more than 256 pure gray colors to the screen.....

PIXELFORMAT_1BPPINDEXED works OK
Function: SaveColorIndexedImage -> set the Color_Count to 2

For the PIXELFORMAT_1BPPINDEXED this is how i did, but it didn´t exported the data. Same error on returning 2 in eax.

The routine i did to select how many colors to be exported are like this:

Code: [Select]
;PIXELFORMAT_1BPPINDEXED 030101
 ;PIXELFORMAT_4BPPINDEXED 030402
 ;PIXELFORMAT_8BPPINDEXED 030803
    mov eax D@PixFormat
    .If al <= 3
        If al =  3
            mov eax 256 ; 2^8
        Else_If al = 2
            mov eax 16 ; 2^4
        Else
            mov eax 2 ; 2^1
        End_If
        call SaveColorIndexedImage D$pImage, OtherSaveFilter, D@ImgType, eax ; RLE encoding
    .Else
        call SaveNonIndexedImage D@pImage, OtherSaveFilter, D@ImgType, D@PixFormat
    .End_If


The only type of image that will be passed through SaveColorIndexedImage are these, right ?

PIXELFORMAT_8BPPINDEXED = 256 colors
PIXELFORMAT_4BPPINDEXED = 16 colors
PIXELFORMAT_1BPPINDEXED = 2 colors
« Last Edit: September 22, 2020, 05:59:32 PM by guga »
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

jj2007

  • Member
  • *****
  • Posts: 10846
  • Assembler is fun ;-)
    • MasmBasic
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #71 on: September 22, 2020, 06:39:56 PM »
I´ll later try testing the routine from images stored with RT_RCDATA type, but after making it work for the RT_BITMAP ones.

Streaming works exactly like loading from file. Therefore the resource must provide you with a pointer to the file content, with no extra manipulation by the OS. So you need the RCDATA format. Attention, the resource compiler does not complain if you call it RC_DATA or RT_RCDATA, but it will simply not run...!

include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
  GuiImage 123, fit  ; stream resource #123, fit image to client area
GuiEnd
Rsrc
123 RCDATA "pics\\beach.jpg"
Rsrc

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #72 on: September 22, 2020, 07:03:53 PM »
Hi guga,

Quote
I´ll later try testing the routine from images stored with RT_RCDATA type, but after making it work for the RT_BITMAP ones.

I think you are making it more complicated than it should be.
Sure you can load it as RT_BITMAP without the BITMAPFILEHEADER but, it has 2 disadvantages.
- you bloat your executable with the uncompressed bitmap data.
- you need to write your own routines to convert it to the format you want.

It's easier to store a .JPG or .PNG in the resource section ( or in the .DATA section ) and let GdiPlus handle all the things for us.
I'm using my own Resource ID (INCBIN) to load all data types.

Quote
Can you do a small test ?  Create a file and insert a 8bpp bitmap (or even a lower quality ones) on RT_BITMAP (And not RT_RCDATA) and then copy the data onto a virtualloc buffer (so, without GlobalAlloc or GdipBitmapLockBits etc) and try to make it convert from the generated buffer in memory, so i can try see exactly what i´m doing wrong ?

As I mentioned in Reply #64,
If I have time this week, I will write a routine that loads all Image formats from:
- Resource
- File
- Memory
- and raw pixels from virtual memory

Quote
For the PIXELFORMAT_1BPPINDEXED this is how i did, but it didn´t exported the data. Same error on returning 2 in eax.

The routine i did to select how many colors to be exported are like this:

Code: [Select]
;PIXELFORMAT_1BPPINDEXED 030101
 ;PIXELFORMAT_4BPPINDEXED 030402
 ;PIXELFORMAT_8BPPINDEXED 030803
    mov eax D@PixFormat
    .If al <= 3
        If al =  3
            mov eax 256 ; 2^8
        Else_If al = 2
            mov eax 16 ; 2^4
        Else
            mov eax 2 ; 2^1
        End_If
        call SaveColorIndexedImage D$pImage, OtherSaveFilter, D@ImgType, eax ; RLE encoding
    .Else
        call SaveNonIndexedImage D@pImage, OtherSaveFilter, D@ImgType, D@PixFormat
    .End_If

The only type of image that will be passed through SaveColorIndexedImage are these, right ?

PIXELFORMAT_8BPPINDEXED = 256 colors
PIXELFORMAT_4BPPINDEXED = 16 colors
PIXELFORMAT_1BPPINDEXED = 2 colors

Look at my routine, it handles everything, you only need to set the Color_Count and it selects the right indexed PIXELFORMAT_ for you.
Remember, you can have Color Palettes with any arbitrary number of entries. ( 2- 256, it saves memory )

Code: [Select]
; save Color Indexed images with 2 to 256 colors. ( any number between 2 to 256 )
SaveColorIndexedImage proc pGdiPlusImage:DWORD,pFilename:DWORD,Image_Type:DWORD,Color_Count:DWORD

    mov     ecx,Color_Count
    mov     eax,offset ColorPalette
    mov     dword ptr [eax],PaletteFlagsGrayScale
    mov     dword ptr [eax+4],ecx
    invoke  GdipInitializePalette,eax,PaletteTypeOptimal,dword ptr [eax+4],FALSE,pGdiPlusImage
    test    eax,eax
    jnz     Done

    mov     ecx,Color_Count
    mov     eax,PixelFormat8bppIndexed ; -> 17 to max 256 colors
    cmp     ecx,16
    ja      ConvertFormat   
    mov     eax,PixelFormat4bppIndexed ; -> 3 to max 16 colors
    cmp     ecx,2
    ja      ConvertFormat   
    mov     eax,PixelFormat1bppIndexed ; 2 colors
   
ConvertFormat:
    invoke  GdipBitmapConvertFormat,pGdiPlusImage,eax,DitherTypeSolid,PaletteTypeOptimal,offset ColorPalette,0
    test    eax,eax
    jnz     Done

    invoke  MultiByteToWideChar,CP_ACP,0,pFilename,-1,offset FilenameW,MAX_PATH-1
    mov     eax,Image_Type
    mov     byte ptr[CLSID_ImageTypes],al
    invoke  GdipSaveImageToFile,pGdiPlusImage,offset FilenameW,offset CLSID_ImageTypes,NULL
Done:
    ret

SaveColorIndexedImage endp
Creative coders use backward thinking techniques as a strategy.

guga

  • Member
  • *****
  • Posts: 1377
  • Assembly is a state of art.
    • RosAsm
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #73 on: September 25, 2020, 03:17:47 AM »
Hi JJ and Marinus many thanks

JJ. I saw that, but i´m currently working on RT_BITMAP and not RCDATA yet. The main problem is that bitmaps in this kind of resources have their main header stripped and it fools gdipus when the file is smaller then 24 bpp. making it not work from memory when using functions like: GdipCreateBitmapFromScan0 and after passing through GdipSaveImageToFile.

When we have no header (the BM tag) gdiplus appears only works correctly when the data is in a Dword chunk, so each dword contains 4 colors (R, G, B and Alpha). But for those weird bitmaps containing a palette without the header that points to the proper pixel offset, it fails to save. (A win32 error is generated internally).

One way to solve could be getting the data pixels from files 1, 4, 8 bpps and converting them to 32 pixels manually. before pointing to  GdipCreateBitmapFromScan0, but this is too overhead. I made a workaround for handling such files (at least, while Marinus don´t do the routines for us to test this weekend when he have more time)


While i was making this alternative way of loading these files, i made a small routine to point to the proper pixels data to see if what i was doing was correct. The function is like this:

Code: [Select]

;;
    GetPixelPosFromBmpNfoHdr
        This function retrieves the Pixel Address from the BITMAPINFOHEADER structure of a bitmap

    Parameters:
        pBmi - Pointer to a BITMAPINFOHEADER from where the position of the pixel data will be found.

    Return value:
        The function will return in eax the adress of the start of the pixel data on a Bitmap

    Remarks:
        This function is particulary usefull to locate the pixels address from bitmaps (1bpp, 4bpp, 256bp etc) located
        on the resoruces section stored as a &RT_BITMAP. This is because the bitmaps stored in this field does not contains
        the BITMAPFILEHEADER structure making it harder to directly manipulate the contents, or saving etc.
       
        If the file contains the BITMAPFILEHEADER structure, you need to add the size of it (14 bytes) to the returning value.

        The function works for:
        BITMAPINFOHEADER, BITMAPV2INFOHEADER, BITMAPV3INFOHEADER, BITMAPV4INFOHEADER, BITMAPV5INFOHEADER

    References: http://fileformats.archiveteam.org/wiki/BMP
                https://github.com/drewnoakes/metadata-extractor/blob/master/Source/com/drew/metadata/bmp/BmpReader.java
                https://rpg.hamsterrepublic.com/source/wip/bitmap.bi

;;

Proc GetPixelPosFromBmpNfoHdr::
    Arguments @pBmi
    Uses esi, ecx

    xor eax eax
    mov esi D@pBmi
    ; Compute the offset to the array of color indices.
    mov ecx D$esi+BITMAPINFOHEADER.biSizeDis | On ecx = 0, ExitP
    mov eax D$esi+BITMAPINFOHEADER.biClrUsedDis
    ...If eax = 0
        ; Convert the color format to a count of bits.
        movzx eax W$esi+BITMAPINFOHEADER.biPlanesDis
        movzx ecx W$esi+BITMAPINFOHEADER.biBitCountDis
        imul eax ecx

        .If ax <= 16
            If ax <= 1
                mov eax 1 ; 1 shl 1 = 2
            Else_If ax <= 4
                mov eax 4 ; 1 shl 4 = 16
            Else_If ax <= 8
                mov eax 8 ; 1 shl 8 = 256
            Else
                mov eax 16 ; 1 shl 16 = 65536
            End_If
            mov ecx eax
            mov eax 1 | shl eax cl
        .Else
            xor eax eax
        .End_If
        mov ecx D$esi+BITMAPINFOHEADER.biSizeDis
    ...End_If

    ; The Size_Of_BITMAPFILEHEADER is only needed to be added below if the file contains a header. Calculations are: 40+2*4 48+14 = 3e => 40+16*4+14 = 076 ==> 40+256*4+14
    lea eax D$ecx+eax*4
    add eax esi

EndP

equates used on the function above. (Note: I implemented the equates and structures up to BITMAPV5INFOHEADER, but on this function it´s not needed to add extra equates, since the used members of the structure (biSize, biClrUsed, biPlanes and biBitCount) are located at the same offset)
Code: [Select]

[BITMAPINFOHEADER.biSizeDis 0
 BITMAPINFOHEADER.biWidthDis 4
 BITMAPINFOHEADER.biHeightDis 8
 BITMAPINFOHEADER.biPlanesDis 12
 BITMAPINFOHEADER.biBitCountDis 14
 BITMAPINFOHEADER.biCompressionDis 16
 BITMAPINFOHEADER.biSizeImageDis 20
 BITMAPINFOHEADER.biXPelsPerMeterDis 24
 BITMAPINFOHEADER.biYPelsPerMeterDis 28
 BITMAPINFOHEADER.biClrUsedDis 32
 BITMAPINFOHEADER.biClrImportantDis 36]

[Size_Of_BITMAPINFOHEADER 40]



I´m also analysing how those gdiplus scan and savings functions works internally from ReactOS and wine sources

https://doxygen.reactos.org/d4/d51/dll_2win32_2gdiplus_2image_8c_source.html#l04455
« Last Edit: September 25, 2020, 04:44:01 AM by guga »
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

Siekmanski

  • Member
  • *****
  • Posts: 2453
Re: Bare Minimum GDIplus code to save different Image Types.
« Reply #74 on: October 03, 2020, 01:15:52 AM »
Sources to load bitmap images from resource, from file and from the .data segment.
Copy bitmap data to memory so, you can manipulate the pixels and save it later in the format or type you want.
Creative coders use backward thinking techniques as a strategy.