Click here to Skip to main content
15,896,539 members
Articles / Desktop Programming / MFC

ImageStone - A Powerful C++ Class Library for Image Manipulation

Rate me:
Please Sign up or sign in to vote.
4.81/5 (250 votes)
6 Dec 2011Zlib3 min read 121.3K   51.5K   405  
An article on a library for image manipulation
/*
 *   Copyright (C) =USTC= Fu Li
 *
 *   Author   :  Fu Li
 *   Create   :  2003-4-17
 *   Home     :  http://www.crazy-bit.com/
 *   Mail     :  crazybitwps@hotmail.com
 *   History  :  
 */
#ifndef __FOO_COLOR__2003_04_17__H__
#define __FOO_COLOR__2003_04_17__H__
#include "StdDefine.h"

//=============================================================================
/**
 *  Color helper class.
 *  all function has no param check.
 */
class FCColor
{
public:
    /**
     *  Combine two pixel, put result into pDest.
     *  @param pDest : must be 32bpp to receive result
     *  @param cr1 : background
     *  @param cr2 : foreground
     *  @param nAlpha2 : alpha value of  cr2
     */
    static void CombineAlphaPixel (void* pDest, RGBQUAD cr1, const void* cr2, BYTE nAlpha2) ;

    /**
     *  AlphaBlend two pixel, put result into pDest.
     */
    static void AlphaBlendPixel (BYTE* pDest, const BYTE* pSrc, BYTE nAlphaSrc)
    {
        if (nAlphaSrc == 0xFF)
        {
            *(WORD*)pDest = *(WORD*)pSrc ;
            ((BYTE*)pDest)[2] = ((BYTE*)pSrc)[2] ;
            return ;
        }
        if (nAlphaSrc == 0)
            return ;

        // needn't bound in [0,0xFF], i have checked :-)
        pDest[0] = (pSrc[0] - pDest[0]) * nAlphaSrc / 0xFF + pDest[0] ;
        pDest[1] = (pSrc[1] - pDest[1]) * nAlphaSrc / 0xFF + pDest[1] ;
        pDest[2] = (pSrc[2] - pDest[2]) * nAlphaSrc / 0xFF + pDest[2] ;
    }

    /**
     *  Calculate pixel's grayscale value.
     *  @param prgb : point a 24bpp or 32bpp pixel address.
     */
    static BYTE GetGrayscale (const void* prgb)
    {
        return (30*PCL_R(prgb) + 59*PCL_G(prgb) + 11*PCL_B(prgb)) / 100 ;
    }

    /** 
     *  Rapid pixel copy.
     *  @param nBytes : can be 1,2,3,4
     */
    static void CopyPixel (void* pDest, const void* pSrc, int nBytes)
    {
        if (nBytes == 4)
            *(DWORD*)pDest = *(DWORD*)pSrc ;
        else if (nBytes == 3)
        {
            *(WORD*)pDest = *(WORD*)pSrc ;
            ((BYTE*)pDest)[2] = ((BYTE*)pSrc)[2] ;
        }
        else if (nBytes == 1)
            *(BYTE*)pDest = *(BYTE*)pSrc ;
        else if (nBytes == 2)
            *(WORD*)pDest = *(WORD*)pSrc ;
        else
        {
            assert(false) ;
            memcpy (pDest, pSrc, nBytes) ;
        }
    }

    /**
     *  @name RGB <==> HLS (Hue, Lightness, Saturation).
     */
    //@{
    /// RGB ==> HLS
    static void     RGBtoHLS (const void* prgb, double* H, double* L, double* S) ;
    /// HLS ==> RGB
    static RGBQUAD  HLStoRGB (const double& H, const double& L, const double& S) ;
    //@}

    /**
     *  @name RGB <==> HSV (Hue, Saturation, Value).
     */
    //@{
    /// RGB ==> HSV
    static void     RGBtoHSV (const void* prgb, double* H, double* S, double* V) ;
    /// HSV ==> RGB
    static RGBQUAD  HSVtoRGB (double H, const double& S, const double& V) ;
    //@}

