📄 bitimage.cpp
字号:
}
for (ColorUsage *entry = color_usage->lists [red][RedOffset] ;
entry != NULL ;
entry = entry->next [RedOffset])
{
if (entry->colors [BlueOffset] == blue
&& entry->colors [GreenOffset] == green)
{
return entry ;
}
}
return NULL ;
}
//
// Description:
// This function adds a new color value to the color hash table.
//
// Parameters:
// red, green, blue: The color value to search for.
//
void BitmapImage::AddColor (UBYTE1 red, UBYTE1 green, UBYTE1 blue)
{
// Create the new color entry.
ColorUsage *entry = new ColorUsage ;
memset (entry, 0, sizeof (*entry)) ;
entry->usage = 1 ;
entry->colors [RedOffset] = red ;
entry->colors [GreenOffset] = green ;
entry->colors [BlueOffset] = blue ;
// Add the new entry to each hash chain.
if (color_usage->lists [red][RedOffset] == NULL)
{
color_usage->lists [red][RedOffset] = entry ;
}
else
{
entry->next [RedOffset] = color_usage->lists [red][RedOffset] ;
color_usage->lists [red][RedOffset] = entry ;
}
if (color_usage->lists[green][GreenOffset] == NULL)
{
color_usage->lists [green][GreenOffset] = entry ;
}
else
{
entry->next [GreenOffset] = color_usage->lists [green][GreenOffset] ;
color_usage->lists [green][GreenOffset] = entry ;
}
if (color_usage->lists [blue][BlueOffset] == NULL)
{
color_usage->lists [blue][BlueOffset] = entry ;
}
else
{
entry->next [BlueOffset] = color_usage->lists [blue][BlueOffset] ;
color_usage->lists [blue][BlueOffset] = entry ;
}
++ color_usage->color_count ;
return ;
}
//
// Description:
// This function converts a 24-bit image to 8-bit.
//
// Parameters:
// image: The image to convert.
//
void BitmapImage::EightBitQuantization (const BitmapImage &image)
{
// If this is not a 24-bit image then there is no need to quantize.
// Instead, we make this a copy operation.
if (image.bit_count != 24)
{
*this = image ;
return ;
}
progress_function = image.progress_function ;
progress_data = image.progress_function ;
// Allocate space for the image.
SetSize (256, 8, image.image_width, image.image_height) ;
// Allocate temporary structures used for color quantization.
color_usage = new ColorUsageTable ;
memset (color_usage, 0, sizeof (*color_usage)) ;
color_areas = new ColorArea [256] ;
try
{
FindColorUsage (image) ;
}
catch (EGraphicsAbort &)
{
FreeColorQuantizationData () ;
return ;
}
catch (...)
{
FreeColorQuantizationData () ;
throw ;
}
// Set the first (zero'th) area to the entire color space.
color_areas [0].color_values [RedOffset].low = 0 ;
color_areas [0].color_values [RedOffset].high = 255 ;
color_areas [0].color_values [GreenOffset].low = 0 ;
color_areas [0].color_values [GreenOffset].high = 255 ;
color_areas [0].color_values [BlueOffset].low = 0 ;
color_areas [0].color_values [BlueOffset].high = 255 ;
color_areas [0].pixel_count = image_height * image_width ;
color_areas [0].color_count = color_usage->color_count ;
color_area_count = 1 ;
// Divide the color area in half.
SplitAreaInHalf (7, // Depth
0, // Retry Count
0, // Area Number
RedOffset) ; // Split Color
// It is possible that some of the areas could not be divided using the
// previous process. If we have some remaining colors we try to assign them
// to the blocks with the largest size in the colorspace.
while (color_area_count < 256)
{
int cb = 0 ;
// Search for the largest colorspace area.
unsigned int value
= SQUARE (color_areas [cb].color_values [RedOffset].high -
color_areas [cb].color_values [RedOffset].low)
+ SQUARE (color_areas [cb].color_values [GreenOffset].high -
color_areas [cb].color_values [GreenOffset].low)
+ SQUARE (color_areas [cb].color_values [BlueOffset].high -
color_areas [cb].color_values [BlueOffset].low)
* color_areas [cb].color_count ;
for (unsigned int ii = 1 ; ii < color_area_count ; ++ ii)
{
if (color_areas [ii].color_count > 1)
{
unsigned int newvalue
= SQUARE (color_areas [ii].color_values [RedOffset].high -
color_areas [ii].color_values [RedOffset].low)
+ SQUARE (color_areas [ii].color_values [GreenOffset].high -
color_areas [ii].color_values [GreenOffset].low)
+ SQUARE (color_areas [ii].color_values [BlueOffset].high -
color_areas [ii].color_values [BlueOffset].low)
* color_areas [ii].color_count ;
if (newvalue > value)
{
value = newvalue ;
cb = ii ;
}
}
}
// If we have not colors to divide then stop.
if (color_areas [cb].color_count == 1)
break ;
// Split this color block in half.
SplitAreaInHalf (0, // Depth
0, // Retry Count
cb, // Area Number
LargestColorRange (color_areas [cb])) ; // Split Color
}
for (unsigned int ii = 0 ; ii < color_area_count ; ++ ii)
{
CreateColor (ii) ;
}
// Assign colors to the image.
try
{
QuantizeSourceImage (image) ;
}
catch (EGraphicsAbort &)
{
FreeColorQuantizationData () ;
return ;
}
catch (...)
{
FreeColorQuantizationData () ;
throw ;
}
FreeColorQuantizationData () ;
return ;
}
//
// Description:
// This function finds the colors that are used and their frequency
// within a source image.
//
// Parameters:
// image: The source image.
//
void BitmapImage::FindColorUsage (const BitmapImage &image)
{
// Create a color entry for each distinct color.
const unsigned int climit = image_width * 3 ;
for (unsigned int rr = 0 ; rr < image_height ; ++ rr)
{
UBYTE1 *rowdata = &image.image_data [rr * image.row_width] ;
for (unsigned int cc = 0 ; cc < climit ; cc += 3 )
{
UBYTE1 red, green, blue ;
red = rowdata [cc + RedOffset] ;
green = rowdata [cc + GreenOffset] ;
blue = rowdata [cc + BlueOffset] ;
// If the color already exists in the table just increment
// its usage count. Otherwise add a new color entry for it.
ColorUsage *entry = FindColor (red, green, blue) ;
if (entry == NULL)
{
AddColor (red, green, blue) ;
}
else
{
++ entry->usage ;
}
}
CallProgressFunction (100 * rr/image_height, 1, 2) ;
}
CallProgressFunction (100, 1, 2) ;
return ;
}
//
// Description:
//
// This function frees all the dynamic data allocated during
// color quantization.
//
void BitmapImage::FreeColorQuantizationData ()
{
// Get rid of al the temporary storage.
for (unsigned int ii = 0 ; ii < 256 ; ++ ii)
{
ColorUsage *next ;
for (ColorUsage *entry = color_usage->lists [ii][RedOffset] ;
entry != NULL ;
entry = next)
{
next = entry->next [RedOffset] ;
delete entry ;
}
}
delete color_usage ; color_usage = NULL ;
delete [] color_areas ;
return ;
}
//
// Description:
//
// This function divides an area of the colorspace in half.
//
// Parameters:
// depth: The search depth
// retrydepth: The number of retries
// areaid: The area to split
// splitcolor: The color to split on.
//
void BitmapImage::SplitAreaInHalf (unsigned int depth,
unsigned int retrydepth,
unsigned int areaid,
unsigned int splitcolor)
{
if (color_areas [areaid].color_count == 1)
{
return ;
}
else if (color_areas [areaid].color_values [splitcolor].high
== color_areas [areaid].color_values [splitcolor].low)
{
if (retrydepth < 2)
{
SplitAreaInHalf (depth, retrydepth + 1, areaid, (splitcolor + 1) % 3) ;
}
return ;
}
unsigned int c1 = (splitcolor + 1) % 3 ;
unsigned int c2 = (splitcolor + 2) % 3 ;
unsigned int splitsize = color_areas [areaid].pixel_count / 2 ;
unsigned int splitpixelcount = 0 ;
unsigned int splitcolorcount = 0 ;
unsigned int newlimit ;
unsigned int newpixelcount ;
unsigned int newcolorcount ;
for (newlimit = color_areas [areaid].color_values [splitcolor].low ;
newlimit <= color_areas [areaid].color_values [splitcolor].high ;
++ newlimit)
{
newpixelcount = 0 ;
newcolorcount = 0 ;
for (ColorUsage *entry = color_usage->lists [newlimit][splitcolor] ;
entry != NULL ;
entry = entry->next [splitcolor])
{
if (entry->colors [c1] >= color_areas [areaid].color_values [c1].low
&& entry->colors [c1] <= color_areas [areaid].color_values [c1].high
&& entry->colors [c2] >= color_areas [areaid].color_values [c2].low
&& entry->colors [c2] <= color_areas [areaid].color_values [c2].high)
{
newpixelcount += entry->usage ;
++ newcolorcount ;
}
}
if (newcolorcount == color_areas [areaid].color_count)
{
// There is no way to split using this color.
if (retrydepth < 2)
{
SplitAreaInHalf (depth, retrydepth + 1, areaid, (splitcolor + 1) % 3) ;
}
return ;
}
else if (newcolorcount > color_areas [areaid].color_count)
{
throw EGraphicsException ("INTERNAL ERROR - Quantization area color count invalid") ;
}
if (splitpixelcount + newpixelcount >= splitsize)
{
if (splitpixelcount + newpixelcount != color_areas [areaid].pixel_count)
{
splitpixelcount += newpixelcount ;
splitcolorcount += newcolorcount ;
}
else
{
-- newlimit ;
}
color_areas [color_area_count] = color_areas [areaid] ;
color_areas [color_area_count].pixel_count = color_areas [areaid].pixel_count - splitpixelcount ;
color_areas [color_area_count].color_count = color_areas [areaid].color_count - splitcolorcount ;
color_areas [color_area_count].color_values [splitcolor].low = newlimit + 1 ;
++ color_area_count ;
color_areas [areaid].color_values [splitcolor].high = newlimit ;
color_areas [areaid].pixel_count = splitpixelcount ;
color_areas [areaid].color_count = splitcolorcount ;
if (depth > 0)
{
SplitAreaInHalf (depth - 1, 0, color_area_count - 1, LargestColorRange (color_areas [color_area_count-1])) ;
SplitAreaInHalf (depth - 1, 0, areaid, LargestColorRange (color_areas [areaid])) ;
}
return ;
}
else
{
splitpixelcount += newpixelcount ;
splitcolorcount += newcolorcount ;
}
}
throw EGraphicsException ("INTERNAL ERROR - Quantization area pixel count invalid") ;
}
//
// Description:
// This function creates a color from a color area then maps the colors
// in the source image to the new color map.
//
// Parameters:
// color: The new color index value
//
void BitmapImage::CreateColor (unsigned int color)
{
unsigned int red = 0 ;
unsigned int green = 0 ;
unsigned int blue = 0 ;
const int c0 = RedOffset ;
const int c1 = GreenOffset ;
const int c2 = BlueOffset ;
unsigned int itemcount = 0 ;
for (unsigned int cc = color_areas [color].color_values [c0].low ;
cc <= color_areas [color].color_values [c0].high ;
++ cc)
{
for (ColorUsage *entry = color_usage->lists [cc][c0] ;
entry != NULL ;
entry = entry->next [c0])
{
if (entry->colors [c1] >= color_areas [color].color_values [c1].low
&& entry->colors [c1] <= color_areas [color].color_values [c1].high
&& entry->colors [c2] >= color_areas [color].color_values [c2].low
&& entry->colors [c2] <= color_areas [color].color_values [c2].high)
{
red += entry->colors [RedOffset] * entry->usage ;
green += entry->colors [GreenOffset] * entry->usage ;
blue += entry->colors [BlueOffset] * entry->usage ;
itemcount += entry->usage ;
}
}
}
if (itemcount == 0)
return ;
color_map [color].red = (red + itemcount/2) / itemcount ;
color_map [color].green = (green + itemcount/2) / itemcount ;
color_map [color].blue = (blue + itemcount/2) / itemcount ;
return ;
}
//
// Description:
// This function finds the largest dimension of a color area.
//
// Parameters:
// area: The color area to use
//
int BitmapImage::LargestColorRange (ColorArea &area)
{
unsigned int deltared = area.color_values [RedOffset].high
- area.color_values [RedOffset].low ;
unsigned int deltagreen = area.color_values [GreenOffset].high
- area.color_values [GreenOffset].low ;
unsigned int deltablue = area.color_values [BlueOffset].high
- area.color_values [BlueOffset].low ;
if (deltared >= deltagreen && deltared >= deltablue)
return RedOffset ;
if (deltablue >= deltagreen && deltablue >= deltared)
return BlueOffset ;
return GreenOffset ;
}
//
// Description:
// This function returns the 8-bit quantized color value for and RGB color.
//
// Parameters:
// red, green, blue: The RGB value to convert
//
unsigned int BitmapImage::QuantizedColor (UBYTE1 red, UBYTE1 green, UBYTE1 blue)
{
for (unsigned int color = 0 ; color < color_area_count ; ++ color)
{
if (red >= color_areas [color].color_values [RedOffset].low
&& red <= color_areas [color].color_values [RedOffset].high
&& green >= color_areas [color].color_values [GreenOffset].low
&& green <= color_areas [color].color_values [GreenOffset].high
&& blue >= color_areas [color].color_values [BlueOffset].low
&& blue <= color_areas [color].color_values [BlueOffset].high)
{
return color ;
}
}
throw EGraphicsException ("INTERNAL ERROR - color not quantized") ;
DUMMY_RETURN // MSVC++ is too stupid to realize we can't get here.
}
//
// Description:
// This function converts the RGB color values in the source image
// to 8-bit quantized color values.
//
// Parameters:
// src: The source image
//
void BitmapImage::QuantizeSourceImage (const BitmapImage &src)
{
for (unsigned int rr = 0 ; rr < image_height ; ++ rr)
{
CallProgressFunction (rr * 100 / image_height, 2, 2) ;
UBYTE1 *srcdata = &src.image_data [rr * src.row_width] ;
UBYTE1 *dstdata = &image_data [rr * row_width] ;
for (unsigned int cc = 0 ; cc < image_width ; ++ cc)
{
UBYTE1 red = srcdata [3 * cc + RedOffset] ;
UBYTE1 green = srcdata [3 * cc + GreenOffset] ;
UBYTE1 blue = srcdata [3 * cc + BlueOffset] ;
dstdata [cc] = QuantizedColor (red, green, blue) ;
}
}
CallProgressFunction (100, 2, 2) ;
return ;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -