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

Quote from: guga on April 13, 2025, 04:40:30 AMLet me know, and i´ll attach them here if needed
If you can , it would be greatly appreciated. :azn:  Other members will probably appreciate it too.(Many members are most likely interested in gdiplus, that don't already use it).  :thumbsup:  Or even links to the posts where some it was used in the past.  :smiley:

Is it RosAsm code, or masm?

NoCforMe

OK, Zedd, this is just for you:
I just finished a li'l GDI+ testbed that lets you play with color changes, plus it shows us some basic things about using GDI+ in general. Package attached here.

You can load any image file and do one of 3 color transformations:
  • swap red<-->blue
  • swap red<-->green
  • swap blue<-->green
Play around with it and see what happens.

I learned a lot about GDI+ coding this.
One thing I already knew, of course, was how piss-poor the available documentation for this API is. (I'm talking specifically about the GDI+ flat model here, which is what we assembler programmers use.)
There is a lot of documentation available; one particularly good resource is Jose Roca's GDI+ flat API reference. He's basically assembled the info from the Microsoft help app and made it accessible.
Problem is, that app has wrong information, and he hasn't bothered to correct it.
I'll go into those details later.
Also, our MASM32 package has some support for this API. Most importantly we have (I think) all of the PROTOs and library entries for the hundreds of functions. Plus we have some (but not all) of the needed structures.

In this app here's what I've done:
o Open and display the original image file with GDI+ (we already know how to do this)
o For the new, modified image:
  o Get the "pixel format" of the original image which we'll need later:
    INVOKE    GdipGetImagePixelFormat, OriginalBitmapHandle, ADDR pixelFormat
  o Make a copy of the original image:
    INVOKE    GdipCloneImage, OriginalBitmapHandle, ADDR newBitmapPtr
  o ; Lock the new image bits so we can play with them:
    INVOKE    GdipBitmapLockBits, NewBitmapHandle, ADDR gdiRect, ImageLockModeRead or ImageLockModeWrite,
            pixelFormat, ADDR gdiBmpData
  o Allocate a buffer for the bitmap bits, using the size of the image
  o Copy the bits from the bitmap to our buffer, using data from the GDI+ Bitmap structure, which gives us a pointer to those bits
  o Manipulate the bits to change the color (see below)
  o Copy the bits back to the bitmap from the buffer
  o Unlock the new bitmap:
    INVOKE    GdipBitmapUnlockBits, NewBitmapHandle, ADDR gdiBmpData
  o Display the new, modified image (we already know how to do that)

Phew.
One important thing I learned about GDI+:
"Images" and "bitmaps" can be treated as if they're the same.
Except when they aren't.


But I didn't find any cases yet where this wasn't true. You'll notice that I mixed and matched Image and Bitmap functions here.

I'll have much more to say on this subject in the coming days.

Regarding the color manipulation I did, here's the code for all three of the cases (swapping colors in a pixel):
;====================================
; Modify the buffer bits by swapping
; colors:
;====================================
    PUSH    EBX
    MOV    ECX, bitmapSize
    SHR    ECX, 2                ;/4 for DWORDS.
    MOV    EBX, bitmapHeap

modify:    PUSH    ECX
    MOV    EAX, [EBX]            ;Get next pixel.
    MOV    ECX, EAX            ;Make a copy of it.
    MOV    EDX, EAX            ;Make another copy.

    CMP    swap, $BGswap
    JE    doBG
    CMP    swap, $RGswap
    JE    doRG

; R<-->B swap:
doRB:    AND    ECX, 0FF0000h            ;Get R value.
    SHR    ECX, 16                ;Get it into low byte.
    MOV    AL, CL                ;Put R into B.
    AND    EDX, 0FFh            ;Isolate B.
    SHL    EDX, 16
    AND    EAX, 0FF00FFFFh            ;Zero out B in pixel.
    OR    EAX, EDX            ;Put B into R.
    JMP    SHORT putpix

; R<-->G swap:
doRG:    AND    ECX, 0FF00h            ;Get G value.
    SHL    ECX, 8                ;Get it into where R goes.
    MOV    EDX, EAX            ;Make another copy.
    AND    EDX, 0FF0000h            ;Get R.
    SHR    EDX, 8                ;Get it into where G goes.
    AND    EAX, 0FF0000FFh            ;Zero out R & G.
    OR    EAX, ECX            ;Put G into R.
    OR    EAX, EDX            ;Put R into G.
    JMP    SHORT putpix

; B<-->G swap:
doBG:    AND    ECX, 0FFh            ;Get B.
    SHL    ECX, 8                ;Put B where G goes.
    AND    EDX, 0FF00h            ;Get G.
    SHR    EDX, 8                ;Put G where B goes.
    AND    EAX, 0FFFF0000h            ;Zero out B & G.
    OR    EAX, ECX            ;Put B into G.
    OR    EAX, EDX            ;Put G into B.

putpix:    MOV    [EBX], EAX            ;Put modified pixel back in buffer.
skip:    ADD    EBX, SIZEOF DWORD
    POP    ECX
    LOOP    modify
    POP    EBX
Pretty basic stuff.
Of course, there are a lot more things you can do here.
Assembly language programming should be fun. That's why I do it.

guga

Hi Zedd

Here goes :) Attached my predefined set of GDI+ functions, also some others for bitmap manipulation and some image effects. Added as well, the SSE2 macros set used for better understanding.

There are still needing some other helper/customized functions for gdi+ and image manipulation, but i didn´t finished those yet.
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

NoCforMe

@Guga: That's nice, and thanks.
But one thing: could you please, please post stuff in MASM, not that kinda strange dialect of your assembler? Takes quite a bit of translation to use it otherwise.
Assembly language programming should be fun. That's why I do it.

zedd151

Well gee, David. Now I am starting to get overwelmed.  :tongue:

Quote from: NoCforMe on April 13, 2025, 05:02:00 AMOne thing I already knew, of course, was how piss-poor the available documentation for this API is. (I'm talking specifically about the GDI+ flat model here, which is what we assembler programmers use.)
Isn't that the truth...

Quote from: NoCforMeOne important thing I learned about GDI+:
"Images" and "bitmaps" can be treated as if they're the same.
Except when they aren't.

But I didn't find any cases yet where this wasn't true. You'll notice that I mixed and matched Image and Bitmap functions here
I am used to that. Usually when dealing with gdiplus, I stay away from functions that have "Bitmap" in their name. Its fine if a .png has only 24 bit color and no alpha channel (a lot of code here on the forum is like that, only using .png because the filesize is smaller than 24 bit .bmp's), but not so for 32 bit images with alpha channel from the few instances I have seen.

And thanks for your contribution to the body of gdiplus code on the forum.  :thumbsup:

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

zedd151

@ guga, It will take me some time to 'masm32ize' your code.

@ NoCforMe, attach an image where your proggy actually worked on. I am having some issues with it here, with various images/bitmaps.

Better yet, what format and bitness is the program expecting? That might help clarify...

guga

Quote from: NoCforMe on April 13, 2025, 05:08:27 AM@Guga: That's nice, and thanks.
But one thing: could you please, please post stuff in MASM, not that kinda strange dialect of your assembler? Takes quite a bit of translation to use it otherwise.

Hi David. You´re welcome  :thumbsup:

About masm, i´m sorry, i don´t code for masm in years, it's hard for me to remember the syntax on a way i could create or port the RosAsm syntax to masm a bit faster. But, i´ll try later to create a kind of translator from RosAsm code to Masm. The syntax is not that hard, at all, really. The problem is time for me to try to create some basic translator and force my memory to remember the proper masm syntax to be ported/converted.

Btw, if i remember, JJ started some simple translator from RosAsm (when using RosAsm macros set) to Masm sometime ago.
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

NoCforMe

Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: zedd151 on April 13, 2025, 05:21:30 AM@ NoCforMe, attach an image where your proggy actually worked on. I am having some issues with it here, with various images/bitmaps.

Better yet, what format and bitness is the program expecting? That might help clarify...
original 24 bit bitmap (zipped)  color red = 00FF0000h
You cannot view this attachment.

result convert red to blue
You cannot view this attachment.

Similar results on 16 bit bitmaps

NoCforMe

Quote from: zedd151 on April 13, 2025, 05:21:30 AM@ NoCforMe, attach an image where your proggy actually worked on. I am having some issues with it here, with various images/bitmaps.

You cannot view this attachment.

QuoteBetter yet, what format and bitness is the program expecting? That might help clarify...

That might be a problem: I guess I'm ASS-U-Ming the image is 24 or 32 bits/pixel, so might not work with other bitnesses.

Something else I learned: when I got the Bitmap structure for my image, one of the elements is the PixelFormat.
Running in the debugger, I saw that this value was 26200A hex, or 2498570 decimal. WTF???
Turned out that this was the correct value. From the GDI+ enumeration
PixelFormat1bppIndexed EQU 196865
PixelFormat4bppIndexed EQU 197634
PixelFormat8bppIndexed EQU 198659
PixelFormat16bppGrayScale EQU 1052676
PixelFormat16bppRGB555 EQU 135173
PixelFormat16bppRGB565 EQU 135174
PixelFormat16bppARGB1555 EQU 397319
PixelFormat24bppRGB EQU 137224
PixelFormat32bppRGB EQU 139273
PixelFormat32bppARGB EQU 2498570
PixelFormat32bppPARGB EQU 925707
PixelFormat48bppRGB EQU 1060876
PixelFormat64bppARGB EQU 3424269
PixelFormat64bppPARGB EQU 29622286
PixelFormatMax EQU 15

It turns out that the format was 32 bpp ARGB, which is what your red pill image is.

So you can use this value to determine the bitness of your image, something you'd obviously need to do to correctly process it.
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on April 13, 2025, 05:39:19 AMIt turns out that the format was 32 bpp ARGB, which is what your red pill image is.
:thumbsup:

I only tested it with bitmaps. :rolleyes:
Works a charm with 32 bit .pngs


I am definitely going to look at this code. A lot better/easier than dealing with the floating point matrix.

Is it ARGB or RGBA? I have seen both used, and that is confusing. I have been saying RGBA in several places lately.

guga

Quote from: NoCforMe on April 13, 2025, 05:39:19 AM
Quote from: zedd151 on April 13, 2025, 05:21:30 AM@ NoCforMe, attach an image where your proggy actually worked on. I am having some issues with it here, with various images/bitmaps.

You cannot view this attachment.

QuoteBetter yet, what format and bitness is the program expecting? That might help clarify...

That might be a problem: I guess I'm ASS-U-Ming the image is 24 or 32 bits/pixel, so might not work with other bitnesses.

Something else I learned: when I got the Bitmap structure for my image, one of the elements is the PixelFormat.
Running in the debugger, I saw that this value was 26200A hex, or 2498570 decimal. WTF???
Turned out that this was the correct value. From the GDI+ enumeration
PixelFormat1bppIndexed EQU 196865
PixelFormat4bppIndexed EQU 197634
PixelFormat8bppIndexed EQU 198659
PixelFormat16bppGrayScale EQU 1052676
PixelFormat16bppRGB555 EQU 135173
PixelFormat16bppRGB565 EQU 135174
PixelFormat16bppARGB1555 EQU 397319
PixelFormat24bppRGB EQU 137224
PixelFormat32bppRGB EQU 139273
PixelFormat32bppARGB EQU 2498570
PixelFormat32bppPARGB EQU 925707
PixelFormat48bppRGB EQU 1060876
PixelFormat64bppARGB EQU 3424269
PixelFormat64bppPARGB EQU 29622286
PixelFormatMax EQU 15

It turns out that the format was 32 bpp ARGB, which is what your red pill image is.

So you can use this value to determine the bitness of your image, something you'd obviously need to do to correctly process it.

Yep, those are the correct values (in decimal). I posted those in one of the sources (In hex, however inside a multiline comment i did while i was creating the functions).


; --- In binary representation

       PIXELFORMAT_INDEXED                    00__0000_0001__0000_0000__0000_0000
        PIXELFORMAT_GDI                        00__0000_0010__0000_0000__0000_0000
        PIXELFORMAT_ALPHA                      00__0000_0100__0000_0000__0000_0000
        PIXELFORMAT_PALPHA                     00__0000_1000__0000_0000__0000_0000
        PIXELFORMAT_EXTENDED                   00__0001_0000__0000_0000__0000_0000
        PIXELFORMAT_CANONICAL                  00__0010_0000__0000_0000__0000_0000

; ------ Hex + Binary representation

        PIXELFORMAT_1BPPINDEXED 030101         00__0000_0011__0000_0001__0000_0001 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_4BPPINDEXED 030402         00__0000_0011__0000_0100__0000_0010 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_8BPPINDEXED 030803         00__0000_0011__0000_1000__0000_0011 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_16BPPGRAYSCALE 0101004     00__0001_0000__0001_0000__0000_0100 ; PIXELFORMAT_EXTENDED
        PIXELFORMAT_16BPPRGB555 021005         00__0000_0010__0001_0000__0000_0101 ; PIXELFORMAT_GDI
        PIXELFORMAT_16BPPRGB565 021006         00__0000_0010__0001_0000__0000_0110 ; PIXELFORMAT_GDI
        PIXELFORMAT_16BPPARGB1555 061007       00__0000_0110__0001_0000__0000_0111 ; PIXELFORMAT_GDI+PIXELFORMAT_ALPHA
        PIXELFORMAT_24BPPRGB 021808            00__0000_0010__0001_1000__0000_1000 ; PIXELFORMAT_GDI
        PIXELFORMAT_32BPPRGB 022009            00__0000_0010__0010_0000__0000_1001 ; PIXELFORMAT_GDI
        PIXELFORMAT_32BPPARGB 026200A          00__0010_0110__0010_0000__0000_1010 ; PIXELFORMAT_GDI+PIXELFORMAT_ALPHA+PIXELFORMAT_CANONICAL
        PIXELFORMAT_32BPPPARGB 0E200B          00__0000_1110__0010_0000__0000_1011 ; PIXELFORMAT_GDI+PIXELFORMAT_ALPHA+PIXELFORMAT_PALPHA
        PIXELFORMAT_48BPPRGB 010300C           00__0001_0000__0011_0000__0000_1100 ; PIXELFORMAT_EXTENDED
        PIXELFORMAT_64BPPARGB 034400D          00__0011_0100__0100_0000__0000_1101 ; PIXELFORMAT_ALPHA+PIXELFORMAT_EXTENDED+PIXELFORMAT_CANONICAL
        PIXELFORMAT_64BPPPARGB 01A400E         00__0001_1010__0100_0000__0000_1110 ; PIXELFORMAT_GDI+PIXELFORMAT_PALPHA+PIXELFORMAT_EXTENDED

        The equates are created like this (Decimal shifts and OR):

        PixelFormat1bppIndexed = (1 | ( 1 << 8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat4bppIndexed = (2 | ( 4 << 8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat8bppIndexed = (3 | ( 8 << 8) | PixelFormatIndexed | PixelFormatGDI),
        PixelFormat16bppRGB555 = (5 | (16 << 8) | PixelFormatGDI),   
        PixelFormat16bppRGB565 = (6 | (16 << 8) | PixelFormatGDI),   
        PixelFormat16bppARGB1555 = (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI), 
        PixelFormat24bppRGB = (8 | (24 << 8) | PixelFormatGDI),   
        PixelFormat32bppRGB = (9 | (32 << 8) | PixelFormatGDI),   
        PixelFormat32bppARGB = (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical),
        PixelFormat32bppPARGB = (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI),
        PixelFormat48bppRGB = (12 | (48 << 8) | PixelFormatExtended),   
        PixelFormat64bppARGB = (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended),
        PixelFormat64bppPARGB = (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended),
        PixelFormatMax = 15         
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

NoCforMe

Quote from: zedd151 on April 13, 2025, 05:44:57 AMI only tested it with bitmaps. :rolleyes:
Works a charm with 32 bit .pngs
A .png is a bitmap.
It'll work correctly with any 32 BPP image format.
QuoteIs it ARGB or RGBA? I have seen both used, and that is confusing.
I'd say ARGB, based on the memory layout of a pixel (I had to draw this for myself to keep things straight):

You cannot view this attachment.
Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: guga on April 13, 2025, 06:00:34 AM; ------ Hex + Binary representation

        PIXELFORMAT_1BPPINDEXED 030101        00__0000_0011__0000_0001__0000_0001 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_4BPPINDEXED 030402        00__0000_0011__0000_0100__0000_0010 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_8BPPINDEXED 030803        00__0000_0011__0000_1000__0000_0011 ; PIXELFORMAT_INDEXED+PIXELFORMAT_GDI
        PIXELFORMAT_16BPPGRAYSCALE 0101004    00__0001_0000__0001_0000__0000_0100 ; PIXELFORMAT_EXTENDED
        PIXELFORMAT_16BPPRGB555 021005        00__0000_0010__0001_0000__0000_0101 ; PIXELFORMAT_GDI
        PIXELFORMAT_16BPPRGB565 021006        00__0000_0010__0001_0000__0000_0110 ;
Guga, sorry to be kind of a pain in the ass about this, but those representations are just not useful for the majority of use who don't use RosAsm. Those numeric representations are just too weird. They may make sense to you, but are pretty useless to the rest of us.

Kind of like how JJ persistently posts code in MasmBasic which not all of us use ...
Assembly language programming should be fun. That's why I do it.