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

Borland C++ ObjectWindows main window class for applications needing color rotation, palette manipulation, and other bitmap graphics operations

I derived my SDib class from the Borland OWL TDib to provide greatly enhanced DIB (device independent bitmap) manipulation and management features that TDib doesn't have.

The SDibWindow class below provides the application-level support for an SDib to make its many capabilities available to the user in the application's interface. It contains:

  • The SDib to which the application can draw and that is displayed on screen.
  • The menu items and corresponding event handlers that give a user access to SDib features.
  • An IdleAction() function that handles color cycling (palette animation such as is often used in Mandelbrot programs).
  • Variables that control graphics behavior such as turning color cycling on and off or changing the cycling direction.
  • Functions that allow the user to drag with the mouse to select an area, and calculations that translate the screen coordinates of the selected area to DIB-relative coordinates.
  • Save the currently displayed image as .BMP or .PIC.
  • Load and display .BMP and .PIC image files.
  • Arrays for holding lists of file names, including color map files.
  • Dialog boxes for loading and saving color map files.
  • and more...

It is derived from TWindow and is intended for use as the main window in a graphics application. Or, more properly, the application's actual main window would be a TFrameWindow, and the SDibWindow would be installed as its client window, so that the TFrameWindow is the application's real main window, but the SDibWindow provides all the application-specific functionality.

You would use SDibWindow in your application when you want to provide palette manipulation, color cycling (palette animation), bitmap flip, and the other transformations SDib can provide.

I used SDibWindow in my largest graphics applications, where you can see examples of it in use:

Click here to download the SDibWindow project (sdibwin.zip, 45 KB). It contains the files specific to SDibWindow: SDIBWIN.H, .CPP, .RH, .RC, and .RTF, and COPYING.TXT (GNU GPL License).

In the #includes of the listing below are links to additional support files required for this large project.


sdibwin.h

/*	sdibwin.h			1/19/02
	Copyright (C)1993-2002 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.

SDibWindow is a versatile base class for graphics display programs,
containing all functions required for managing an SDib and its color palette,
doing palette animation, loading MAP, BMP, PIC files for display, and any other
variables and functions that I found duplicated in my various graphics programs
that might commonly be useful in derived classes.  Add enhancements here to
make them available to all the programs.

Figure out, and document, the simplest procedures for putting this to use in an old
project and for using this to begin a new project. (This procedure isn't all that simple.)
Using this class is easier that starting from scratch, but not easy:

1. Use response table entries to determine which TWindow-derived object handles a command.
   If you want SDibWindow to handle a command, let it fall through.
2. Add SDIBWIN.RC as a project node.  This will ensure that the main project starts with
   all the resources it needs.   Create your project's main menu, in ITS OWN .RC file,
   using a COPY of the COREMENU menu, which has items for all the SDibWindow commands
   you may want to use.  Also COPY the COREMENU accelerator table.
3. The main project's .rh file must #include "sdibwin.rh".  When you add identifiers in the .RC,
   ensure, manually if necessary, that they go into the correct .RH file.
4. Add SDIBWIN.RTF at the end of the [FILES] section of your project's .HPJ file.
   If a help topic applies to all sdibwin-based applications, be sure to keep it in
   SDIBWIN.RTF, so you only have to make revisions in one location.  You may need to
   specifically number the BrowseButton sequence (+ 0 footnotes) to force the topic
   order you want.  Leave lots of space between numbers (0010 0020 0030, etc.).
5. Delete or move resources using Text Edit on the .RC file.  Otherwise, identifiers
   that you still need may get autodeleted by Resource Workshop if it considers them
   unused; it doesn't know you have 2 RC files.

------
To Do:

FirstFreeColor hasn't been propagated into apps that could use it.

------
Notes:
--This class is currently used in WBIF, WANIMATE, WLIFE2D, WINBROT, WSHOWFS, WADAPT.
  Any changes made here while working on one project could affect the others.
--

*/
#ifndef __SDIBWIN_H
#define __SDIBWIN_H

#include <owl\owlpch.h>
#include "c:\bcs\my.h"
#include "c:\bcs\library\sdib.h"
#include "c:\bcs\library\filearay.h"
#include "c:\bcs\library\stopwatc.h"
#include "c:\bcs\library\doubrect.h"

//////////////////////////////////////////////////////////////////////////////
// DIALOGS AND UTILITY CLASSES USED BY CLASS SDIBWINDOW.
//////////////////////////////////////////////////////////////////////////////
// transfer information for the file list editor dialog box
struct ListEditorStruct
{
	ListEditorStruct() { filechars[0] = 0; }
	char filechars[10000];    	// big: *.xxx can create lists with hundreds of files
};
//////////////////////////////////////////////////////////////////////////////
// dialog box for editing the file lists (MAP files and command line arguments).
// Wshowfs.cpp has a dialog with more capability (resizable), as does WTalk.cpp (custom font),
// but no additional capability is needed here.  It's fine.
class SListEditDialog : public TDialog
{
public:
	SListEditDialog(TWindow* parent, ListEditorStruct* tts);		// constructor

protected:
	void CmHelp();

DECLARE_RESPONSE_TABLE(SListEditDialog);
};
//////////////////////////////////////////////////////////////////////////////
// transfer struct for the PICOptionsDialog
struct PICOpStruct
{
	PICOpStruct();    			// ctor

	BOOL overhead;           	// draw modes (only one can be TRUE)
	BOOL drawpts;
	BOOL ovpts;
	BOOL drawlines;
	BOOL ovlines;				// this is fast and visually good
	BOOL areaheight;
	BOOL areanear;      		// visually best

	char iniskiptext[10]; 		// text for iniskip
	char skiptext[10]; 			// text for skip
	char vertsizetext[10]; 		// text for vertical size adj

	// seems like it should be ok to have additional members here, after the real ones,
	// that don't participate in the transfer(?).  Could keep the numeric
	// versions of the text here.

};								//PICOpStruct
//////////////////////////////////////////////////////////////////////////////
// Options dialog for a PIC file.
class PICOptionsDialog : public TDialog
{
public:
	PICOptionsDialog(TWindow* parent, PICOpStruct* ots);

protected:
	void CmHelp();

DECLARE_RESPONSE_TABLE(PICOptionsDialog);
};								//PICOptionsDialog

//////////////////////////////////////////////////////////////////////////////
// CLASS SDIBWINDOW
//////////////////////////////////////////////////////////////////////////////
class SDibWindow : public TWindow
{
public:
	SDibWindow(TWindow *parent = 0);
	~SDibWindow();

	FileArray FileList;			// command line arguments and/or user-selected files

	virtual BOOL IdleAction(long idlecount);

protected:
	// pointers
	SDib* dib;					// holds currently displayed design
	TClientDC* DragDC;			// used while selecting a new calculate region,

	// other variables
	FileArray MapFiles;			// list of all available .MAP files (see ctor)
	string MAPDir;				// last-used directories for MAP files and app-specific DATA
	string DATADir;				// (without trailing backslashes)
	Stopwatch sw;				// available for timing things
	BOOL fittowindow;			// whether to stretch to fit the display window
								// in some places, this is used as a state flag
								// (i.e. to indicate window rect != dib.Rect), but you
								// should phase out that usage.  If that is what you want
								// to know, use that test instead.  use fittowindow only
								// to indicate an override: e.g. that you want to NOT
								// stretch to window in a situation that you normally would.
								// EVENTUALLY RENAME THIS TO StretchToWindow & revise all refs

	int FirstFreeColor;			// first palette index that can be freely randomized or changed
	int autorotatecolors;		// whether and which direction to rotate palette in IdleAction
								// values are -1,0,+1.
	TPoint DragStart, DragEnd;	// corners of region being selected with box
	TRect DragRect, DibDragRect;// dragged area in window, and in dib, coordinates
	DoubleRect DragPct;			// dragged area, as percentages of screen dimensions

	// FUNCTIONS
	virtual BOOL makenewdib(const char* filename = 0, int w = 0, int h = 0,
							BOOL copypixels = TRUE);
	BOOL LoadPIC(const string& filename); 			// read and display a .PIC file
	BOOL LoadPIC3d(const string& filename);			// allows 3d options
	virtual BOOL LoadBMP(const string& filename); 	// read and display a .BMP file
	BOOL LoadMAP(const string& filename);         	// load a color palette file
	virtual void SetParentCaption(const string& caption);
	void EraseWindow(TColor color = TColor::Black);	// immediate, no wait for Invalidate
	BOOL QuerySavePalette();

	// TWindow overridden virtuals
	virtual void SetupWindow();
	virtual void CleanupWindow();
	virtual BOOL CanClose();
	virtual void Paint(TDC& dc, BOOL erase, TRect& invalidarea);

	// event handlers.  response table functions should be protected
	void EvDropFiles(TDropInfo dropInfo);
	void EvSize(UINT sizeType, TSize& size);
	void EvLButtonDown(UINT modkeys, TPoint& point);	// wshowfs.cpp requires this be nonvirtual
	void EvLButtonUp(UINT modkeys, TPoint& point);		// wshowfs requires this be nonvirtual
	void EvRButtonDown(UINT modkeys, TPoint& point);    // wshowfs requires this be nonvirtual
	void EvMouseMove(UINT, TPoint&);
	void CmEditFileList(WPARAM cmd);		// edit any file list, in a dialog box
	virtual void CmFileSaveAs();			// save current design as a .bmp or .pic
	virtual void CmFileExit();

	virtual void CmColorsManip(WPARAM cmd);	// handles various palette manipulations
	void CmColorsLoad();
	void CmColorsSaveAs();
	virtual void CmViewFitToWindow();		// toggles fittowindow
	virtual void CmViewAutoRotateColors();
	void CmHelpIndex();
	void CmEffectsAverage();
	void CmEffectsRandomizeHeights();
	void CmEffectsInvertHeights();
	void CmEffectsRandomizePixels();
	void CmEffectsFlipHorizontal();
	void CmEffectsFlipVertical();

DECLARE_RESPONSE_TABLE(SDibWindow);
};
//////////////////////////////////////////////////////////////////////////////
#endif			// __SDIBWIN_H

sdibwin.cpp

