home *** CD-ROM | disk | FTP | other *** search
/ Windows Graphics Programming / Feng_Yuan_Win32_GDI_DirectX.iso / Samples / include / Octree.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-11  |  12.2 KB  |  523 lines

  1. //-----------------------------------------------------------------------------------//
  2. //              Windows Graphics Programming: Win32 GDI and DirectDraw               //
  3. //                             ISBN  0-13-086985-6                                   //
  4. //                                                                                   //
  5. //  Written            by  Yuan, Feng                             www.fengyuan.com   //
  6. //  Copyright (c) 2000 by  Hewlett-Packard Company                www.hp.com         //
  7. //  Published          by  Prentice Hall PTR, Prentice-Hall, Inc. www.phptr.com      //
  8. //                                                                                   //
  9. //  FileName   : octree.cpp                                                             //
  10. //  Description: Octree color quantilization, color reduction                        //
  11. //  Version    : 1.00.000, May 31, 2000                                              //
  12. //-----------------------------------------------------------------------------------//
  13.  
  14. #define STRICT
  15. #define WIN32_LEAN_AND_MEAN
  16. #include <windows.h>
  17. #include <tchar.h>
  18. #include <assert.h>
  19.  
  20. #include "DIB.h"
  21. #include "Image.h"
  22. #include "Octree.h"
  23.  
  24. /////////////////////////////////////////////////
  25. //                                               //
  26. //    O C T R E E  Q U A N T I L I Z A T I O N   //
  27. //                                               //    
  28. /////////////////////////////////////////////////
  29.  
  30. KNode::RemoveAll(void)
  31. {
  32.     for (int i=0; i<8; i++)
  33.         if ( Child[i] )
  34.         {    
  35.             Child[i]->RemoveAll();
  36.             Child[i] = NULL;
  37.         }
  38.     
  39.     delete this;
  40. }
  41.  
  42.  
  43. int KNode::PickLeaves(RGBQUAD * pEntry, int * pFreq, int size)
  44. {
  45.     if ( size==0 )
  46.         return 0;
  47.  
  48.     if ( IsLeaf )
  49.     {
  50.         * pFreq             = Pixels;
  51.  
  52.         pEntry->rgbRed      = ( SigmaRed   + Pixels/2 ) / Pixels;
  53.         pEntry->rgbGreen    = ( SigmaGreen + Pixels/2 ) / Pixels;
  54.         pEntry->rgbBlue     = ( SigmaBlue  + Pixels/2 ) / Pixels;
  55.         pEntry->rgbReserved = 0;
  56.  
  57.         return 1;
  58.     }
  59.     else
  60.     {
  61.         int sum = 0;
  62.     
  63.         for (int i=0; i<8; i++)
  64.             if ( Child[i] )
  65.                 sum += Child[i]->PickLeaves(pEntry+sum, pFreq+sum, size-sum);
  66.  
  67.         return sum;
  68.     }
  69. }
  70.  
  71.  
  72. void KOctree::AddColor (BYTE r, BYTE g, BYTE b)
  73. {
  74.     KNode * pNode = pRoot;
  75.  
  76.     for (BYTE mask=0x80; mask!=0; mask>>=1) // follow the path until leaf node
  77.     {
  78.         // add pixel to it
  79.         pNode->Pixels ++;
  80.         pNode->SigmaRed   += r;
  81.         pNode->SigmaGreen += g;
  82.         pNode->SigmaBlue  += b;
  83.  
  84.         if ( pNode->IsLeaf )
  85.             break;
  86.  
  87.         // take one bit each of RGB to form an index
  88.         int index = ( (r & mask) ? 4 : 0 ) + ( (g & mask) ? 2 : 0 ) + ( (b & mask) ? 1 : 0 );
  89.         
  90.         // create a new node if it's a new branch
  91.         if ( pNode->Child[index]==NULL )
  92.         {
  93.             pNode->Child[index] = new KNode(mask==2);
  94.             TotalNode ++;
  95.  
  96.             if ( mask==2 )
  97.                 TotalLeaf ++;
  98.         }
  99.  
  100.         // follow the path
  101.         pNode = pNode->Child[index];
  102.     }
  103.  
  104.     for (int threshold=1; TotalNode>MAXMODE; threshold++ )
  105.         Reduce(pRoot, threshold);
  106. }
  107.  
  108.  
  109. // Combine node with leaf only child nodes, and no more than threshold pixels
  110. // Combine leaf node with no more than threshold pixels, into closest sibling
  111. void KOctree::Reduce(KNode * pTree, unsigned threshold)
  112. {
  113.     if ( pTree==NULL )
  114.         return;
  115.  
  116.     bool childallleaf = true;
  117.  
  118.     // recursively call all non-leaf child nodes
  119.     for (int i=0; i<8; i++)
  120.         if ( pTree->Child[i] && ! pTree->Child[i]->IsLeaf )
  121.         {
  122.             Reduce(pTree->Child[i], threshold);
  123.  
  124.             if ( ! pTree->Child[i]->IsLeaf )
  125.                 childallleaf = false;
  126.         }
  127.  
  128.     // if all children are leaves, combined is not big enough, combine them
  129.     if ( childallleaf & (pTree->Pixels<=threshold) )
  130.     {
  131.         for (int i=0; i<8; i++)
  132.             if ( pTree->Child[i] )
  133.             {
  134.                 delete pTree->Child[i];
  135.                 pTree->Child[i] = NULL;
  136.                 TotalNode --;
  137.                 TotalLeaf --;
  138.             }
  139.     
  140.         pTree->IsLeaf = true;
  141.         TotalLeaf ++;
  142.  
  143.         return;
  144.     }
  145.  
  146.     // merge small child leaf nodes
  147.     for (i=0; i<8; i++)
  148.         if ( pTree->Child[i] && pTree->Child[i]->IsLeaf && (pTree->Child[i]->Pixels<=threshold) )
  149.         {
  150.             KNode temp = * pTree->Child[i];
  151.  
  152.             delete pTree->Child[i];
  153.             pTree->Child[i] = NULL;
  154.             TotalNode --;
  155.             TotalLeaf --;
  156.         
  157.             for (int j=0; j<8; j++)
  158.                 if ( pTree->Child[j] )
  159.                 {
  160.                     Merge(pTree->Child[j], temp);
  161.                     break;
  162.                 }
  163.         }
  164. }
  165.  
  166.  
  167. void KOctree::Merge(KNode * pNode, KNode & target)
  168. {
  169.     while ( true )
  170.     {
  171.         pNode->Pixels     += target.Pixels;
  172.         pNode->SigmaRed   += target.SigmaRed;
  173.         pNode->SigmaGreen += target.SigmaGreen;
  174.         pNode->SigmaBlue  += target.SigmaBlue;
  175.  
  176.         if ( pNode->IsLeaf )
  177.             break;
  178.  
  179.         KNode * pChild = NULL;
  180.  
  181.         for (int i=0; i<8; i++)
  182.             if ( pNode->Child[i] )
  183.             {
  184.                 pChild = pNode->Child[i];
  185.                 break;
  186.             }
  187.  
  188.         if ( pChild==NULL )
  189.         {
  190.             assert(FALSE);
  191.             return;
  192.         }
  193.         else
  194.             pNode = pChild;
  195.     }
  196. }
  197.  
  198.  
  199. void KOctree::ReduceLeaves(int limit)
  200. {
  201.     for (unsigned threshold=1; TotalLeaf>limit; threshold++)
  202.         Reduce(pRoot, threshold);
  203. }
  204.  
  205.  
  206. int KOctree::GenPalette(RGBQUAD entry[], int * pFreq, int size)
  207. {
  208.     ReduceLeaves(size);
  209.     
  210.     return pRoot->PickLeaves(entry, pFreq, size);
  211. }
  212.  
  213.  
  214. int GenPalette(BITMAPINFO * pDIB, RGBQUAD * pEntry, int * pFreq, int size)
  215. {
  216.     KImage        dib;
  217.     KPaletteGen palgen;
  218.  
  219.     dib.AttachDIB(pDIB, NULL, 0);
  220.  
  221.     palgen.AddBitmap(dib);
  222.     
  223.     return palgen.GetPalette(pEntry, pFreq, size);
  224. }
  225.  
  226.  
  227. BITMAPINFO * KColorReduction::Convert8bpp(BITMAPINFO * pDIB)
  228. {
  229.     m_nBPS     = (pDIB->bmiHeader.biWidth + 3) / 4 * 4;    // scanline size for 8-bpp DIB
  230.     
  231.     int headsize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
  232.  
  233.     BITMAPINFO * pNewDIB = (BITMAPINFO *) new BYTE[headsize + m_nBPS * abs(
  234.         pDIB->bmiHeader.biHeight)];
  235.  
  236.     memset(pNewDIB, 0, headsize);
  237.  
  238.     pNewDIB->bmiHeader.biSize         = sizeof(BITMAPINFOHEADER); 
  239.     pNewDIB->bmiHeader.biWidth         = pDIB->bmiHeader.biWidth; 
  240.     pNewDIB->bmiHeader.biHeight         = pDIB->bmiHeader.biHeight;
  241.     pNewDIB->bmiHeader.biPlanes         = 1; 
  242.     pNewDIB->bmiHeader.biBitCount     = 8; 
  243.     pNewDIB->bmiHeader.biCompression = BI_RGB; 
  244.  
  245.     memset(pNewDIB->bmiColors, 0, 256 * sizeof(RGBQUAD));
  246.  
  247.     int freq[236];
  248.  
  249.     m_Matcher.Setup(GenPalette(pDIB, pNewDIB->bmiColors, freq, 236), pNewDIB->bmiColors);
  250.  
  251.     m_pBits  = (BYTE*) & pNewDIB->bmiColors[256];
  252.         
  253.     if ( pNewDIB==NULL )
  254.         return NULL;
  255.  
  256.     KImage dib;
  257.  
  258.     dib.AttachDIB(pDIB, NULL, 0);
  259.  
  260.     DWORD t1 = GetTickCount();
  261.  
  262.     dib.PixelTransform(* this);
  263.         
  264.     TCHAR temp[32];
  265.  
  266.     wsprintf(temp, _T("Time %d"), GetTickCount() - t1);
  267.     MessageBox(NULL, temp, _T("Convert8bpp"), MB_OK);
  268.  
  269.     return pNewDIB;
  270. }
  271.  
  272.  
  273. inline void ForwardDistribute(int error, int * curerror, int & nexterror)
  274. {
  275.     if ( (error<-2) || (error>2) ) // -2..2, not big enough
  276.     {
  277.         nexterror    = curerror[1] + error * 7 / 16;
  278.     
  279.         curerror[-1] += error * 3 / 16;        //            X   7/16
  280.         curerror[ 0] += error * 5 / 16;     //    3/16  5/16  1/16
  281.         curerror[ 1] += error     / 16;
  282.     }
  283.     else
  284.         nexterror = curerror[1];
  285. }
  286.  
  287. inline void BackwardDistribute(int error, int * curerror, int & nexterror)
  288. {
  289.     if ( (error<-2) || (error>2) ) // -2..2, not big enough
  290.     {
  291.         nexterror    = curerror[-1] + error * 7 / 16;
  292.     
  293.         curerror[ 1] += error * 3 / 16;        //    7/16   X  
  294.         curerror[ 0] += error * 5 / 16;     //    1/16  5/16  3/16
  295.         curerror[-1] += error     / 16;
  296.     }
  297.     else
  298.         nexterror = curerror[-1];
  299. }
  300.  
  301.  
  302.  
  303. BITMAPINFO * KErrorDiffusionColorReduction::Convert8bpp(BITMAPINFO * pDIB)
  304. {
  305.     int extwidth = pDIB->bmiHeader.biWidth + 2;
  306.  
  307.     int * error   = new int[extwidth*3];
  308.     memset(error, 0, sizeof(int) * extwidth * 3);
  309.  
  310.     red_error   = error + 1;
  311.     green_error = red_error   + extwidth;
  312.     blue_error  = green_error + extwidth;
  313.  
  314.     BITMAPINFO * pNew = KColorReduction::Convert8bpp(pDIB);
  315.  
  316.     delete [] error;
  317.  
  318.     return pNew;
  319. }
  320.  
  321.  
  322. void KErrorDiffusionColorReduction::Map24bpp(BYTE * pBuffer, int width)
  323. {
  324.     int next_red, next_green, next_blue;
  325.  
  326.     if ( m_bForward )
  327.     {
  328.         next_red   =   red_error[0];
  329.         next_green = green_error[0];
  330.         next_blue  =  blue_error[0];
  331.  
  332.         for (int i=0; i<width; i++)
  333.         {
  334.             int red   = pBuffer[2];
  335.             int green = pBuffer[1];
  336.             int blue  = pBuffer[0];
  337.  
  338.             BYTE match = m_Matcher.ColorMatch( red+next_red, green+next_green, blue+next_blue );
  339.             
  340.             ForwardDistribute(red   - m_Matcher.m_Colors[match].rgbRed  , red_error  +i, next_red);
  341.             ForwardDistribute(green - m_Matcher.m_Colors[match].rgbGreen, green_error+i, next_green);
  342.             ForwardDistribute(blue  - m_Matcher.m_Colors[match].rgbBlue,  blue_error +i, next_blue);
  343.             
  344.             * m_pPixel ++= match;
  345.  
  346.             pBuffer += 3;
  347.         }
  348.     }
  349.     else
  350.     {
  351.         next_red   =   red_error[width-1];
  352.         next_green = green_error[width-1];
  353.         next_blue  =  blue_error[width-1];
  354.  
  355.         pBuffer  += 3 * width - 3;
  356.         m_pPixel += width - 1;
  357.  
  358.         for (int i=width-1; i>=0; i--)
  359.         {
  360.             int red   = pBuffer[2];
  361.             int green = pBuffer[1];
  362.             int blue  = pBuffer[0];
  363.  
  364.             BYTE match = m_Matcher.ColorMatch( red+next_red, green+next_green, blue+next_blue );
  365.             
  366.             BackwardDistribute(red   - m_Matcher.m_Colors[match].rgbRed  , red_error  +i, next_red);
  367.             BackwardDistribute(green - m_Matcher.m_Colors[match].rgbGreen, green_error+i, next_green);
  368.             BackwardDistribute(blue  - m_Matcher.m_Colors[match].rgbBlue,  blue_error +i, next_blue);
  369.             
  370.             * m_pPixel --= match;
  371.  
  372.             pBuffer -= 3;
  373.         }
  374.     }        
  375. }
  376.  
  377.  
  378. BITMAPINFO * Convert8bpp(BITMAPINFO * pDIB)
  379. {
  380.     KColorReduction cnv;
  381.  
  382.     return cnv.Convert8bpp(pDIB);
  383. }
  384.  
  385. BITMAPINFO * Convert8bpp_ErrorDiffusion(BITMAPINFO * pDIB)
  386. {
  387.     KErrorDiffusionColorReduction cnv;
  388.  
  389.     return cnv.Convert8bpp(pDIB);
  390. }
  391.  
  392.  
  393. // Get to DIB color table
  394. const BYTE * GetColorTable(const BITMAPINFO * pDIB, int & nSize, int & nColor)
  395. {
  396.     const BYTE * pRGB;
  397.  
  398.     if ( pDIB->bmiHeader.biSize==sizeof(BITMAPCOREHEADER) )    // OS/2 BMP format
  399.     {
  400.         pRGB   = (const BYTE *) pDIB + sizeof(BITMAPCOREHEADER);
  401.         nSize  = sizeof(RGBTRIPLE);
  402.         nColor = 1 << ((BITMAPCOREHEADER *) pDIB)->bcBitCount;
  403.     }
  404.     else 
  405.     {
  406.         nColor = 0;
  407.  
  408.         if ( pDIB->bmiHeader.biBitCount<=8 )
  409.             nColor = 1 << pDIB->bmiHeader.biBitCount;
  410.  
  411.         if ( pDIB->bmiHeader.biClrUsed )
  412.             nColor = pDIB->bmiHeader.biClrUsed;
  413.  
  414.         if ( pDIB->bmiHeader.biClrImportant )
  415.             nColor = pDIB->bmiHeader.biClrImportant;
  416.  
  417.         pRGB  = (const BYTE *) & pDIB->bmiColors;
  418.         nSize = sizeof(RGBQUAD);
  419.         
  420.         if ( pDIB->bmiHeader.biCompression==BI_BITFIELDS )
  421.             pRGB += 3 * sizeof(RGBQUAD);
  422.     }
  423.  
  424.     if ( nColor>256 )
  425.         nColor = 256;
  426.  
  427.     if ( nColor==0 )
  428.         return NULL;
  429.  
  430.     return pRGB;
  431. }
  432.  
  433.  
  434. HPALETTE LUTCreatePalette(const BYTE * pRGB, int nSize, int nColor)
  435. {
  436.     if ( (pRGB==NULL) || (nColor==0) )
  437.         return NULL;
  438.  
  439.     LOGPALETTE * pLogPal = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * (nColor-1)];
  440.     HPALETTE     hPal;
  441.  
  442.     if ( pLogPal )
  443.     {
  444.         pLogPal->palVersion    = 0x0300;
  445.         pLogPal->palNumEntries = nColor;
  446.  
  447.         for (int i=0; i<nColor; i++)
  448.         {
  449.             pLogPal->palPalEntry[i].peBlue  = pRGB[0];
  450.             pLogPal->palPalEntry[i].peGreen = pRGB[1];
  451.             pLogPal->palPalEntry[i].peRed   = pRGB[2]; 
  452.             pLogPal->palPalEntry[i].peFlags = 0; 
  453.  
  454.             pRGB += nSize;
  455.         }
  456.  
  457.         hPal = CreatePalette(pLogPal);
  458.     }
  459.  
  460.     delete [] (BYTE *) pLogPal;
  461.     
  462.     return hPal;
  463. }
  464.  
  465.  
  466. // Create palette from Color table in a DIB
  467. HPALETTE CreateDIBPalette(const BITMAPINFO * pDIB)
  468. {
  469.     int       nSize;
  470.     int    nColor;
  471.  
  472.     const BYTE * pRGB = GetColorTable(pDIB, nSize, nColor);
  473.  
  474.     if ( pRGB==NULL )
  475.     {
  476.         RGBQUAD         RGB[256];
  477.         int             freq[256];
  478.  
  479.         nColor = GenPalette((BITMAPINFO *) pDIB, RGB, freq, 236);
  480.  
  481. #ifdef _DEBUG
  482.         for (int i=0; i<nColor; i++)
  483.         {
  484.             TCHAR temp[64];
  485.  
  486.             wsprintf(temp, _T("{ %d, %d, %d }, // %3d, %d\n"), RGB[i].rgbRed, RGB[i].rgbGreen, RGB[i].rgbBlue,
  487.                 i, freq[i]);
  488.             OutputDebugString(temp);
  489.         }
  490. #endif
  491.  
  492.         return LUTCreatePalette((BYTE *) RGB, sizeof(RGBQUAD), nColor);
  493.     }
  494.     else
  495.         return LUTCreatePalette(pRGB, nSize, nColor);
  496. }
  497.  
  498.  
  499. BITMAPINFO * IndexColorTable(BITMAPINFO * pDIB, HPALETTE hPal)
  500. {
  501.     int nSize;
  502.     int nColor;
  503.  
  504.     const BYTE * pRGB = GetColorTable(pDIB, nSize, nColor);
  505.  
  506.     if ( pDIB->bmiHeader.biBitCount>8 )    // no change
  507.         return pDIB;
  508.  
  509.     // allocate a new BITMAPINFO for modification
  510.     BITMAPINFO * pNew = (BITMAPINFO *) new BYTE[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*nColor];
  511.  
  512.     pNew->bmiHeader = pDIB->bmiHeader;
  513.  
  514.     WORD * pIndex = (WORD *) pNew->bmiColors;
  515.  
  516.     for (int i=0; i<nColor; i++, pRGB+=nSize)
  517.         if ( hPal )
  518.             pIndex[i] = GetNearestPaletteIndex(hPal, RGB(pRGB[2], pRGB[1], pRGB[0]));
  519.         else
  520.             pIndex[i] = i;
  521.  
  522.     return pNew;
  523. }