|
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 Payments Humor Music |
Extension of the Borland OWL TDib C++ classSDib is an extension of the Borland C++ ObjectWindows Library (OWL) TDib class, adding palette manipulation functions, support for color cycling, bitmap flip vertical and horizontal, and some substantially faster methods for getting and setting pixel values. SDib only provides support for 256-color DIBs. Most of its features have no applicability to 24-bit full-color bitmaps because they have no palette. |
/* sdib.h 8-9-00
Copyright (C)1999, 2000, 2006 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 is an extension of the Borland OWL TDib, with additional member functions for palette manipulation.
------
To do:
optimize everything for 256 colors because any fewer is pointless for any app that would use
this class, and a 24-bit dib has no palette anyway, so most of SDib's extensions are no use there;
you can use TDib directly for those.
in RandomizeColors(int start), a totally random palette is poor for color rotation.
add a parameter BOOL graded? If TRUE, you could treat the RGB values as 3 odometer wheels
that you can rotate independently so that 0,0,0 would no longer line up.
0,0,0 might become 0,1,2
1,1,1 1,2,3
2,2,2... 2,3,4
And in each wheel, all 256 values wouldn't have to appear in order, either,
so you could have random length blocks whose values change only slowly within the block.
The point is to get a random palette where, however, the are at least some
regions with some continuity, where the color values are fairly close to each other.
*/
#ifndef __SDIB_H
#define __SDIB_H
#include <owl\gdiobjec.h>
#include "c:\bcs\my.h"
#pragma hdrstop
//////////////////////////////////////////////////////////////////////////////
class SDib : public TDib
{
public:
SDib(int width, int height, int nColors = 256, WORD mode = DIB_RGB_COLORS);
SDib(const TRect& rect, int nColors = 256, WORD mode = DIB_RGB_COLORS);
SDib(const string& filename);
SDib(const SDib& other); // copy constructor
// functions
// BOOLs return TRUE if they did what you wanted, or FALSE if palette was locked.
TRect Rect() const { return TRect(0,0,Width(),Height()); }
BOOL Erase(TColor color = TColor::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(const TDib& 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();
// 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) const;
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
BOOL SetColor(int entry, TColor color);
BOOL SetIndex(int entry, WORD index);
BOOL ChangeModeToPal(const TPalette& pal);
BOOL ChangeModeToRGB(const TPalette& pal);
int MapColor(TColor fromColor, TColor 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.
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, int colors = 0,
BOOL copypixels = FALSE);
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
#endif // __SDIB_H
/* sdib.cpp 12-10-01
Copyright (C)1997-2001, 2006 Steven Whitney.
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 "c:\bcs\library\sdib.h"
#include <classlib\arrays.h>
//----------------------------------------------------------------------------
// constructor
SDib::SDib(int width, int height, int nColors, WORD mode) :
TDib(width, height, nColors, mode)
{
Locked = palettechanged = FALSE;
GrayScale(); // start with gray scale palette (it includes Black at 0)
Erase(); // overwrite random data
}
//----------------------------------------------------------------------------
// constructor
SDib::SDib(const TRect& rect, int nColors, WORD mode) :
TDib(rect.Width(), rect.Height(), nColors, mode)
{
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;
}
//----------------------------------------------------------------------------
// copy all colors from another dib
BOOL SDib::CopyColors(const TDib& other)
{
if(Locked || !IsOK())
return(FALSE);
for(int i = 0 ; (i < NumColors()) && (i < other.NumColors()) ; 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 < NumColors() ; i++)
SetColor(i,TColor(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);
TColor newcolor;
for(int i = start ; i < NumColors() ; i++) // if 24-bit dib, NumColors is 0, nothing happens
{
BOOL dup;
do
{
newcolor = TColor(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
//----------------------------------------------------------------------------
// Loads color palette from a Fractint-format .MAP file, Format: r g b possiblecomment\n
// (Reminder: non-color-related ".MAP" files do exist: TLINK.EXE creates them.)
// 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 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(int i = 0 ; i < NumColors() ; 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,TColor(r,g,b));
getline(infile,t,'\n'); // read and ignore rest of line: comment
}
for(int j = i ; j < NumColors() ; 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 < NumColors() ; i++)
{
TColor c = GetColor(i);
outfile << setw(3) << (int)(c.Red()) << " " // I think this is a different format
<< setw(3) << (int)(c.Green()) << " " // from TColor op <<
<< setw(3) << (int)(c.Blue()) << 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() || !NumColors())
return(FALSE);
TColor c;
int i;
if(count > 0)
{
for(int k = 0 ; k < count ; k++)
{
c = GetColor((int)NumColors() - 1); // save top color
for(i = ((int)NumColors() - 1) ; 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 < (NumColors() - 1) ; i++)
SetColor(i,GetColor(i+1));
SetColor((int)NumColors() - 1, c); // set top color
}
}
return(TRUE);
} //RotateColors
//----------------------------------------------------------------------------
// reverse the order of the colors in the palette
BOOL SDib::ReverseColors()
{
if(Locked || !IsOK() || !NumColors())
return(FALSE);
int top = (int)NumColors() - 1;
int half = (int)NumColors() / 2;
TColor 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. This seems to work even when the color isn't
// in the palette (though it shouldn't, and maybe really doesn't). It might
// use the closest color, which is often so dark you can't tell the difference.
BOOL SDib::Erase(TColor 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);
TColor c;
for(int i = 0 ; i < NumColors() ; i++)
{
c = GetColor(i);
SetColor(i,TColor(255 - c.Red(), 255 - c.Green(), 255 - c.Blue()));
}
return(TRUE);
} // ComplementColors
//----------------------------------------------------------------------------
// increase each rgb component by 1
BOOL SDib::LightenColors()
{
if(Locked || !IsOK())
return(FALSE);
TColor c;
// int red, green, blue;
for(int i = 0 ; i < NumColors() ; i++)
{
c = GetColor(i);
// old: slow
// after any component reaches 255, the color balance also gets affected
// red = min((int)c.Red() + 1, 255);
// green = min((int)c.Green() + 1, 255);
// blue = min((int)c.Blue() + 1, 255);
// SetColor(i,TColor(red, green, blue));
// new: after 256 calls, you do get back where you started
SetColor(i,TColor(c.Red() + 1, c.Green() + 1, c.Blue() + 1));
}
return(TRUE);
} // LightenColors
//----------------------------------------------------------------------------
// decrease each rgb component by 1
BOOL SDib::DarkenColors()
{
if(Locked || !IsOK())
return(FALSE);
TColor c;
// int red, green, blue;
for(int i = 0 ; i < NumColors() ; i++)
{
c = GetColor(i);
// old: slow
// after any component reaches 0, the color balance also gets affected
// red = max((int)c.Red() - 1, 0);
// green = max((int)c.Green() - 1, 0);
// blue = max((int)c.Blue() - 1, 0);
// SetColor(i,TColor(red, green, blue));
// new: after 256 calls, you do get back where you started
SetColor(i,TColor(c.Red() - 1, c.Green() - 1, c.Blue() - 1));
}
return(TRUE);
} // DarkenColors
//----------------------------------------------------------------------------
// set all pixels to random colors from the palette. for 256-color dibs only.
// 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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS) || (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)NumColors())));
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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS))
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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS))
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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS))
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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS))
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.
// (See below for the new method that replaced this one.)
//----------------------------------------------------------------------------
BOOL SDib::AveragePixels(BOOL creep)
{
if(!IsOK() || Locked || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS))
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 || (NumColors() != 256) || (Usage() != DIB_RGB_COLORS) ||
!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() || (NumColors() != 256) || !filename.length() || (Usage() != DIB_RGB_COLORS))
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, TColor color)
{
if(Locked || !IsOK())
return(FALSE);
TDib::SetColor(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);
}
//----------------------------------------------------------------------------
BOOL SDib::ChangeModeToPal(const TPalette& pal)
{
return((Locked || !IsOK()) ? FALSE : TDib::ChangeModeToPal(pal));
}
//----------------------------------------------------------------------------
BOOL SDib::ChangeModeToRGB(const TPalette& pal)
{
return((Locked || !IsOK()) ? FALSE : TDib::ChangeModeToRGB(pal));
}
//----------------------------------------------------------------------------
// #error HAVEN'T CHECKED IF THE "LOCKED" RETURN VALUES ARE APPROPRIATE FOR THESE 2 FNS
//----------------------------------------------------------------------------
int SDib::MapColor(TColor fromColor, TColor 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) const
{
if(!IsOK() || (NumColors() != 256) || (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 TColor. It searches the entire color table looking for the
// best match. In most cases, I got the TColor 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() || (NumColors() != 256) || (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() || (NumColors() != 256) || (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() || (NumColors() != 256) || (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, int colors, BOOL copypixels)
{
// MUST have a source dib, that is in RGB mode
if(!source || !source->IsOK() || (source->Usage() != DIB_RGB_COLORS))
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->NumColors();
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->NumColors() ; i++)
if(i < source->NumColors()) // 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,TColor(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
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
Copyright ©2012 Steven Whitney. Last modified Sun 07/29/2012 11:13:53 -0700. |
||