Click here to Skip to main content
15,884,838 members
Articles / Desktop Programming / WTL

Making of a Color Spy utility with WTL

Rate me:
Please Sign up or sign in to vote.
4.92/5 (28 votes)
30 Sep 2003MIT8 min read 92K   1.7K   51  
Making of color picker utility using WTL and recap of clipboard management APIs.
//////////////////////////////////////////////////////////////////////
// ColorSpy Copyright 2003 Tom Furuya
//
// Readers of this article may copy the code for use in developing their own applications.
// If the code is used in a commercial application, however, an acknolegement must 
// be included in the following form: 
// "Segment of the code (c) 2003 Tom Furuya for CodeProject.com".
//////////////////////////////////////////////////////////////////////

// ColorHelper.h: interface for the CColorHelper class.

//
// RGB to HSB conversion code
//
// Copyright restrictions
// ======================
//
// The Gri programming languages, and all manuals and online help-files,
// are (c) 1991-2002 Dan E. Kelley <Dan.Kelley@Dal.CA>.
//  

#if !defined(AFX_COLORHELPER_H__FC0D0C25_B591_494D_9C6A_2E3D92E2C903__INCLUDED_)
#define AFX_COLORHELPER_H__FC0D0C25_B591_494D_9C6A_2E3D92E2C903__INCLUDED_
#pragma once

#include "Duo.h"

//
// Color name-to-RGB value mapping
//
// rgbtab.h - Color table from Xpm library of XFree86.
// Original: [ftp://ftp.xfree86.org/pub/XFree86/4.3.0/source/X430src-1.tgz]
//  
#include "rgbtab.h" // Modified. You can replace the table as you wish.


// Color Helper class

class CColorHelper 
{
public:
    static bool s_bStaticInit;
    static int  s_nMaxPatternLen;
    
    typedef enum {
        CF_WEB,
        CF_DEC,
        CF_HSB,
    } ColorFormat;
    
    CColorHelper()
    {
        _Init();
    }
    
    CString ToString(ColorFormat fmt, COLORREF clr)
    {
        static LPCTSTR FMT_WEB = TEXT("%02X%02X%02X");
        static LPCTSTR FMT_DEC = TEXT("R:%d G:%d B:%d");
        
        CString str;
        
        switch (fmt)
        {
        default:
        case CF_WEB:
            str.Format(FMT_WEB, GetRValue(clr), GetGValue(clr), GetBValue(clr));
            break;
        case CF_DEC:
            str.Format(FMT_DEC, GetRValue(clr), GetGValue(clr), GetBValue(clr));
            break;
        case CF_HSB:
            str = ToHSB(clr);
            break;
        }
        return str;
    }
    
    CString ToString(COLORREF clr)
    {
        return ToString(CF_WEB, clr);
    }
    
    DuoT<bool, COLORREF> GetColor(const CString& s)
    {
        ATLASSERT(s.GetLength() == 6);
        const DuoT<bool, COLORREF> InvalidColor = make_duo(false, 0);
        
        if (s.GetLength() != 6) return InvalidColor;
        if (!IsHex(s[0]) || !IsHex(s[1]) || !IsHex(s[2]) ||
            !IsHex(s[3]) || !IsHex(s[4]) || !IsHex(s[5]))
            return InvalidColor;
        
        CString strR = s.Mid(0, 2);
        CString strG = s.Mid(2, 2);
        CString strB = s.Mid(4, 2);
        
        TCHAR  *stopstring;
        unsigned long ul;
        BYTE r, g, b;
        
        ul = _tcstoul( (LPCTSTR)strR, &stopstring, 16);
        if (*stopstring != 0) return InvalidColor;
        r = LOBYTE(LOWORD(ul));
        ul = _tcstoul( (LPCTSTR)strG, &stopstring, 16);
        if (*stopstring != 0) return InvalidColor;
        g = LOBYTE(LOWORD(ul));
        if (*stopstring != 0) return InvalidColor;
        ul = _tcstoul( (LPCTSTR)strB, &stopstring, 16);
        b = LOBYTE(LOWORD(ul));
        
        return make_duo(true, RGB(r, g, b));
    }
    
    CString GetColorName(COLORREF clr)
    {
        rgbRecord* pos = theRGBRecords;
        while (pos->name)
        {
            if (clr == pos->rgb)
                return pos->name;
            pos++;
        }
        return _T("");
    }
    
    DuoT<bool, COLORREF> GetColorByName(CString name)
    {
        const DuoT<bool, COLORREF> InvalidColor = make_duo(false, 0);
        const int nLen = name.GetLength();

        if (nLen >= s_nMaxPatternLen) return InvalidColor;

        name.MakeLower();

        // theRecords: Color name-to-RGB value mapping table. See rgbtable.h for details

        const int nRecords = NELEM(theRGBRecords);
        for (int i = 0; i < nRecords; i++)
        {
            if (nLen != thePatternLen[i] || name[0] != *theRGBRecords[i].name)
                continue;

            if (name == theRGBRecords[i].name)
                return make_duo(true, theRGBRecords[i].rgb);
        }
        return InvalidColor;
    }

