void R_InitColormaps(void) { dboolean COLORMAP = (W_CheckMultipleLumps("COLORMAP") > 1); int i; byte *palsrc, *palette; wad_file_t *colormapwad; if (W_CheckNumForName("C_START") >= 0 && W_CheckNumForName("C_END") >= 0) { firstcolormaplump = W_GetNumForName("C_START"); lastcolormaplump = W_GetNumForName("C_END"); numcolormaps = lastcolormaplump - firstcolormaplump; colormaps = Z_Malloc(sizeof(*colormaps) * numcolormaps, PU_STATIC, NULL); colormaps[0] = W_CacheLumpName("COLORMAP", PU_STATIC); for (i = 1; i < numcolormaps; i++) colormaps[i] = W_CacheLumpNum(i + firstcolormaplump, PU_STATIC); } else { colormaps = Z_Malloc(sizeof(*colormaps), PU_STATIC, NULL); colormaps[0] = W_CacheLumpName("COLORMAP", PU_STATIC); } colormapwad = lumpinfo[W_CheckNumForName("COLORMAP")]->wad_file; C_Output("Using the COLORMAP lump in %s file %s.", (colormapwad->type == IWAD ? "IWAD" : "PWAD"), uppercase(colormapwad->path)); // [BH] There's a typo in dcolors.c, the source code of the utility Id // Software used to construct the palettes and colormaps for DOOM (see // http://www.doomworld.com/idgames/?id=16644). When constructing colormap // 32, which is used for the invulnerability powerup, the traditional // Y luminance values are used (see http://en.wikipedia.org/wiki/YIQ), but a // value of 0.144 is used when it should be 0.114. So I've grabbed the // offending code from dcolor.c, corrected it, put it here, and now colormap // 32 is manually calculated rather than grabbing it from the colormap lump. // The resulting differences are minor. palsrc = palette = W_CacheLumpName("PLAYPAL", PU_CACHE); for (i = 0; i < 255; i++) { float red = *palsrc++ / 256.0f; float green = *palsrc++ / 256.0f; float blue = *palsrc++ / 256.0f; float gray = red * 0.299f + green * 0.587f + blue * 0.114f/*0.144f*/; grays[i] = FindNearestColor(palette, (int)(gray * 255.0f), (int)(gray * 255.0f), (int)(gray * 255.0f)); if (!COLORMAP) { gray = (1.0f - gray) * 255.0f; colormaps[0][32 * 256 + i] = FindNearestColor(palette, (int)gray, (int)gray, (int)gray); } } }
static byte *GenerateStretchTable(byte *palette, int pct) { byte *result; int x, y; int r, g, b; byte *col1; byte *col2; result = Z_Malloc(256 * 256, PU_STATIC, NULL); for (x=0; x<256; ++x) { for (y=0; y<256; ++y) { col1 = palette + x * 3; col2 = palette + y * 3; r = (((int) col1[0]) * pct + ((int) col2[0]) * (100 - pct)) / 100; g = (((int) col1[1]) * pct + ((int) col2[1]) * (100 - pct)) / 100; b = (((int) col1[2]) * pct + ((int) col2[2]) * (100 - pct)) / 100; result[x * 256 + y] = FindNearestColor(palette, r, g, b); } } return result; }
UINT32 cgfx_FindColor(CoreGfxBase *CoreGfxBase, struct CRastPort *rp, UINT32 c) { PixMap *psd = rp->crp_PixMap; //DPrintF("psd->PixType: %x\n", psd->pixtype); switch(psd->pixtype) { case PF_TRUECOLOR8888: return COLOR2PIXEL8888(c); case PF_TRUECOLORABGR: return COLOR2PIXELABGR(c); case PF_TRUECOLOR888: return COLOR2PIXEL888(c); case PF_TRUECOLOR565: return COLOR2PIXEL565(c); case PF_TRUECOLOR555: return COLOR2PIXEL555(c); case PF_TRUECOLOR1555: return COLOR2PIXEL1555(c); case PF_TRUECOLOR332: return COLOR2PIXEL332(c); case PF_TRUECOLOR233: return COLOR2PIXEL233(c); } //if (psd->ncolors == 2 && scrdev.pixtype != PF_PALETTE) return c & 1; return FindNearestColor(rp, (int)psd->ncolors, c); }
static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap, int nCLevels ) { int iBlue, iGreen, iRed; /* -------------------------------------------------------------------- */ /* Loop over all the cells in the high density cube. */ /* -------------------------------------------------------------------- */ for( iBlue = 0; iBlue < nCLevels; iBlue++ ) { for( iGreen = 0; iGreen < nCLevels; iGreen++ ) { for( iRed = 0; iRed < nCLevels; iRed++ ) { int nRedValue, nGreenValue, nBlueValue; nRedValue = (iRed * 255) / (nCLevels-1); nGreenValue = (iGreen * 255) / (nCLevels-1); nBlueValue = (iBlue * 255) / (nCLevels-1); int nBestIndex = FindNearestColor( nColors, panPCT, nRedValue, nGreenValue, nBlueValue ); pabyColorMap[iRed + iGreen*nCLevels + iBlue*nCLevels*nCLevels] = (GByte)nBestIndex; } } } }
PalettizedImage FloydSteinbergDither(RGBImage image, RGBPalette palette) { PalettizedImage result; result.width = image.width; result.height = image.height; result.pixels = malloc(sizeof(unsigned char) * result.width * result.height); { int x, y; for(y = 0; y < image.height; y++) { for(x = 0; x < image.width; x++) { RGBTriple* currentPixel = &(image.pixels[x + y*image.width]); unsigned char index = FindNearestColor(*currentPixel, palette); result.pixels[x + y*result.width] = index; { int error; compute_disperse(R); compute_disperse(G); compute_disperse(B); } } } } return result; }
byte V_Colorize (byte *playpal, int cr, byte source, boolean keepgray109) { vect rgb, hsv; extern int FindNearestColor(byte *palette, int r, int g, int b); // [crispy] preserve gray drop shadow in IWAD status bar numbers if (cr == CR_NONE || (keepgray109 && source == 109)) return source; rgb.x = playpal[3 * source + 0] / 255.; rgb.y = playpal[3 * source + 1] / 255.; rgb.z = playpal[3 * source + 2] / 255.; rgb_to_hsv(&rgb, &hsv); if (cr == CR_DARK) hsv.z *= 0.5; else if (cr == CR_GRAY) hsv.y = 0; else { // [crispy] hack colors to full saturation hsv.y = 1.0; if (cr == CR_GREEN) { // hsv.x = ((16.216 * hsv.z) + 100.784)/360.; hsv.x = 135./360.; } else if (cr == CR_GOLD) { // hsv.x = ((51.351 * hsv.z) + 8.648)/360.; hsv.x = 45./360.; } else if (cr == CR_RED) { hsv.x = 0.; } else if (cr == CR_BLUE) { hsv.x = 240./360.; } } hsv_to_rgb(&hsv, &rgb); rgb.x *= 255.; rgb.y *= 255.; rgb.z *= 255.; return FindNearestColor(playpal, (int) rgb.x, (int) rgb.y, (int) rgb.z); }
main() { int c; DPRINTF("%d\n", ((int)&stdpalette[1]) - (int)&stdpalette[0]); c = FindNearestColor(stdpalette, 224, 224, 224); DPRINTF("%d = %02x %02x %02x\n", c, stdpalette[c].r, stdpalette[c].g, stdpalette[c].b); }
void R_InitColormaps(void) { int lump; boolean COLORMAP = (W_CheckMultipleLumps("COLORMAP") > 1); // Load in the light tables, // 256 byte align tables. lump = W_GetNumForName("COLORMAP"); colormaps = (lighttable_t *)W_CacheLumpNum(lump, PU_STATIC); // [BH] There's a typo in dcolors.c, the source code of the utility Id // Software used to construct the palettes and colormaps for DOOM (see // http://www.doomworld.com/idgames/?id=16644). When constructing colormap // 32, which is used for the invulnerability powerup, the traditional // Y luminence values are used (see http://en.wikipedia.org/wiki/YIQ), but a // value of 0.144 is used when it should be 0.114. So I've grabbed the // offending code from dcolor.c, corrected it, put it here, and now colormap // 32 is manually calculated rather than grabbing it from the colormap lump. // The resulting differences are minor. { int i; float red, green, blue, gray; byte *palsrc, *palette; palsrc = palette = W_CacheLumpName("PLAYPAL", PU_CACHE); for (i = 0; i < 255; i++) { red = *palsrc++ / 256.0f; green = *palsrc++ / 256.0f; blue = *palsrc++ / 256.0f; gray = red * 0.299f + green * 0.587f + blue * 0.114f/*0.144f*/; grays[i] = FindNearestColor(palette, (int)(gray * 255.0f), (int)(gray * 255.0f), (int)(gray * 255.0f)); if (!COLORMAP) { gray = (1.0f - gray) * 255.0f; colormaps[32 * 256 + i] = FindNearestColor(palette, (int)gray, (int)gray, (int)gray); } } } }
void FindNearestColors(byte *palette) { int i; if (W_CheckMultipleLumps("PLAYPAL") > 1) for (i = 0; i < PALETTESIZE; ++i) nearestcolors[i] = FindNearestColor(palette, originalcolors[i].red, originalcolors[i].green, originalcolors[i].blue); else for (i = 0; i < PALETTESIZE; ++i) nearestcolors[i] = i; }
static byte *GenerateTintTable(byte *palette, int percent, byte filter[PALETTESIZE], int colors) { byte *result = Z_Malloc(PALETTESIZE * PALETTESIZE, PU_STATIC, NULL); int foreground, background; for (foreground = 0; foreground < PALETTESIZE; ++foreground) { if ((filter[foreground] & colors) || colors == ALL) { for (background = 0; background < PALETTESIZE; ++background) { byte *color1 = palette + background * 3; byte *color2 = palette + foreground * 3; int r, g, b; if (percent == ADDITIVE) { if ((filter[background] & BLUES) && !(filter[foreground] & WHITES)) { r = ((int)color1[0] * 25 + (int)color2[0] * 75) / 100; g = ((int)color1[1] * 25 + (int)color2[1] * 75) / 100; b = ((int)color1[2] * 25 + (int)color2[2] * 75) / 100; } else { r = MIN(color1[0] + color2[0], 255); g = MIN(color1[1] + color2[1], 255); b = MIN(color1[2] + color2[2], 255); } } else { r = ((int)color1[0] * percent + (int)color2[0] * (100 - percent)) / 100; g = ((int)color1[1] * percent + (int)color2[1] * (100 - percent)) / 100; b = ((int)color1[2] * percent + (int)color2[2] * (100 - percent)) / 100; } *(result + (background << 8) + foreground) = FindNearestColor(palette, r, g, b); } } else for (background = 0; background < PALETTESIZE; ++background) *(result + (background << 8) + foreground) = foreground; } return result; }
int GDALDitherRGB2PCTInternal( GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue, GDALRasterBandH hTarget, GDALColorTableH hColorTable, int nBits, GInt16* pasDynamicColorMap, /* NULL or at least 256 * 256 * 256 * sizeof(GInt16) bytes */ int bDither, GDALProgressFunc pfnProgress, void * pProgressArg ) { VALIDATE_POINTER1( hRed, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hGreen, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hBlue, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hTarget, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hColorTable, "GDALDitherRGB2PCT", CE_Failure ); int nXSize, nYSize; CPLErr err = CE_None; /* -------------------------------------------------------------------- */ /* Validate parameters. */ /* -------------------------------------------------------------------- */ nXSize = GDALGetRasterBandXSize( hRed ); nYSize = GDALGetRasterBandYSize( hRed ); if( GDALGetRasterBandXSize( hGreen ) != nXSize || GDALGetRasterBandYSize( hGreen ) != nYSize || GDALGetRasterBandXSize( hBlue ) != nXSize || GDALGetRasterBandYSize( hBlue ) != nYSize ) { CPLError( CE_Failure, CPLE_IllegalArg, "Green or blue band doesn't match size of red band.\n" ); return CE_Failure; } if( GDALGetRasterBandXSize( hTarget ) != nXSize || GDALGetRasterBandYSize( hTarget ) != nYSize ) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Target band doesn't match size of source bands.\n" ); return CE_Failure; } if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; /* -------------------------------------------------------------------- */ /* Setup more direct colormap. */ /* -------------------------------------------------------------------- */ int nColors, iColor; #ifdef USE_SSE2 int anPCTUnaligned[256+4]; /* 4 for alignment on 16-byte boundary */ int* anPCT = ALIGN_INT_ARRAY_ON_16_BYTE(anPCTUnaligned); #else int anPCT[256*4]; #endif nColors = GDALGetColorEntryCount( hColorTable ); if (nColors == 0 ) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Color table must not be empty.\n" ); return CE_Failure; } else if (nColors > 256) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Color table cannot have more than 256 entries.\n" ); return CE_Failure; } for( iColor = 0; iColor < nColors; iColor++ ) { GDALColorEntry sEntry; GDALGetColorEntryAsRGB( hColorTable, iColor, &sEntry ); CAST_PCT(anPCT)[4*iColor+0] = sEntry.c1; CAST_PCT(anPCT)[4*iColor+1] = sEntry.c2; CAST_PCT(anPCT)[4*iColor+2] = sEntry.c3; CAST_PCT(anPCT)[4*iColor+3] = 0; } #ifdef USE_SSE2 /* Pad to multiple of 8 colors */ int nColorsMod8 = nColors % 8; if( nColorsMod8 ) { for( iColor = 0; iColor < 8 - nColorsMod8; iColor ++) { anPCT[nColors+iColor] = anPCT[nColors-1]; } } #endif /* -------------------------------------------------------------------- */ /* Setup various variables. */ /* -------------------------------------------------------------------- */ GByte *pabyRed, *pabyGreen, *pabyBlue, *pabyIndex; GByte *pabyColorMap = NULL; int *panError; int nCLevels = 1 << nBits; ColorIndex* psColorIndexMap = NULL; pabyRed = (GByte *) VSIMalloc(nXSize); pabyGreen = (GByte *) VSIMalloc(nXSize); pabyBlue = (GByte *) VSIMalloc(nXSize); pabyIndex = (GByte *) VSIMalloc(nXSize); panError = (int *) VSICalloc(sizeof(int),(nXSize+2) * 3); if (pabyRed == NULL || pabyGreen == NULL || pabyBlue == NULL || pabyIndex == NULL || panError == NULL) { CPLError( CE_Failure, CPLE_OutOfMemory, "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" ); err = CE_Failure; goto end_and_cleanup; } if( pasDynamicColorMap == NULL ) { /* -------------------------------------------------------------------- */ /* Build a 24bit to 8 bit color mapping. */ /* -------------------------------------------------------------------- */ pabyColorMap = (GByte *) VSIMalloc(nCLevels * nCLevels * nCLevels * sizeof(GByte)); if( pabyColorMap == NULL ) { CPLError( CE_Failure, CPLE_OutOfMemory, "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" ); err = CE_Failure; goto end_and_cleanup; } FindNearestColor( nColors, anPCT, pabyColorMap, nCLevels); } else { pabyColorMap = NULL; if( nBits == 8 && (GIntBig)nXSize * nYSize <= 65536 ) { /* If the image is small enough, then the number of colors */ /* will be limited and using a hashmap, rather than a full table */ /* will be more efficient */ psColorIndexMap = (ColorIndex*)pasDynamicColorMap; memset(psColorIndexMap, 0xFF, sizeof(ColorIndex) * PRIME_FOR_65536); } else { memset(pasDynamicColorMap, 0xFF, 256 * 256 * 256 * sizeof(GInt16)); } } /* ==================================================================== */ /* Loop over all scanlines of data to process. */ /* ==================================================================== */ int iScanline; for( iScanline = 0; iScanline < nYSize; iScanline++ ) { int nLastRedError, nLastGreenError, nLastBlueError, i; /* -------------------------------------------------------------------- */ /* Report progress */ /* -------------------------------------------------------------------- */ if( !pfnProgress( iScanline / (double) nYSize, NULL, pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); err = CE_Failure; goto end_and_cleanup; } /* -------------------------------------------------------------------- */ /* Read source data. */ /* -------------------------------------------------------------------- */ GDALRasterIO( hRed, GF_Read, 0, iScanline, nXSize, 1, pabyRed, nXSize, 1, GDT_Byte, 0, 0 ); GDALRasterIO( hGreen, GF_Read, 0, iScanline, nXSize, 1, pabyGreen, nXSize, 1, GDT_Byte, 0, 0 ); GDALRasterIO( hBlue, GF_Read, 0, iScanline, nXSize, 1, pabyBlue, nXSize, 1, GDT_Byte, 0, 0 ); /* -------------------------------------------------------------------- */ /* Apply the error from the previous line to this one. */ /* -------------------------------------------------------------------- */ if( bDither ) { for( i = 0; i < nXSize; i++ ) { pabyRed[i] = (GByte) MAX(0,MIN(255,(pabyRed[i] + panError[i*3+0+3]))); pabyGreen[i] = (GByte) MAX(0,MIN(255,(pabyGreen[i] + panError[i*3+1+3]))); pabyBlue[i] = (GByte) MAX(0,MIN(255,(pabyBlue[i] + panError[i*3+2+3]))); } memset( panError, 0, sizeof(int) * (nXSize+2) * 3 ); } /* -------------------------------------------------------------------- */ /* Figure out the nearest color to the RGB value. */ /* -------------------------------------------------------------------- */ nLastRedError = 0; nLastGreenError = 0; nLastBlueError = 0; for( i = 0; i < nXSize; i++ ) { int iIndex, nError, nSixth; int nRedValue, nGreenValue, nBlueValue; nRedValue = MAX(0,MIN(255, pabyRed[i] + nLastRedError)); nGreenValue = MAX(0,MIN(255, pabyGreen[i] + nLastGreenError)); nBlueValue = MAX(0,MIN(255, pabyBlue[i] + nLastBlueError)); if( psColorIndexMap ) { GUInt32 nColorCode = MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue); GUInt32 nIdx = nColorCode % PRIME_FOR_65536; //int nCollisions = 0; //static int nMaxCollisions = 0; while( TRUE ) { if( psColorIndexMap[nIdx].nColorCode == nColorCode ) { iIndex = psColorIndexMap[nIdx].nIndex; break; } if( (int)psColorIndexMap[nIdx].nColorCode < 0 ) { psColorIndexMap[nIdx].nColorCode = nColorCode; iIndex = FindNearestColor( nColors, anPCT, nRedValue, nGreenValue, nBlueValue ); psColorIndexMap[nIdx].nIndex = (GByte) iIndex; break; } if( psColorIndexMap[nIdx].nColorCode2 == nColorCode ) { iIndex = psColorIndexMap[nIdx].nIndex2; break; } if( (int)psColorIndexMap[nIdx].nColorCode2 < 0 ) { psColorIndexMap[nIdx].nColorCode2 = nColorCode; iIndex = FindNearestColor( nColors, anPCT, nRedValue, nGreenValue, nBlueValue ); psColorIndexMap[nIdx].nIndex2 = (GByte) iIndex; break; } if( psColorIndexMap[nIdx].nColorCode3 == nColorCode ) { iIndex = psColorIndexMap[nIdx].nIndex3; break; } if( (int)psColorIndexMap[nIdx].nColorCode3 < 0 ) { psColorIndexMap[nIdx].nColorCode3 = nColorCode; iIndex = FindNearestColor( nColors, anPCT, nRedValue, nGreenValue, nBlueValue ); psColorIndexMap[nIdx].nIndex3 = (GByte) iIndex; break; } do { //nCollisions ++; nIdx+=257; if( nIdx >= PRIME_FOR_65536 ) nIdx -= PRIME_FOR_65536; } while( (int)psColorIndexMap[nIdx].nColorCode >= 0 && psColorIndexMap[nIdx].nColorCode != nColorCode && (int)psColorIndexMap[nIdx].nColorCode2 >= 0 && psColorIndexMap[nIdx].nColorCode2 != nColorCode&& (int)psColorIndexMap[nIdx].nColorCode3 >= 0 && psColorIndexMap[nIdx].nColorCode3 != nColorCode ); /*if( nCollisions > nMaxCollisions ) { nMaxCollisions = nCollisions; printf("nCollisions = %d for R=%d,G=%d,B=%d\n", nCollisions, nRedValue, nGreenValue, nBlueValue); }*/ } } else if( pasDynamicColorMap == NULL ) { int iRed = nRedValue * nCLevels / 256; int iGreen = nGreenValue * nCLevels / 256; int iBlue = nBlueValue * nCLevels / 256; iIndex = pabyColorMap[iRed + iGreen * nCLevels + iBlue * nCLevels * nCLevels]; } else { GUInt32 nColorCode = MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue); GInt16* psIndex = &pasDynamicColorMap[nColorCode]; if( *psIndex < 0 ) iIndex = *psIndex = FindNearestColor( nColors, anPCT, nRedValue, nGreenValue, nBlueValue ); else iIndex = *psIndex; } pabyIndex[i] = (GByte) iIndex; if( !bDither ) continue; /* -------------------------------------------------------------------- */ /* Compute Red error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nRedValue - CAST_PCT(anPCT)[4*iIndex+0]; nSixth = nError / 6; panError[i*3 ] += nSixth; panError[i*3+6 ] = nSixth; panError[i*3+3 ] += nError - 5 * nSixth; nLastRedError = 2 * nSixth; /* -------------------------------------------------------------------- */ /* Compute Green error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nGreenValue - CAST_PCT(anPCT)[4*iIndex+1]; nSixth = nError / 6; panError[i*3 +1] += nSixth; panError[i*3+6+1] = nSixth; panError[i*3+3+1] += nError - 5 * nSixth; nLastGreenError = 2 * nSixth; /* -------------------------------------------------------------------- */ /* Compute Blue error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nBlueValue - CAST_PCT(anPCT)[4*iIndex+2]; nSixth = nError / 6; panError[i*3 +2] += nSixth; panError[i*3+6+2] = nSixth; panError[i*3+3+2] += nError - 5 * nSixth; nLastBlueError = 2 * nSixth; } /* -------------------------------------------------------------------- */ /* Write results. */ /* -------------------------------------------------------------------- */ GDALRasterIO( hTarget, GF_Write, 0, iScanline, nXSize, 1, pabyIndex, nXSize, 1, GDT_Byte, 0, 0 ); } pfnProgress( 1.0, NULL, pProgressArg ); /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ end_and_cleanup: CPLFree( pabyRed ); CPLFree( pabyGreen ); CPLFree( pabyBlue ); CPLFree( pabyIndex ); CPLFree( panError ); CPLFree( pabyColorMap ); return err; }
int CPL_STDCALL GDALDitherRGB2PCT( GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue, GDALRasterBandH hTarget, GDALColorTableH hColorTable, GDALProgressFunc pfnProgress, void * pProgressArg ) { VALIDATE_POINTER1( hRed, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hGreen, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hBlue, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hTarget, "GDALDitherRGB2PCT", CE_Failure ); VALIDATE_POINTER1( hColorTable, "GDALDitherRGB2PCT", CE_Failure ); int nXSize, nYSize; CPLErr err = CE_None; /* -------------------------------------------------------------------- */ /* Validate parameters. */ /* -------------------------------------------------------------------- */ nXSize = GDALGetRasterBandXSize( hRed ); nYSize = GDALGetRasterBandYSize( hRed ); if( GDALGetRasterBandXSize( hGreen ) != nXSize || GDALGetRasterBandYSize( hGreen ) != nYSize || GDALGetRasterBandXSize( hBlue ) != nXSize || GDALGetRasterBandYSize( hBlue ) != nYSize ) { CPLError( CE_Failure, CPLE_IllegalArg, "Green or blue band doesn't match size of red band.\n" ); return CE_Failure; } if( GDALGetRasterBandXSize( hTarget ) != nXSize || GDALGetRasterBandYSize( hTarget ) != nYSize ) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Target band doesn't match size of source bands.\n" ); return CE_Failure; } if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; /* -------------------------------------------------------------------- */ /* Setup more direct colormap. */ /* -------------------------------------------------------------------- */ int nColors, anPCT[768], iColor; nColors = GDALGetColorEntryCount( hColorTable ); if (nColors == 0 ) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Color table must not be empty.\n" ); return CE_Failure; } else if (nColors > 256) { CPLError( CE_Failure, CPLE_IllegalArg, "GDALDitherRGB2PCT(): " "Color table cannot have more than 256 entries.\n" ); return CE_Failure; } for( iColor = 0; iColor < nColors; iColor++ ) { GDALColorEntry sEntry; GDALGetColorEntryAsRGB( hColorTable, iColor, &sEntry ); anPCT[iColor ] = sEntry.c1; anPCT[iColor+256] = sEntry.c2; anPCT[iColor+512] = sEntry.c3; } /* -------------------------------------------------------------------- */ /* Build a 24bit to 8 bit color mapping. */ /* -------------------------------------------------------------------- */ GByte *pabyColorMap; pabyColorMap = (GByte *) CPLMalloc(C_LEVELS * C_LEVELS * C_LEVELS * sizeof(int)); FindNearestColor( nColors, anPCT, pabyColorMap ); /* -------------------------------------------------------------------- */ /* Setup various variables. */ /* -------------------------------------------------------------------- */ GByte *pabyRed, *pabyGreen, *pabyBlue, *pabyIndex; int *panError; pabyRed = (GByte *) VSIMalloc(nXSize); pabyGreen = (GByte *) VSIMalloc(nXSize); pabyBlue = (GByte *) VSIMalloc(nXSize); pabyIndex = (GByte *) VSIMalloc(nXSize); panError = (int *) VSICalloc(sizeof(int),(nXSize+2) * 3); if (pabyRed == NULL || pabyGreen == NULL || pabyBlue == NULL || pabyIndex == NULL || panError == NULL) { CPLError( CE_Failure, CPLE_OutOfMemory, "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" ); err = CE_Failure; goto end_and_cleanup; } /* ==================================================================== */ /* Loop over all scanlines of data to process. */ /* ==================================================================== */ int iScanline; for( iScanline = 0; iScanline < nYSize; iScanline++ ) { int nLastRedError, nLastGreenError, nLastBlueError, i; /* -------------------------------------------------------------------- */ /* Report progress */ /* -------------------------------------------------------------------- */ if( !pfnProgress( iScanline / (double) nYSize, NULL, pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); err = CE_Failure; goto end_and_cleanup; } /* -------------------------------------------------------------------- */ /* Read source data. */ /* -------------------------------------------------------------------- */ GDALRasterIO( hRed, GF_Read, 0, iScanline, nXSize, 1, pabyRed, nXSize, 1, GDT_Byte, 0, 0 ); GDALRasterIO( hGreen, GF_Read, 0, iScanline, nXSize, 1, pabyGreen, nXSize, 1, GDT_Byte, 0, 0 ); GDALRasterIO( hBlue, GF_Read, 0, iScanline, nXSize, 1, pabyBlue, nXSize, 1, GDT_Byte, 0, 0 ); /* -------------------------------------------------------------------- */ /* Apply the error from the previous line to this one. */ /* -------------------------------------------------------------------- */ for( i = 0; i < nXSize; i++ ) { pabyRed[i] = (GByte) MAX(0,MIN(255,(pabyRed[i] + panError[i*3+0+3]))); pabyGreen[i] = (GByte) MAX(0,MIN(255,(pabyGreen[i] + panError[i*3+1+3]))); pabyBlue[i] = (GByte) MAX(0,MIN(255,(pabyBlue[i] + panError[i*3+2+3]))); } memset( panError, 0, sizeof(int) * (nXSize+2) * 3 ); /* -------------------------------------------------------------------- */ /* Figure out the nearest color to the RGB value. */ /* -------------------------------------------------------------------- */ nLastRedError = 0; nLastGreenError = 0; nLastBlueError = 0; for( i = 0; i < nXSize; i++ ) { int iIndex, nError, nSixth, iRed, iGreen, iBlue; int nRedValue, nGreenValue, nBlueValue; nRedValue = MAX(0,MIN(255, pabyRed[i] + nLastRedError)); nGreenValue = MAX(0,MIN(255, pabyGreen[i] + nLastGreenError)); nBlueValue = MAX(0,MIN(255, pabyBlue[i] + nLastBlueError)); iRed = nRedValue * C_LEVELS / 256; iGreen = nGreenValue * C_LEVELS / 256; iBlue = nBlueValue * C_LEVELS / 256; iIndex = pabyColorMap[iRed + iGreen * C_LEVELS + iBlue * C_LEVELS * C_LEVELS]; pabyIndex[i] = (GByte) iIndex; /* -------------------------------------------------------------------- */ /* Compute Red error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nRedValue - anPCT[iIndex ]; nSixth = nError / 6; panError[i*3 ] += nSixth; panError[i*3+6 ] = nSixth; panError[i*3+3 ] += nError - 5 * nSixth; nLastRedError = 2 * nSixth; /* -------------------------------------------------------------------- */ /* Compute Green error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nGreenValue - anPCT[iIndex+256]; nSixth = nError / 6; panError[i*3 +1] += nSixth; panError[i*3+6+1] = nSixth; panError[i*3+3+1] += nError - 5 * nSixth; nLastGreenError = 2 * nSixth; /* -------------------------------------------------------------------- */ /* Compute Blue error, and carry it on to the next error line. */ /* -------------------------------------------------------------------- */ nError = nBlueValue - anPCT[iIndex+512]; nSixth = nError / 6; panError[i*3 +2] += nSixth; panError[i*3+6+2] = nSixth; panError[i*3+3+2] += nError - 5 * nSixth; nLastBlueError = 2 * nSixth; } /* -------------------------------------------------------------------- */ /* Write results. */ /* -------------------------------------------------------------------- */ GDALRasterIO( hTarget, GF_Write, 0, iScanline, nXSize, 1, pabyIndex, nXSize, 1, GDT_Byte, 0, 0 ); } pfnProgress( 1.0, NULL, pProgressArg ); /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ end_and_cleanup: CPLFree( pabyRed ); CPLFree( pabyGreen ); CPLFree( pabyBlue ); CPLFree( pabyIndex ); CPLFree( panError ); CPLFree( pabyColorMap ); return err; }