/*	sdibwin.cpp			1/27/02
	Copyright (C)1993-2002 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 <owl\opensave.h>
#include <owl\chooseco.h>
#include <owl\inputdia.h>
#include <owl\radiobut.h>
#include <owl\edit.h>
#include <iomanip.h>
#include "c:\bcs\my.h"
#include "c:\bcs\library\filearay.h"
#include "c:\bcs\library\stopwatc.h"
#pragma hdrstop

#include "c:\bcs\library\sdibwin.h"
#include "c:\bcs\library\sdibwin.rh"	// identifiers required by this file

extern TColor MyCustomColors[16];   	// this is in mylib.cpp, for TChooseColor

// you must define these in the main app's .cpp file, as char HelpFilename[], etc.
extern char HelpFilename[];  			// name of the app's associated .HLP file
extern char INIFilename[];  			// name of the app's associated .INI file

//////////////////////////////////////////////////////////////////////////////
// DIALOGS AND UTILITY CLASSES
//////////////////////////////////////////////////////////////////////////////
// class SListEditDialog
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(SListEditDialog,TDialog)
	EV_COMMAND(IDHELP,CmHelp),
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// constructor
// no TResID arg in ctor, so this block is the only loc where all the resource IDs
// associated with this dialog are used.  Change to this method in other pgms.
SListEditDialog::SListEditDialog(TWindow* parent, ListEditorStruct* tts)
	: TDialog(parent,TResId(FILELISTEDITOR))	// <-TResID is always the same, anyway
{
	new TEdit(this,IDC_LISTEDIT,sizeof(tts->filechars));
	SetTransferBuffer(tts);
}
//----------------------------------------------------------------------------
void SListEditDialog::CmHelp()
{ WinHelp(HelpFilename,HELP_PARTIALKEY,(DWORD)(LPSTR)"File List Editor"); }
//----------------------------------------------------------------------------
//					end class SListEditDialog
//////////////////////////////////////////////////////////////////////////////
// class PICOpStruct
//----------------------------------------------------------------------------
// constructor
PICOpStruct::PICOpStruct()
{
overhead = TRUE;
drawlines = drawpts = ovpts = ovlines = areaheight = areanear = FALSE;
iniskiptext[0] = skiptext[0] = vertsizetext[0] = 0;
}                       		//constructor
//----------------------------------------------------------------------------
//							end class PICOpStruct
//////////////////////////////////////////////////////////////////////////////
// class PICOptionsDialog
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(PICOptionsDialog,TDialog)
	EV_COMMAND(IDHELP,CmHelp),
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// constructor
PICOptionsDialog::PICOptionsDialog(TWindow* parent, PICOpStruct* ots)
	: TDialog(parent,TResId(PICFILEOPTIONS))
{
new TRadioButton(this,IDC_OVERHEAD,0);  	// 0 = TGroupBox*
new TRadioButton(this,IDC_DRAWPOINTS,0);
new TRadioButton(this,IDC_OVERLAYPOINTS,0);
new TRadioButton(this,IDC_DRAWLINES,0);
new TRadioButton(this,IDC_OVERLAYLINES,0);
new TRadioButton(this,IDC_AREAHEIGHT,0);
new TRadioButton(this,IDC_AREANEARNESS,0);

new TEdit(this,IDC_INISKIP,10);
new TEdit(this,IDC_SKIP,10);
new TEdit(this,IDC_VERTSIZE,10);

SetTransferBuffer(ots);
}                            	// constructor
//----------------------------------------------------------------------------
void PICOptionsDialog::CmHelp()
{ WinHelp(HelpFilename,HELP_PARTIALKEY,(DWORD)(LPSTR)"PIC file options"); }
//----------------------------------------------------------------------------
//					// end class PICOptionsDialog
//////////////////////////////////////////////////////////////////////////////
// class SDibWindow
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(SDibWindow, TWindow)
	EV_WM_DROPFILES,
	EV_WM_SIZE,
	EV_WM_LBUTTONDOWN,
	EV_WM_LBUTTONUP,
	EV_WM_RBUTTONDOWN,
	EV_WM_MOUSEMOVE,
	EV_COMMAND(CM_FILESAVEAS,CmFileSaveAs),
	EV_COMMAND(CM_FILEEXIT,CmFileExit),
	EV_COMMAND(CM_COLORSLOAD, CmColorsLoad),
	EV_COMMAND(CM_COLORSSAVEAS, CmColorsSaveAs),
	EV_COMMAND(CM_EFFECTSAVERAGE, CmEffectsAverage),
	EV_COMMAND(CM_EFFECTSRANDOMIZEHEIGHTS, CmEffectsRandomizeHeights),
	EV_COMMAND(CM_EFFECTSFLIPHORIZONTAL, CmEffectsFlipHorizontal ),
	EV_COMMAND(CM_EFFECTSFLIPVERTICAL, CmEffectsFlipVertical),
	EV_COMMAND(CM_EFFECTSINVERTHEIGHTS, CmEffectsInvertHeights),
	EV_COMMAND(CM_EFFECTSRANDOMIZEPIXELS, CmEffectsRandomizePixels),
	EV_COMMAND(CM_VIEWFITTOWINDOW, CmViewFitToWindow),
	EV_COMMAND(CM_VIEWAUTOROTATECOLORS,CmViewAutoRotateColors),
	EV_COMMAND(CM_HELPINDEX,CmHelpIndex),
	EV_COMMAND_AND_ID(CM_FILEEDITLIST,CmEditFileList),
	EV_COMMAND_AND_ID(CM_COLORSEDITLIST,CmEditFileList),
	EV_COMMAND_AND_ID(CM_COLORSRANDOMIZE, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSREVERSE, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSCOMPLEMENT, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSLIGHTEN, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSDARKEN, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSROTATEFORWARD, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSROTATEBACKWARD, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSNEXTMAP, CmColorsManip),
	EV_COMMAND_AND_ID(CM_COLORSPREVIOUSMAP, CmColorsManip),
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// constructor
SDibWindow::SDibWindow(TWindow* parent) : TWindow(parent)
{
SetBkgndColor(TColor::Black);	// black background

// pointers
// the dib remains always valid throughout life of the window (if not, it's a fatal error)
dib = new SDib(300,200,256,DIB_RGB_COLORS); // startup size is arbitrary
dib->SetColor(0,TColor::Black);				// background to match window
dib->RandomizeColors(1);					// preserve the bkcolor we just set
FirstFreeColor = 0;							// but start out allowing any colors to change
DragDC = 0;              					// this is only nonzero during a drag

// other variables
DragStart = DragEnd = TPoint(0,0);
DibDragRect = DragRect = TRect(DragStart,DragEnd);
DragPct = DoubleRect(0,0,0,0);
fittowindow = TRUE;   			// startup dib unlikely to match window size
autorotatecolors = 0;         	// set autorotate OFF

//--------------------
#if defined(__WIN32__)			// get command line arguments (list of files to display)
	string pgmname;				// discard program name, then the rest are file names
	istrstream(GetCommandLine()) >> pgmname >> FileList;
#else
	for(int i = 1 ; i < _argc ; i++)
		FileList.AddFile(string(_argv[i]));
#endif	//(__WIN32__)

//--------------------
// LOAD DATADIR AND MAPDIR FROM THE APP'S INI FILE.
char buf[MAXPATH];      			// item from .INI file
getcwd(buf,MAXPATH);	      		// temp use to get current logged dir
string cwd = buf;					// copy it to use as the default parameter
GetPrivateProfileString("info","datadir",cwd.c_str(),buf,MAXPATH,INIFilename);
DATADir = buf;
// return to last-saved data directory only if there were no files on command line:
// if there were files, assume user wants THEIR dir.
if(!FileList.GetItemsInContainer())
	FilePathParser(DATADir + "\\").ChDir();
// default MAPDir is DATADir, which may simply be current logged dir
GetPrivateProfileString("info","mapdir",DATADir.c_str(),buf,MAXPATH,INIFilename);
MAPDir = buf;
//----------
MapFiles.AddFile(MAPDir + "\\*.map"); // load all available .MAP files

}                         		//constructor
//----------------------------------------------------------------------------
// destructor
SDibWindow::~SDibWindow()
{
if(dib) delete dib;
if(DragDC) delete DragDC;

WritePrivateProfileString("info","datadir",DATADir.c_str(),INIFilename);
WritePrivateProfileString("info","mapdir",MAPDir.c_str(),INIFilename);
}                           	//destructor
//----------------------------------------------------------------------------
// called after construction, but window isn't displayed, and GetClientRect() is not available.
void SDibWindow::SetupWindow()
{
TWindow::SetupWindow();			// must do base first
DragAcceptFiles(TRUE);
}								//SetupWindow
//----------------------------------------------------------------------------
void SDibWindow::CleanupWindow()
{
// pointless?  one example does it, in another it's commented out.
// DragAcceptFiles(FALSE);
TWindow::CleanupWindow();    	// must do base last
}                   		   	//CleanupWindow
//----------------------------------------------------------------------------
void SDibWindow::CmHelpIndex() { WinHelp(HelpFilename,HELP_INDEX,0); }
//----------------------------------------------------------------------------
// my virtual CmFileExit() corrects for CmExit not being virtual, and its actual
// closing method is more direct.
void SDibWindow::CmFileExit() { GetApplication()->GetMainWindow()->CloseWindow(); }
//----------------------------------------------------------------------------
// this is also called at startup: i.e. a newly created window gets resized on creation.
// you must never cause a GetClientRect() call by your actions here.
void SDibWindow::EvSize(UINT sizeType, TSize& size)
{
TWindow::EvSize(sizeType,size);
if(HWindow && fittowindow)
	Invalidate(FALSE);			// force new stretched redraw in new window size
}		                      	//EvSize
//----------------------------------------------------------------------------
// this EvDrop is called if you drop the files in THIS window's client area.
// adds dropped files to this window's list.
// useful because CmFileOpen can only open a few files at a time:
// MAXPATH used by the dialog box is only 80, and the path alone uses up much of it.
// with this, you can drop an entire directory!
void SDibWindow::EvDropFiles(TDropInfo dropinfo)
{
int count = dropinfo.DragQueryFileCount();
for(int i = 0 ; i < count ; i++)
{
	int length = dropinfo.DragQueryFileNameLen(i) + 1;
	char* filename = new char[length];
	dropinfo.DragQueryFile(i,filename,length);
	FileList.AddFile(string(filename));
	delete[] filename;
}
dropinfo.DragFinish();
}            					//EvDropFiles
//----------------------------------------------------------------------------
// returns FALSE if user pressed cancel, otherwise TRUE (i.e. "issue is resolved").
// you MUST either get a NO, CANCEL, or DO a save.
BOOL SDibWindow::QuerySavePalette()
{
// 9/12/2006 Bug fix: changed from while() to if() to avoid infinite loop possibility
// in IDNO case.
if(dib->palettechanged)  	// only true if user manually altered specific colors
	switch(MessageBox("Save changed palette as .MAP?",".MAP File Save",
							MB_YESNOCANCEL | MB_ICONQUESTION))
	{
		case IDYES:		CmColorsSaveAs(); break;
		case IDNO:      break;
		case IDCANCEL:	return(FALSE);	// user didn't resolve the question.
	}
return TRUE;
}                 				//QuerySavePalette
//----------------------------------------------------------------------------
BOOL SDibWindow::CanClose()
{
if(!TWindow::CanClose() || !QuerySavePalette())
	return(FALSE);
return(TRUE);
}                				//CanClose
//----------------------------------------------------------------------------
// Called whenever window needs redrawing.  OWLPG:37  modified from bmpview.cpp
// copies information from the permanent dib to the current screen.
// Note the parms are provided for your USE: dc is pre-created for you, erase indicates
// whether YOUR OWN Invalidate() calls specified TRUE, and invalidarea has been
// accumulated by Windows as the total of regions specified by your Invalidate() calls.
// See VCPP:49 for drawing speed tips.
void SDibWindow::Paint(TDC& dc, BOOL /* erase */, TRect& invalidarea)
{
// minimized, no point doing anything, or selecting an area, leave screen alone
if(DragDC || IsIconic())
// #error test sometime whether an iconic window makes all its children iconic.
// here, a Parent TMDIChild COULD be even if the main window isn't.
// || GetApplication()->GetMainWindow()->IsIconic() || (Parent && Parent->IsIconic()))
	return;

// "redraw entire window because palette or fittowindow mode may have changed"
// still true?  Maybe not.  Each action SHOULD be Invalidating only the area it affects.
// So even a simple test if(invalidarea == GetClientRect()) might help.

// This is the original version, reliable.  Keep.
// if(fittowindow) dc.StretchDIBits(GetClientRect(),dib->Rect(),*dib,SRCCOPY);
// else 		   dc.SetDIBitsToDevice(dib->Rect(),TPoint(0,0),*dib);

// stretch method won't change; just stretch entire design to entire window:
// if you try to do partial region, you must calculate the dib startpoint
// to be the same % of its size as the invalidarea is of the screen's size.
// e.g. if the invalidarea starts at (50% width, 50% height) of the window, you must
// get data from the dib at (50% width, 50% height) of IT.  But this seems likely to
// introduce rounding-to-int error artifacts, and any error will be obvious in the display.

// SEE WINAPI PAINTING AND DRAWING OVERVIEW, and GetUpdateRect()
// Experiments to retry in SetDIBitsToDevice.  Low priority because they may be difficult,
// drawing could be inaccurate, and current drawing isn't that slow.
// 1. replace dib->Rect() with GetClientRect because it's really supposed to be the destination.
//    first try, it didn't crash when dib didn't have enough data to fill the area,
//    but there were errors.
// 2. change parms to (invalidarea,invalidarea.TopLeft(),*dib);
// 3. Use IntersectClipRect to determine what portion of the invalid area intersects
//    the data that the dib can provide to draw in it?

// StretchDIBits seems to be implemented as SetDIBits if the 2 areas are identical.
// if so, you could call Stretch, and fittowindow would be unnecessary, EXCEPT that
// in some pgms I specifically DON'T want to stretch, even if I could.  This is a
// reminder NOT to make that change.

int i;   					// temporary, to see if errors CAN occur (yes, they can)
if(fittowindow)	// fittowindow means "always force stretch"
							// dest        source
	i = dc.StretchDIBits(GetClientRect(),dib->Rect(),*dib,SRCCOPY);  // fits to screen
else            // this one can be used, even if the 2 rects aren't the same
							// dest      dib origin
	i = dc.SetDIBitsToDevice(dib->Rect(),TPoint(0,0), *dib);
if(!i)
{
	ostrstream os;
	os << "StretchDIBits or SetDIBitsToDevice failure in Paint" << endl;
	os << "Client rect: " << GetClientRect() << endl;
	os << "Dib rect   : " << dib->Rect() << endl;
	os << "invalidarea: " << invalidarea << endl;
	os << ends;
	string s = os.str();
	delete[] os.str();
	logerror(s,"error.log");
}
}								//Paint
//----------------------------------------------------------------------------
BOOL SDibWindow::IdleAction(long /* idlecount */)
{
if(dib->RotateColors(autorotatecolors))	// rotate by -1, 0, or +1
	Invalidate(FALSE); 					// if it WAS rotated, Invalidate entire screen
return(TRUE);
}                 				//IdleAction
//----------------------------------------------------------------------------
// fill the window with black when you don't want to wait for Invalidate
void SDibWindow::EraseWindow(TColor color)
{
TClientDC(*this).FillRect(GetClientRect(),TBrush(color));
}  	        					//EraseWindow
//----------------------------------------------------------------------------
// sets caption for this window and Parent, since this class always sits in a parent window.
void SDibWindow::SetParentCaption(const string& caption)
{
SetCaption(caption.c_str());
if(Parent)
	Parent->SetCaption(caption.c_str());
}                  				//SetParentCaption
//----------------------------------------------------------------------------
// Edit one of the FileLists in a dialog box with a multi-line edit control.
void SDibWindow::CmEditFileList(WPARAM cmd)
{
FileArray* list = 0;
switch(cmd)
{
	case CM_COLORSEDITLIST: list = &MapFiles; break;
	case CM_FILEEDITLIST:	list = &FileList; break;
	default: return;
}
ListEditorStruct* tts = new ListEditorStruct;	// newed because it's big

// first write (in TEdit-incompatible format) using filechars as a temp buf
ostrstream(tts->filechars,sizeof(tts->filechars)) << (*list) << ends;

// now change text to TEdit format and write it back in again
LFtoCRLF(string(tts->filechars)).copy(tts->filechars,sizeof(tts->filechars));

if(SListEditDialog(this,tts).Execute() == IDOK)
{
	list->Flush();						// load the edited list
	istrstream(tts->filechars) >> (*list);
}
delete tts;
}	                     	//CmEditFileList
//----------------------------------------------------------------------------
// Some CmView menu toggles can be handled in one fn CmViewItemToggle(WPARAM cmd),
// with the item-specific code in switch cases, but by the time there are enough
// items to warrant it, it's probably worth creating a dialog box for the settings.
//----------------------------------------------------------------------------
// toggle whether to fit the design to the window and reflect the change on the menu bar
void SDibWindow::CmViewFitToWindow()
{
fittowindow = !fittowindow;
CheckMenuItem(GetApplication()->GetMainWindow()->GetMenu(),CM_VIEWFITTOWINDOW,
					MF_BYCOMMAND | (fittowindow ? MF_CHECKED : MF_UNCHECKED));
Invalidate(FALSE);		// force redisplay in new fittowindow mode
}                      	//CmViewFitToWindow
//----------------------------------------------------------------------------
// -1 = backwards, 0 = none, 1 = forward.  menu item is checked if direction is nonzero.
void SDibWindow::CmViewAutoRotateColors()
{
if(++autorotatecolors > 1)
	autorotatecolors = -1;
CheckMenuItem(GetApplication()->GetMainWindow()->GetMenu(),CM_VIEWAUTOROTATECOLORS,
					MF_BYCOMMAND | (autorotatecolors ? MF_CHECKED : MF_UNCHECKED));
}                      	//CmViewAutoRotateColors
//----------------------------------------------------------------------------
// handles the various palette manipulation commands, especially the ones
// where user might hold down key to auto-repeat.  You can disallow these when
// pgm is busy (calculating or drawing) by overriding this virtual fn
// in your derived class, and calling this one only if(when) you want to allow it,
// OR turning off other activities when these are called.  (see wshowfs.cpp for example)
void SDibWindow::CmColorsManip(WPARAM cmd)
{
BOOL result = FALSE;
switch(cmd)
{
	case CM_COLORSRANDOMIZE:	result = dib->RandomizeColors(FirstFreeColor); 	break;
	case CM_COLORSREVERSE:      result = dib->ReverseColors(); 					break;
	case CM_COLORSCOMPLEMENT:   result = dib->ComplementColors(); 				break;
	case CM_COLORSLIGHTEN:      result = dib->LightenColors(); 					break;
	case CM_COLORSDARKEN:       result = dib->DarkenColors(); 					break;
	case CM_COLORSROTATEFORWARD:
	case CM_COLORSROTATEBACKWARD:
		while(autorotatecolors) 		// user wants manual rotation, so turn autorotate off
			CmViewAutoRotateColors();   // (and provides a 1-step method to stop autorotation)
		result = dib->RotateColors((cmd == CM_COLORSROTATEFORWARD) ? 1 : -1);
		break;
	case CM_COLORSNEXTMAP: 		LoadMAP(MapFiles.Next()); result = TRUE; break;
	case CM_COLORSPREVIOUSMAP:  LoadMAP(MapFiles.Prev()); result = TRUE; break;
	default:
		break;
}
if(HWindow)		// This is supposed to allow calling this fn from ctors.
	if(result)
		Invalidate(FALSE);
}                        		//CmColorsManip
//----------------------------------------------------------------------------
// begin defining a new area to calculate
void SDibWindow::EvLButtonDown(UINT modkeys , TPoint& point)
{
TWindow::EvLButtonDown(modkeys,point);
if(DragDC)
	return;
SetCapture(); 					// all mouse events to go current window
DragDC = new TClientDC(*this);
DragStart = DragEnd = point;	// so (start == end) can test for a useless drag
DragDC->MoveTo(point);			// our starting current position
}                        		//EvLButtonDown
//----------------------------------------------------------------------------
// drag cursor to define a new calculation area
void SDibWindow::EvMouseMove(UINT /* modkeys */, TPoint& point)
{
if(!DragDC)		// only allow processing if left button is currently pressed.
	return;
TPoint old;
DragDC->GetCurrentPosition(old);
DragDC->DrawFocusRect(TRect(DragStart,old).Normalize());	// erase the old rectangle
					// you could constrain selection box's shape here to match the window.
DragDC->DrawFocusRect(TRect(DragStart,point).Normalize());	// draw the new rectangle
DragDC->MoveTo(point);										// update current position
// Invalidate(FALSE);		// save as reminder for other pgms that this is unnecessary
}                      		//EvMouseMove
//----------------------------------------------------------------------------
// RELEASED left mouse button after defining a selection area.
void SDibWindow::EvLButtonUp(UINT modkeys, TPoint& point)
{
TWindow::EvLButtonUp(modkeys,point);
if(!DragDC)				// if something messed up, do nothing.
	return;
ReleaseCapture();		// Windows API function, release mouse capture
delete DragDC;
DragDC = 0;
DragEnd = point;		// caller can now use DragStart and DragEnd as needed.
						// calculate other useful stats about the drag
						// Normalize in case user dragged up and left
DibDragRect = DragRect = TRect(DragStart,DragEnd).Normalize();

TRect clientrect = GetClientRect();					// get screen dimensions
// calculate what percent the point coordinates are of the screen dimensions
// ex: if width is 800, rightmost point of 799 translates to 1.0.
// store in a DoubleRect for convenience.
// THE CONSTRUCTOR NORMALIZES IT, WHICH MAKES IT UPSIDE DOWN COMPARED TO DRAGRECT,
// WHICH HAS HIGHER Y VALUE AT BOTTOM.  DON'T CHANGE THE CALCULATION HERE,
// BECAUSE DOUBLERECT FREQUENTLY AUTO-NORMALIZES ITSELF, WHICH WOULD UNDO THE CHANGE, ANYWAY.
// INSTEAD, BE SURE TO NORMALIZE() ANY TRECT THAT YOU CREATE USING DRAGPCT.
DragPct = DoubleRect((double)(DragRect.TopLeft().x) 	/ (clientrect.Width() - 1),
					 (double)(DragRect.TopLeft().y) 	/ (clientrect.Height() - 1),
					 (double)(DragRect.BottomRight().x) / (clientrect.Width() - 1),
					 (double)(DragRect.BottomRight().y) / (clientrect.Height() - 1) );

// if image ACTUALLY IS being stretched, calculate the drag region in dib coordinates, too.
// otherwise DibDragRect, which is in client coordinates, IS already the area we want!
if(fittowindow && (clientrect != dib->Rect()))
{
	// translate screen percents to coordinates in dib.
	// Normalize() is REQUIRED because DragPct is inherently upside down.
	DibDragRect = TRect(DragPct.left   * dib->Width(),
						DragPct.top    * dib->Height(),
						DragPct.right  * dib->Width(),
						DragPct.bottom * dib->Height() ).Normalize();
}
}       	            		//EvLButtonUp
//----------------------------------------------------------------------------
// Edit the color under the cursor at the time of the mouse click
void SDibWindow::EvRButtonDown(UINT modkeys, TPoint& point)
{
// See OWLRG:61.  It should be possible to make the box modeless, AND allow
// user to click around in the design, changing the dialog's color accordingly.
// You add to a particular location by clicking that location before Adding color.
TWindow::EvRButtonDown(modkeys,point);			// calls DefaultProcessing();
if(dib->Locked)
{
	MessageBox("Colors are locked while drawing.","Cannot Edit Color",MB_OK);
	return;
}
TColor color = TClientDC(*this).GetPixel(point);// get color at the screen cursor point
TPoint dibpoint = point;		// but what we really need is the color at the DIB's pixel,
TRect r = GetClientRect();
if(r != dib->Rect())			// and if stretch is on, it's not the same point.
{
	// calculate the equivalent point in the dib, if possible
	dibpoint = TPoint(MulDiv(dibpoint.x, dib->Width() - 1, r.Width() - 1),
					  MulDiv(dibpoint.y, dib->Height() - 1, r.Height() - 1));
	// if the color at the dib's pixel is the same as the screen's color at its pixel,
	// then our calculation probably succeeded, and we have a good chance of
	// identifying the exact palette entry to change.  else require that window:dib be 1:1.
	if(TDibDC(*dib).GetPixel(dibpoint) != color)
	{
		MessageBox("Could not locate color to edit.\nTurn off FitToWindow and retry.",
					"Cannot Edit Color",MB_OK);
		return;
	}
}
TChooseColorDialog::TData choose;				// variable declaration: a structure
choose.Color = color;
choose.CustColors = MyCustomColors;				// a global colors array in mylib.cpp
choose.Flags = CC_FULLOPEN | CC_RGBINIT;
if(TChooseColorDialog(this,choose).Execute() == IDOK)
{
// 	dib->MapColor(color,choose.Color,FALSE);  	// old method: could set wrong pal entry
	dib->SetColor(dib->GetPixelByte(dibpoint),choose.Color);	// new: set exact entry
	dib->palettechanged = TRUE;
	Invalidate(FALSE);
}
}							//EvRButtonDown
//----------------------------------------------------------------------------
// Loads color palette from a Fractint .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 TRUE if loaded, FALSE if it failed.
BOOL SDibWindow::LoadMAP(const string& filename)
{
if(!QuerySavePalette())
	return FALSE;
Invalidate(FALSE);			// even for error, so you can see whatever did result
if(!dib->LoadColors(filename) && !dib->Locked)
{
	string s = ".MAP file (" + filename + ") invalid or corrupt";
	MessageBox(s.c_str(),"Cannot Load Colors",MB_OK);
	return(FALSE);
}
return(TRUE);
}              				//LoadMAP
//----------------------------------------------------------------------------
// user selects MAP file to load
void SDibWindow::CmColorsLoad()
{
chdir(MAPDir.c_str());  	// this works; never did figure out InitialDir usage
// static so that the name of the last-loaded .map file is the default
static TOpenSaveDialog::TData COLFileData(OFN_FILEMUSTEXIST,	// flags ok
	"MAP Files (*.MAP)|*.MAP|All Files (*.*)|*.*|",0,0,"MAP");
if(TFileOpenDialog(this,COLFileData).Execute() != IDOK)			// select file
	return;
char buf[MAXPATH];
getcwd(buf,MAXPATH);       			// if user changed directory,
MAPDir = buf;                       // save the new default.
LoadMAP(COLFileData.FileName);
}               			//CmColorsLoad
//----------------------------------------------------------------------------
// Save the current colors into a Fractint-compatible .MAP file
void SDibWindow::CmColorsSaveAs()
{
chdir(MAPDir.c_str());
// static:  since you can't change dir, this at least remembers what it was.
static TOpenSaveDialog::TData COLFileData(						// flags ok
	OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
	"MAP Files (*.MAP)|*.MAP|All Files (*.*)|*.*|",0,0,"MAP");
if(TFileSaveDialog(this,COLFileData).Execute() != IDOK)
	return;
char buf[MAXPATH];
getcwd(buf,MAXPATH);
MAPDir = buf;
dib->WriteColors(COLFileData.FileName);
}                  	    		//CmColorsSaveAs
//----------------------------------------------------------------------------
void SDibWindow::CmEffectsRandomizeHeights()
{
HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
if(dib->RandomizeHeights())
	Invalidate(FALSE);
::SetCursor(oldcursor);  	// restore cursor
}                   			//CmEffectsRandomizeHeights
//----------------------------------------------------------------------------
// any user-specified settings, and any multiple passes, should be handled here.
// it is too trivial an effect to provide for it in IdleAction, with its complications.
void SDibWindow::CmEffectsAverage()
{
static uint passcount = 1;
static int creep = FALSE;
char buf[20];
ostrstream(buf,sizeof(buf)) << passcount << " " << creep << ends;
if(TInputDialog(this,"Number of Averaging Passes and Mode",
	"Modes 0=average, 1=creep\nEntry Format: passcount mode",
	buf,sizeof(buf)).Execute() != IDOK)
		return;
istrstream(buf) >> passcount >> creep;
if(!passcount)
	return;
HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
for(int i = 0 ; i < passcount ; i++)
	if(!dib->AveragePixels(creep))
		break;
Invalidate(FALSE);
::SetCursor(oldcursor);  	// restore cursor
}                         	//CmEffectsAverage
//----------------------------------------------------------------------------
void SDibWindow::CmEffectsInvertHeights()
{
HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
if(dib->InvertHeights())
	Invalidate(FALSE);
::SetCursor(oldcursor);  	// restore cursor
}
//----------------------------------------------------------------------------
void SDibWindow::CmEffectsRandomizePixels()
{
HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
if(dib->RandomizeField())
	Invalidate(FALSE);
::SetCursor(oldcursor);  	// restore cursor
}
//----------------------------------------------------------------------------
void SDibWindow::CmEffectsFlipHorizontal()
{
if(dib->FlipHorizontal())
	Invalidate(FALSE);
}
//----------------------------------------------------------------------------
void SDibWindow::CmEffectsFlipVertical()
{
if(dib->FlipVertical())
	Invalidate(FALSE);
}
//----------------------------------------------------------------------------
// reinitialize dib, 1) from .BMP file or 2) with specified extents, or 3) sized to window,
// depending on which parameters are supplied. Optionally copy the old dib's pixels
// so that whatever portion of an OLD design hasn't yet been overwritten remains visible.
// Otherwise, it's just a big blank area.
// This is a base class.  Keep in mind what is general enough to do here.
// It now contains all the methods from all makenewdib() fns in all pgms.
// returns FALSE if file load failed, TRUE otherwise.  Any serious error closes application.
// This is a virtual fn.
BOOL SDibWindow::makenewdib(const char* filename, int width, int height, BOOL copypixels)
{
BOOL result = TRUE;
SDib* newdib = 0;
if(filename)         						// create the dib from a file
{
	newdib = new SDib(filename);
	if(newdib->IsOK())  					// tentatively ok.  now check modes.
	{
		// ensure colors is 256, mode is RGB. There are still .BMP files created by
		// other apps that can cause nonspecific GDI Errors.
		if(newdib->Usage() != DIB_RGB_COLORS)
		{
			// logerror is just to see if it ever happens, and succeeds; then rem out
			logerror("Changing DIB mode to DIB_RGB_COLORS","error.log",TRUE);
			// give it dib's palette because we know it has 256 colors in it
			if(!newdib->ChangeModeToRGB(TPalette(*dib)))
			{
				// currently, I have no idea how this can be fixed.
				delete newdib;
				newdib = 0;
				MessageBox("Could not convert DIB to RGB mode.","DIB Load Error",MB_OK);
				result = FALSE;  		// consider it a nondisaster, unless it ever crashes
			}
		}
		if(newdib->NumColors() != 256)	// catch both < and >
		{
			// this does occur, and is successfully converted.
			// logerror("Changing DIB to 256 COLORS","error.log",TRUE);
			SDib* biggerdib = 0;
			if((biggerdib = DibResize(newdib,0,0,256,TRUE)) != 0)
			{
				delete newdib;
				newdib = biggerdib;
			}
			else
			{
				delete newdib;
				newdib = 0;
				MessageBox("Could not resize DIB color table.","DIB Error",MB_OK);
				result = FALSE;  		// consider a nondisaster, unless it ever crashes
			}
		}
		// seems very rare you'd want this for a file, but keep to remember how.
		// newdib->CopyColors(*dib);		// copy colors from the old dib to the new
	}
	else									// file read failed
	{
		delete newdib;
		newdib = 0;
		string s = string("Bitmap file ") + filename + " is not usable by this program.";
		MessageBox(s.c_str(),"File Load Error",MB_OK);
		result = FALSE;
	}
}
if(!newdib) 								// if no filename, or file failed
{
	if(!width || !height)	 				// if sizing it to the display window,
	{                                       // get window size into width,height
		TRect clientrect = GetClientRect(); // current size of the display window
		if(clientrect == dib->Rect())		// if already matched, no changes required.
		{
			// caller wanted a fresh dib, so simulate it by erasing.
			// I decided against enabling this:  caller may have specified
			// copypixels == FALSE only to save time, which this would defeat.
			// I'm also unsure how enabling it would affect existing apps that use this.
			// So if you want a blank dib, call Erase() after calling makenewdib().
			// if(!copypixels)
			// 	dib->Erase(dib->GetColor(0));
			return(result);					// existing dib is still ok to use.
		}
		width = clientrect.Width();
		height = clientrect.Height();
	}
	// copy palette and optionally pixels, 0=fail
	newdib = DibResize(dib,width,height,256,copypixels);	// this does the IsOK() test
	if(!newdib)
	{
		// closing is not strictly necessary, since app still has valid old dib to use,
		// but without knowing exactly what went wrong, closing seems safest.
		// There is no normal reason that DibResize in this situation should fail.
		string s = "DIB creation error. Application will close.";
		MessageBox(s.c_str(),GetApplication()->GetName(),MB_OK);
		CmFileExit();
	}
}
delete dib;						// delete old dib
dib = newdib;  					// install new dib
return(result);
}								//makenewdib
//----------------------------------------------------------------------------
// opens the .PIC file, and uses its data to draw onto dib.
// returns TRUE if file successfully loaded, FALSE if not
// Could check whether associated .inf file is available; if so, read it into curr.
// KEEP THIS VERSION as a reference, even though it's obsolete.  It's small.
BOOL SDibWindow::LoadPIC(const string& filename)
{
int maxx = 0, maxy = 0;			// default values in case of file error

ifstream infile(filename.c_str(),ios::binary);
infile >> maxx; infile.ignore(1); infile >> maxy; infile.ignore(1);	// width,height\n
if(infile.fail() || !maxx || !maxy)	// if the reads failed, file is no good (or none)
	return(FALSE);

SetParentCaption(string(GetApplication()->GetName()) + " - " + filename);

HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
// no point copying pixels into area the next loop will completely overwrite,
// UNLESS this fn gets broken into IdleAction pieces (no need.  this is fast enough)
makenewdib(0,maxx,maxy,FALSE);  // create new dib to match image size; the new dib is unlocked
TDibDC dibdc(*dib);				// for drawing to dib
TClientDC sc(*this);   			// allow watching it being drawn (because it takes a while)
// int x;
TColor color;
uchar* buf = new uchar[maxx];
for(int y = 0 ; infile && (y < maxy) ; y++)
	if(infile.read(buf,maxx))
	{
		dib->SetScanLineBytes(y,buf);	// new: set BMP bytes directly
// 		for(x = 0 ; x < maxx ; x++)
// 		{
// 			color = dib->GetColor(buf[x]);
// 			dibdc.SetPixel(x,y,color);	// old: set each pixel
// 			sc.SetPixel(x,y,color);		// draw on screen (only minor slowdown)
// 		}

	}
delete[] buf;
if(fittowindow)             // first display is in its "real" size
	CmViewFitToWindow();    // so toggle it to turn it off
Invalidate(FALSE);			// force displaying the entire new image
::SetCursor(oldcursor);  	// restore cursor
return(!infile.fail());
}							//LoadPIC
//----------------------------------------------------------------------------
// loads a .PIC file onto dib (and screen), with optional 3d drawing options.
// Some 3d modes seem pretty useless, but leave them in so I don't forget what they
// look like and then want them back, having forgotten that they're useless!
// returns TRUE if file successfully loaded, FALSE if not
BOOL SDibWindow::LoadPIC3d(const string& filename)
{
static uint iniskip = 0;		// number of leading lines to skip over completely
static uint skip = 0;			// number of lines to skip between displayed lines
static uint dispmode = 0;       // how to plot.  default = overhead.
static double vertsizeadj = 1;	// a vertical scaling control for displayed image
//----------------------------	// get 3d drawing options from user
PICOpStruct w;        			// transfer struct for the dialog

w.overhead 	 = (dispmode == 0);
w.drawpts 	 = (dispmode == 1);
w.ovpts 	 = (dispmode == 2);
w.drawlines	 = (dispmode == 3);
w.ovlines 	 = (dispmode == 4);
w.areaheight = (dispmode == 5);
w.areanear 	 = (dispmode == 6);

ostrstream(w.iniskiptext,sizeof(w.iniskiptext)) << iniskip << ends;
ostrstream(w.skiptext,sizeof(w.skiptext)) << skip << ends;
ostrstream(w.vertsizetext,sizeof(w.vertsizetext)) << vertsizeadj << ends;

if(PICOptionsDialog(this,&w).Execute() != IDOK)		// dialog: get 3d options from user
	return(TRUE);  									// prevents error message

if(w.overhead)		dispmode = 0;
else if(w.drawpts) 		dispmode = 1;
else if(w.ovpts) 		dispmode = 2;
else if(w.drawlines) 	dispmode = 3;
else if(w.ovlines) 		dispmode = 4;
else if(w.areaheight) 	dispmode = 5;
else if(w.areanear) 	dispmode = 6;
else					dispmode = 0;

istrstream(w.iniskiptext) >> iniskip;
istrstream(w.skiptext) >> skip;
double d;
istrstream(w.vertsizetext) >> d;
if((d > 0) && (d < 256))	// 0 illegal, > 255 pointless (way off screen vertically)
	vertsizeadj = d;
//----------------------------
int maxx = 0, maxy = 0;						// default values are in case of file read error

ifstream infile(filename.c_str(),ios::binary);
infile >> maxx; infile.ignore(1); infile >> maxy; infile.ignore(1);	// width,height\n
int fline = 0;								// scanline # from file being displayed
for(int i = 0 ; i < iniskip ; i++, fline++)	// skip over initial ignored lines.
	infile.ignore(maxx);            		// ignore(iniskip * maxx) could overflow int
if(infile.fail() || !maxx || !maxy)			// if the read failed, file's no good, or none,
	return(FALSE);                          // or used up.

SetParentCaption(string(GetApplication()->GetName()) + " - " + filename);

HCURSOR oldcursor = ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
makenewdib(0,maxx,maxy,FALSE);  // create new dib to match PIC file image size
TDibDC dibdc(*dib);				// for drawing to dib

EraseWindow();					// prepare to draw to the screen simultaneously
TClientDC sc(*this);   			// to draw on (REQUIRED when erasing between plots)
sc.SetBkColor(TColor::Black); 	// background and text colors for line number status display
sc.SetTextColor(TColor::White);

//----------
// Proper drawing to the dib so the end display is good is the primary task for all cases,
// so the dib is sized to match the PIC file, and heights are scaled to the dib height.
// the accuracy of the window drawing is secondary.
// all options draw to both dib and window.  dib version (where points HAVE
// accumulated even if overlay wasn't used) gets painted to screen after exit.
// NOTE: YOU NOW *MUST* SIZE TO DIB, NOT WINDOW

int dibheight = dib->Height() - 1;		// Height() - 1 is correct

// scalefactor scales input values so the lowest possible (0) is at dib bottom,
// highest possible (255) is at dib top.  vertsizeadj then determines what percent
// of this maximum possible vertical range is actually used.
double scalefactor = (double)dibheight / 255;	// .pic file can only have values 0 to 255
scalefactor *= vertsizeadj;             		// adjust final vertical height
//----------
// precalculate just once all the possible y values you may have to plot
int* yvals = new int[256];		// holds all possible actual y axis values to plot
for(i = 0 ; i < 256 ; i++)
	yvals[i] = (int)range((double)0,(double)dibheight,dibheight - (i * scalefactor));
//----------
// points array for Polyline
TPoint* pts = new TPoint[maxx + 2];
pts[maxx]     = TPoint(maxx - 1, dibheight);	// preset bottom right
pts[maxx + 1] = TPoint(0       , dibheight);	// preset bottom left
//----------
uchar* buf = new uchar[maxx];	// enough chars for 1 x-axis line of pixels (=1 scanline)
char textbuf[20];  				// text of the line number, for status line display
TColor color = dibdc.GetNearestColor(TColor::White);	// initialized for cases 1,3
int colorindex = 0;  		// index into dib colors, initialization reqd for cases 2,4,6
int x, y;                   // point being plotted
BOOL firstentry = TRUE;		// some cases use it
switch(dispmode)
{
	case 0:           		// overhead: straight copy .PIC to screen, color = height
		for(y = 0 ; infile.read(buf,maxx) ; y++)
		{
			// keep for some cases: the line #s help you plan iniskip and skip values
// 			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
// 			for(x = 0 ; x < maxx ; x++)
// 			{
// 				color = dib->GetColor(buf[x]);
// 				dibdc.SetPixel(x,y,color);			// uses dib's palette
// 				sc.SetPixel(x,y,color);				// draw on screen (only minor slowdown)
// 			}
			dib->SetScanLineBytes(y,buf);			// new: set BMP bytes directly - FAST!
			for(i = 0 ; i < skip ; i++, fline++)    // if skip is 0, nothing happens
				infile.ignore(maxx);                // eof ok, will just exit on next pass
		}
		break;
	case 1:										// points, with erase, 1 color
		colorindex = dib->FindColor(color);		// get index (any ok) of nearest color
		if(colorindex < 0) colorindex = 1;		// prevent negative: must have SOMETHING
		while(infile.read(buf,maxx))
		{
// 			EraseWindow();								// old: screen flicker
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			// points stayed on screen for maximum possible time, through read, etc.
			if(!firstentry)
				for(x = 0 ; x < maxx ; x++)				// erase prior points
					sc.SetPixel(pts[x],TColor::Black);	// assume black for now
			for(x = 0 ; x < maxx ; x++)      			// calc and set the points
			{
				pts[x] = TPoint(x,yvals[buf[x]]);		// save, for erasure
//				dibdc.SetPixel(pts[x],color);			// dib uses its own preset (above) color
				dib->SetPixelByte(pts[x],(uchar)colorindex);  // new: write byte directly
				sc.SetPixel(pts[x],TColor::White);		// screen uses white
			}
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
			firstentry = FALSE;
		}
		break;
	case 2:										// overlay points, color = nearness
		while(infile.read(buf,maxx))
		{
// 			EraseWindow();  					// here in case cases 2,3 get merged
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			for(x = 0 ; x < maxx ; x++)
			{
				y = yvals[buf[x]];
				dib->SetPixelByte(x,y,(uchar)colorindex);  // new: write byte directly
				color = dib->GetColor(colorindex);
// 				dibdc.SetPixel(x,y,color);
				sc.SetPixel(x,y,color);
			}
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
			if(++colorindex >= dib->NumColors())
				colorindex = 0;
		}
		break;
	case 3:										// LINES, with erase, 1 color
		while(infile.read(buf,maxx))
		{
// 			EraseWindow();
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			if(!firstentry)
			{
				sc.SelectObject(TPen(TColor::Black));
				sc.MoveTo(pts[0]);                      // erase prior line on screen only
				sc.Polyline(pts,maxx);					// very fast
				sc.RestorePen();
			}
			for(x = 0 ; x < maxx ; x++)
				pts[x] = TPoint(x,yvals[buf[x]]);
			dibdc.SelectObject(TPen(color));
			sc.SelectObject(TPen(TColor::White));
			dibdc.MoveTo(pts[0]);					// set starting cp
			sc.MoveTo(pts[0]);
			dibdc.Polyline(pts,maxx);        		// might not set last pt
			sc.Polyline(pts,maxx);					// very fast
			dibdc.RestorePen();
			sc.RestorePen();
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
			firstentry = FALSE;
		}
		break;
	case 4:									// OVERLAY LINES, color = nearness
		while(infile.read(buf,maxx))
		{
// 			EraseWindow();
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			for(x = 0 ; x < maxx ; x++)
				pts[x] = TPoint(x,yvals[buf[x]]);
			color = dib->GetColor(colorindex);
			dibdc.SelectObject(TPen(color));
			sc.SelectObject(TPen(color));
			dibdc.MoveTo(pts[0]);
			sc.MoveTo(pts[0]);
			dibdc.Polyline(pts,maxx);
			sc.Polyline(pts,maxx);
			dibdc.RestorePen();
			sc.RestorePen();
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
			if(++colorindex >= dib->NumColors())
				colorindex = 0;
		}
		break;
	case 5:									// FILL AREAS, color = height
		while(infile.read(buf,maxx))
		{
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			for(x = 0 ; x < maxx ; x++)
			{
				y = yvals[buf[x]];
				color = dib->GetColor(buf[x]);
				dibdc.SelectObject(TPen(color));
				sc.SelectObject(TPen(color));
				dibdc.MoveTo(x,y);
				sc.MoveTo(x,y);
				dibdc.LineTo(x,dibheight);		// probably doesn't set bottom pixel
				sc.LineTo(x,dibheight);
				dibdc.RestorePen();
				sc.RestorePen();
			}
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
		}
		break;
	case 6:									// FILL AREAS, color = nearness
		// this case is now very fast
		while(infile.read(buf,maxx))
		{
			ostrstream(textbuf,sizeof(textbuf)) << ++fline << ends; sc.TextOut(0,0,textbuf);
			for(x = 0 ; x < maxx ; x++)
				pts[x] = TPoint(x,yvals[buf[x]]);
			color = dib->GetColor(colorindex);
			dibdc.SelectObject(TPen(color));
			sc.SelectObject(TPen(color));
			dibdc.SelectObject(TBrush(color));
			sc.SelectObject(TBrush(color));
			dibdc.MoveTo(pts[0]);
			sc.MoveTo(pts[0]);
			dibdc.Polygon(pts,maxx + 2);
			sc.Polygon(pts,maxx + 2);
			dibdc.RestorePen();
			sc.RestorePen();
			dibdc.RestoreBrush();
			sc.RestoreBrush();
			for(i = 0 ; i < skip ; i++, fline++)
				infile.ignore(maxx);
			if(++colorindex >= dib->NumColors())
				colorindex = 0;
		}
		break;
}						// END SWITCH
delete[] pts;
delete[] yvals;
delete[] buf;
if(fittowindow)             // first display is in its "real" size
	CmViewFitToWindow();    // so toggle it to turn it off
Invalidate(FALSE);			// force displaying the entire new image
::SetCursor(oldcursor);  	// restore cursor
return(TRUE);
// return(!infile.fail());	// old: the whiles() above read UNTIL it fails
}		   		           	//LoadPIC3d
//----------------------------------------------------------------------------
// opens the .BMP file.
// returns TRUE if file successfully loaded, FALSE if not
// This now allows palette animation, etc. with any BMP file!  But some BMP files
// created by other apps seem incompatible with OWL TDib format (GDI Errors).
BOOL SDibWindow::LoadBMP(const string& filename)
{
Invalidate(FALSE);			// force displaying the entire new image (whatever it is)
if(fittowindow)             // first display is in its "real" size
	CmViewFitToWindow();    // so toggle it to turn it off
if(!makenewdib(filename.c_str()))
	return(FALSE);
SetParentCaption(string(GetApplication()->GetName()) + " - " + filename);
return(TRUE);
}							//LoadBMP
//----------------------------------------------------------------------------
// Write current design to a .bmp file
void SDibWindow::CmFileSaveAs()
{
// static so that the name of the last-saved file is the default
static TOpenSaveDialog::TData bmpfiledata(							// flags ok
	OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
	"Bitmap Files (*.BMP)|*.BMP|PIC Files (*.PIC)|*.PIC|All Files (*.*)|*.*|",0,0,"BMP");
if(TFileSaveDialog(this,bmpfiledata).Execute() != IDOK)	// select file
	return;
string s = bmpfiledata.FileName;
BOOL result = FALSE;
if(s.contains(".bmp"))
	result = dib->WriteFile(s.c_str());
else
	if(s.contains(".pic"))
		result = dib->WriteFilePIC(s);
	else
		MessageBox("Can only save .PIC,.BMP files.",GetApplication()->GetName(),MB_OK);
if(!result)						// load error
{
	s.to_upper();
	s.prepend("Error saving file: ");
	MessageBox(s.c_str(),GetApplication()->GetName(),MB_OK);
}

//----------------------------------
#if 0
// THIS SECTION TESTS SDIB::GETPIXELBYTE AND SETPIXELBYTE
// KEEP, COMMENTED OUT, FOR FURTHER TESTING.
// this section creates a newdib to match dib, and uses SetPixelByte to copy the pixels.
// then write it to a BMP, and test this bmp against the one just saved above.
// IF THIS WORKS, IT IS A GENERAL METHOD FOR OTHER PGMS: WAVGPIC, wlife2d, etc.
SDib* newdib = DibResize(dib,0,0,256,FALSE);
if(!newdib)
{
	string s = "DIB creation error. Application will close.";
	MessageBox(s.c_str(),GetApplication()->GetName(),MB_OK);
	CmFileExit();
}
TDibDC dibdc(*dib);
uchar value;
for(int y = 0 ; y < dib->Height(); y++)
	for(int x = 0 ; x < dib->Width(); x++)
	{
		value = dib->GetPixelByte(x,y);

// often IS diff: finds first occurrence
// 		int foundcolor = dib->FindColor(dibdc.GetPixel(x,y));
// 		if(value != (uchar)foundcolor)	// test against old method
// 		{
// 			string s = string("value = ") + tostring((int)value) + ", FindColor = " +
// 				tostring(foundcolor);
// 			logerror(s,"error.log",TRUE);
// 		}
		newdib->SetPixelByte(x,y,value);
	}
newdib->WriteFile("c:\\temp\\copied.bmp");
// THIS SECTION NOW WRITES NEWDIB AS A CONVERTED .PIC FILE, TO TEST SDIB::GETPIXELBYTE
// (this is also necessary because 2 bmp files never match: their pad bytes differ)
newdib->WriteFilePIC("c:\\temp\\bmp.pic");

delete newdib;
//----------------------------------
#endif 		// 0

}                      		//CmFileSaveAs
//----------------------------------------------------------------------------
// 						end class SDibWindow
//////////////////////////////////////////////////////////////////////////////

