News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Has anyone used gdiplus to change hue?

Started by zedd151, April 11, 2025, 08:15:16 AM

Previous topic - Next topic

zedd151


sinsi

This seems to let you get a copy of the bitmap pixels.
I know it's .net but it seems pretty straightforward to convert.

private void LockUnlockBitsExample(PaintEventArgs e)
    {

        // Create a new bitmap.
        Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");

        // Lock the bitmap's bits. 
        Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        System.Drawing.Imaging.BitmapData bmpData =
            bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
            bmp.PixelFormat);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;

        // Declare an array to hold the bytes of the bitmap.
        int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
        byte[] rgbValues = new byte[bytes];

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

        // Set every third value to 255. A 24bpp bitmap will look red. 
        for (int counter = 2; counter < rgbValues.Length; counter += 3)
            rgbValues[counter] = 255;

        // Copy the RGB values back to the bitmap
        System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

        // Unlock the bits.
        bmp.UnlockBits(bmpData);

        // Draw the modified image.
        e.Graphics.DrawImage(bmp, 0, 150);
    }
Where it says Set every third value to 255 is where you would put your code for colour manipulation.

NoCforMe

Thanks, nice.
So far as the .net-ness of it goes, should be easy enough to convert to the GDI+ "flat" model:

This
            bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
            bmp.PixelFormat);

becomes
            INVOKE GdipBitmapLockBits, ADDR rect, <flags>, <format>, ADDR <bits>
Assembly language programming should be fun. That's why I do it.

zedd151

During my research, I find that these are needed (or something similar - regarding the matrix)...
The matrix needed will be determined by the degree of hue rotation (the matrix here is for +90 degree rotation).


.data
; For example (90-degree hue rotation matrix)  flat 5x5 matrix
HueMatrix REAL4 \
    0.213, 0.715, 0.072, 0.0, 0.0, \
    0.213, 0.715, 0.072, 0.0, 0.0, \
    0.213, 0.715, 0.072, 0.0, 0.0, \
    0.0,   0.0,   0.0,   1.0, 0.0, \
    0.0,   0.0,   0.0,   0.0, 1.0

.code
; Create ImageAttributes object
    invoke GdipCreateImageAttributes, offset imageAttr

; Set color matrix for hue rotation
    invoke GdipSetImageAttributesColorMatrix, imageAttr, ColorAdjustTypeDefault, TRUE, offset HueMatrix, 0, ColorMatrixFlagsDefault
Then draw the image with new hue.
This is a brief summary so far, and obviously does not include gdiplus initialization, image loading, hgraphics setup etc., etc.

I am continuing my research... :smiley:
And will be experimenting with the information that I find from various sources ...

I will be looking for code necessary for setting the matrix needed for a given rotation...

In Photoshop adjusting hue from red to blue for instance requires around -128 degree rotation (depending on how red the source image is, and how much blue the destination image should be) if that makes any sense.

Plus I do not know why a 5x5 matrix is needed or how those values are determined. Will look into that, too.

sinsi

Quote from: zedd151 on April 12, 2025, 10:53:14 PMPlus I do not know why a 5x5 matrix is needed. Will look into that, too.

Maybe this will help?
ImageAttributes::SetColorMatrix
QuoteAn ImageAttributes object maintains color and grayscale settings for five adjustment categories: default, bitmap, brush, pen, and text. For example, you can specify a color-adjustment matrix for the default category, a different color-adjustment matrix for the bitmap category, and still a different color-adjustment matrix for the pen category.

zedd151

Quote from: sinsi on April 12, 2025, 11:06:07 PMMaybe this will help?
ImageAttributes::SetColorMatrix

QuoteAn ImageAttributes object maintains color and grayscale settings for five adjustment categories: default, bitmap, brush, pen, and text. For example, you can specify a color-adjustment matrix for the default category, a different color-adjustment matrix for the bitmap category, and still a different color-adjustment matrix for the pen category.

Thanks sinsi, I would have gotten there eventually.
Btw, I ran that code (in your gdip startup test) and both of those calls returned zero. So I must be on the right track...  :biggrin:

