Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Modern Direct2D Color Picker for Plain Win32

0.00/5 (No votes)
2 Jun 2020 1  
C++, Direct2D, one function and you got a nice picker
A one function call to replace the old Win32 picker with a modern one with RGB, HSL, alpha and picker support

Introduction

Image 1

Image 2

Very often, I want a picker or a nice HSL slider or an alpha channel. Here is a one-file library for your code, which works with Direct2D and provides a modern interface for picking a color.

Using the Code

You have a single function call:

#include "colorpick.hpp"
COLORPICK p;
D2D1_COLOR_F c1 = {1.0f,0,0,1.0f}; // init with red
HRESULT hr = p.Show(0, c);
if (hr == S_OK) { ... } // Color is set

and possibly, passing an optional structure with more options:

struct COLORPICKOPT
{
    bool Alpha = 1;
    int Mode = 1;
    float rsl = 0.1f;
    bool Dlg = 1;
    bool LUpdate = 0;
    bool AlsoUseSystem = 1;
    bool UsePicker = 1;
    float Resolution = 0.1f;
};

Deep Into It

RGB and HSL

Let's deep into it. Conversions from RGB to HSL and vice versa is done with the fromRGBtoHSL and fromHSLtoRGB functions - see more.

Drawing

All drawing is done by a ID2D1HwndRenderTarget which will be created on WM_PAINT. This, depending on the Mode used, will:

  • Draw 117 squares with predefined colors for RGB mode (taken from here).
  • Draw a color wheel in HSL mode. The color wheel is drawn pixel by pixel and line by line. Each line has a specified Hue value which , when rotating, takes a 360 max value, i.e., the entire circle. The radius of this line is the saturation of the color.The Hue slider is drawn with a 360-value stop Direct2D Linear Brush.

The hue bar can be drawn with a linear brush:

std::vector<D2D1_GRADIENT_STOP>  gst(360);
for (int i = 0; i < 360; i++)
{
    float hsl[3] = { 1,1,L };
    hsl[0] = (360 -  i) / 360.0f;
    hsl[0] *= 6.0f;
    float rgb[3] = {};
    fromHSLtoRGB(hsl, rgb);
    gst[i].position = i / 360.0f;
    gst[i].color.r = rgb[0];
    gst[i].color.g = rgb[1];
    gst[i].color.b = rgb[2];
    gst[i].color.a = 1.0f;
}
pGradientStops = 0;
lbr = 0;
p->CreateGradientStopCollection(
    gst.data(),
    360,
    D2D1_GAMMA_2_2,
    D2D1_EXTEND_MODE_CLAMP,
    &pGradientStops
);
p->CreateLinearGradientBrush(
    D2D1::LinearGradientBrushProperties(
        D2D1::Point2F(LRect.left, LRect.top),
        D2D1::Point2F(LRect.right, LRect.bottom)),
    pGradientStops,
    &lbr);

This linear gradient brush can be used now to draw a hue bar. The other bars (S,L or R,G,B, Alpha) are plain solid brushes.

Keyboard

WM_COMMAND and WM_KEYDOWN are processed to handle IDOK/IDCANCEL and VK_RETURN/VK_ESCAPE to submit or cancel the color picking.

Inline Editing

Clicking on the Alpha value, the R,G,B (in RGB mode) or in H,S,L (in HSL mode) values displays an inline edit box (with ES_NUMBER) which allows you to enter a value (0-350 for H, 0-100 for alpha, 0-255 for others). When the edit is active, VK_RETURN/VK_ESCAPE act on the editing value.

Mouse Actions

Click on:

  • the RGB values to select the clicked color
  • the color wheel to selected the clicked color
  • the R,G,B or H,S,L bars to set the value
  • the Alpha, R,G,B or H,S,L values to inline edit the values

Drag the:

  • RGB or HSL bars to set the value
  • Color Wheel to set the HSL value

Wheel actions:

  • on the bars to rotate the value
  • on the RGB/HSL values to rotate the value

The System Dialog

The common dialog picker may be used for those that are used to it. Pressing the "S" button will display the common dialog and set/get the value selected.

The Picker

Very often, you want to pick the color you see, but you can't name it. The control includes a picker which you can click on, then drag it on the screen (outside the color picker) and it captures any color beneath it. It uses the SetCapture API.

The Parameters

  • bool Alpha = 1; By default, the color picker also shows alpha controls. If you don't want to alter the Alpha, pass 0 to this. The color returned will have an alpha of 1.0f.
  • int Mode = 1; // Starts with RGB. If 0, starts with HSL.
  • bool Dlg = 1; // If 0, it's a windows instead of a dialog.
  • bool LUpdate = 0; // If 1, on setting the L the color wheel is updated (slower)
  • bool AlsoUseSystem = 1; // Displays the "S" button for allowing the usage of the system common control
  • bool UsePicker = 1; // Displays the cross for enabling the picker
  • float Resolution = 0.1f; // Resolution of the color wheel (smaller value -> smaller speed -> better view)

The Project

#include colorpick.hpp in your application and you are ready. The github repo includes an example solution which you can study.

History

  • 2nd June, 2020 - First release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here