sdibwin.rh

// This file is part of the SDIBWIN project.
// Copyright (C)1993-2002 Steven Whitney.
// Initially published by http://25yearsofprogramming.com.
// Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
//
// Because portions of the code used for this file are copyrighted by Borland,
// there are some sections below where I indicate which Borland files you
// must copy into your copy of this file to make it functional.
// You need to copy into this file the #defined identifiers from
// the following files (look for the #define directives).

// 2/16/06: This is a list of the files you need to copy.
// Don't just add #includes for them, because I think that's what makes them available to
// Resource Workshop to change at their source, which is what we're trying to avoid.

\owl\window.rh
\owl\mdi.rh
\owl\except.rh
\owl\edit.rh
\owl\editsear.rh
\owl\editfile.rh
\owl\validate.rh
\owl\slider.rh
\owl\printer.rh

//----------------------------------------------------------------------------
// The following are identifiers to copy from *RC* (not RH) files.
//----------------------------------------------------------------------------
owl\statusba.rc
owl\editview.rc
owl\listview.rc
owl\docview.rc

// And #defines from...
\owl\inputdia.rh


//----------------------------------------------------------------------------
// Below this point are identifiers specific to SDIBWIN.

// FILELISTEDITOR	defines
//----------------------------------------------------------------------------
#define FILELISTEDITOR	2000
#define IDC_LISTEDIT	2001