I will play with this a bit (in actual code, with an image) and post the results.

zedd151

Quote from: zedd151 on April 12, 2025, 11:14:02 PMI will play with this a bit (in actual code, with an image) and post the results.
I inserted that code into a working example, no changes noticed in the image.
I must be missing something.  :undecided:

Back to the drawing board.  :biggrin:

zedd151

Success!! Well, partially...

I had to use GdipDrawImageRectRectI, to get the attributes actually set in the image.
The first time around I used GdipDrawImageRectI - thus no attributes set,  :mrgreen:

DrawImage proc hDC:dword, Image:dword, x:dword, y:dword
local wid:dword, hgt:dword, pGraphics:dword
    invoke GdipCreateFromHDC, hDC, addr pGraphics
    invoke GdipGetImageWidth, Image, addr wid
    invoke GdipGetImageHeight, Image, addr hgt

    ; draw original image
    invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
   
    ; Create ImageAttributes object
    invoke GdipCreateImageAttributes, offset imageAttr

    ; Set color matrix for hue rotation
    invoke GdipSetImageAttributesColorMatrix, imageAttr, ColorAdjustTypeDefault, 1, offset HueMatrix, 0, ColorMatrixFlagsDefault

    ; Draw the image with new attributes
    add x, 90
    invoke GdipDrawImageRectRectI, pGraphics, Image, x, y, wid, hgt, 0, 0, wid, hgt, UnitPixel, imageAttr, 0, 0
   
    invoke GdipDisposeImageAttributes, imageAttr
    invoke GdipDeleteGraphics, pGraphics
    ret
DrawImage endp

The result using that matrix from post #18:
The image on the left is the original'
The image on the right has the hue adjusted.



The image (on the right) is highly saturated (or contrast is blown out), will need to look into remedying that.

I will post a proper example shortly. I reused a previous project for this demonstration that uses Vortex' method of loading the image as an object file - look Ma, no (discreet) resources :biggrin:
Executable for this demo is attached below

zedd151

After blindly tweaking the matrix:
HueMatrix REAL4 \
    0.213, 0.615, 0.072, 0.0, 0.0, \
    0.213, 0.615, 0.072, 0.0, 0.0, \
    0.213, 0.615, 0.072, 0.0, 0.0, \
    0.0,  0.0,  0.0,  0.7, 0.0, \
    0.0,  0.0,  0.0,  0.0, 0.7



zedd151

I might make a dialog box with 2 images.
One for before adjustment and one for after.
It will have 25 controls corresponding to each matrix cell. Sliders? Up down? Or simply text boxes?
Text boxes maybe the easiest to implement.
And a button to update the matrix and process the image.

It will be a test fixture of sorts to see what effect each of the 25 floats has on the output image. I have to find out the range for those. I think it should probably be from -1.0 to +1.0, or maybe from 0 to 1.0? I will have to look into that first.

It seems that I have gone down the rabbit hole with this gdiplus stuff. So many new things for me.

guga

Better use your own algorithm to change the Hue. Also, not sure what you plan to do, is it only to work with your game test or is it for general usage ?

If it is for other usages (Image or video processing), HSL is not the proper or best way to change colors. Better is you use CieLCH colorspace in order to achieve a precise color correspondence of the human vision. The only downside using CieLCH is that you needs to use cos functions, but, this also can be worked if you do your own algorithm (using tables as well, works fine) to retrieve the proper cosine angles. Siekmanski has nice algorithms to calculate fast and precise those trigonometric functions.

Keep in mind that, for Colorspaces like CieLCH or even Cielab (i don´t recommend this , btw) the Luma is, in fact, just a representation of greys. So, you can use an integer as input and output for those particular colorspaces. of course, you won´t find any of those grey correspondencies with luma on "normal" papers, but i can tell you by my own experience while trying to build such algorithms that Luma is nothing more, nothing less than a table of greys.

