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