//----------------------------------------------------------------------------
// THE COREMENU IS ONLY A SEED MENU FOR NEW PROJECTS.  YOU DON'T ACTUALLY USE IT.
//----------------------------------------------------------------------------
#define COREMENU					2
#define CM_FILEEDITLIST				24336
#define CM_FILEEXIT					24338
#define CM_VIEWAUTOROTATECOLORS		113
#define CM_VIEWFITTOWINDOW			106
#define CM_COLORSREVERSE			1000
#define CM_COLORSDARKEN				1005
#define CM_COLORSLIGHTEN			1004
#define CM_COLORSCOMPLEMENT			1003
#define CM_COLORSPREVIOUSMAP		1001
#define CM_COLORSNEXTMAP			1002
#define CM_COLORSROTATEBACKWARD		105
#define CM_COLORSROTATEFORWARD		104
#define CM_COLORSSAVEAS				103
#define CM_COLORSRANDOMIZE			102
#define CM_COLORSLOAD				101
#define CM_COLORSEDITLIST			116
#define CM_EFFECTSFLIPHORIZONTAL	123
#define CM_EFFECTSFLIPVERTICAL		122
#define CM_EFFECTSINVERTHEIGHTS		120
#define CM_EFFECTSRANDOMIZEHEIGHTS	118
#define CM_EFFECTSAVERAGE			119
#define CM_EFFECTSRANDOMIZEPIXELS	121
#define CM_HELPINDEX				24341