So, each table acts like a range (or bands if you prefer this name) for Hue and Chroma values. Plus, Hue and Chroma are also correlated to each other. So, for example, say that you are working with Luma whose intensity is around 155, this particular value has a defined and limited amount of variations of Hue/Chroma that are used in all of those refined colorspaces. Therefore, each luma, in fact, also corresponds to a particular range of Hue (that also has his own range of chroma). Ex:
Luma (Grey) = 155
Hue Range: 65.6897º to 77.5454º
Chroma: 221.45 to 321.69

You can normalize chroma and hue as well, as long you know which "band" they belongs to. So, at the end, you just create your own algo with the following inputs and outputs
Luma (Integer): from 0 to 255
Chroma (Percentage from 0 to 100% on each band of 255 grey values) = Or also you can normalize by integers as well, from 0 to 255
Hue (Percentage from 0 to 100% on each band of 255 grey values) = Or also you can normalize by integers as well, from 0 to 255

As long you save a table predefined those ranges for you retrieve the proper values, you are good to use.

The main confusion is when you change the band, and migrate from Luma 155 to another value, say...30. You have 2 options for your algorithm
  • a) Keep a proportion based on the defined range of chroma and hue for each luma, and then, take their proportions to retrieve the values on other band (Ex:  At Luma = 155, you are using Hue = 37% and Chroma 42%. When you change to Luma  = 30, you can as well, keep the same proportion 37% and 42% and apply them of the ranges of Chroma and Hue belonging to that new luma
  • b) Make a general usage, allowing the change of Luma to exceeds the limitation on their own band. So, you can create a general value Min/Max) for Chroma, based on all the values found in the luma table, and use this as a proportion, instead stay connected to each band. This way seems to me what happens to the human eye, and tends to generate more natural colors, since the new calculated ranges, ends to result in other values of Hue, regardless the Luma band they originally became from.


of course, you can enhance a bit more depending on the profile you are using, on a way to distinguish a colorspace made for NTSC-RGB, ECT RGB, Ekta, Adobe etc etc
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

zedd151

To be honest guga, I only understand maybe 5% of all of that - well maybe a little bit more. Will this work for images with an alpha channel? I used the gdiplus functions since I knew that they did support 32 color ARGB (or is it RGBA) meaning 24 bit color plus alpha channel, plus I am learning how to use gdiplus in general these past couple of weeks.

I had known that Siekmanski had some tables, but honestly I thought those were for his direct draw or direct 3D stuff. I wasn't doing any graphics work back then so I didn't pay it much attention. I'll have to ring him up.  :tongue:

Thanks for the tips, more new things for me.

I was only experimenting with the posted code, just to see what the hue adjustment would look like. It is definitely not ready to go into any production code just yet. Just for my experimentation.  :smiley:

Oh strictly for use with images, no video. I already have too much to learn with just learning gdiplus.

guga

Quote from: zedd151 on April 13, 2025, 02:48:50 AMTo be honest guga, I only understand maybe 5% of all of that - well maybe a little bit more. Will this work for images with an alpha channel? I used the gdiplus functions since I knew that they did support 32 color ARGB (or is it RGBA) meaning 24 bit color plus alpha channel, plus I am learning how to use gdiplus in general these past couple of weeks.

I had known that Siekmanski had some tables, but honestly I thought those were for his direct draw or direct 3D stuff. I wasn't doing any graphics work back then so I didn't pay it much attention. I'll have to ring him up.  :tongue:

Thanks for the tips, more new things for me.

I was only experimenting with the posted code, just to see what the hue adjustment would look like. It is definitely not ready to go into any production code just yet. Just for my experimentation.  :smiley:

Oh strictly for use with images, no video. I already have too much to learn with just learning gdiplus.

Hi Zedd

For alpha channels ? Yes, those works as well. Faster way is converting from RGBA to RGB and vice-versa, before passing the values onto those color spaces.On this way you will have the actual mix of colors (background + foreground used as a layer for the alpha channel) to use those spaces.

GDiPlus is really fun. I gave a try sometime ago. A bit hard at the beginning, but once you know what it is doing, you can make helpers to work with that easier. I made some functions like: GdipCreateBitmapFromBmpNfo, GdipSaveColorIndexedImage, GetPixelDataFromGdiImage etc. Some of them adapted from Siekmanski algorithms. For instance, to take the pixel from a gdi you can use:

;;
    GetPixelDataFromGdiImage
        This function copies a GDI+ bitmap image to memory, ensuring it is converted to a 32-bit ARGB format.

    Parameters:

        pGdiPlusImage(In): Pointer to the GDI+ image object.
        pImageMemory(Out): Pointer to a pImageMemory structure that will receive the bitmap data in memory.

    Return Values

        Returns &S_OK if the image is loaded successfully.
        Returns 0-1 if any error occurs.

    Remarks:
        The function converts the GDI+ image to a 32-bit ARGB format before copying the pixel data.
        It handles memory allocation and ensures the memory is freed if previously allocated.
        The function uses GdipBitmapLockBits and GdipBitmapUnlockBits for accessing the pixel data safely.
        In the context of this function, the PixelFormat and BitsPerPixel members of the returned pImageMemory structure will always
        return PIXELFORMAT_32BPPARGB and 32 bpps, respectivelly.
       
        The pImageMemory structure holds information about the bitmap in memory, including its dimensions, pixel format, and pixel data.
       
        It is defined as follows:

        [pImageMemory:
         pImageMemory.dwWidth: D$ 0
         pImageMemory.dwHeight: D$ 0
         pImageMemory.lpvBits: D$ 0
         pImageMemory.lpvBitsSize: D$ 0
         pImageMemory.PixelFormat: D$ 0
         pImageMemory.BitsPerPixel: D$ 0
         pImageMemory.Pitch: D$ 0]

        And the equates displacement are:
       
        [pImageMemory.dwWidthDis 0
         pImageMemory.dwHeightDis 4
         pImageMemory.lpvBitsDis 8
         pImageMemory.lpvBitsSizeDis 12
         pImageMemory.PixelFormatDis 16
         pImageMemory.BitsPerPixelDis 20
         pImageMemory.PitchDis 24]
       
        [Size_Of_pImageMemory 28]
         
         Members:
            dwWidth: The width of the bitmap in pixels.
            dwHeight: The height of the bitmap in pixels.
            lpvBits: Pointer to the pixel data of the bitmap. The pixel data is displacaed on ARGB format
            lpvBitsSize: The total size of the pixel data in bytes.
            PixelFormat: The pixel format of the bitmap, typically PIXELFORMAT_32BPPARGB . But it can be any of the following equates:
                            PIXELFORMAT_UNDEFINED 0
                            PIXELFORMAT_1BPPINDEXED 030101
                            PIXELFORMAT_4BPPINDEXED 030402
                            PIXELFORMAT_8BPPINDEXED 030803
                            PIXELFORMAT_16BPPGRAYSCALE 0101004
                            PIXELFORMAT_16BPPRGB555 021005
                            PIXELFORMAT_16BPPRGB565 021006
                            PIXELFORMAT_16BPPARGB1555 061007
                            PIXELFORMAT_24BPPRGB 021808
                            PIXELFORMAT_32BPPRGB 022009
                            PIXELFORMAT_32BPPARGB 026200A
                            PIXELFORMAT_32BPPPARGB 0E200B
                            PIXELFORMAT_48BPPRGB 010300C
                            PIXELFORMAT_64BPPARGB 034400D
                            PIXELFORMAT_64BPPPARGB 01A400E
            BitsPerPixel: The number of bits per pixel, usually 32 for ARGB format.
            Pitch: The number of bytes per scan line, also known as stride.

    See Also:
        SetGdiImageFromPixelData

References:
    https://museum2020.it-berater.org/index.php?topic=1866.0
    https://www.autoitscript.com/autoit3/docs/libfunctions/_GDIPlus_ImageGetPixelFormat.htm
    https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/gdiplus/-gdiplus-constant-image-pixel-format-constants.md
    see GetPixelDataFromGdiImage from DBScanTest10a.exe
;;