    /// Computes bilinear interpolation of four pixels.
    /// The pixels in 'crPixel' in the following (x,y) order: [0,0], [1,0], [0,1], [1,1].
    static RGBQUAD Get_Bilinear_Pixel (double x, double y, bool bHasAlpha, const BYTE* crPixel[4]) ;
    /// Color black.
    static RGBQUAD crBlack() {return PCL_RGBA(0,0,0);}
    /// Color white.
    static RGBQUAD crWhite() {return PCL_RGBA(0xFF,0xFF,0xFF);}

private:
    static double __HLS_Value (const double& m1, const double& m2, double h) ;
    static bool __IsRGBEqual (const void* p1, const void* p2)
    {
        return (PCL_B(p1) == PCL_B(p2)) && (PCL_G(p1) == PCL_G(p2)) && (PCL_R(p1) == PCL_R(p2)) ;
    }
    static RGBQUAD __DoubleRGB_to_RGB (const double& r, const double& g, const double& b)
    {
        return PCL_RGBA (FClamp0255((int)(r*255)), FClamp0255((int)(g*255)), FClamp0255((int)(b*255))) ;
    }
};

//=============================================================================
// inline implement
//=============================================================================
inline void FCColor::CombineAlphaPixel (void* pDest, RGBQUAD cr1, const void* cr2, BYTE nAlpha2)
{
    if (PCL_A(&cr1) || nAlpha2)
    {
        if (nAlpha2 == 0)
        {
            *(RGBQUAD*)pDest = cr1 ;
            return ;
        }
        if ((PCL_A(&cr1) == 0) || (nAlpha2 == 0xFF))
        {
            CopyPixel (pDest, cr2, 3) ;
            PCL_A(pDest) = nAlpha2 ;
            return ;
        }
        // needn't bound in [0,0xFF], i have checked :-)
        int   nTmp1 = 0xFF * PCL_A(&cr1), nTmp2 = 0xFF * nAlpha2,
              nTmp12 = PCL_A(&cr1) * nAlpha2,
              nTemp = nTmp1 + nTmp2 - nTmp12 ;
        PCL_B(pDest) = (nTmp2*PCL_B(cr2) + (nTmp1 - nTmp12)*PCL_B(&cr1))/nTemp ;
        PCL_G(pDest) = (nTmp2*PCL_G(cr2) + (nTmp1 - nTmp12)*PCL_G(&cr1))/nTemp ;
        PCL_R(pDest) = (nTmp2*PCL_R(cr2) + (nTmp1 - nTmp12)*PCL_R(&cr1))/nTemp ;
        PCL_A(pDest) = nTemp / 0xFF ;

/*      // un-optimized, easier to read
        int    nTemp = 0xFF*(PCL_A(&cr1) + nAlpha2) - PCL_A(&cr1)*nAlpha2 ;
        PCL_B(pDest) = (0xFF*PCL_B(cr2)*nAlpha2 + (0xFF - nAlpha2)*PCL_B(&cr1)*PCL_A(&cr1))/nTemp ;
        PCL_G(pDest) = (0xFF*PCL_G(cr2)*nAlpha2 + (0xFF - nAlpha2)*PCL_G(&cr1)*PCL_A(&cr1))/nTemp ;
        PCL_R(pDest) = (0xFF*PCL_R(cr2)*nAlpha2 + (0xFF - nAlpha2)*PCL_R(&cr1)*PCL_A(&cr1))/nTemp ;
        PCL_A(pDest) = nTemp / 0xFF ;*/
    }
    else
    {
        PCL_B(pDest) = PCL_G(pDest) = PCL_R(pDest) = 0xFF ;
        PCL_A(pDest) = 0 ;
    }
}
//-----------------------------------------------------------------------------
inline void FCColor::RGBtoHLS (const void* prgb, double* H, double* L, double* S)
{
    const int   n_cmax = FMax (PCL_R(prgb), FMax (PCL_G(prgb), PCL_B(prgb))),
                n_cmin = FMin (PCL_R(prgb), FMin (PCL_G(prgb), PCL_B(prgb))) ;

    *L = (n_cmax + n_cmin) / 2.0 / 255.0 ;
    if (n_cmax == n_cmin)
    {
        *S = *H = 0.0 ;
        return ;
    }

    const double   r = PCL_R(prgb) / 255.0,
                   g = PCL_G(prgb) / 255.0,
                   b = PCL_B(prgb) / 255.0,
                   cmax = n_cmax / 255.0,
                   cmin = n_cmin / 255.0,
                   delta = cmax - cmin ;

    if (*L < 0.5) 
        *S = delta / (cmax+cmin) ;
    else
        *S = delta / (2.0-cmax-cmin) ;

    if (PCL_R(prgb) == n_cmax)
        *H = (g-b) / delta ;
    else if (PCL_G(prgb) == n_cmax)
        *H = 2.0 + (b-r) / delta ;
    else
        *H = 4.0 + (r-g) / delta ;
    *H /= 6.0 ;
    if (*H < 0.0)
        *H += 1.0 ;
}
//-----------------------------------------------------------------------------
inline double FCColor::__HLS_Value (const double& m1, const double& m2, double h)
{
    if (h > 6.0)
        h -= 6.0 ;
    else if (h < 0.0)
        h += 6.0 ;

    if (h < 1.0)
        return m1 + (m2 - m1) * h ;
    else if (h < 3.0)
        return m2 ;
    else if (h < 4.0)
        return m1 + (m2 - m1) * (4.0 - h) ;
    return m1 ;
}
inline RGBQUAD FCColor::HLStoRGB (const double& H, const double& L, const double& S)
{
    if (S < FLT_EPSILON) // == 0
        return __DoubleRGB_to_RGB (L, L, L) ;

    double     m1, m2 ;
    if (L < 0.5)
        m2 = L * (1.0 + S) ;
    else
        m2 = L + S - L*S ;
    m1 = 2.0*L - m2 ;

    return __DoubleRGB_to_RGB (__HLS_Value (m1, m2, H*6.0 + 2.0),
                               __HLS_Value (m1, m2, H*6.0),
                               __HLS_Value (m1, m2, H*6.0 - 2.0)) ;
}
//-----------------------------------------------------------------------------
inline void FCColor::RGBtoHSV (const void* prgb, double* H, double* S, double* V)
{
    const int      n_cmax = FMax (PCL_R(prgb), FMax (PCL_G(prgb), PCL_B(prgb))),
                   n_cmin = FMin (PCL_R(prgb), FMin (PCL_G(prgb), PCL_B(prgb))) ;
    const double   r = PCL_R(prgb) / 255.0,
                   g = PCL_G(prgb) / 255.0,
                   b = PCL_B(prgb) / 255.0,
                   delta = (n_cmax - n_cmin) / 255.0 ;
    *V = n_cmax / 255.0 ;
    if (n_cmax == n_cmin)
    {
        *S=0.0 ; *H=0.0;
        return ;
    }

    *S = (n_cmax - n_cmin) / (double)n_cmax ;
    if (PCL_R(prgb) == n_cmax)
        *H = (g - b) / delta ;
    else if (PCL_G(prgb) == n_cmax)
        *H = 2.0 + (b - r) / delta ;
    else if (PCL_B(prgb) == n_cmax)
        *H = 4.0 + (r - g) / delta ;

    *H /= 6.0 ;
    if (*H < 0.0)
        *H += 1.0 ;
    else if (*H > 1.0)
        *H -= 1.0 ;
}
//-----------------------------------------------------------------------------
inline RGBQUAD FCColor::HSVtoRGB (double h, const double& s, const double& v)
{
    if (s < FLT_EPSILON) // == 0
        return __DoubleRGB_to_RGB (v, v, v) ;

    if (h == 1.0)
        h = 0.0 ;
    h *= 6.0 ;

    const double  f = h - (int)h,
                  p = v * (1.0 - s),
                  q = v * (1.0 - s * f),
                  t = v * (1.0 - s * (1.0 - f)) ;
    switch ((int)h)
    {
        case 0 : return __DoubleRGB_to_RGB (v, t, p) ;
        case 1 : return __DoubleRGB_to_RGB (q, v, p) ;
        case 2 : return __DoubleRGB_to_RGB (p, v, t) ;
        case 3 : return __DoubleRGB_to_RGB (p, q, v) ;
        case 4 : return __DoubleRGB_to_RGB (t, p, v) ;
        case 5 : return __DoubleRGB_to_RGB (v, p, q) ;
    }
    return __DoubleRGB_to_RGB (0, 0, 0) ;
}
//-----------------------------------------------------------------------------
// Computes bilinear interpolation of four pixels.
// The pixels in 'crPixel' in the following order: [0,0], [1,0], [0,1], [1,1].
// !!! the pointer must 24bit or 32bit color
inline RGBQUAD FCColor::Get_Bilinear_Pixel (double x, double y, bool bHasAlpha, const BYTE* crPixel[4])
{
    const BYTE    * pPixel0 = crPixel[0], * pPixel1 = crPixel[1],
                  * pPixel2 = crPixel[2], * pPixel3 = crPixel[3] ;
    RGBQUAD       crRet = {0xFF, 0xFF, 0xFF, 0xFF} ;

    if ( bHasAlpha && (*(DWORD*)pPixel0 == *(DWORD*)pPixel1) &&
                      (*(DWORD*)pPixel0 == *(DWORD*)pPixel2) &&
                      (*(DWORD*)pPixel0 == *(DWORD*)pPixel3) )
    {
        return *(RGBQUAD*)pPixel0 ;
    }

    if ( !bHasAlpha && __IsRGBEqual(pPixel0, pPixel1) &&
                       __IsRGBEqual(pPixel0, pPixel2) &&
                       __IsRGBEqual(pPixel0, pPixel3) )
    {
        FCColor::CopyPixel (&crRet, pPixel0, 3) ;
        return crRet ;
    }

    // test x-[0,1] y-[0,1]
    assert ((x > -FLT_EPSILON) && (x < 1.0) && (y > -FLT_EPSILON) && (y < 1.0)) ;

    // x && y is zero
    if ((x < FLT_EPSILON) && (y < FLT_EPSILON))
    {
        FCColor::CopyPixel (&crRet, pPixel0, bHasAlpha ? 4 : 3) ;
        return crRet ;
    }

    if (!bHasAlpha || ((PCL_A(pPixel0) & PCL_A(pPixel1) & PCL_A(pPixel2) & PCL_A(pPixel3)) == 0xFF))
    {
        // if 24bit color
        for (int i=0 ; i < 3 ; i++)
        {
            const double   m0 = pPixel0[i] + x * (pPixel1[i] - pPixel0[i]),
                           m1 = pPixel2[i] + x * (pPixel3[i] - pPixel2[i]),
                           my = m0 + y * (m1 - m0) ;
            ((BYTE*)&crRet)[i] = (int)my ; // needn't bound
        }
    }
    else
    {
        // under is 32bit color with alpha
        int       nAlpha[4] = {pPixel0[3], pPixel1[3], pPixel2[3], pPixel3[3]} ;
        // calc dest alpha value
        double    m0 = nAlpha[0] + x * (nAlpha[1] - nAlpha[0]),
                  m1 = nAlpha[2] + x * (nAlpha[3] - nAlpha[2]),
                  my = m0 + y * (m1 - m0) ;
        PCL_A(&crRet) = (int)my ;
        if (PCL_A(&crRet)) // has alpha
            for (int i=0 ; i < 3 ; i++)
            {
                int   nSum0 = nAlpha[0] * pPixel0[i],
                      nSum2 = nAlpha[2] * pPixel2[i] ;

                m0 = nSum0 + x * (pPixel1[i] * nAlpha[1] - nSum0) ;
                m1 = nSum2 + x * (pPixel3[i] * nAlpha[3] - nSum2) ;
                my = m0 + y * (m1 - m0) ;
                ((BYTE*)&crRet)[i] = int(my) / PCL_A(&crRet) ;
            }
    }
    return crRet ;
}

#endif

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 zlib/libpng License


Written By
Team Leader PhoXo
China China
graduate from University of Science and Technology of China at 2002.

Now I work at www.phoxo.com.

Comments and Discussions