none
Color.FromArgb(Int32) not always possible RRS feed

  • Question

  • I was thinking of rewriting this part ...

                    int b = (int)Pixels[i] & 0xff;
                    int g = (int)Pixels[i + 1] & 0xff;
                    int r = (int)Pixels[i + 2] & 0xff;
                    clr = Color.FromArgb(r, g, b);

    as this...

                    uint pix = (uint) Pixels[i] | 0xFF000000;
                    clr = Color.FromArgb(pix);

    But I can't. Here's why. FromArgb() takes a *signed* integer. The components of A, R, G, B are equivalent to unsigned BYTE, that is, each part can have value of 0xFF. In Int32, the A part can have a maximum value of 0x7F, making the pixel about 50% transparent. :(

    Why was it designed like that? Can Microsoft write a new method FromArgb(UInt32)?



    VP

    Friday, August 15, 2014 1:31 PM

Answers

  • "I just showed what I wanted to do."

    Well, yes, but for that to work the Pixels array type needs to be uint or int. My guess is that it is not because you say that you have only RGB, no A. That means 3 bytes per pixel and that is problematic to deal with if you use a uint/int array.

    "The int/uint is complicated. Alternatively, should I have -128 * 256 * 256 * 256? -2,147,483,648? How do I represent that as 0x....? I'm having a little trouble using that as a negative. -0x... won't work."

    I'm not quite sure what you're trying to say. Perhaps an example will help:

    uint x = 0xFFFFFFFF;
    Color c = Color.FromArgb(unchecked((int)x));

    The resulting color is (255, 255, 255, 255). And you get the same color if you use Color.FromArgb(-1).

    "Alternately, I'm starting to use ColorF (a struct that I created) to handle graphics needs."

    Probably that's a good choice if you're doing any significant image processing and you need to support 48 bit images. But that also means that you'll need the individual rgb bytes instead of a int/uint.

    "CLS Compliant? Is that like using Int32 vs int?"

    No, int and Int32 are the same and they are CLS compliant. unsigned integer types are not CLS compliant in the idea that some other .NET languages may not understand such types. See more here: http://msdn.microsoft.com/en-us/library/vstudio/bhc3fa7f(v=vs.100).aspx

    For your own code that's only relevant when you make a library that others may use from languages other than C#.


    Friday, August 15, 2014 3:19 PM
    Moderator

All replies

  • "Why was it designed like that?"

    Unsigned integers are (unfortunately) a bit of an odd ball in .NET. They're not CLS compliant and because of that they're rarely used in framework APIs.

    "Can Microsoft write a new method FromArgb(UInt32)?"

    That won't happen. Nor there's any actual need for it.

    In cases like yours the numeric value of the int/uint is irrelevant, what matters is the bit pattern. And an int can have a bit pattern of all ones, it's -1. You'll only get into trouble if you compile with overflow checks enabled, in that case you'll get an exception for uint values larger than int.MaxValue. You can avoid that by using unchecked.

    But I'm not sure how exactly do you plan to rewrite that code, are you also going to change the type of the array from byte[] to int[]/uint[]? The cast to uint is confusing.

    Friday, August 15, 2014 2:14 PM
    Moderator
  • > But I'm not sure how exactly do you plan to rewrite that code, are you also going to change the type of the array from byte[] to int[]/uint[]? The cast to uint is confusing.

    I just showed what I wanted to do.

                    uint pix = (uint) Pixels[i] | 0xFF000000;
                    clr = Color.FromArgb(pix);

    ...at least I was hoping to do that. The data contains RGB information (no A), and I was hoping to load that in a hurry. 0x00rrggbb gives me a transparent pixel. 0x7Frrggbb gives me a 50% transparent pixel. The current code (without changing it) works fine... but not as fast as I was hoping to go.

    The int/uint is complicated. Alternatively, should I have -128 * 256 * 256 * 256? -2,147,483,648? How do I represent that as 0x....? I'm having a little trouble using that as a negative. -0x... won't work.

    Alternately, I'm starting to use ColorF (a struct that I created) to handle graphics needs. Color is limited to maximum values of 255 per sample. This won't work on 48-bit PNG/TIF/PPM images. With floating-point values ranging from 0.0 to 1.0 all kinds of mathematical operations on them are simplified.

    CLS Compliant? Is that like using Int32 vs int?


    VP

    Friday, August 15, 2014 2:47 PM
  • "I just showed what I wanted to do."

    Well, yes, but for that to work the Pixels array type needs to be uint or int. My guess is that it is not because you say that you have only RGB, no A. That means 3 bytes per pixel and that is problematic to deal with if you use a uint/int array.

    "The int/uint is complicated. Alternatively, should I have -128 * 256 * 256 * 256? -2,147,483,648? How do I represent that as 0x....? I'm having a little trouble using that as a negative. -0x... won't work."

    I'm not quite sure what you're trying to say. Perhaps an example will help:

    uint x = 0xFFFFFFFF;
    Color c = Color.FromArgb(unchecked((int)x));

    The resulting color is (255, 255, 255, 255). And you get the same color if you use Color.FromArgb(-1).

    "Alternately, I'm starting to use ColorF (a struct that I created) to handle graphics needs."

    Probably that's a good choice if you're doing any significant image processing and you need to support 48 bit images. But that also means that you'll need the individual rgb bytes instead of a int/uint.

    "CLS Compliant? Is that like using Int32 vs int?"

    No, int and Int32 are the same and they are CLS compliant. unsigned integer types are not CLS compliant in the idea that some other .NET languages may not understand such types. See more here: http://msdn.microsoft.com/en-us/library/vstudio/bhc3fa7f(v=vs.100).aspx

    For your own code that's only relevant when you make a library that others may use from languages other than C#.


    Friday, August 15, 2014 3:19 PM
    Moderator
  • "unchecked"!! Yes, I think I can use that one....

                   uint pix = (uint) Pixels[i] | 0xFF000000;
                    clr = Color.FromArgb(unchecked((int)pix));

    Like that?

    Actually I won't be doing this now. Pixels is a BYTE array, and converting it into UINT or INT array is more work than I would like to do. I may have a use for this later though. Thanks.

    And RGB without A... that's exactly the situation when loading a 24-bit image. But the Color struct has ARGB, thus the idea to enforce A part as 0xFF. Using the or-mask means I'll get an opaque RGB pixel.

    In my ColorF, the A, R, G, B parts are type float. My idea is to detect the maximum value in the incoming image. Usually (but not always) the max value is 255. In the study of computer graphics, the pixel values should be reformatted to fall in range from 0.0 to 1.0. If you do that, calculations on pixels become a lot easier. The work load changes from ... {

    c = GetPixel(x, y)

    r = c.R / 255; // I hope the max value is 255!!

    g = c.G/255;

    b = c.B/255;

    processedrgb = process(r, g, b);

    pr = processedrgb[0] * 256; if (pr == 256) pr = 255; // Yeah, that's the correct way to do it.

    pg = processedrgb[1] * 256; if (pg == 256) pg = 255;

    pb = processedrgb[2] * 256; if (pb == 256) pb = 255;

    processedc.FromArgb(pr, pg, pb);

    SetPixel(x, y, processedc)

    }

    ... hoping all of that will work, to ... {

    c = GetPixelF(x, y)

    processedc = process(c.R, c.G, c.B);

    SetPixelF(x, y, processedc)

    }

    I currently have GammaSat() and FixTrapezoid() and plan to write up many more. And the less I need to consider (like maximum value), the better.


    VP

    Friday, August 15, 2014 6:23 PM
  • By the way, instead of ‘int b = (int)Pixels[i] & 0xff’ you can write ‘int b = Pixels[i]’, assuming that Pixels is an array of bytes (byte [ ]). Therefore you can write ‘clr = Color.FromArgb( Pixels[i], Pixels[i + 1], Pixels[i + 2] )’ or create a helper function: ‘clr = MyHelpers.FromArgb( Pixels, i )’ or ‘clr = Pixels.ToArgb( i )’.

    I think that you should write ‘pr = processedrgb[0] * 255’ instead of ‘pr = processedrgb[0] * 256; if (pr == 256) pr = 255; // Yeah, that's the correct way to do it.’.


    • Edited by Viorel_MVP Saturday, August 16, 2014 6:54 PM
    Saturday, August 16, 2014 6:51 PM
  • "r = c.R / 255; // I hope the max value is 255!!"

    It is 255 because those properties are of type byte, there's no way you can get a value larger than 255.

    "pr = processedrgb[0] * 256; if (pr == 256) pr = 255; // Yeah, that's the correct way to do it."

    As Viorel already said, it's * 255, not * 256. And you really want to catch all values higher than 255, not only those that are equal to 256:

    if (pr > 255) pr = 255;

    You may also want to check for negative values and replace them with 0.

    And of course, for 48bpp images you need 65535 instead of 255.

    Sunday, August 17, 2014 5:43 AM
    Moderator
  • > By the way, instead of ‘int b = (int)Pixels[i] & 0xff’ you can write ‘int b = Pixels[i]’...

    I have thought of that but somehow forgot to actually do that.

    > I think that you should write ‘pr = processedrgb[0] * 255’ instead of ‘pr = processedrgb[0] * 256; if (pr == 256) pr = 255;

    I too used to think so... but the more I studied it, the more I understand that Foley/VanDam method are more correct. However, I did take a shortcut: "pr = processedrgb[0] * (256 - epsilon);" where epsilon is the smallest number larger than zero. In that method, I got to skip "if (pr == 256) pr = 255" step.


    VP

    Sunday, August 17, 2014 2:39 PM
  • > You may also want to check for negative values and replace them with 0.

    I am mindful of it, but currently negatives are not possible.

    I started with the source code here:

    http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp

    and I modified it to make use of ColorF. And I created ColorF to handle floating-point color values between 0 and 1.

    I may eventually redo Pixels as an INT or UINT array or another kind of array when I enable 48-bit and 64-bit and 1-bit formats. And I will review again the possibility of negative values.


    VP

    Sunday, August 17, 2014 2:52 PM