[pImageMemory.dwWidthDis 0
 pImageMemory.dwHeightDis 4
 pImageMemory.lpvBitsDis 8
 pImageMemory.lpvBitsSizeDis 12
 pImageMemory.PixelFormatDis 16
 pImageMemory.BitsPerPixelDis 20
 pImageMemory.PitchDis 24]

[Size_Of_pImageMemory 28]

Proc GetPixelDataFromGdiImage::
    Arguments @pGdiPlusImage, @pImageMemory
    Local @TmpMem, @ImagePixelFormat, @ImgSize, @ReturnValue
    Structure @GDIPLUSBITMAPDATA 24, @GDIPLUSBITMAPDATA.dwWidthDis 0, @GDIPLUSBITMAPDATA.dwHeightDis 4, @GDIPLUSBITMAPDATA.StrideDis 8,
                                     @GDIPLUSBITMAPDATA.PixelFormatDis 12, @GDIPLUSBITMAPDATA.Scan0Dis 16, @GDIPLUSBITMAPDATA.ReservedDis 20
    Uses edi, ecx, edx

    mov D@ReturnValue 0-1

    call 'RosMem.ZeroMemory' D@GDIPLUSBITMAPDATA, Size_Of_GDIPLUSBITMAPDATA
    ; Always to be converted to 32 bit ARGB format!
    call 'gdiplus.GdipBitmapLockBits' D@pGdiPlusImage, &NULL, IMAGELOCK_MODE_READ, PIXELFORMAT_32BPPARGB, D@GDIPLUSBITMAPDATA;GDIplusBitmapData
    .If eax = &S_OK
        ; Gather some BitMap info from the original bitmap image
        mov edi D@pImageMemory
        mov D$edi+pImageMemory.BitsPerPixelDis 32
        mov D$edi+pImageMemory.PixelFormatDis PIXELFORMAT_32BPPARGB
        mov eax D@GDIPLUSBITMAPDATA.dwWidthDis | mov D$edi+pImageMemory.dwWidthDis eax
        mov eax D@GDIPLUSBITMAPDATA.dwHeightDis | mov D$edi+pImageMemory.dwHeightDis eax

        ; get the lenght of the bitmap data in bytes
        imul eax D@GDIPLUSBITMAPDATA.StrideDis | mov D$edi+pImageMemory.lpvBitsSizeDis eax | mov D@ImgSize eax
        mov eax D@GDIPLUSBITMAPDATA.StrideDis | mov D$edi+pImageMemory.PitchDis eax

        If D$edi+pImageMemory.lpvBitsDis <> 0
            call 'RosMem.VMemFree' D$edi+pImageMemory.lpvBitsDis
            mov D$edi+pImageMemory.lpvBitsDis 0
        End_If

        lea eax D@TmpMem
        call 'RosMem.VMemAlloc' eax, D@ImgSize
        If eax <> 0
            mov D$edi+pImageMemory.lpvBitsDis eax
            call 'RosMem.FastMemcpy' eax, D@GDIPLUSBITMAPDATA.Scan0Dis, D@ImgSize
        End_If
        call 'gdiplus.GdipBitmapUnlockBits' D@pGdiPlusImage, D@GDIPLUSBITMAPDATA
        call 'gdiplus.GdipDisposeImage' D@pGdiPlusImage
        mov D@ReturnValue &S_OK
    .End_If

    mov eax D@ReturnValue

EndP

If you need to load the images from the resource section of a PE you can use:


;;
    GdipLoadImageFromResource
        Load an image from the resource section of a file. It can load cursors, icons, gif, bitmap, png, jpg, webp, tiff.

    Parameters

    [in, optional] hModule: A handle to the module whose portable executable file or an accompanying MUI file contains the resource.
                            If this parameter is NULL, the function searches the module used to create the current process.
    [in] lpName: The name of the resource. Alternately, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID
                 is the integer identifier of the resource. For more information, see the Remarks section below.
    [in] lpType: The resource type. Alternately, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is
                 the integer identifier of the given resource type.
                 For standard resource types, the following equates are allowed:
                    RT_CURSOR           Hardware-dependent cursor resource.
                    RT_BITMAP           Bitmap resource.
                    RT_ICON             Hardware-dependent icon resource.
                    RT_RCDATA           Application-defined resource (raw data).
                    RT_NEWBITMAP        Another Bitmap Resource (older version)

    [out] pGdiPlusImage: A pointer to a variable that will hold the GdiImage plus object

    Return value
        If the function succeeds, it returns &S_OK
        If the function fails, it returns 0-1.
       
        To get extended error information, call GetLastError.

    Remarks:
            The RT_RCDATA windows type can be used to load gif, png, webp and any other image format needed.
    References:
        https://developers.google.com/speed/webp/docs/api
        https://github.com/webmproject/webp-wic-codec/tree/main
        https://chromium.googlesource.com/webm/webp-wic-codec/+/cc19e37b6ca673e750610785707261ebc8e2a0a3/src/libwebp/webp/decode.h
        https://stackoverflow.com/questions/72687289/getting-actual-frame-from-animated-webp-using-cwebp-libwebp
        https://cpp.hotexamples.com/examples/-/-/WebPDecodeBGRA/cpp-webpdecodebgra-function-examples.html
        https://www.google.com/search?client=firefox-b-d&q=animated+webp#ip=1
        https://ezgif.com/webp-maker
        https://groups.google.com/a/webmproject.org/g/webp-discuss/c/39WSf2zRxz0
        https://colinbendell.github.io/webperf/animated-gif-decode/webp.html
        https://www.reddit.com/r/photoshop/comments/17cat1l/saving_out_animated_webp_files/?rdt=40758
        https://resource.dopus.com/t/animated-webp-image-format-support/35777
        https://groups.google.com/a/webmproject.org/g/webp-discuss/c/x1KhpfpMk0M
        https://developers.google.com/speed/webp/docs/webpmux
        https://stackoverflow.com/questions/45190469/how-to-identify-whether-webp-image-is-static-or-animated
        https://developers.google.com/speed/webp/docs/riff_container
       
        license of webp
        https://www.webmproject.org/license/software/
;;

Proc GdipLoadImageFromResource::
    Arguments @hModule, @lpName, @lpType, @pGdiPlusImage
    Local @hRsrc, @ImageSize, @ImagePointer, @hLibwebp, @hWebPDecodeBGRA, @pImgWidth, @pImgHeight
    Uses esi, ecx, edx

    .If_And D@lpType >= &RT_MENU, D@lpType <= &RT_MANIFEST
        If_Or D@lpType = &RT_RCDATA, D@lpType = 13, D@lpType = 15, D@lpType = 18, D@lpType = &RT_NEWBITMAP
        ELse
            mov eax 0-1 | ExitP
        End_If
    .Else_if_Or D@lpType = &RT_DLGINIT, D@lpType = &RT_TOOLBAR, D@lpType = &RT_NEWRESOURCE, D@lpType = &RT_NEWMENU, D@lpType = &RT_NEWDIALOG, D@lpType = &RT_ERROR
        mov eax 0-1 | ExitP
    .End_If

    call 'kernel32.FindResourceA' D@hModule, D@lpName, D@lpType
    .If eax <> 0
        ; Found a possible valid resource. Let´ check if it is a webp file. before we try to stream it
        ; Get the data pointer and the data size from the resource Image data
        mov D@hRsrc eax
        call 'kernel32.SizeofResource' &NULL, eax
        mov D@ImageSize eax
        call 'kernel32.LoadResource' &NULL, D@hRsrc
        call 'kernel32.LockResource' eax
        mov D@ImagePointer eax
        If_And D$eax+RIFFCHUNK_WEBP_FORMAT.chunkIDDis = 'RIFF', D$eax+RIFFCHUNK_WEBP_FORMAT.FormatDis = 'WEBP'
            call GdipDecodeWebp D@ImagePointer, D@ImageSize, D@pGdiPlusImage
        Else
            call GdipStreamImageFromMemory D@ImagePointer, D@ImageSize, D@pGdiPlusImage
        End_If
    .Else
        mov eax 0-1
    .End_If