//----------------------------------------------------------------------------
// PICOptionsDialog defines
//----------------------------------------------------------------------------

#define PICFILEOPTIONS		1
#define IDC_AREAHEIGHT		106
#define IDC_AREANEARNESS	107
#define IDC_INISKIP			108
#define IDC_GROUPBOX1		111
#define IDC_VERTSIZE		110
#define IDC_SKIP			109
#define IDC_DRAWLINES		104
#define IDC_OVERLAYLINES	105
#define IDC_OVERLAYPOINTS	103
#define IDC_DRAWPOINTS		102
#define IDC_OVERHEAD		101

sdibwin.rc

/*

SDIBWIN.RC

This file is part of the SDIBWIN project.

Copyright (C)1999 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.

This file is not normally used as-is in your project; you copy some portions of it
into your new project's own .RC file. See the OWLRES.RC/.RH project.
Much of this file was either constructed from OWLRES.RC or built in the same manner,
and its purpose is the same, to provide a new project with all the starting 
resources it needs so later resources you add don't create conflicts.

Because portions of the code used for this file are copyrighted by Borland, 
there are sections below where I indicate which Borland files you
must copy into your copy of this file to make it functional.
When you have doubts about how much of a file you need, 
copy everything from the file to here. You can always remove
things that cause problems.

In places where you need to copy files (or portions of files), 
don't just add #includes for them, because I think that's what makes them 
available to Resource Workshop to change at their source.

*/