    static void _Init()
    {
        if (s_bStaticInit) return;

        // performance improvement
        for (int i = 0; i < NELEM(theRGBRecords); i++)
        {
            thePatternLen[i] = lstrlen(theRGBRecords[i].name);
            if (thePatternLen[i] >= s_nMaxPatternLen)
                s_nMaxPatternLen = thePatternLen[i];
        }
        // done
        s_bStaticInit = true;
    }

    inline bool IsHex(TCHAR ch)
    {
         return (ch == _T('0') || ch == _T('1') || ch == _T('2') || 
                 ch == _T('3') || ch == _T('4') || ch == _T('5') || 
                 ch == _T('6') || ch == _T('7') || ch == _T('8') || 
                 ch == _T('9') || ch == _T('A') || ch == _T('B') || 
                 ch == _T('C') || ch == _T('D') || ch == _T('E') || 
                 ch == _T('F') || ch == _T('a') || ch == _T('b') || 
                 ch == _T('c') || ch == _T('d') || ch == _T('e') || 
                 ch == _T('f')); 
    }

    CString ToHSB(COLORREF clr)
    {
        static LPCTSTR FMT_HSB = TEXT("H:%d S:%d B:%d");

        CString str;
        int i = GetRValue(clr);
        int i1 = GetGValue(clr);
        int i2 = GetBValue(clr);
        double h, s, br;
        rgb2hsb(i / 255.f, i1 / 255.f, i2 / 255.f, &h, &s, &br);

         // Make Hue value close to what P*******p tell us.
        CComVariant varIn = h, varResult; 
        VarRound(&varIn, 0, &varResult);
        str.Format(FMT_HSB, int(varResult.dblVal), int(s * 100), int(br * 100));
        return str;
    }

/*
 * (red, green, blue) in range from 0 to 1 is translated to (hue, saturation,
 * brightness) in same range.
 * 
 * NOTE: false checking on range of (r,g,b) is done, and strange results may result
 * outside this range.
 * 
 * Algorithm: Foley + Van Dam
 */
    void
    rgb2hsb(double r, double g, double b, double *h, double *s, double *br)
    {
        double          mx, mn;

        mx = r;
        if (g > mx)
            mx = g;
        if (b > mx)
            mx = b;
        mn = r;
        if (g < mn)
            mn = g;
        if (b < mn)
            mn = b;
        *br = mx;
        if (mx)
            *s = (mx - mn) / mx;
        else
            *s = 0.0;
        
        if (*s == 0.0) 
        {
            *h = 0.0;	/* doesn't matter, since black anyway */
        }
        else
        {
            *h = GetHue(r,g,b);
#if NEVER
            double          rc, gc, bc;

            rc = (mx - r) / (mx - mn);
            gc = (mx - g) / (mx - mn);
            bc = (mx - b) / (mx - mn);
            if (r == mx)
                *h = bc - gc;
            else if (g == mx)
                *h = 2.f + rc - bc;
            else if (b == mx)
                *h = 4.f + gc - rc;
            *h /= 6.f;
            if (*h < 0.f)
                *h = *h + 1.f;
#endif
        }
    }

    double GetHue(double r, double g, double b)
    {
        double f = r;
        double f1 = g;
        double f2 = b;
        double f6 = 0.0f;
        double f3 = f;
        double f4 = f;
        if (f1 > f3)
            f3 = f1;
        if (f2 > f3)
            f3 = f2;
        if (f1 < f4)
            f4 = f1;
        if (f2 < f4)
            f4 = f2;
        double f5 = f3 - f4;
        if (f == f3)
        {
            f6 = (f1 - f2) / f5;
        }
        else
        {
            if (f1 == f3)
                f6 = 2.f + (f2 - f) / f5;
            else if (f2 == f3)
                f6 = 4.f + (f - f1) / f5;
        }
        f6 *= 60.f;
        if (f6 < 0.f)
            f6 += 360.f;
        return f6;
    }

};

__declspec(selectany) int CColorHelper::s_nMaxPatternLen = 0;
__declspec(selectany) bool CColorHelper::s_bStaticInit = false;


#endif // !defined(AFX_COLORHELPER_H__FC0D0C25_B591_494D_9C6A_2E3D92E2C903__INCLUDED_)

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Japan Japan
Live (1994 "Throwing Copper" till 1999 "The Distance To Here") is one of his favorite bands.

After 9 years life in U.S, he lives in his hometown Yokohama, working in Automotive After-sales business.
He sometimes found himself drunk once in a while in Munich.



He has put the period to his windows development after writing first and last article about a light-weight memory program, called colorspy (which is amusingly running on his latest windows except for dual display support.)
He has a message to the WTL author, "you rock. you proved that WTL kicks ass, M*F*C". F, in the middle, always reminds him of somewhat different wording.


Time lapse



His codepen is live.copper. His main focus has changed to various web technologies, to build fastest EPC services for automotive clients instead of pharmaceutical ones.
Ironically, he has not yet been released from the chaotic Windows software development installations even though he is no longer programming for Windows but for server side development.




Ein Prosit, Ein Prosit! He is still with a company in Munich as he loves to help people all over the world who need to fix their cars.

Comments and Discussions