EndP


;;
    GdipDecodeWebp decodes a WebP image from memory and creates a GDI+ Bitmap object from the decoded pixel data.
   
    Parameters:
        ImagePointer(in): A pointer to the memory buffer containing the WebP image data.
        ImageSize(in): The size of the WebP image data in bytes.
        pGdiPlusImage(out): A pointer to the GDI+ Bitmap object created from the decoded WebP image.

    Return Value:
        Success: Returns 0 (&S_OK) if the WebP image was successfully decoded and the GDI+ Bitmap object was created.
        Failure: Returns -1 if there was an error loading the libwebp library, retrieving the WebPDecodeBGRA function, or decoding the WebP image.

    Remarks:
        Dependency: This function depends on libwebp.dll to decode the WebP image. So, make sure you have the proper dll on your system
                    or in the working directory of your app.
        Memory Management: The decoded pixel data from the WebP image is managed by the GDI+ Bitmap object created by GdipCreateScanImageFromMemory.
                           Do not free the pixel data manually; it will be handled by GDI+.
        Error Handling: Ensure proper error handling in case the libwebp library fails to load, the WebPDecodeBGRA function
                        is not found, or the WebP image fails to decode.
        Thread Safety: Ensure that the function is called in a thread-safe manner if used in a multi-threaded environment.   
   
    Function Internals: The function performs the following steps:
                        Loads the libwebp.dll library.
                        Retrieves the WebPDecodeBGRA function from the libwebp library.
                        Decodes the WebP image data using WebPDecodeBGRA.
                        Creates a GDI+ Bitmap object from the decoded pixel data using GdipCreateScanImageFromMemory.
                        Frees the libwebp library.
                        Returns the status of the operation.

    Example of usage:
        1 - Ensure the libwebp.dll is available in your application's directory or in a location that can be found by the application.
        2 - Prepare the WebP image data in memory.
        3 - Call GdipDecodeWebp with appropriate parameters.
       
        RosAsm Syntax:
       
        [pGdiPlusImage: D$ 0]
       
            lea ecx D@FileSize
            lea eax D@pFileData
            call 'FastCRT.ReadOpenedFile' &NULL, D@pFilename, eax, ecx, 0
            .If eax = &TRUE
                call 'FastCRT.GdipDecodeWebp' D@pFileData, D@FileSize, pGdiPlusImage
                If eax <> &S_OK
                    mov D@ReturnValue 0-1
                End_If
                call 'RosMem.VMemFree' D@pFileData ; we ca now safely release the allocated data
                mov eax D@ReturnValue

;;

Proc GdipDecodeWebp::
    Arguments @ImagePointer, @ImageSize, @pGdiPlusImage
    Local @hLibwebp, @hWebPDecodeBGRA, @pImgWidth, @pImgHeight, @ReturnValue
    Uses ecx, edx

    call 'KERNEL32.LoadLibraryA' {B$ "libwebp.dll", 0}
    If eax = 0
        mov eax 0-1 | ExitP
    End_If
    mov D@hLibwebp eax
    call 'KERNEL32.GetProcAddress' eax, {B$ 'WebPDecodeBGRA', 0} | mov D@hWebPDecodeBGRA eax
    If eax = 0
        call 'KERNEL32.FreeLibrary' D@hLibwebp
        mov eax 0-1
        ExitP
    End_If
    lea eax D@pImgWidth | mov D$eax 0
    lea ecx D@pImgHeight | mov D$ecx 0
    C_call D@hWebPDecodeBGRA D@ImagePointer, D@ImageSize, eax, ecx
    If eax <> 0
        ; The format of he pixels in webp are in RGBQUAD (same as BGRA)
        ; The data pixels at eax from webp image will be attached to the pixels object thereforem can´t be freed
        call GdipCreateScanImageFromMemory eax, D@pImgWidth, D@pImgHeight, D@pGdiPlusImage, PIXELFORMAT_32BPPARGB
        mov D@ReturnValue eax
    Else
        mov D@ReturnValue 0-1
    End_If
    call 'KERNEL32.FreeLibrary' D@hLibwebp
    mov eax D@ReturnValue

EndP



Proc GdipStreamImageFromMemory::
    Arguments @ImagePointer, @ImageSize, @pGdiPlusImage
    Local @ReturnMessage, @TmpMem, @ImageBuffer, @pStream
    Uses ecx, edx

    ; Default to failure and validate input parameters
    mov eax 0-1
    If_Or D@ImagePointer = 0, D@ImageSize = 0, D@pGdiPlusImage = 0
        ExitP
    End_If

    mov D@ReturnMessage eax

    ; Try allocating memory using VMemAlloc
    lea eax D@TmpMem
    call 'RosMem.VMemAlloc' eax, D@ImageSize
    ...If eax <> &NULL
        mov D@ImageBuffer eax
        call 'RosMem.FastMemcpy' D@ImageBuffer, D@ImagePointer, D@ImageSize
        call 'shlwapi.SHCreateMemStream' D@ImageBuffer, D@ImageSize
        .If eax <> &NULL
            mov D@pStream eax
            call 'gdiplus.GdipCreateBitmapFromStream' D@pStream, D@pGdiPlusImage
            mov D@ReturnMessage eax
            ; Release the stream
            If D@pStream <> 0
                call IStream_Release D@pStream
            End_If
        .End_If
        ; Free the allocated memory
        call 'RosMem.VMemFree' D@ImageBuffer
    ...Else
        ; cannot allocate memory, try with GloballAlloc Method. Fallback to GlobalAlloc if VMemAlloc fails
        call 'kernel32.GlobalAlloc' &GMEM_FIXED__&GMEM_ZEROINIT, D@ImageSize ; allocate memory for the stream
        ..If eax <> &NULL
            mov D@ImageBuffer eax
            call 'RosMem.FastMemcpy' D@ImageBuffer, D@ImagePointer, D@ImageSize
            lea eax D@pStream | mov D@pStream &NULL ; must be zero before creating a new stream
            call 'ole32.CreateStreamOnHGlobal' D@ImageBuffer, &TRUE, eax ; TRUE -> hGlobal will be released by IStream_Release
            .If eax = &S_OK
                call 'gdiplus.GdipCreateBitmapFromStream' D@pStream, D@pGdiPlusImage
                mov D@ReturnMessage eax
            .Else
                call 'kernel32.GlobalFree' D@ImageBuffer
            .End_If
            ; Release the stream
            If D@pStream <> 0
                call IStream_Release D@pStream
            End_If
        ..End_If
    ...End_If
    mov eax D@ReturnMessage

EndP



Personally, i think that creating a predefined set of functions is way easier than having to hard code all over again. You can create some personalized set of functions to work with GDI+ on your projects.

Btw...about the Hue, i presume you are using HSL to RGB and vice-versa, right ? For buttons, or simple images, it works fine.

About Siekmanski tables, well, it works for a wide range of things, not only for Dx. His algorithms are fantastic btw.
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

zedd151

Wow guga, thanks for all of the study/research material.  :thumbsup:
Yes, customized functions would be preferable by me as well, coding the same code line by line all the time gets tedious.

Thank you for your gdiplus code. That will come in handy pretty quickly I would imagine.

Thanks for the info regarding Siekmanskis tables as well. That will take a little more time to research and understand how and where they could be used.

I agree, gdiplus is hard to learn, but the rewards are substantial. A big improvement over only using gdi32 and others, especially when real partially transparent images are needed and not the phony transparency that TransparentBlt*  (yeah, I know that's not in gdi32, but you know what I mean) offers.

Later:Oh yeah msimg32* (I couldn't think of it earlier (for TransparentBlt)...

guga

If you want, i can send u the set of customized functions i created for working with Gdi+. On the forum, i guess i also posted examples a long time ago. Cannot post now other examples, because i lost one of my HDs 2 years ago, and some of the code and apps i used as backup was there. But, some of the sources are here yet. Also some that works with text effects (Really fun to learn working with text on gdi+). Let me know, and i´ll attach them here if needed
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