// However, THIS include is necessary.
#include "c:\bcs\library\sdibwin.rh"

// The menu items that give access to SDibWin features

COREMENU MENU
{
 POPUP "&File"
 {
  MENUITEM "&SaveAs...\tS", CM_FILESAVEAS
  MENUITEM SEPARATOR
  MENUITEM "Edit &List...", CM_FILEEDITLIST
  MENUITEM SEPARATOR
  MENUITEM "E&xit", CM_FILEEXIT
 }

POPUP "&Colors"
 {
  MENUITEM "Load .&MAP File...\tM", CM_COLORSLOAD
  MENUITEM "Save&As .MAP File...", CM_COLORSSAVEAS
  MENUITEM SEPARATOR
  MENUITEM "&Next .MAP File\t]", CM_COLORSNEXTMAP
  MENUITEM "&Previous .MAP File\t[", CM_COLORSPREVIOUSMAP
  MENUITEM "&Edit .MAP File List...", CM_COLORSEDITLIST
  MENUITEM SEPARATOR
  MENUITEM "&Randomize\tR", CM_COLORSRANDOMIZE
  MENUITEM "Re&verse\tV", CM_COLORSREVERSE
  MENUITEM "&Complement\tC", CM_COLORSCOMPLEMENT
  MENUITEM "&Lighten\tL", CM_COLORSLIGHTEN
  MENUITEM "&Darken\tD", CM_COLORSDARKEN
  MENUITEM SEPARATOR
  MENUITEM "Rotate&Forward\t>", CM_COLORSROTATEFORWARD
  MENUITEM "Rotate&Backward\t<", CM_COLORSROTATEBACKWARD
 }

POPUP "&View"
 {
  MENUITEM "&Fit to Window\tW", CM_VIEWFITTOWINDOW
  MENUITEM SEPARATOR
  MENUITEM "&AutoRotateColors\t= or +", CM_VIEWAUTOROTATECOLORS
 }

POPUP "&Effects"
 {
  MENUITEM "&Average Pixels\tG", CM_EFFECTSAVERAGE
  MENUITEM "Randomize &Pixels", CM_EFFECTSRANDOMIZEPIXELS
  MENUITEM SEPARATOR
  MENUITEM "&Rearrange Heights\tH", CM_EFFECTSRANDOMIZEHEIGHTS
  MENUITEM "&Invert Heights", CM_EFFECTSINVERTHEIGHTS
  MENUITEM SEPARATOR
  MENUITEM "Flip &Vertical", CM_EFFECTSFLIPVERTICAL
  MENUITEM "Flip &Horizontal", CM_EFFECTSFLIPHORIZONTAL
 }

POPUP "&Help"
 {
  MENUITEM "&Index\tF1", CM_HELPINDEX
 }

}

