c++delphitcolor

How to Convert D3DCOLORVALUE to delphi tcolor


I have a list of integers, obtained through C ++ code and which representing different D3DCOLORVALUE ambient, etc...

What is the correct method to obtain the same colors, from this numbers, in Delphi?.

For example, using a program in C ++ (Visual Studio 2012), by calling a library routine in C ++ (separate dll file) I can obtain the following values:

-1.44852785e-016 = 2770796799 (represent the color)
 1.57844226e+11  = 1376977151 (represent the color)
-1.98938735e+034 = 4168431103 (represent the color)
 3.39617733e+038 = 4294967295 (represent the color)

@Rudy Velthuis, The original code is: (some values contained in the array are those shown above)

int32_t index = ifcObject->indicesForFaces[0];

uint32_t    ambient  = ((int32_t*) vertices)[index * (vertexElementSize / sizeof(float)) + 6],
            diffuse  = ((int32_t*) vertices)[index * (vertexElementSize / sizeof(float)) + 7],
            emissive = ((int32_t*) vertices)[index * (vertexElementSize / sizeof(float)) + 8],
            specular = ((int32_t*) vertices)[index * (vertexElementSize / sizeof(float)) + 9];

Then in Delphi XE6 using

PInteger (@ColorsArray [index * sizeOfVertices + 6])^

I get the same values but, in delphi the colors are black or null. Sometimes negative numbers are obtained, it is understood that in C ++ it is possible to represent a color with a negative value but in delphi does not work.

How can I convert a color value, obtained in D3DCOLORVALUE format, to a Delphi's TColor?

That the values are used to obtain the colors of objects in D3DMATERIAL9, D3DCOLORVALUE Ambient (Ambient color RGB)

FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;

This function make the color:

void    SetColor(
                D3DCOLORVALUE   * iColor,
                uint32_t        color
            )
{
    iColor->r = (float) (color & ((unsigned int) 255 * 256 * 256 * 256)) / (256 * 256 * 256);
    iColor->r /= 255.f;

    iColor->g = (float) (color & (255 * 256 * 256)) / (256 * 256);
    iColor->g /= 255.f;

    iColor->b = (float) (color & (255 * 256)) / 256;
    iColor->b /= 255.f;

    iColor->a = (float) (color & (255));
    iColor->a /= 255.f;
}

But in Delphi it does not work, the color obtained is wrong.

Comparison of colors with the @Dsm solution:

Left: Color obtained Right: Desired color

enter image description here enter image description here

enter image description here enter image description here

I have made a simple example to illustrate the differences:

enter image description here

The first color corresponds to the foot of the column and the second color corresponds to the column. The figure with the platform is the example with the library viewer, with the correct colors for that version and the figure on the right is an image of the watch in Visual Studio 2012 to show that the content of the array is the same and the value obtained with the cast is also the same but, when converting these numbers to colors they do not give the same result.


Solution

  • This looks like the original C++ extracts the colour knowing intimately the internal structure of an array of records. In your code you should not use (int32_t*) because that creates a pointer to a constant, and you just want to cast the float to a uint_32_t, so I think what you want to satisfy your test code is just

    ambient := uint32_t(-1.44852785e-016);
    diffuse := uint32_t(1.57844226e+11);
    etc;
    

    That doesn't compile in Delphi (invalid typecast), so instead you need to do something like

    var
      p : pointer;
      iFloat : single;
      iColour : TColor;
    begin
      iFloat := 1.57844226e+11;
      p := @(iFloat);
      iColour := TColor(p^);
      diffuse := iColour;
    
    end;
    

    which gives a red colour.

    Note that all your colours appear similar because you are taking the start of several similar addresses.

    Expressed as a function this might become

    function FloatToColor( const pVal : single ) : TColor;
    var
      p : pointer;
      iFloat : single;
    begin
      iFloat := 1.57844226e+11;
      p := @(iFloat);
      Result := TColor(p^);
    end;
    

    Edit

    Based on the fact that $FF appears in the wrong place, it occurred to me that these values might be CMYK format. In tests I found that it was a modified CMYK format - more a YMCK format, if you will, so to get the correct colours I had to reverse the roles of red and blue.

    There is no true conversion from CMYK to RGB, but the following is approximate.

    function CMYKtoRGB( const pValue : TColor) : TColor;
    var
      c,m,y,k,r,g,b : byte;
    begin
      c := GetCValue( pValue );
      m := GetMValue( pValue );
      y := GetYValue( pValue );
      k := GetKValue( pValue );
    
      //r := $FF xor c;
      r := $FF - c;
      b := $FF - y;
      g := $FF - m;
      if k <> $FF then
      begin
        k := $FF - k;
        r := r * k;
        g := g * k;
        b := b * k;
      end;
      Result := RGB( b, g, r );
    end;
    
    function FloatToColor( const pVal : single ) : TColor;
    var
      p : pointer;
      iFloat : single;
    begin
      iFloat := pVal;
      p := @(iFloat);
      Result := CMYKtoRGB(TColor(p^));
    end;
    

    Note that I use RGB( b, g, r) rather than the expected RGB( r, g, b) to account for the reversed colour order.