I was wondering if anyone here has any experience with changing the hue of an image using gdiplus.
My idea is instead of using several different colored .pngs, I was thinking, "why not use only one .png image and just change the hue to any color the user chooses?" The user then can change the color of all of the graphic elements, if I can figure out a way to implement it.
Any ideas where to start? What exactly should I research?
Welll, if you can somehow get the image as just a raw bitmap (NOT a .BMP), and you know
- the image width & height
- the image bitness (# bits/pixel)
then you could cruise through the bitmap and change colors.
Somehow. That might not be easy. But you could probably figure out how to change red (or reddish) pixels to blue or blueish.
Quote from: NoCforMe on April 11, 2025, 08:39:25 AMWelll, if you can somehow get the image as just a raw bitmap (NOT a .BMP), and you know
- the image width & height
- the image bitness (# bits/pixel)
then you could cruise through the bitmap and change colors.
Somehow. That might not be easy. But you could probably figure out how to change red (or reddish) pixels to blue or blueish.
It's not that easy. The images are not single color, plus they have an alpha channel. Hence the need for gdiplus. Otherwise I'd just plunk a new color on the colormap in pace of the old color. (For 1, 4, 16, or 256 color bitmaps that is.) That's easy.
If you can demonstrate your idea, I'll try it. :smiley: I guess you could swap the R G and B? Or somehow perform voodoo on them.
Quote from: zedd151 on April 11, 2025, 08:43:08 AMIt's not that easy. The images are not single color
I would assume not.
Quoteplus they have an alpha channel. Hence the need for gdiplus.
Not necessarily; the alpha channel is just the 4th byte, otherwise unused, of the RGB color.
You can simply leave that data alone to preserve the transparency of those pixels.
QuoteOtherwise I'd just plunk a new color on the colormap in pace of the old color. (For 1, 4, 16, or 256 color bitmaps that is.) That's easy.
Yes; I've done that in my BMP toolbar creator, where I created a new palette (what you called the "colormap") to combine several images into one with a common palette.
But that wouldn't be the way to do it, I don't think. What I was doing was preserving the colors of the component images (more or less) and combining them into a single image with a single palette.
QuoteIf you can demonstrate your idea, I'll try it. :smiley: I guess you could swap the R G and B? Or somehow perform voodoo on them.
Not swap; that would give really weird results.
More the second choice: voodoo.
Let's say you have an image that's predominantly one color, say red: not a single color, but the image is mainly red or reddish.
You could identify those pixels (say by selecting those with a minimum R value) and change them to blue or blue-ish, by increasing B and decreasing R.
Still not easy, but probably do-able.
What I don't know how to do is getting the raw bitmap pixels from a GDI+-loaded image.
Anyone know the magic incantation to do that? If so, I could experiment a bit.
Sounds like a neat coding exercise, but I'm not up for that kind of challenge.
Probably would be time consuming.
Let me take a look at my png's in Gimp, to see exactly how many colors there are.... brb. I don't think they are 24 bit color...
Quote from: zedd151 on April 11, 2025, 09:01:59 AMI don't think they are 24 bit color...
They are 32 bit RGBA. I thought I had reduced the colors. I guess not.
red.png
PostImage screwed with the image I posted originally. Made it 180x180 and 43 kb.
The one above is original size 80x80 and ~9kb. You can right click it and "Save image as..." if you want to experiment with it.
I found a way to get access to the pixels:
GdipBitmapGetPixel()
Not the best way (probly slow), but all you need is the (x,y) coords. of the pixel.
And of course there's a GdipBitmapSetPixel() as well.
Quote from: zedd151 on April 11, 2025, 09:01:59 AMSounds like a neat coding exercise, but I'm not up for that kind of challenge.
Don't give up so easily. It could boil down to something like this:
loop through all pixels (x,y)
for each pixel
get R
if R > (some threshold)
set R low
set B high
When I get done with (part of) my other 85,792 projects I might play around with this.
Quote from: NoCforMe on April 11, 2025, 09:16:11 AMI found a way to get access to the pixels:
GdipBitmapGetPixel()
Not the best way (probly slow), but all you need is the (x,y) coords. of the pixel.
And of course there's a GdipBitmapSetPixel() as well.
I don't know about that - it might be a fun experiment, but surely there has to be a better way.
In any case, I don't mind having multiple images, but it would be nice to be able to change the hue 'on-the-fly' without too much acrobatics.
Quote from: NoCforMe on April 11, 2025, 09:19:42 AMWhen I get done with (part of) my other 85,792 projects I might play around with this.
:biggrin:
Yes, always too many things to do. But so little time. Same here.
Found this on reddit but it might be a bit too involved :biggrin: (and it's .net :dazzled: )
Quote from: Replacing all pixels of a certain colour with another colour in a bitmapYou can use GDI+ for this, using a DrawAttributes and a ColorMap. basically load the image then create a new blank canvas and use ImageAttributes to draw the loaded image into that new canvas using a ColorMap table.
Load existing image.
Create new Bitmap the same size as the original image.
use Graphics.FromImage() to create a new canvas from the second, new, image.
Create a new ImageAttributes instance, create a array of ColorMap, one for each mapping of old to new colours, presumably from your dictionary. Assign to the ImageAttributes class via .SetRemapTable().
use DrawImage on your second canvas and draw the first image loaded from the file to the canvas using the Image Attributes parameter.
If desired you can now save the replaced bitmap by saving the second bitmap you created.
I'm sure that would work, but just think how complex that would be:
You'd have to create that "dictionary", which sounds quite non-trivial.
(Unless you could maybe grab someone's already-made dictionary?)
My proposal is to change the image in situ, without having to create a new image from scratch like they do.
btw, I asked ChatGPT for code for this...
Too many errors in that code. :tongue:
Like two arguments short on a very important invoke...
it's not totally useless. But usually for the most interesting stuff, it is about as useless as it can get. :joking:
Quote from: zedd151 on April 11, 2025, 10:15:11 AMbtw, I asked ChatGPT for code for this...
Too many errors in that code. :tongue:
it's not totally useless. But usually for the most interesting stuff, it is about as useless as it can get. :joking:
"Hallucinations 'Я' Us"
Quote from: NoCforMe on April 11, 2025, 10:28:34 AM"Hallucinations 'Я' Us"
I keep telling you, you gotta lay off of them 'shrooms. :biggrin:
Like it or not, AI is sometimes useful. No like, no use.
Me no like, no use.
"Artificial Ignorance"
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.
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>
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.
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 (https://learn.microsoft.com/en-us/windows/win32/api/gdiplusimageattributes/nf-gdiplusimageattributes-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.
Quote from: sinsi on April 12, 2025, 11:06:07 PMMaybe this will help?
ImageAttributes::SetColorMatrix (https://learn.microsoft.com/en-us/windows/win32/api/gdiplusimageattributes/nf-gdiplusimageattributes-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.
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:
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.
(https://i.postimg.cc/c1m368VK/untitled.png)
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
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
(https://i.postimg.cc/qvf65XK5/untitled.png)
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.
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
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.
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.
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)...
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
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?
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 (http://www.jose.it-berater.org/gdiplus/iframe/index.htm). 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.
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.
@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.
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:
Btw about the links on the forum, here are some:
Demuxing Webp (https://masm32.com/board/index.php?topic=12105.msg132521#msg132521)
BlendPixels (https://masm32.com/board/index.php?topic=12097.msg132426#msg132426)
Gdi+ Examples (https://masm32.com/board/index.php?topic=12084.msg132230#msg132230)
DBScan and image homogeneity - I really needs to finish those eventually (https://masm32.com/board/index.php?topic=11919.0)
FAst median algorithms discussions (https://masm32.com/board/index.php?topic=8671.150)
libwebpdemux (https://masm32.com/board/index.php?topic=12105.msg132521#msg132521)
Gdi+ Gif Animation Test (https://masm32.com/board/index.php?topic=8917.msg97377#msg97377)
Icons and GDIPlus (https://masm32.com/board/index.php?topic=8849.msg96583#msg96583)
Rainbow text with GdipDrawString issue (https://masm32.com/board/index.php?topic=8963.0)
@ 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...
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.
Nice, @Guga; we need a lot more stuff like that here.
I started a new thread about all this. (https://masm32.com/board/index.php?topic=12696.0)
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
redd bitmap.zip
result convert red to blue
untitled.PNG
Similar results on 16 bit bitmaps
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.
screenshot.jpg
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.
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
(https://i.postimg.cc/65Z2ZW5Z/untitled.png)
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.
Quote from: NoCforMe on April 13, 2025, 05:39:19 AMQuote 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.
screenshot.jpg
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
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):
ARGB.gif
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 ...
@Zedd: Try the color swapper on any regular JPG picture; it works, but the change is usually very subtle, even hard to see.
Quote from: NoCforMe on April 13, 2025, 06:02:01 AMQuote 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):
ARGB.gif
Fair enough. But during my research I kept seeing RGBA all over the place, so naturally that's what I thought was most proper. :rolleyes:
In retrospect ARGB sounds more likely upon thinking about it.
Quote from: NoCforMe on April 13, 2025, 06:11:21 AM@Zedd: Try the color swapper on any regular JPG picture; it works, but the change is usually very subtle, even hard to see.
Okay, gimme about an hour. It's time to feed, then walk, the doggies.
Hi David
these are the ones inside the comments.
Hexadecimal in RosAsm can use the ending "h" or not. Hexadecimals are represented by 1 leading 0 ate the beginning. It also can contains separators "_" for better reading if needed. But, "x" is not allowed, since it is used for mathematical expressions.
Ex:
0123FA25D
can also written as:
0123FA25Dh - similar to masm syntax, i presume
0123F_A25Dh - similar to masm syntax, i presume (except for the separator)
0123F_A25D
0_123F_A25D
0_123F_A25Dh - similar to masm syntax, i presume (except for the separator)
Separators do nothing at all, unless make the numbers (or labels, variables names etc) better to read.
Decimals always starts from 0 to 9. So, not changes here for masm syntax
Binary always starts with 2 leading 0. Also may contains optional identification "b" and optional separator "_"
Ex:
The number 196865 (in decimal), can be represented in binary as:
00__0000_0011__0000_0001__0000_0001
00__0000_0011__0000_0001__0000_0001b
00000000110000000100000001b (less readable) - similar to masm syntax, i presume
00110000000100000001b (less readable) - similar to masm syntax, i presume
00000000110000000100000001 (less readable)
00110000000100000001 (less readable)
Personally, i prefer without the ending identificators and using separators "_" when is better to read.
Quote from: NoCforMe on April 13, 2025, 06:11:21 AM@Zedd: Try the color swapper on any regular JPG picture; it works, but the change is usually very subtle, even hard to see.
(https://i.postimg.cc/vZY4Npd2/untitled.png)
OK, those artifacts are because I'm ASS-U-Ming a 32bpp image where it's actually 24bpp.
Smarter manipulation code would take care of this.
Quote from: guga on April 13, 2025, 06:37:57 AMHexadecimal in RosAsm can use the ending "h" or not. Hexadecimals are represented by 1 leading 0 ate the beginning. It also can contains separators "_" for better reading if needed. But, "x" is not allowed, since it is used for mathematical expressions.
Ex:
0123FA25D
can also written as:
0123FA25Dh - similar to masm syntax, i presume
0123F_A25Dh - similar to masm syntax, i presume (except for the separator)
0123F_A25D
0_123F_A25D
0_123F_A25Dh - similar to masm syntax, i presume (except for the separator)
Sorry, too weird for most of us here.
"Except for the separator": that's the problem.
I won't bother even trying to decipher or convert such representations.
Life's too short.
Quote from: NoCforMe on April 13, 2025, 07:23:24 AMOK, those artifacts are because I'm ASS-U-Ming a 32bpp image where it's actually 24bpp.
Ah, okay. So the present code only works for 32bpp... got it.
Quote from: zedd151 on April 13, 2025, 07:27:41 AMQuote from: NoCforMe on April 13, 2025, 07:23:24 AMOK, those artifacts are because I'm ASS-U-Ming a 32bpp image where it's actually 24bpp.
Ah, okay. So the present code only works for 32bpp... got it.
Yes. Take a look at the pixel-changing code I posted.
It would take a lot more checking (on the bitmap format) and branching in that code to handle all those other formats.
Could be done, of course; I just wanted to throw together a small demo to show how it might work.
Quote from: NoCforMe on April 13, 2025, 07:35:55 AMIt would take a lot more checking (on the bitmap format) and branching in that code to handle all those other formats.
Could be done, of course; I just wanted to throw together a small demo to show how it might work.
No thats fine. I might even play with the code a bit, at least to cover 24bpp even if it is a seperate program from the 32bpp version.
If you wanted to extend my program, you could look for the pixel format value of PixelFormat24bppRGB in the color-swapping code, and branch to a new section where you work on 24-bit pixels.
A little more complicated, as you'll need to read 3 bytes at a time, but still do-able. (You could use the same swapping code, just change the loop to read 3 bytes instead of a DWORD per pixel.)
Quote from: NoCforMe on April 13, 2025, 07:56:10 AMIf you wanted to extend my program, you could look for the pixel format value of PixelFormat24bppRGB in the color-swapping code, and branch to a new section where you work on 24-bit pixels.
A little more complicated, as you'll need to read 3 bytes at a time, but still do-able. (You could use the same swapping code, just change the loop to read 3 bytes instead of a DWORD per pixel.)
That's what I had in mind. At least something similar. :thumbsup:
Now if I can put all of this aside long enough to finish my frigging game (Connect 4), I'll be as happy as a clam. :joking:
All of these different ways to work with graphics is kind of addicting, always wanting more tools for the toolbox.
Quote from: NoCforMe on April 13, 2025, 07:25:27 AMQuote from: guga on April 13, 2025, 06:37:57 AMHexadecimal in RosAsm can use the ending "h" or not. Hexadecimals are represented by 1 leading 0 ate the beginning. It also can contains separators "_" for better reading if needed. But, "x" is not allowed, since it is used for mathematical expressions.
Ex:
0123FA25D
can also written as:
0123FA25Dh - similar to masm syntax, i presume
0123F_A25Dh - similar to masm syntax, i presume (except for the separator)
0123F_A25D
0_123F_A25D
0_123F_A25Dh - similar to masm syntax, i presume (except for the separator)
Sorry, too weird for most of us here.
"Except for the separator": that's the problem.
I won't bother even trying to decipher or convert such representations.
Life's too short.
I´ll try to write some basics translator in time. What´s the proper syntax for masm, then ? Only 0123FA25Dh, right ? Or there are other fixed tokens/syntax ?
You only need the leading 0, if the first hex digit is A,B,C,D,E or F.
In your 0123FA25Dh
While it doesn't hurt to leave it there, normally it's not done like that.
Ought to be
123FA25Dh
Conversely:
ABCDEF01h would need the leading zero: 0ABCDEF01h else the assembler will assume it's a variable name even with the trailing h.
Quote from: guga on April 13, 2025, 08:03:48 AMWhat´s the proper syntax for masm, then ? Only 0123FA25Dh, right ?
Decimal: just like anywhere else. (No "d" required.)
Hex:
XXXXXh (must put in a leading zero if the first digit is A-F)
Binary:
XXXXXbSymbolic constants:
name EQU valueData declarations:
name, optional DB/DW/DD/DQ value(s)Structure definition:
name STRUCT
data declaration
data declaration
data declaration
. . .
name ENDS
Structure declaration:
name, optional struct name <value(s), optional>(angle brackets at end required)
That's it.
MASM keywords are case-insensitive; names aren't, unless an option is set to make them so.
(hopefully my nomenclature is clear)
@Zedd: try the attached new version. Handles 24bpp images correctly (like almost all JPGs). Code's a little more complicated, but not too much.
Tks guys. Good to know these diferences.
NoCForme, about
QuoteSymbolic constants:
name EQU value
Data declarations:
name, optional DB/DW/DD/DQ value(s)
Yeah, i remember simlar of those in RosAsm when activating the Preparse Alternates. I never used, though.
Is these syntax more similar to Masm ? Those are the ones the current version of rosAsm can assemble:
mov ecx D[Counter]
mov ecx dWord [Counter]
mov ecx dWord Ptr [Counter]
mov ecx [Counter]
mov ax [Counter]
mov [Counter] bl
; Seems the same as your examples.
[TRUE = 1, FALSE = 0]
[TRUE EQU 1, FALSE EQU 0]
Also syntax with things like
Byte Ptr[Value]
Dword Ptr[Value]
Qword Ptr[Value]
etc are also allowed
What i never saw was the Struct token, but this can be implemented if needed. On this way, it would also be easier to create a sort of translator later.
I never used the alternates preparser so far. I personally don´t like it, but it may be needed by someone in the future, so once i succeed to finish the major updates i´ll try to focus on those alternatives syntaxes. Of course, i don´t plan to make RosAsm assemble all masm syntax (i.e.: all of the fixed tokens), specially because we don´t use things like directives, assume, .data, .code etc, but i guess it won´t hurt adding additional alternatives syntaxes a bit more compatible to masm. I´ll put those information you and Zedd did on the development area on the source, so i won´t forget about it on the future.
Quote from: guga on April 13, 2025, 09:34:25 AMIs these syntax more similar to Masm ?
mov ecx D[Counter]
mov ecx dWord [Counter]
mov ecx dWord Ptr [Counter]
You need a comma there:
mov ecx, Counter
No "D[ ]" weirdness, either.
Quote; Seems the same as your examples.
[TRUE = 1, FALSE = 0]
[TRUE EQU 1, FALSE EQU 0]
Again, lose the square brackets, but yes:
you can use either the equal sign or
EQU, although they have slightly different meanings (an EQU cannot be changed later)
QuoteAlso syntax with things like
Byte Ptr[Value]
Dword Ptr[Value]
Qword Ptr[Value]
etc are also allowed
Yes; those are modifiers, like
MOV ByteSizeVar, BYTE PTR [EDX]
MOV AL, BYTE PTR DwordSizeVar
Note no square brackets around that last one.
Quote from: NoCforMe on April 13, 2025, 09:28:23 AM@Zedd: try the attached new version. Handles 24bpp images correctly (like almost all JPGs). Code's a little more complicated, but not too much.
result
(https://i.postimg.cc/QM3CLw5W/untitled.png)
source jpg image
untitledb.JPG
It works fine for 24 bit bitmaps though.
Ack.
Could you post that image file so I can check it out?
I think I might know what the problem is. Was just thinking about it.
When processing bitmap images, you have to take into account this thing called stride, which is the distance between scan lines.
That's because some bitmaps have padding, since a scan line must begin on a DWORD boundary.
So depending on the bitmap size, lines may have padding.
I'm just going through the bitmap as if it's just a big linear array.
Wrong. The correct way to process a bitmap is line by line, treating it as a 2D array.
Just means you need an inner and an outer loop, where you move [stride] bytes from one line to the next.
Quote from: NoCforMe on April 13, 2025, 10:06:02 AMAck.
Could you post that image file so I can check it out?
it is in above post. That file was saved with photoshop. right click and save image as...
For jpgs saved with mspaint, it works fine. :thumbsup:
Quote from: NoCforMe on April 13, 2025, 09:54:29 AMQuote from: guga on April 13, 2025, 09:34:25 AMIs these syntax more similar to Masm ?
mov ecx D[Counter]
mov ecx dWord [Counter]
mov ecx dWord Ptr [Counter]
You need a comma there:
mov ecx, Counter
No "D[ ]" weirdness, either.
Quote; Seems the same as your examples.
[TRUE = 1, FALSE = 0]
[TRUE EQU 1, FALSE EQU 0]
Again, lose the square brackets, but yes:
you can use either the equal sign or EQU, although they have slightly different meanings (an EQU cannot be changed later)
QuoteAlso syntax with things like
Byte Ptr[Value]
Dword Ptr[Value]
Qword Ptr[Value]
etc are also allowed
Yes; those are modifiers, like
MOV ByteSizeVar, BYTE PTR [EDX]
Oh, the commas are mandatory in masm then. Ok, the current version of RosAsm can also handle those commas (but they are not fixed, is optional to use - the internal parsers will simply skip those). But, currently, those can also be assembled as:
MOV ByteSizeVar, BYTE PTR [EDX].
About removing the squared brackets, i´ll need a way to implement this. Or simply leave this to the translator. Currently, the brackets are made to distinguish the several different statements (Variables, equates, macros)
[TRUE = 1, FALSE = 0]
[TRUE EQU 1, FALSE EQU 0]
Can be written one on each bracket
[TRUE = 1]
[FALSE = 0]
[TRUE EQU 1]
[FALSE EQU 0]
But, on masm then we should use no brackets at all ? Just this ?
TRUE = 1
FALSE = 0
TRUE EQU 1
FALSE EQU 0
But., how the assembler will distinguish if they are data, equates, variables or code ? Or the absence of brackets in masm only works for equates ?
Quote from: guga on April 13, 2025, 10:08:41 AMBut, on masm then we should use no brackets at all ? Just this ?
TRUE = 1
FALSE = 0
TRUE EQU 1
FALSE EQU 0
Yep, that's it.
QuoteBut., how the assembler will distinguish if they are data, equates, variables or code ? Or the absence of brackets in masm only works for equates ?
They're neither code nor data, just symbolic constants, meaning they evaluate to a numeric value and can be used in place of numbers in statements.
The
EQU operator does, however, have other uses: for example,
$CRLF EQU <13, 10>
results in a sequence of two bytes if used in a DB statement, like so:
Message DB "Halt! Who goes there?", $CRLF, 0
Then there are text macros. Let's don't even go there yet ...
I guess you could say that EQU and friends are all part of the preprocessor, like in C.
Ok, tks, David :thumbsup: :thumbsup: :thumbsup:
I inserted the information you guys provided to the development part of the source.
Hi guga, could you start a new topic for your rosasm to masm translation issues? When you were posting about it here, it kind of interrupted the flow of the conversation underway between NoCforMe and myself.
I am not asking that any posts be removed from here or deleted, but if you want to continue with your rosasm to masm syntax discussions I am asking very politley to please start a new topic and do not continue here, please. :smiley:
Quote from: NoCforMe on April 13, 2025, 09:28:23 AM@Zedd: try the attached new version. Handles 24bpp images correctly (like almost all JPGs). Code's a little more complicated, but not too much.
Now back to the topic.
NoCforMe...
Why do you only have 3 choices to change hue. I think there should be six.
Red to Blue
Blue to Red
Red to Green
Green to Red
Blue to Green
Green to Blue
Also:
What I would (and might) do is use the same static to display the results and the source image.
Any changes the would be applied to the already processed image, after initially processing the source image.
If your methods are sound, changing from red to blue and back again, the result should match the source image or have the perception of being the same.
Anyway, these were some things I was thinking about. I don't have a lot of time for coding the next few days (6 acre property, only half finished with yard work), but when I do, I will explore doing what I had mentioned above.
Quote from: zedd151 on April 15, 2025, 03:22:38 AMQuote from: NoCforMe on April 13, 2025, 09:28:23 AM@Zedd: try the attached new version. Handles 24bpp images correctly (like almost all JPGs). Code's a little more complicated, but not too much.
Now back to the topic.
NoCforMe...
Why do you only have 3 choices to change hue. I think there should be six.
Red to Blue
Blue to Red
No.
They're
swaps; red<-->blue interchanges the two, so only 1 choice needed there.
There are, of course, lots of other ways you could mess with a bitmap.
If you do play with that testbed program, be sure to implement those inner and outer loops I mentioned earlier so you're processing the bitmap line-by-line. Otherwise, knock yourself out.
Just be sure to post any interesting results here.
Quote from: NoCforMe on April 15, 2025, 03:38:44 AMJust be sure to post any interesting results here.
Absolutely! Even if they are not very interesting.
Quote from: NoCforMe on April 15, 2025, 03:38:44 AMThey're swaps; red<-->blue interchanges the two, so only 1 choice needed there.
I did not realize that, clicking the button again did nothing so I figured it was only a one way color shift. I have mainly been testing with images/bitmaps with only a few colors. I will look at it in more detail with many more colors in the same image to see for myself.
I did a quick test by using a blue image and clicking red<--->blue. Indeed it turned red. :rolleyes: I missed that "<--->" meant bidirectional. :toothy:
Well, now maybe you can come up with your own wicked way of changing bitmap pixels around ...
I'm all out of ideas ATM.
Quote from: zedd151 on April 15, 2025, 02:44:28 AMHi guga, could you start a new topic for your rosasm to masm translation issues? When you were posting about it here, it kind of interrupted the flow of the conversation underway between NoCforMe and myself.
I am not asking that any posts be removed from here or deleted, but if you want to continue with your rosasm to masm syntax discussions I am asking very politley to please start a new topic and do not continue here, please. :smiley:
Hi zedd
It wasn´t suppose to be a new topic, it was just a conversation on how to help since the code i sent was not easy to follow. But, another topic of a future translator, is not a bad idea. When i have time to start coding it, i´ll open a new topic asking for all major differences of masm tokens to be able to create the necessary translator. :thumbsup:
Quote from: guga on April 15, 2025, 12:12:26 PMBut, another topic of a future translator, is not a bad idea. When i have time to start coding it, i´ll open a new topic asking for all major differences of masm tokens to be able to create the necessary translator. :thumbsup:
I wish you the best of luck in your endeavors in creating that translator. If I can be of any assistance, let me know. :thumbsup:
Quote from: NoCforMe on April 15, 2025, 11:22:56 AMWell, now maybe you can come up with your own wicked way of changing bitmap pixels around ...
I'm all out of ideas ATM.
As soon as I get ample free time to experiment (which itself may take a considerable amount of time) with it, I have a few ideas to possibly make it even better than it is already. It really is a great little program as is, imo. It just needs some finishing touches,
And as always I myself have a few yet unfinished programs that I want to finish. So I really don't know when I will be able to get to really working on this. So many things need to get done, and so little time as usual.