//----------------------------------------------------------------------------
// The accelerator keys that give access to SDibWin features
// COREMENU ACCELERATORS ARE ONLY SEEDS FOR NEW PROJECTS.
// A PROJECT MUST HAVE *ALL* ITS ACCELERATORS IN A SINGLE TABLE.
//----------------------------------------------------------------------------
COREMENU ACCELERATORS
{
 "m", CM_COLORSLOAD
 "M", CM_COLORSLOAD
 "r", CM_COLORSRANDOMIZE
 "R", CM_COLORSRANDOMIZE
 ".", CM_COLORSROTATEFORWARD
 ">", CM_COLORSROTATEFORWARD
 ",", CM_COLORSROTATEBACKWARD
 "<", CM_COLORSROTATEBACKWARD
 "]", CM_COLORSNEXTMAP
 "[", CM_COLORSPREVIOUSMAP
 "v", CM_COLORSREVERSE
 "V", CM_COLORSREVERSE
 "c", CM_COLORSCOMPLEMENT
 "C", CM_COLORSCOMPLEMENT
 "l", CM_COLORSLIGHTEN
 "L", CM_COLORSLIGHTEN
 "d", CM_COLORSDARKEN, ASCII
 "D", CM_COLORSDARKEN
 "w", CM_VIEWFITTOWINDOW
 "W", CM_VIEWFITTOWINDOW
 "+", CM_VIEWAUTOROTATECOLORS
 "=", CM_VIEWAUTOROTATECOLORS
 "s", CM_FILESAVEAS
 "S", CM_FILESAVEAS
 "h", CM_EFFECTSRANDOMIZEHEIGHTS
 "g", CM_EFFECTSAVERAGE, ASCII
 VK_F1, CM_HELPINDEX, VIRTKEY
}

// The strings describing SDibWin features

STRINGTABLE
{
 CM_FILEEXIT, "Exit program"
 CM_FILEEDITLIST, "Edit the list of DATA files"
 CM_FILESAVEAS, "Save current display in one of the supported formats"
 CM_COLORSNEXTMAP, "Load next .MAP file in rotating list."
 CM_COLORSPREVIOUSMAP, "Load previous .MAP file in rotating list."
 CM_COLORSLOAD, "Load color palette from a .MAP file"
 CM_COLORSROTATEFORWARD, "Rotate color palette 1 step forward"
 CM_COLORSROTATEBACKWARD, "Rotate color palette 1 step backward"
 CM_COLORSEDITLIST, "Edit the rotation list of .MAP files."
 CM_COLORSCOMPLEMENT, "Change each color to its complement (negative)"
 CM_COLORSLIGHTEN, "Lighten each color"
 CM_COLORSDARKEN, "Darken each color"
 CM_COLORSREVERSE, "Reverse the order of colors in the palette"
 CM_COLORSRANDOMIZE, "Fill palette with randomly generated colors"
 CM_COLORSSAVEAS, "Save the current palette to a .MAP file"
 CM_EFFECTSRANDOMIZEHEIGHTS, "Set all pixels to random color values"
 CM_EFFECTSAVERAGE, "Make 1 averaging pass"
 CM_EFFECTSINVERTHEIGHTS, "Change mountains to valleys and vice versa"
 CM_EFFECTSRANDOMIZEPIXELS, "Initialize display with a field of random pixels"
 CM_EFFECTSFLIPVERTICAL, "Flip display upside down"
 CM_EFFECTSFLIPHORIZONTAL, "Flip display left to right"
}

// dialogs used by SDibWin

FILELISTEDITOR DIALOG 97, 24, 170, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME
CAPTION "File List Editor"
FONT 8, "MS Sans Serif"
{
 EDITTEXT IDC_LISTEDIT, 12, 6, 147, 165, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_BORDER | WS_TABSTOP
 DEFPUSHBUTTON "OK", IDOK, 6, 183, 50, 14
 PUSHBUTTON "Cancel", IDCANCEL, 60, 183, 50, 14
 PUSHBUTTON "Help", IDHELP, 114, 183, 50, 14
}


PICFILEOPTIONS DIALOG 37, 26, 280, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION ".PIC File Drawing Options"
FONT 8, "MS Sans Serif"
{
 AUTORADIOBUTTON "&0  Overhead view (Normal)", IDC_OVERHEAD, 17, 19, 190, 18, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP
 AUTORADIOBUTTON "&1  Draw points, erase between plots", IDC_DRAWPOINTS, 17, 33, 190, 18, BS_AUTORADIOBUTTON
 AUTORADIOBUTTON "&2  Overlay points (no erase)", IDC_OVERLAYPOINTS, 17, 47, 190, 18, BS_AUTORADIOBUTTON
 AUTORADIOBUTTON "&3  Draw lines, erase between plots", IDC_DRAWLINES, 17, 61, 190, 18, BS_AUTORADIOBUTTON
 AUTORADIOBUTTON "&4  Overlay lines (no erase)", IDC_OVERLAYLINES, 17, 75, 190, 18, BS_AUTORADIOBUTTON
 AUTORADIOBUTTON "&5  Fill area below curves, colors = Height", IDC_AREAHEIGHT, 17, 89, 190, 18, BS_AUTORADIOBUTTON
 AUTORADIOBUTTON "&6  Fill area below curves, colors = Nearness", IDC_AREANEARNESS, 17, 103, 190, 18, BS_AUTORADIOBUTTON
 LTEXT "&INITIAL file lines to ignore (not drawn):", -1, 10, 136, 168, 12
 EDITTEXT IDC_INISKIP, 161, 135, 45, 12, WS_BORDER | WS_GROUP | WS_TABSTOP
 LTEXT "&Skip lines BETWEEN drawn lines:", -1, 10, 155, 168, 12
 EDITTEXT IDC_SKIP, 161, 154, 45, 12, WS_BORDER | WS_GROUP | WS_TABSTOP
 LTEXT "&VERTICAL SIZE adjustment factor:", -1, 10, 175, 168, 12
 EDITTEXT IDC_VERTSIZE, 161, 173, 45, 12, WS_BORDER | WS_GROUP | WS_TABSTOP
 DEFPUSHBUTTON "OK", IDOK, 218, 10, 50, 14
 PUSHBUTTON "Cancel", IDCANCEL, 218, 28, 50, 14, WS_GROUP | WS_TABSTOP
 PUSHBUTTON "Help", IDHELP, 218, 46, 50, 14, WS_GROUP | WS_TABSTOP
 GROUPBOX "Drawing Method:", IDC_GROUPBOX1, 10, 9, 198, 117, BS_GROUPBOX | WS_GROUP
 LTEXT "Normal = 0", -1, 212, 136, 54, 13
 LTEXT "Normal = 0", -1, 212, 155, 54, 13
 LTEXT "Normal = 1", -1, 212, 174, 54, 13
}


// a custom TInputDialog style that I prefer
IDD_INPUTDIALOG DIALOG 36, 33, 214, 121
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_SETFONT
FONT 8, "Helv"
{
 LTEXT "", ID_PROMPT, 10, 8, 195, 51
 EDITTEXT ID_INPUT, 10, 71, 195, 12, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL
 DEFPUSHBUTTON "&OK", IDOK, 69, 96, 40, 14
 PUSHBUTTON "&Cancel", IDCANCEL, 115, 96, 40, 14
}


// Copy to here the bitmap definition of the...
IDI_OWLAPP ICON

// Copy to here the string table resources from the following OWL files...

\owl\except.rc
\owl\statusba.rc
\owl\validate.rc

// Copy to here the resources from 

\owl\slider.rc
\owl\editsear.rc
\owl\editfile.rc

sdibwin.rtf

This is the Microsoft Word 6.0 version.

SDIBWIN.RTF. This file is part of the SDIBWIN project.

Copyright (C)1995-2001 Steven Whitney.

Initially published by http://25yearsofprogramming.com.

Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.

