Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members   Related Pages  

gdaldither.cpp

00001 /******************************************************************************
00002  * $Id: gdaldither_cpp-source.html,v 1.2 2002/12/21 19:13:12 warmerda Exp $
00003  *
00004  * Project:  CIETMap Phase 2
00005  * Purpose:  Convert RGB (24bit) to a pseudo-colored approximation using
00006  *           Floyd-Steinberg dithering (error diffusion). 
00007  * Author:   Frank Warmerdam, warmerdam@pobox.com
00008  *
00009  ******************************************************************************
00010  * Copyright (c) 2001, Frank Warmerdam
00011  *
00012  * Permission is hereby granted, free of charge, to any person obtaining a
00013  * copy of this software and associated documentation files (the "Software"),
00014  * to deal in the Software without restriction, including without limitation
00015  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00016  * and/or sell copies of the Software, and to permit persons to whom the
00017  * Software is furnished to do so, subject to the following conditions:
00018  *
00019  * The above copyright notice and this permission notice shall be included
00020  * in all copies or substantial portions of the Software.
00021  *
00022  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00023  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00024  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00025  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00026  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00027  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00028  * DEALINGS IN THE SOFTWARE.
00029  ******************************************************************************
00030  *
00031  * Notes:
00032  *
00033  * [1] Floyd-Steinberg dither:
00034  *  I should point out that the actual fractions we used were, assuming
00035  *  you are at X, moving left to right:
00036  *
00037  *                  X     7/16
00038  *           3/16   5/16  1/16    
00039  *
00040  *  Note that the error goes to four neighbors, not three.  I think this
00041  *  will probably do better (at least for black and white) than the
00042  *  3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
00043  *  seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
00044  *  but I have no idea who the credit really belongs to.
00045  *  -- 
00046  *                                          Lou Steinberg
00047  *
00048  * $Log: gdaldither_cpp-source.html,v $
00048  * Revision 1.2  2002/12/21 19:13:12  warmerda
00048  * updated
00048  *
00049  * Revision 1.2  2001/07/18 04:43:13  warmerda
00050  * added CPL_CVSID
00051  *
00052  * Revision 1.1  2001/01/22 22:30:59  warmerda
00053  * New
00054  *
00055  */
00056 
00057 #include "gdal_priv.h"
00058 #include "gdal_alg.h"
00059 
00060 CPL_CVSID("$Id: gdaldither_cpp-source.html,v 1.2 2002/12/21 19:13:12 warmerda Exp $");
00061 
00062 #define C_LEVELS        32
00063 #define C_SHIFT         3
00064 
00065 static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap );
00066 
00067 /************************************************************************/
00068 /*                      GDALComputeMedianCutPCT()                       */
00069 /************************************************************************/
00070 
00071 int GDALDitherRGB2PCT( GDALRasterBandH hRed, 
00072                        GDALRasterBandH hGreen, 
00073                        GDALRasterBandH hBlue, 
00074                        GDALRasterBandH hTarget, 
00075                        GDALColorTableH hColorTable,
00076                        GDALProgressFunc pfnProgress, 
00077                        void * pProgressArg )
00078 
00079 {
00080     int         nXSize, nYSize;
00081     
00082 /* -------------------------------------------------------------------- */
00083 /*      Validate parameters.                                            */
00084 /* -------------------------------------------------------------------- */
00085     nXSize = GDALGetRasterBandXSize( hRed );
00086     nYSize = GDALGetRasterBandYSize( hRed );
00087 
00088     if( GDALGetRasterBandXSize( hGreen ) != nXSize 
00089         || GDALGetRasterBandYSize( hGreen ) != nYSize 
00090         || GDALGetRasterBandXSize( hBlue ) != nXSize 
00091         || GDALGetRasterBandYSize( hBlue ) != nYSize )
00092     {
00093         CPLError( CE_Failure, CPLE_IllegalArg,
00094                   "Green or blue band doesn't match size of red band.\n" );
00095 
00096         return CE_Failure;
00097     }
00098 
00099     if( GDALGetRasterBandXSize( hTarget ) != nXSize 
00100         || GDALGetRasterBandYSize( hTarget ) != nYSize )
00101     {
00102         CPLError( CE_Failure, CPLE_IllegalArg,
00103                   "GDALDitherRGB2PCT(): "
00104                   "Target band doesn't match size of source bands.\n" );
00105 
00106         return CE_Failure;
00107     }
00108 
00109     if( pfnProgress == NULL )
00110         pfnProgress = GDALDummyProgress;
00111 
00112 /* -------------------------------------------------------------------- */
00113 /*      Setup more direct colormap.                                     */
00114 /* -------------------------------------------------------------------- */
00115     int         nColors, anPCT[768], iColor;
00116 
00117     nColors = GDALGetColorEntryCount( hColorTable );
00118     for( iColor = 0; iColor < nColors; iColor++ )
00119     {
00120         GDALColorEntry  sEntry;
00121 
00122         GDALGetColorEntryAsRGB( hColorTable, iColor, &sEntry );
00123         
00124         anPCT[iColor    ] = sEntry.c1;
00125         anPCT[iColor+256] = sEntry.c2;
00126         anPCT[iColor+512] = sEntry.c3;
00127     }
00128     
00129 /* -------------------------------------------------------------------- */
00130 /*      Build a 24bit to 8 bit color mapping.                           */
00131 /* -------------------------------------------------------------------- */
00132     GByte       *pabyColorMap;
00133 
00134     pabyColorMap = (GByte *) CPLMalloc(C_LEVELS * C_LEVELS * C_LEVELS 
00135                                        * sizeof(int));
00136     
00137     FindNearestColor( nColors, anPCT, pabyColorMap );
00138 
00139 /* -------------------------------------------------------------------- */
00140 /*      Setup various variables.                                        */
00141 /* -------------------------------------------------------------------- */
00142     GByte       *pabyRed, *pabyGreen, *pabyBlue, *pabyIndex;
00143     int         *panError;
00144 
00145     pabyRed = (GByte *) CPLMalloc(nXSize);
00146     pabyGreen = (GByte *) CPLMalloc(nXSize);
00147     pabyBlue = (GByte *) CPLMalloc(nXSize);
00148 
00149     pabyIndex = (GByte *) CPLMalloc(nXSize);
00150 
00151     panError = (int *) CPLCalloc(sizeof(int),(nXSize+2) * 3);
00152 
00153 /* ==================================================================== */
00154 /*      Loop over all scanlines of data to process.                     */
00155 /* ==================================================================== */
00156     int         iScanline;
00157 
00158     for( iScanline = 0; iScanline < nYSize; iScanline++ )
00159     {
00160         int     nLastRedError, nLastGreenError, nLastBlueError, i;
00161 
00162 /* -------------------------------------------------------------------- */
00163 /*      Report progress                                                 */
00164 /* -------------------------------------------------------------------- */
00165         if( !pfnProgress( iScanline / (double) nYSize, NULL, pProgressArg ) )
00166         {
00167             CPLFree( pabyRed );
00168             CPLFree( pabyGreen );
00169             CPLFree( pabyBlue );
00170             CPLFree( panError );
00171             CPLFree( pabyIndex );
00172             CPLFree( pabyColorMap );
00173 
00174             CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" );
00175             return CE_Failure;
00176         }
00177 
00178 /* -------------------------------------------------------------------- */
00179 /*      Read source data.                                               */
00180 /* -------------------------------------------------------------------- */
00181         GDALRasterIO( hRed, GF_Read, 0, iScanline, nXSize, 1, 
00182                       pabyRed, nXSize, 1, GDT_Byte, 0, 0 );
00183         GDALRasterIO( hGreen, GF_Read, 0, iScanline, nXSize, 1, 
00184                       pabyGreen, nXSize, 1, GDT_Byte, 0, 0 );
00185         GDALRasterIO( hBlue, GF_Read, 0, iScanline, nXSize, 1, 
00186                       pabyBlue, nXSize, 1, GDT_Byte, 0, 0 );
00187 
00188 /* -------------------------------------------------------------------- */
00189 /*      Apply the error from the previous line to this one.             */
00190 /* -------------------------------------------------------------------- */
00191         for( i = 0; i < nXSize; i++ )
00192         {
00193             pabyRed[i] =   MAX(0,MIN(255,(pabyRed[i]   + panError[i*3+0+3])));
00194             pabyGreen[i] = MAX(0,MIN(255,(pabyGreen[i] + panError[i*3+1+3])));
00195             pabyBlue[i] =  MAX(0,MIN(255,(pabyBlue[i]  + panError[i*3+2+3])));
00196         }
00197 
00198         memset( panError, 0, sizeof(int) * (nXSize+2) * 3 );
00199 
00200 /* -------------------------------------------------------------------- */
00201 /*      Figure out the nearest color to the RGB value.                  */
00202 /* -------------------------------------------------------------------- */
00203         nLastRedError = 0;
00204         nLastGreenError = 0;
00205         nLastBlueError = 0;
00206 
00207         for( i = 0; i < nXSize; i++ )
00208         {
00209             int         iIndex, nError, nSixth, iRed, iGreen, iBlue;
00210             int         nRedValue, nGreenValue, nBlueValue;
00211 
00212             nRedValue =   MAX(0,MIN(255, pabyRed[i]   + nLastRedError));
00213             nGreenValue = MAX(0,MIN(255, pabyGreen[i] + nLastGreenError));
00214             nBlueValue =  MAX(0,MIN(255, pabyBlue[i]  + nLastBlueError));
00215 
00216             iRed   = nRedValue *   C_LEVELS   / 256;
00217             iGreen = nGreenValue * C_LEVELS / 256;
00218             iBlue  = nBlueValue *  C_LEVELS  / 256;
00219             
00220             iIndex = pabyColorMap[iRed + iGreen * C_LEVELS 
00221                                  + iBlue * C_LEVELS * C_LEVELS];
00222         
00223             pabyIndex[i] = iIndex;
00224 
00225 /* -------------------------------------------------------------------- */
00226 /*      Compute Red error, and carry it on to the next error line.      */
00227 /* -------------------------------------------------------------------- */
00228             nError = nRedValue - anPCT[iIndex    ];
00229             nSixth = nError / 6;
00230             
00231             panError[i*3    ] += nSixth;
00232             panError[i*3+6  ] = nSixth;
00233             panError[i*3+3  ] += nError - 5 * nSixth;
00234             
00235             nLastRedError = 2 * nSixth;
00236 
00237 /* -------------------------------------------------------------------- */
00238 /*      Compute Green error, and carry it on to the next error line.    */
00239 /* -------------------------------------------------------------------- */
00240             nError = nGreenValue - anPCT[iIndex+256];
00241             nSixth = nError / 6;
00242 
00243             panError[i*3  +1] += nSixth;
00244             panError[i*3+6+1] = nSixth;
00245             panError[i*3+3+1] += nError - 5 * nSixth;
00246             
00247             nLastGreenError = 2 * nSixth;
00248 
00249 /* -------------------------------------------------------------------- */
00250 /*      Compute Blue error, and carry it on to the next error line.     */
00251 /* -------------------------------------------------------------------- */
00252             nError = nBlueValue - anPCT[iIndex+512];
00253             nSixth = nError / 6;
00254             
00255             panError[i*3  +2] += nSixth;
00256             panError[i*3+6+2] = nSixth;
00257             panError[i*3+3+2] += nError - 5 * nSixth;
00258             
00259             nLastBlueError = 2 * nSixth;
00260         }
00261 
00262 /* -------------------------------------------------------------------- */
00263 /*      Write results.                                                  */
00264 /* -------------------------------------------------------------------- */
00265         GDALRasterIO( hTarget, GF_Write, 0, iScanline, nXSize, 1, 
00266                       pabyIndex, nXSize, 1, GDT_Byte, 0, 0 );
00267     }
00268 
00269 /* -------------------------------------------------------------------- */
00270 /*      Cleanup                                                         */
00271 /* -------------------------------------------------------------------- */
00272     CPLFree( pabyRed );
00273     CPLFree( pabyGreen );
00274     CPLFree( pabyBlue );
00275     CPLFree( pabyIndex );
00276     CPLFree( panError );
00277 
00278     CPLFree( pabyColorMap );
00279 
00280     pfnProgress( 1.0, NULL, pProgressArg );
00281 
00282     return CE_None;
00283 }
00284 
00285 /************************************************************************/
00286 /*                          FindNearestColor()                          */
00287 /*                                                                      */
00288 /*      Finear near PCT color for any RGB color.                        */
00289 /************************************************************************/
00290 
00291 static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap )
00292 
00293 {
00294     int         iBlue, iGreen, iRed;
00295     int         iColor;
00296 
00297 /* -------------------------------------------------------------------- */
00298 /*      Loop over all the cells in the high density cube.               */
00299 /* -------------------------------------------------------------------- */
00300     for( iBlue = 0; iBlue < C_LEVELS; iBlue++ )
00301     {
00302         for( iGreen = 0; iGreen < C_LEVELS; iGreen++ )
00303         {
00304             for( iRed = 0; iRed < C_LEVELS; iRed++ )
00305             {
00306                 int     nRedValue, nGreenValue, nBlueValue;
00307                 int     nBestDist = 768, nBestIndex = 0;
00308 
00309                 nRedValue   = (iRed * 255) / (C_LEVELS-1);
00310                 nGreenValue = (iGreen * 255) / (C_LEVELS-1);
00311                 nBlueValue  = (iBlue * 255) / (C_LEVELS-1);
00312 
00313                 for( iColor = 0; iColor < nColors; iColor++ )
00314                 {
00315                     int         nThisDist;
00316 
00317                     nThisDist = ABS(nRedValue   - panPCT[iColor    ]) 
00318                               + ABS(nGreenValue - panPCT[iColor+256])
00319                               + ABS(nBlueValue  - panPCT[iColor+512]);
00320 
00321                     if( nThisDist < nBestDist )
00322                     {
00323                         nBestIndex = iColor;
00324                         nBestDist = nThisDist;
00325                     }
00326                 }
00327                 
00328                 pabyColorMap[iRed + iGreen*C_LEVELS 
00329                             + iBlue*C_LEVELS*C_LEVELS] = nBestIndex;
00330             }
00331         }
00332     }
00333 }
00334 

Generated at Sat Dec 21 14:01:59 2002 for GDAL by doxygen1.2.3-20001105 written by Dimitri van Heesch, © 1997-2000