|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Ads Donate Humor |
|
|
SDib: VC++ class for palette manipulation, color cyclingSDib is a Visual C++ .NET conversion from an OWL-derived class of the same name. It is not yet as capable as the old version, but it has at least one feature (random gradient color palettes) that the old version doesn't have. It holds a 256-color palette and provides functions for creating random palettes with abrupt or gradient color transitions, and color cycling (palette rotation, animation). Some of the code is commented out and non-functional because it hasn't yet been needed for the projects currently being converted to MSVC. |
|
/* sdib.h 6-26-2008 MSVC++
Copyright (C)1999, 2000, 2006, 2008 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------
Notes:
SDib was originally an extension of the Borland OWL TDib, with additional member functions for palette manipulation.
This preliminary conversion to MS Visual C++ was mainly done so I could use
color cycling palette rotation in other programs being converted.
The color palette is currently just a 256 array of Color.
Have not yet decided whether to bring a Bitmap member into this class
or to try to derive it from Bitmap, or leave the Bitmap completely separate.
Large sections of this code are commented out because they aren't yet needed
and remain unconverted.
------
To do:
*/
#ifndef __SDIB_H
#define __SDIB_H
//////////////////////////////////////////////////////////////////////////////
class SDib //: public TDib
{
public:
SDib(int width = 0, int height = 0);
SDib(const string& filename);
//SDib(const SDib& other); // copy constructor
~SDib();
// functions
// BOOLs return TRUE if they did what you wanted, or FALSE if palette was locked.
BOOL IsOK() { return TRUE; }
//TRect Rect() const { return TRect(0,0,Width(),Height()); }
//BOOL Erase(Color color = Color::Black); // erase to black (or nearest to black?)
BOOL GrayScale(); // set palette to gray scale
long RandomizeField(double percent = 100.); // set dots to random colors
BOOL RandomizeHeights(); // reassign "heights"
BOOL RandomizeColors(int startpos = 0); // set palette to all random colors
BOOL RotateColors(int amount = 1); // rotate the palette in either direction
int LoadColors(const string& filename); // load colors from a .MAP file
BOOL WriteColors(const string& filename); // write the palette to a file
BOOL CopyColors(SDib& other); // copy the colors FROM the given dib
BOOL ReverseColors(); // reverse the order of the colors in the palette
BOOL ComplementColors(); // change each color to its complement
BOOL LightenColors(); // increase each rgb component by 1, max 255
BOOL DarkenColors(); // decrease each rgb component by 1, min 0
//BOOL WriteFilePIC(const string& filename, TRect r = TRect(0,0,0,0)) const;
BOOL AveragePixels(BOOL creep = FALSE);
BOOL InvertHeights();
BOOL FlipVertical();
BOOL FlipHorizontal();
uchar GetNextRandomWalkColor(int currentcolor, int maxstep);
BOOL RandomWalkColors(int start = 0);
// CALCULATE OFFSET (FROM START OF BITS) OF PIXEL(X,Y)'S DATA
long GetPixelOffset(uint x, uint y) const;
// THESE ARE EXPERIMENTAL, AND COULD BE DANGEROUS TO PUT TO GENERAL USE,
// SINCE THEY DIRECTLY ACCESS THE BITS ARRAY, BYPASSING ALL ERROR CHECKING.
// AND THEY APPLY ONLY TO 8-BIT 256-COLOR COLOR TABLES.
// 1/18/2006 However, I did over time put them into "general use" because their speed and
// other improvements were too great to do without. Have had no problems with them,
// but I DO only use 8-BIT 256-COLOR COLOR TABLES. They won't work for anything else,
// and removal of all "apparent" support for any other types of color tables
// is in the "to do" list for this class, at the top of this file.
uchar GetPixelByte(uint x, uint y);
//uchar GetPixelByte(const TPoint& p) const { return(GetPixelByte(p.x,p.y)); }
void SetPixelByte(uint x, uint y, uchar value);
//void SetPixelByte(const TPoint& p, uchar value) { SetPixelByte(p.x,p.y,value); }
BOOL GetScanLineBytes(uint row, uchar* destination) const;
BOOL SetScanLineBytes(uint row, uchar* source);
// overridden TDib functions
Color GetColor(int i) { return Colors[i]; }
BOOL SetColor(int entry, Color color);
//BOOL SetIndex(int entry, WORD index);
int MapColor(Color fromColor, Color toColor, BOOL doAll = FALSE);
//int MapIndex(WORD fromIndex, WORD toIndex, BOOL doAll = FALSE);
// variables
// if Locked is TRUE, palette-changing (and maybe some other) functions do nothing.
// Its main purpose is to allow the prevention of palette changes
// while an application is drawing to the dib, especially when the drawing is done
// with multiple calls to IdleAction, between which the user could issue palette-
// changing commands which must be ignored.
//Bitmap^ dib; // holds currently displayed design
gcroot<array<Color>^> Colors; // Any # ok, and will work with indexed or true color Bitmaps
BOOL Locked;
BOOL palettechanged; // this class mainly sets it FALSE at specific times,
// and never tests for it. you should set it TRUE externally
// according to what constitutes a sufficient change for the app.
string Title; // for general use: to hold file it came from, or whatever.
protected:
// overridden TDib functions
//void InfoFromHandle();
//BOOL LoadFile(const char* name);
//BOOL LoadResource(HINSTANCE instance, TResID resID);
//BOOL Read(TFile& file, long offBits = 0);
// variables
};
//----------------------------------------------------------------------------
// end class SDib
//////////////////////////////////////////////////////////////////////////////
// PROTOTYPES FOR GLOBAL FUNCTIONS USING SDIB
//----------------------------------------------------------------------------
SDib* DibResize(const SDib* source, int width = 0, int height = 0, BOOL copypixels = FALSE);
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
#endif // __SDIB_H
/* sdib.cpp 6-26-2008 VC++ Version
Copyright (C)1997-2001, 2006, 2008 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "sdib.h"
//----------------------------------------------------------------------------
// constructor
SDib::SDib(int width, int height) //:TDib(width, height)
{
//dib = gcnew Bitmap(width,height,System::Drawing::Imaging::PixelFormat::Format8bppIndexed);
Colors = gcnew array<Color>(256);
Locked = palettechanged = FALSE;
GrayScale(); // start with gray scale palette (it includes Black at 0)
//Erase(); // overwrite random data
}
//----------------------------------------------------------------------------
// filename constructor
//SDib::SDib(const string& filename) : TDib(filename.c_str())
//{
//Locked = palettechanged = FALSE;
//}
//----------------------------------------------------------------------------
// copy constructor
//SDib::SDib(const SDib& other) : TDib(other)
//{
//Locked = other.Locked;
//palettechanged = other.palettechanged;
//}
//----------------------------------------------------------------------------
SDib::~SDib()
{
//if(dib) delete dib;
//if(Colors) delete Colors; //reminder that this is not necessary or allowed
}
//----------------------------------------------------------------------------
// copy all colors from another dib
BOOL SDib::CopyColors(SDib& other)
{
if(Locked || !IsOK())
return(FALSE);
for(int i = 0 ; i < 256 ; i++)
SetColor(i,other.GetColor(i));
palettechanged = FALSE;
return(TRUE);
}
//----------------------------------------------------------------------------
// set the palette to a gray scale
BOOL SDib::GrayScale()
{
if(Locked || !IsOK())
return(FALSE);
for(int i = 0 ; i < 256 ; i++)
SetColor(i,Color::FromArgb(255,i,i,i));
palettechanged = FALSE;
return(TRUE);
}
//----------------------------------------------------------------------------
// Assign random colors to all elements of our palette starting at index position (start)
// you can use start to preserve preset colors you want to save.
BOOL SDib::RandomizeColors(int start)
{
if(Locked || !IsOK())
return(FALSE);
Color newcolor;
for(int i = start ; i < 256 ; i++)
{
BOOL dup;
do
{
newcolor = Color::FromArgb(255,random(256),random(256),random(256));
dup = FALSE;
for(int j = 0 ; j < i ; j++) // compare against ALL preceding colors
if(GetColor(j) == newcolor) // disallow duplicating already-assigned
{ // colors (below this one)
dup = TRUE;
break;
}
}
while(dup);
SetColor(i,newcolor);
}
palettechanged = FALSE; // very unlikely to want to save a random palette
return(TRUE);
} //RandomizeColors
//----------------------------------------------------------------------------
//returns a random color component (0-255) that is a random walk from the given one and wrapped into legal range for a color.
//MaxStep limits the maximum step.
uchar SDib::GetNextRandomWalkColor(int b, int MaxStep)
{
//As noted below, MaxStep here must be >= 2 so random(MaxStep) CAN potentially be > 0, but it is not a problem HERE if it IS 0.
int c = b + random(MaxStep);
if(c < 0) // wrap into range
c += 256;
if(c > 255)
c -= 256;
return (uchar)c;
} //GetNextRandomColor
//----------------------------------------------------------------------------
//Creates a color palette in which the R, G, and B color values do independent random walks
//(in one direction only, upward) from random starting values. The result is a palette
//with gradients in it. The colors gradually migrate unpredictably in steps of varying
//amounts. The result is much like a hand-designed gradient palette, without any hand designing,
//and with some random "creativity" with respect to the colors that a human probably wouldn't think of.
BOOL SDib::RandomWalkColors(int start)
{
if(Locked || !IsOK())
return(FALSE);
//Set a color as the initial starting point from which to random walk.
Color newcolor = Color::FromArgb(255,random(256),random(256),random(256));
Color prevcolor = newcolor;
//each color has a different MaxStep, so they jump by different average amounts and cover unequal portions of their possible ranges,
//which gives each palette a different overall hue.
//maxincrement determines how fine the gradations will be -
//a small number creates a palette with fine gradations so you can hardly tell one band from the next.
//a large number makes the color jumps more abrupt and wild.
//In general, a palette works best if at least one large section of it has very fine gradations.
int maxincrement = 2 + random(12); // master switch limits size of step any color can take. was 8. Abs min = 2.
int RedMaxStep = 2 + random(maxincrement); // These are used in random(n), so cannot be 0 or 1.
int GreenMaxStep = 2 + random(maxincrement); // In the case where all 3 were 0/1, the loop below would hang the program.
int BlueMaxStep = 2 + random(maxincrement); // But in any one pass below, it's ok if the end result is 0 and an individual color component doesn't change.
BOOL dup;
for(int i = start ; i < 256 ; i++)
{
do
{
newcolor = Color::FromArgb(255,
GetNextRandomWalkColor(prevcolor.R,RedMaxStep),
GetNextRandomWalkColor(prevcolor.G,GreenMaxStep),
GetNextRandomWalkColor(prevcolor.B,BlueMaxStep) );
dup = FALSE;
for(int j = 0 ; j < i ; j++) // compare against ALL preceding colors
if(GetColor(j) == newcolor) // disallow duplicating already-assigned
{ // colors (below this one)
dup = TRUE;
break;
}
}
while(dup);
SetColor(i,newcolor);
prevcolor = newcolor;
}
palettechanged = FALSE; // very unlikely to want to save a random palette
return(TRUE);
}
//----------------------------------------------------------------------------
// Loads color palette from a Fractint-format .MAP file, Format: r g b possiblecomment\n
// If file is a valid .map file, but too short, colors are reused in order.
// If file isn't a valid .map file, colors aren't changed.
// returns the number of colors actually read from the file (not counting any reused)
int SDib::LoadColors(const string& filename)
{
if(Locked || !IsOK())
return(0); // note Locked return is same as for bad file read
int i, r(0), g(0), b(0); // initial values in case of file read error
string t; // comment in file
ifstream infile(filename.c_str());
for(i = 0 ; i < 256 ; i++) // only reads as many colors as the dib has
{
if(!(infile >> r >> g >> b)) // file too short
{
if(i == 0) // couldn't even get 1 good record
return(0); // quit trying
break; // we read some colors that we can repeat to fill
}
SetColor(i,Color::FromArgb(255,r,g,b));
getline(infile,t,'\n'); // read and ignore rest of line: comment
}
for(int j = i ; j < 256 ; j++) // fill in any missing colors,
SetColor(j,GetColor(j - i)); // copying from colors[0] forward
palettechanged = FALSE;
return(i);
} //LoadColors
//----------------------------------------------------------------------------
// Save the current colors into a Fractint-compatible .MAP file
BOOL SDib::WriteColors(const string& filename)
{
if(!IsOK())
return(FALSE);
ofstream outfile(filename.c_str());
for(int i = 0 ; i < 256 ; i++)
{
Color c = GetColor(i);
outfile << setw(3) << (int)(c.R) << " "
<< setw(3) << (int)(c.G) << " "
<< setw(3) << (int)(c.B) << endl;
}
palettechanged = FALSE;
return(!outfile.fail());
} //WriteColors
//----------------------------------------------------------------------------
// Rotate colors by given amount in the direction of the sign of count.
BOOL SDib::RotateColors(int count) // default is forward by 1
{
if(!count || Locked || !IsOK())
return(FALSE);
Color c;
int i;
if(count > 0)
{
for(int k = 0 ; k < count ; k++)
{
c = GetColor(255); // save top color
for(i = 255 ; i > 0 ; i--)
SetColor(i,GetColor(i-1));
SetColor(0,c); // set first color
}
}
else
{
for(int k = 0 ; k > count ; k--)
{
c = GetColor(0); // save first color
for(i = 0 ; i < 255 ; i++)
SetColor(i,GetColor(i+1));
SetColor(255, c); // set top color
}
}
return(TRUE);
} //RotateColors
//----------------------------------------------------------------------------
// reverse the order of the colors in the palette
BOOL SDib::ReverseColors()
{
if(Locked || !IsOK())
return(FALSE);
int top = 255;
int half = 255 / 2;
Color c;
for(int i = 0 ; i < half ; i++)
{
c = GetColor(i);
SetColor(i,GetColor(top - i));
SetColor(top - i,c);
}
return(TRUE);
} // ReverseColors
//----------------------------------------------------------------------------
// Erase: fill the dib with given color.
//BOOL SDib::Erase(Color color)
//{
//if(Locked || !IsOK())
// return(FALSE);
//return(TDibDC(*this).FillRect(Rect(),TBrush(color)));
//} // Erase
//----------------------------------------------------------------------------
// change each color to its complementary color
BOOL SDib::ComplementColors()
{
if(Locked || !IsOK())
return(FALSE);
Color c;
for(int i = 0 ; i < 256 ; i++)
{
c = GetColor(i);
SetColor(i,Color::FromArgb(255,255 - c.R, 255 - c.G, 255 - c.B));
}
return(TRUE);
} // ComplementColors
//----------------------------------------------------------------------------
// increase each rgb component by 1
BOOL SDib::LightenColors()
{
if(Locked || !IsOK())
return(FALSE);
Color c;
// int red, green, blue;
for(int i = 0 ; i < 256 ; i++)
{
c = GetColor(i);
// old: slow
// after any component reaches 255, the color balance also gets affected
// red = min((int)c.R + 1, 255);
// green = min((int)c.G + 1, 255);
// blue = min((int)c.B + 1, 255);
// SetColor(i,Color::FromArgb(255,red, green, blue));
// new: after 256 calls, you do get back where you started
SetColor(i,Color::FromArgb(255,(uchar)((int)c.R + 1), (uchar)((int)c.G + 1), (uchar)((int)c.B + 1)));
}
return(TRUE);
} // LightenColors
//----------------------------------------------------------------------------
// decrease each rgb component by 1
BOOL SDib::DarkenColors()
{
if(Locked || !IsOK())
return(FALSE);
Color c;
// int red, green, blue;
for(int i = 0 ; i < 256 ; i++)
{
c = GetColor(i);
// old: slow
// after any component reaches 0, the color balance also gets affected
// red = max((int)c.R - 1, 0);
// green = max((int)c.G - 1, 0);
// blue = max((int)c.B - 1, 0);
// SetColor(i,Color::FromArgb(255,red, green, blue));
// new: after 256 calls, you do get back where you started
SetColor(i,Color::FromArgb(255,(uchar)((int)c.R - 1), (uchar)((int)c.G - 1), (uchar)((int)c.B - 1)));
}
return(TRUE);
} // DarkenColors
//----------------------------------------------------------------------------
// set all pixels to random colors from the palette.
// returns the number of pixels that were set. (wlife2d.cpp uses this as the starting population)
// percent specifies % (0 to 100) to fill with random dots, and is double to allow < 1%.
//long SDib::RandomizeField(double percent)
//{
//if(!IsOK() || Locked || (percent <= 0.))
// return(0);
//
//uchar HUGE* bits = (uchar HUGE*)GetBits();
//uchar HUGE* loc;
//
//percent = min(percent,100.);
//if(percent == 100.)
//{
// for(int y = 0 ; y < Height() ; y++)
// {
// loc = bits + GetPixelOffset(0,y); // address of first pixel in row y
// for(int x = 0 ; x < Width() ; x++, loc++)
// *loc = (uchar)random(256);
// }
// // old: worked, but SLOW
// // for(int y = 0 ; y < Height() ; y++)
// // for(int x = 0 ; x < Width() ; x++)
// // dc.SetPixel(x,y,GetColor(random((int)256)));
// return(Width() * Height());
//}
//// assumes GetColor(0) is background color, and GetColor(1) is dot color.
//Erase(GetColor(0)); // start blank
//double dibdots = (double)Width() * Height();
//long pointsneeded = dibdots * percent / 100.;
//for(long population = 0; population < pointsneeded ; )
//{
// loc = bits + GetPixelOffset(random(Width()),random(Height()));
// if(*loc == 0) // only if pixel is background (not already hit)
// {
// // a new function parameter topval would allow
// // *loc = 1 + (uchar)random(topval - 1); (?)
// // to create a random field containing random states, for automata with > 2 states.
// *loc = 1; // set it to hit color
// population++; // and increment count
// }
//}
//return(population);
//} // RandomizeField
//----------------------------------------------------------------------------
// (moved here from wavgpic.cpp) Randomly reassign the heights of the pixels: not just the colors.
// Every occurrence of one height is transformed to the same different height.
// i.e. shuffle the height values. It addresses this problem:
// In DOS, the pixel color values were used AS heights, so shuffling them also
// randomized the heights and created a whole new starting point for the averaging.
// Here, color and height are separate. absolute heights continually grow closer
// together, flattening the design, even if you randomize the colors, and the
// board becomes stable after a small number of passes.
//BOOL SDib::RandomizeHeights()
//{
//if(!IsOK() || Locked)
// return(FALSE);
//
//struct CARD // from textbali.cpp: initialize and shuffle the deck for a new game
//{
// CARD(uchar lett = 0) : letter(lett) { sequence = random(30000); }
// BOOL operator == (const CARD& other) const { return(letter == other.letter); }
// BOOL operator < (const CARD& other) const { return(sequence < other.sequence); }
//
// uchar letter; // the alphabetic letter on the face of the card
// uint sequence; // a random number: when array is sorted by them, it is shuffled
//};
//TSArrayAsVector<CARD> cards(256,0,1); // create a card for each elevation 0-255, and
//for(int i = 0 ; i < 256 ; i++) // putting them into the sorted container
// cards.Add(CARD((uchar)i)); // shuffles them.
//
//// use current value (0-255) at each board location as an index to look up what
//// its NEW value (0-255) should be.
//uchar HUGE* bits = (uchar HUGE*)GetBits();
//uchar HUGE* loc;
//for(int y = 0 ; y < Height() ; y++)
//{
// loc = bits + GetPixelOffset(0,y); // address of first pixel in row y
// for(int x = 0 ; x < Width() ; x++, loc++)
// *loc = cards[*loc].letter; // set new "elevation" value
//}
//return(TRUE);
//} //RandomizeHeights
//----------------------------------------------------------------------------
//BOOL SDib::InvertHeights()
//{
//if(!IsOK() || Locked)
// return(FALSE);
//uchar HUGE* bits = (uchar HUGE*)GetBits();
//uchar HUGE* loc;
//for(int y = 0 ; y < Height() ; y++)
//{
// loc = bits + GetPixelOffset(0,y); // address of first pixel in row y
// for(int x = 0 ; x < Width() ; x++, loc++)
// *loc = (uchar)(255 - *loc); // set new "elevation" value
//}
//return(TRUE);
//} //InvertHeights
//----------------------------------------------------------------------------
//BOOL SDib::FlipVertical()
//{
//if(!IsOK() || Locked)
// return(FALSE);
//uchar HUGE* temp = new uchar[Width()];
//uchar HUGE* bits = (uchar HUGE*)GetBits();
//uchar HUGE* top; // top line
//uchar HUGE* bottom; // bottom line
//int half = Height() / 2;
//for(int y = 0 ; y < half ; y++)
//{
// // addresses of first pixels in the rows we are about to swap
// top = bits + GetPixelOffset(0,y); // top line
// bottom = bits + GetPixelOffset(0,Height() - 1 - y); // bottom line
// memcpy(temp,top,Width());
// memcpy(top,bottom,Width());
// memcpy(bottom,temp,Width());
//}
//delete[] temp;
//return(TRUE);
//} //FlipVertical
//----------------------------------------------------------------------------
//BOOL SDib::FlipHorizontal()
//{
//if(!IsOK() || Locked)
// return(FALSE);
//uchar HUGE* bits = (uchar HUGE*)GetBits();
//uchar HUGE* loc; // first char in line
//uchar HUGE* end; // last char in line
//for(int y = 0 ; y < Height() ; y++)
//{
// loc = bits + GetPixelOffset(0,y); // first char in line
// end = loc + Width() - 1; // last char in line
// for( ; loc < end ; loc++, end--)
// swap(*loc,*end);
//}
//return(TRUE);
//} //FlipHorizontal
//----------------------------------------------------------------------------
#if 0
// old method: works, but not fast enough.
// keep for reference because it was a simple method.
//----------------------------------------------------------------------------
BOOL SDib::AveragePixels(BOOL creep)
{
if(!IsOK() || Locked)
return(FALSE);
// a second dib to get source values from. wasteful of memory, but simple.
SDib* source = DibResize(this,0,0,0,TRUE);
if(!source)
return(FALSE);
int maxx = Width() - 1; // highest legal actual values
int maxy = Height() - 1;
int above, below, left, right;
uint average;
for(int y = 0 ; y < Height() ; y++)
{
above = wrap(y - 1,0,maxy);
below = wrap(y + 1,0,maxy);
// address of first pixel in row y of THIS (the destination) dib
uchar HUGE* loc = (uchar HUGE*)GetBits() + GetPixelOffset(0,y);
for(int x = 0 ; x < Width() ; x++, loc++)
{
left = wrap(x - 1,0,maxx);
right = wrap(x + 1,0,maxx);
average = source->GetPixelByte(left,above) // above left
+ source->GetPixelByte(x,above) // above
+ source->GetPixelByte(right,above) // above right
+ source->GetPixelByte(left,y) // point to left
+ source->GetPixelByte(x,y) // (the point itself)
+ source->GetPixelByte(right,y) // point to right
+ source->GetPixelByte(left,below) // below left
+ source->GetPixelByte(x,below) // below
+ source->GetPixelByte(right,below);// below right
average = (average + 4) / 9; // rounding method
// this version increments or decrements instead of actually averaging.
// causes values to creep slowly toward each other.
if(creep)
{
if(average > *loc)
{
if(*loc < 255)
(*loc)++;
}
else
if((average < *loc) && (*loc > 0))
(*loc)--;
}
else // this version actually averages
*loc = (uchar)average; // which makes image flatten quickly
// if(dc)
// dc->SetPixel(x,y,GetColor((int)(*loc))); // set pixel on screen
} // end for(x)
} // end for(y)
delete source;
return(TRUE);
} //AveragePixels
//----------------------------------------------------------------------------
#endif // 0
//----------------------------------------------------------------------------
// average the pixels in the dib.
// uses good rounding method for integer arithmetic: add the result of INTEGER division
// (itemcount/2) to sum before doing the division for the average. if original division
// would have left a remainder of (n/2) or greater, (.5), now when it's truncated,
// it will be to nearest whole number (rounded).
// screen wraps in all directions. note (Height,Width) is off screen
// Each call makes one averaging pass through the dib.
// some additional speed improvement may still be possible.
// This new method is faster.
//BOOL SDib::AveragePixels(BOOL creep)
//{
//if(!IsOK() || Locked ||
// !Height() || !Width())
// return(FALSE);
//
//// a second dib to get source values from. wasteful of memory, but simple.
//SDib* source = DibResize(this,0,0,0,TRUE);
//if(!source)
// return(FALSE);
//
//uchar HUGE* bits = (uchar HUGE*)GetBits(); // in this dib
//uchar HUGE* loc;
//
//uchar HUGE* srcbits = (uchar HUGE*)source->GetBits(); // in source dib
//// addresses of first pixel in the rows we need for calc,
//// initialized to the values they'll need for the first pass through loop
//// lineabove starts out as the LAST line, so it's wrapped
//uchar HUGE* lineabove = srcbits + source->GetPixelOffset(0,source->Height() - 1);
//uchar HUGE* thisline = srcbits + source->GetPixelOffset(0,0);
//uchar HUGE* linebelow = srcbits + source->GetPixelOffset(0,1);
//
//// possible names for the 9 separate ptrs below (if used)
//// aboveleft
//// above
//// aboveright
//// toleft
//// itself
//// toright
//// belowleft
//// below
//// belowright
//
//int left, right;
//uint average;
//for(int y = 0 ; y < Height() ; y++) // Height() and source->Height() are the same
//{
// loc = bits + GetPixelOffset(0,y);
// left = Width() - 1; // starts out auto-wrapped
// right = 1;
// for(int x = 0 ; x < Width() ; x++, loc++)
// {
// // use ptrs for all 9, and use ++? HUGE ptr + addition is slow.
// average =
// *(lineabove + left) // above left
// + *(thisline + left) // point to left
// + *(linebelow + left) // below left
//
// + *(lineabove + x) // above
// + *(thisline + x) // (the point itself)
// + *(linebelow + x) // below
//
// + *(lineabove + right) // above right
// + *(thisline + right) // point to right
// + *(linebelow + right); // below right
//
// average = (average + 4) / 9; // rounding method
//
// // this version increments or decrements instead of actually averaging.
// // causes values to creep slowly toward each other.
// if(creep)
// {
// if(average > *loc)
// {
// if(*loc < 255)
// (*loc)++;
// }
// else
// if((average < *loc) && (*loc > 0))
// (*loc)--;
// }
// else // this version actually averages
// *loc = (uchar)average; // which makes image flatten quickly
//
// left = x;
// if(++right >= Width()) // wrap right for last pixel in line
// right = 0;
// } // end for(x)
// lineabove = thisline;
// thisline = linebelow;
// if((y + 2) < Height()) // y + 2 because y itself hasn't been incremented yet
// linebelow = srcbits + source->GetPixelOffset(0,y + 2);
// else // last pass requires first line (wrap)
// linebelow = srcbits + source->GetPixelOffset(0,0);
//} // end for(y)
//delete source;
//return(TRUE);
//} //AveragePixels
//----------------------------------------------------------------------------
// write entire dib, or a portion of it, in .PIC format.
// no test for filename already exists: you must do it before you call it.
//BOOL SDib::WriteFilePIC(const string& filename, TRect r) const
//{
//if(!IsOK() || !filename.length())
// return(FALSE);
//
//if(r == TRect(0,0,0,0)) // if it's the default
// r = Rect(); // set it to full size of this dib
//if(r != Rect())
// r &= Rect(); // cut off any portion this dib doesn't contain
//r.Normalize();
//
//// open .PIC file, write header, and MUST be binary so every LF(endl) is not expanded to CRLF.
//ofstream outfile(filename.c_str(),ios::binary);
//outfile << r.Width() << "," << r.Height() << endl; // w,h of the actual portion to write
//
//if(r == Rect()) // if it's the whole dib, do it the fast way
//{
// uchar HUGE* ubits = (uchar HUGE*)GetBits();
// uchar HUGE* loc;
// for(int y = 0 ; y < r.Height() ; y++)
// {
// loc = ubits + GetPixelOffset(0,y); // get address of first pixel of row
// // cast added 12/2/01 for wshowfs=win16.
// outfile.write((const uchar far*)loc,r.Width()); // write its bytes directly to file
// }
//}
//else // write the specified rectangle, 1 byte at a time
//{
// for(uint y = r.top ; y < r.bottom ; y++)
// for(uint x = r.left ; x < r.right ; x++)
// outfile << GetPixelByte(x,y);
//}
//outfile.close();
//return(!outfile.fail());
//} //WriteFilePIC
//----------------------------------------------------------------------------
// "Locked"-related overrides for TDib functions:
// I've changed the return types, so remember these are completely distinct from TDib versions.
//----------------------------------------------------------------------------
BOOL SDib::SetColor(int entry, Color color)
{
if(Locked || !IsOK())
return(FALSE);
Colors[entry] = color;
// palettechanged = TRUE; // can't do this: it's used too much internally
return(TRUE);
}
//----------------------------------------------------------------------------
//BOOL SDib::SetIndex(int entry, WORD index)
//{
//if(Locked || !IsOK())
// return(FALSE);
//TDib::SetIndex(entry,index);
//// palettechanged = TRUE;
//return(TRUE);
//}
//----------------------------------------------------------------------------
// #error HAVEN'T CHECKED IF THE "LOCKED" RETURN VALUES ARE APPROPRIATE FOR THESE 2 FNS
//----------------------------------------------------------------------------
//int SDib::MapColor(Color fromColor, Color toColor, BOOL doAll)
//{
//if(Locked || !IsOK())
// return(FindColor(fromColor));
//palettechanged = TRUE;
//return(TDib::MapColor(fromColor,toColor,doAll));
//}
//----------------------------------------------------------------------------
//int SDib::MapIndex(WORD fromIndex, WORD toIndex, BOOL doAll)
//{
//if(Locked || !IsOK())
// return(FindIndex(fromIndex));
//palettechanged = TRUE;
//return(TDib::MapIndex(fromIndex,toIndex,doAll));
//}
//----------------------------------------------------------------------------
// I DON'T THINK I'VE EVER USED THESE:
//----------------------------------------------------------------------------
//void SDib::InfoFromHandle()
//{
//if(!Locked && IsOK())
// TDib::InfoFromHandle();
//}
//----------------------------------------------------------------------------
//BOOL SDib::LoadFile(const char* name)
//{
//if(Locked || !IsOK())
// return(FALSE);
//palettechanged = FALSE;
//return(TDib::LoadFile(name));
//}
//----------------------------------------------------------------------------
//BOOL SDib::LoadResource(HINSTANCE instance, TResID resID)
//{
//if(Locked || !IsOK())
// return(FALSE);
//palettechanged = FALSE;
//return(TDib::LoadResource(instance,resID));
//}
//----------------------------------------------------------------------------
//BOOL SDib::Read(TFile& file, long offBits)
//{
//if(Locked || !IsOK())
// return(FALSE);
//palettechanged = FALSE;
//return(TDib::Read(file,offBits));
//}
//----------------------------------------------------------------------------
// to get a pixel's address, add this value to Bits.
// it does no error checking (because what would you return?)
// so you must ensure that dib is valid before calling.
// also, it should return a long because in the future you may want to cast the
// entire value GetBits() + GetPixelOffset to a type other than uchar*
// if you want to support bitcounts other than 8.
//long SDib::GetPixelOffset(uint x, uint y) const
//{
//// the LINES in the BMP are upside down. See WinAPI | Bitmaps Overview
//return(((Height() - 1 - y) * ((((long)Width() * 8L + 31L) / 32L) * 4L)) + x);
//}
//----------------------------------------------------------------------------
// THESE FOUR ARE EXPERIMENTAL. WELL, THEY WERE EXPERIMENTAL. ACTUALLY, I'VE
// USED THEM A LOT, AND THEY SEEM TO WORK FINE. See notes in SDIB.H.
//----------------------------------------------------------------------------
// In a 256 color dib, each byte in the Bits array is an index into the color table,
// and defines the color for 1 pixel. But TDib provides no functions for accessing
// pixels at all. And TDC(TDibDC) only allows getting the COLOR at a pixel.
// To get the raw value (color table index) of a pixel safely, you must use both together:
// TDib* dib; TDibDC(*dib) dibdc;
// int value = dib->FindColor(dibdc->GetPixel(x,y));
// dibdc looks up the raw value, looks it up in the color table, and returns the color.
// then dib takes the color, re-searches the color table for it, and returns the index!
// when it was right there all along. Further, if the palette has duplicate colors,
// FindColor returns the FIRST it finds, which may NOT be the byte value at (x,y).
// This function simply looks up the raw value and returns it immediately.
//
// seemed to work properly in a brief test (Win32):
// I created a PIC file and matching BMP, saved and reloaded the BMP,
// saved it as PIC using this fn, and the result matched the original PIC.
//uchar SDib::GetPixelByte(uint x, uint y)
//{
//if(!IsOK() || (x >= Width()) || (y >= Height()))
// return(0);
//return(* ((uchar HUGE*)GetBits() + GetPixelOffset(x,y)) );
//} //GetPixelByte
//----------------------------------------------------------------------------
// THIS FN DIRECTLY MODIFIES THE BITS ARRAY.
// This function addresses an even worse problem posed by TDibDC->SetPixel(),
// which seems terribly slow.
// You provide it a Color. It searches the entire color table looking for the
// best match. In most cases, I got the Color from the color table in the first place,
// so I know it's there. In a 256-color dib, you KNOW you can legally set any
// index to a raw value of 0-255, so that's what this function does.
//
// this also seemed to work properly in a brief test (Win32):
// I created a 2nd dib, used SetPixelByte to copy the pixel values from the first,
// and wrote both BMP and PIC files for both dibs. The PIC files matched exactly, (fc.exe)
// and the BMP files had a few differences (pad bytes?), which made no visible
// difference when compared to each other using WAnimate.exe.
// saved it as PIC using this fn, and the result matched the original PIC.
// void return is for greatest possible speed.
//void SDib::SetPixelByte(uint x, uint y, uchar value)
//{
//if(!IsOK() || (x >= Width()) || (y >= Height()) ||
// (Usage() != DIB_RGB_COLORS)) // helps screen out dibs I didn't create.
// return;
//uchar HUGE* loc = (uchar HUGE*)GetBits() + GetPixelOffset(x,y); // calc address
//*loc = value;
//} //SetPixelByte
//----------------------------------------------------------------------------
// before calling, be sure destination is big enough to hold a full scan line!
//BOOL SDib::GetScanLineBytes(uint row, uchar* destination) const
//{
//if(!IsOK() || (row >= Height()) || !destination)
// return(FALSE);
//uchar HUGE* loc = (uchar HUGE*)GetBits() + GetPixelOffset(0,row); // first pixel in the row
//memcpy(destination,loc,Width());
//return(TRUE);
//} //GetScanLineBytes
//----------------------------------------------------------------------------
//BOOL SDib::SetScanLineBytes(uint row, uchar* source)
//{
//if(!IsOK() || (row >= Height()) || !source)
// return(FALSE);
//// this isn't strictly necessary, but it helps screen out dibs that I didn't create
//if(Usage() != DIB_RGB_COLORS)
// return(FALSE);
//uchar HUGE* loc = (uchar HUGE*)GetBits() + GetPixelOffset(0,row); // first pixel in the row
//memcpy(loc,source,Width());
//return(TRUE);
//} //SetScanLineBytes
//----------------------------------------------------------------------------
// end class SDib
//////////////////////////////////////////////////////////////////////////////
//
// GLOBAL FUNCTIONS USING SDIB
//----------------------------------------------------------------------------
// 7-3-97, 7-14-00
// creates a complete copy of the source dib, possibly resized, and
// with a possibly resized color table. It requires DIB_RGB_COLORS, which is all I use.
// you can create a dib with fewer colors than the original, but some pixels
// may change color, if their color is omitted in the new table.
// you can create a smaller dib, cropping dib in from the right and up from the bottom.
// returns a pointer to the new SDib if successful, 0 if not
//
// This can't be an SDib member function. It replaces the given SDib, rather than
// just changing its data members, and the replacement SDib is dynamically allocated,
// whether the source dib was or not.
//
// Suggested use:
// SDib* olddib, biggerdib;
// if((biggerdib = DibResize(olddib,0,0,256)) != 0)
// {
// delete olddib;
// olddib = biggerdib;
// }
//
// the default for copypixels used to be TRUE: revise as necessary in any old pgms (probably none)
// SDib* DibResize(const SDib* source, int width = 0, int height = 0, int colors = 0,
// BOOL copypixels = FALSE);
//----------------------------------------------------------------------------
//SDib* DibResize(const SDib* source, int width, int height, BOOL copypixels)
//{
//// MUST have a source dib, that is in RGB mode
//if(!source || !source->IsOK())
// return(0);
// // if a parameter is 0, use value from source dib
//if(!width) width = source->Width();
//if(!height) height = source->Height();
//if(!colors) colors = (int)source->256;
//
//SDib* newdib = new SDib(width,height,colors,DIB_RGB_COLORS);
//if(!newdib->IsOK())
//{
// if(newdib)
// delete newdib;
// return(0);
//}
//// copy the color table using special method for this purpose
//newdib->palettechanged = source->palettechanged;// just transfer setting
//for(int i = 0 ; i < newdib->256 ; i++)
// if(i < source->256) // if old dib has a color here, use it.
// newdib->SetColor(i,source->GetColor(i));
// else // else a random color so something's there
// newdib->SetColor(i,Color::FromArgb(255,random(256),random(256),random(256)));
//
//if(copypixels)
//{
// // new, should be very fast (seems to be):
// // dest dib origin
// TDibDC(*newdib).SetDIBitsToDevice(source->Rect(),TPoint(0,0), *source);
//
// // old, worked. keep for method:
// // copy the pixels. stop when maximum extent of either dib is reached.
//// TDibDC dibdc(*source);
//// for(int y = 0 ; ((y < newdib->Height()) && (y < source->Height())) ; y++)
//// for(int x = 0 ; ((x < newdib->Width()) && (x < source->Width())) ; x++)
//// newdibdc.SetPixel(x,y,dibdc.GetPixel(x,y));
//}
//return(newdib);
//} //DibResize
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
|
|
|
|