This file is the .RTF source for the Windows 3.1 Help (WinHelp) file that explains the functions that the SDibWindow OWL class provides to an application. Compile with Help Compiler and include the .hlp file with programs that use SDibWindow. Before compiling, delete this header section and the Page Break that follows it.

This file can be edited with any .RTF file editor, such as Microsoft Word 6.0. If you edit it with Word, this file is based on the HELPFILE.DOT template, which provides a macro that helps you create the footnotes required in WinHelp files.

#$K+ Files and File Formats

This section contains information used by several different applications. If features are mentioned that do not seem to apply to the application you are using, those features are not (or are not yet) implemented in that application.

INF Files

Contains the bounding coordinates (left,top,right,bottom) of a Mandelbrot region. These can be used to recalculate the region at any resolution and size. This very small file is the only one you need to duplicate the image at a later time. You can also use the numbers (but probably not the file itself) to specify the region in other Mandelbrot-calculation applications such as Fractint.

PIC Files

Contains the escape time values (0 to 255) for each pixel in the display window at the time a Mandelbrot region was calculated. These can be loaded and translated to colors for redisplay, but the image can only be reproduced at the resolution and size at which the original calculation was done. A PIC file is essentially a crude bitmap file containing only palette indexes and no color table.

Usually, as this program does, the escape time values are translated into different colors, which become visually interpreted as "geographical" heights. The values in the file, however, are simply unsigned characters with values 0 to 255, which can be used for any purpose. A PIC file has the following format:

Width,Height<linefeed> <- the first line
Width times Height bytes (each 0 to 255) <- subsequent data block

Width and Height are the width and height of the image, in pixels. If you TYPE a PIC file, you will be able to read these values on the first line; the rest will appear as garbage.

When creating the PIC file, the image is scanned from left to right, top to bottom, so that the first pixel in the file is the top left.

PIC files are the only ones that can be loaded with 3d options, and the PIC format is also used by some of my other applications: WAVGPIC.EXE, DLA.EXE.

BMP Files

If you just want to save a file for later viewing, .BMP is the preferred format.

These are standard Windows BMP device-independent bitmap (DIB) files. The ones created by this program are 256-color bitmaps that use 8-bits (1 byte) for each pixel ("8 bit color table").

MAP Files

MAP files are color palettes in the same format as MAP files used by the popular fractal generating program, Fractint. MAP files are text files that can be created and edited with any text editor. A file can contain up to 256 color definitions, each on its own line. Any above 256 are ignored. If there are less than 256 in the file, the colors that are in the file, when loaded into the program's palette, are repeated (cycled) in the palette until its 256 slots are filled. Each file line has the format: RED GREEN BLUE optional comment. RED GREEN BLUE are decimal (base 10) integer values from 0 to 255, defining the RGB components of the color.

Example:
0 0 255 this is a comment. This line specifies Red=0, Green=0, Blue=255.

Note: There are other applications that use the extension .MAP for files that have nothing to do with color palettes. Before you load a MAP file into this application, be sure that the file is actually a color palette file.

#$K+ PIC File Load Options Dialog Box

This section contains information used by several different applications. If features are mentioned that do not seem to apply to the application you are using, those features are not (or are not yet) implemented in that application. Loading of PIC files is not supported at all in some applications that use this help file.

Display Modes

0 Overhead view (copy .PIC directly to screen)
1 Draw points, erase between plots
2 Overlay points (no erase)
3 Draw lines, erase between plots
4 Overlay lines (no erase)
5 Fill area below curves, colors = Height
6 Fill area below curves, colors = Nearness

When using mode 6, one pass through the palette for one pass through file is desirable. Use iniskip and skip.

Initial Skipped Lines

Sets the number of lines read from the start of the file and ignored. Use this to omit a portion of the top of the image. If you only want to draw the top of the image, skipping lines at the bottom, you must save the image as PIC or BMP, load it, then select an area of it with the mouse.

Skip Lines Between

When reading the PIC file, skips over this many lines between each line that is read and displayed. Useful if the PIC file contains more detail than you want to make use of.

Example (assumes first line is line 1):
If InitialSkip = 99 and Skip Lines Between is 9, program will use these lines from the file:
100, 110, 120, 130...

Vertical Size Adjust

This can be used to increase or decrease the vertical size of the image. PIC files normally contain values in the range 0 to 255, inclusive. Winbrot scales these values 0-255 so the display fills the entire screen vertically. You can always reduce the vertical range by setting Vertical Size Adjust to < 1.0. You can sometimes increase the vertical range by using a value > 1.0, but you must know that the PIC file contains only small values. For example:

A value of .25 will make the final image 1/4 the vertical size that it would have been. This works well for Mandelbrot PIC files, which tend to have a very high vertical relief that must be scaled down (use .25 to .50) to look at all natural.

A value of 10 will make the final image 10 times higher than it would have been, but if any raw value read from the file, times 10, exceeds 255, the display may draw strangely, with points out of range.

Misc Notes on 3D modes

  • Files output after averaging passes in Winbrot.exe load well in 3d modes. Works for random pics generated by it and Winbrot files processed through it.
  • Area-Height: This mode lacks effectiveness in general.
  • Overlay-Lines: Works much better on moving average files. Because areas aren't painted, it's important to use iniskip to start drawing with highest region at the back.

Good Map Files:

  • Area-Nearness: Royal, Neon, Volcano
  • Area-Height: Landscap, any grayscale (Green, Gray), Royal, Topo (shows contours as colors slide into them), Volcano
  • Overlay-Lines:
    Movavg Files:
  • Chroma, Default, Glasses2 (good when cycling), Grid (filters out many contours), Neon, Royal, Landscap, Topo,
  • Raw Files: Default, Glasses1 (only), Neon

#$k+ Colors Menu Commands

This section contains information that applies to several different applications. If features are mentioned that do not seem to apply to the application you are using, those features are not (or are not yet) implemented in that application.

While calculation of an image is in progress, all functions that can change the color palette are temporarily disabled.

This program can store its color palettes in the same format MAP files used by the popular shareware program, Fractint. MAP files can be created and edited with any text editor. A file can contain up to 256 color definitions, each on its own line. Any above 256 are ignored. If there are less than 256, colors are repeated (cycled) in the palette until its 256 slots are filled. Each file line has the format: RED GREEN BLUE optional comment. RED GREEN BLUE are values from 0 to 255, defining the RGB components of the color.

Load

Loads the color palette from a MAP file. The program maintains a 256-color palette, and can use up to 256 colors from the MAP file.

SaveAs

Saves the color palette as a MAP file whose name you specify.

The easiest way to create a new palette is with Corel Photo-Paint's palette manipulation routines.

  1. In Winbrot, save a small design as BMP.
  2. Load it into Corel Photo-Paint.
  3. Change palette as desired and re-save the BMP file (as 8-bit color, 256 colors). Your new palette is  saved with the BMP.
  4. Load the BMP back into Winbrot.
  5. Save the new palette as a MAP file with Colors|SaveAs.

Randomize

Fills the color palette with random colors.

Reverse

Reverses the order of the colors in the current palette, so that the first becomes the last, second becomes next-to-last, etc.

Complement

Change each color to its complementary color. Each color value (R,G,B) becomes (255 - R, 255-G, 255-B).

Lighten

Increment each RGB component of each color by 1. This does not always lighten, since whenever a component reaches 255, it goes to 0. If you use this command 256 times, you can get back to the colors you started with.

Darken

Decrement each RGB component of each color by 1. See "Lighten", above.

Edit .MAP File List...

At startup, the program makes an internal list of all the MAP files available to it at that time. This option allows you to edit the list in the File List Editor DialogHID_FILELISTEDITOR. The files in the list are used by the Colors|Next and Colors|Previous commands (see next paragraph), and when the program automatically starts a new design it uses a randomly selected file from this list for the new palette, for variety.

Next .MAP File / Previous .MAP File

These menu items allow you to easily cycle through the list in either forward or reverse order.

Rotate Forward/Backward

Rotates the color palette so that colors appear to flow through the region. It is faster when Fit to Window is OFF.

Editing the Color Palette with the Mouse

You can redefine any color displayed on the screen. Position the cursor over an example of the color you want to change and click the RIGHT mouse button. A dialog box will open in which you can redefine the color. The change will be reflected as soon as you close the dialog box. It can be difficult to position the cursor over a dot of exactly the color you want to edit, so be sure that the color initially displayed in the dialog box is the right one.

#$K+ File List Editor Dialog Box

This dialog allows you to edit one of the file lists that the program maintains.

The format for both lists is one filename per line, and if the file is not in the current default directory, you must specify its full path.

Map Files

Edits the list of .MAP color palette files used for color rotation.

MAP files remain permanently in their rotation list unless you delete them manually.

Data Files

Edits the list of files (INF, PIC, or other data files supported by the application) that was on the command line when the program was started. If the command line contained wildcards, this list contains the full path names of all the files that matched the wildcards. You can also use this to manually create a list of files for sequential display

In some programs, this list behaves differently from the MAP files list. As each file in this list is loaded and displayed, it may be removed from the list.

Exactly what types of files appear in the data file list depends on which program you are using.

#$K+ Effects Menu Commands

This section contains information that applies to several different applications. If features are mentioned that do not seem to apply to the application you are using, those features are not (or are not yet) implemented in that application.

Most of the Effects commands are purely for graphic effect. Once you perform these transformations, the pixels no longer represent true Mandelbrot escape times (or no longer represent the results of whatever other method was used to create them), but they can create visually interesting designs.

Average Pixels

Mandelbrot PIC files tend to have high thin peaks immediately next to narrow low valleys, a ragged topography that is not realistic if you want to interpret the Mandelbrot region as a landscape. Averaging pixels smoothes a Mandelbrot region by recalculating each pixel to be the average "height" of the other pixels surrounding it. Repeated averaging passes effectively flatten the landscape so that the peaks and valleys blend more smoothly into each other. While doing so, it can create images that are interesting and beautiful, and the resulting PIC files work particularly well when reloaded using one of the three-dimensional modes.

Randomize Pixels

Initializes the window with all random pixel color values 0-255, creating a completely random design. Creates interesting patterns during averaging passes.

Rearrange Heights

When you repeatedly Average Pixels, heights in the design grow closer together, flattening it out, and the board becomes stable after a few passes. Rearrange Heights randomly shuffles the height values. Each unique height (escape time) in the design is transformed to a new unique height, creating a whole new starting point for the averaging.

Invert Heights

Inverts the escape time values from (0 to 255) to (255 to 0), turning mountains to valleys and vice versa.

Flip Vertical

Unlike File|Flip Vertical, which calculates the corresponding region on the other side of the x axis, this merely flips the displayed graphic image upside down. This flipping of the graphic image is much faster.

Flip Horizontal

Flips the displayed graphic image horizontally, left to right.

 

Valid HTML 4.01 Transitional Valid CSS
Yahoo! Search
Search the web Search this site
View content labeling at ICRA.