25 June 2013

What to do with an OLE_COLOR?

An OCX/ActiveX control color property can be assigned any valid Win32 color reference. However, the OCX/COM libraries provide a special data type for color properties, the OLE_COLOR data type. This new type was invented to give VB-like programs an opportunity to differentiate between a normal Win32 color reference and system colors. These programs could then present a special color-selection dialog box that allowed the selection of system colors. The snapshot of the GB32 Ole-color selection is shown below.


RGB colors are of type COLORREF
Like an RGB-color value, an OLE_COLOR data type is internally represented as a 32-bit long integer. An RGB-color is referred to as a COLORREF value and is used with GDI device context functions. A COLORREF is simply defined as a 32-bit value used to specify an red, green, and blue components of a color requiring only 3 bytes. When specifying an explicit RGB color, the COLORREF value has the following hexadecimal form.


The low-order byte contains a value for the relative intensity of red, the second byte contains a value for green, and the third byte contains a value for blue. The high-order byte must be zero. The maximum value for a single byte is 0xFF.
To create a COLORREF color value, you can use the GB32 RGB() function. To extract the individual values for the red, green, and blue components of a color value, use the GetRValue, GetGValue, and GetBValue functions, respectively. For performance reasons these GFA-BASIC 32 functions are directly translated to assembly code; they are not implemented as function calls.

The OLE_COLOR type
When a COM object property (most often .ForeColor and .BackColor only) requires an OLE_COLOR color, you can either pass an RGB  value (COLORREF) or a COM specific formatted system color index value. A COM specific format has it’s high-byte set 0x80. An OLE_COLOR can have two formats only:

  • 0x00bbggrr (COLORREF value)
  • 0x800000xx, where xx is the index used by GetSysColor().

The object doesn’t do much with the OLE_COLOR value, it is more a kind of a storage type. For instance, you cannot pass an OLE_COLOR to a GDI device context handle. GDI doesn’t know what to do with a COM formatted system index color (0x800000xx). The OLE_COLOR value is simply saved in the private data of the COM object and remains unchanged until the application explicitly changes the value again. This way, the Get-color-property variant of the Put-color-property will return exactly the same value as was first put in.

How the OLE_COLOR is used
Since the format of the OLE_COLOR data type isn’t known to Win32 drawing functions, they must be converted to RGB colors first. Usually, the OLE_COLOR is converted on a just-in-time basis. Just before a painting operation is performed the OLE_COLOR (32-bit long integer) is tested for a high-byte value of 0x80. When the high-byte is indeed 0x80, the low-byte value is used as a parameter to the GetSysColor() API function, which in turn returns the RGB color value of the specified display element. See the code below.

Aside: Identifying display elements
Display elements are the parts of a window and the display that appear on the system display screen. These elements are identified using the API COLOR_* constants, like COLOR_BTNFACE, COLOR_WINDOW, etc. These API constants may also be used in the GFA-BASIC 32 function SysCol(idx), which is slightly easier to use and a bit faster due to caching.

Dim rgb% = SysCol(COLOR_WINDOW)

GFA-BASIC 32 also defines a set of OLE_COLOR constants to identify the system display elements. These constants are very much like the Windows API COLOR_* constants. However, instead starting their names with ‘COLOR_’ they start with ‘col’, like colBtnFace and colWindow. These GB32 OLE_COLOR constants have the format of 0x800000xx, where xx is the index for the display element. See the code below.

Convert an OLE_COLOR
When you want to use an OLE_COLOR value, make sure to convert the OLE_COLOR to a COLORREF (RGB) value first. This is the only color format Win32 understands. In GB32 you get hold of an OLE_COLOR when you use one the col* constants or retrieve a color from one of the Ocx-color properties. For instance, when you want to use the same color as the form’s background, you might very well end up with an OLE_COLOR with the high-byte set to 0x80.

Now, there are two possible ways to convert an OLE_COLOR to a COLORREF value.

1. Manually, in your code. For example this converts to an RGB-value:

Trace Hex(SysCol(colBtnFace))   ' =0 (SysCol: what is a colBtnFace?)
Trace Hex(colBtnFace)           ' =8000000F (an OLE_COLOR)
Trace Hex(OleToRGB(colBtnFace)) ' =F0F0F0 (the RGB value)
Trace Hex(OleToRGB(0x667788))   ' =667788 (no conversion)

Function OleToRGB(olecolor As Long) As Long Naked
  If (olecolor And 0xFF000000) == 0x80000000
    Return SysCol(olecolor And 0xFF)  ' LoByte
    Return olecolor                   ' RGB!

2. Use the system OLE function OleTranslateColor()

Declare Function OleTranslateColor Lib "oleaut32.dll" ( _
  ByVal OleColor As Long, _
  ByVal hPal As Handle, _
  ByRef ColorRef As Long) As Long

Dim rgb As Long Long

If OleTranslateColor(colBtnFace, Null, rgb) == S_OK Then _ Trace Hex(rgb)

The second option using OleTranlateColor() might be preferable because it can also return other GDI color formats, like a palette-index value. In addition in case of an error, it let you investigate the type of the error. See the SDK for more info.

More flexibility
You can use OLE_COLOR types with the GB32 graphic commands, though. All drawing statements, font statements, and color statements accept an OLE_COLOR type. GB32 converts the OLE_COLOR to a COLORREF before it invokes the corresponding GDI function.

No comments:

Post a Comment