Go Back
#include <fstream.h>
// Named after Russ Cox of Harvard; a little safety mechanism
// to prevent reaching beyond the limits of an array.
#define RUSS_FACTOR 5
#define RED 0
#define GREEN 1
#define BLUE 2
typedef struct image image;
struct image
{
// image variables: the names are self-explanatory.
char type[2+RUSS_FACTOR];
int width, height, colorscale;
int *rdata, *gdata, *bdata;
// Create an image from file.
void load(char *file)
{
int i, j, k;
unsigned char r, g, b;
// The dummy variables are for the purpose of wasting the header.
char dummy, dummyl[128];
ifstream infile(file, ios::in | ios::binary);
infile >> type;
while (infile.peek() <= 13) infile.get(dummy);
while (infile.peek() == '#') infile.getline(dummyl,128,'\n');
infile >> width >> height;
infile >> colorscale;
// Create a place to store the color data.
rdata = new int[width*height+RUSS_FACTOR];
gdata = new int[width*height+RUSS_FACTOR];
bdata = new int[width*height+RUSS_FACTOR];
for (j = 0; j < height; j++)
for (i = 0; i < width; i++)
{
k = pos(i,j);
// If P3, read in ASCII values.
if (type[1]=='3')
infile >> rdata[k]
>> gdata[k]
>> bdata[k];
// If P6, read in binary chars and convert to ASCII.
else
{
// Yes, I could just do infile >> b etc., but
// this is left over from when I was not opening
// the file as binary but wanted to include the
// hard return character as valid data.
b = infile.peek();
infile.ignore(1);
r = infile.peek();
infile.ignore(1);
g = infile.peek();
infile.ignore(1);
rdata[k] = int(r);
gdata[k] = int(g);
bdata[k] = int(b);
}
}
infile.close();
}
// Given a column and row number, return the absolute position
// of the pixel in the data arrays.
long pos(int w, int h)
{
return (width*long(h))+long(w);
}
// Given a point at (i, j) and an attribute, return the scaled
// value of the attribute (0.0 - 1.0)
double getColor(int i, int j, int rgb)
{
int k = pos(i, j);
switch (rgb)
{
case RED: return double(rdata[k])/double(colorscale);
case GREEN: return double(gdata[k])/double(colorscale);
case BLUE: return double(bdata[k])/double(colorscale);
default: return 0;
};
}
int differ(int a, int b, float factor)
{
// A helper function for outline. If the ratio of the attributes
// for two pixels are out of a certain range, return 1/12 the
// maximum possible attribute value. Since this function is
// called 12 times per pixel, if a pixel is completely different
// from the four surrounding it the corresponding new pixel will
// be black. If it's the same as the others, it will be white, and
// otherwise it will be some intermediate shade of gray.
double ratio = double(a)/double(b);
if (ratio >= (1+factor) || ratio <= 1/(1+factor))
return (colorscale/12);
else return 0;
}
void outline(float dfactor)
{
int i, j, k;
int *bw = new int[width*height+RUSS_FACTOR];
// We only need one array; for grayscale images, all RGB components
// are the same. Initialize all values to 0 (black).
for (k = 0; k < width*height+RUSS_FACTOR; k++)
bw[k]=0;
for (i = 0; i < width; i++)
{
for (j = 0; j < height; j++)
{
k = pos(i,j);
// Check all three attributes to the left
if (i > 0)
{
bw[k] += differ(rdata[k],rdata[k-1],dfactor);
bw[k] += differ(gdata[k],gdata[k-1],dfactor);
bw[k] += differ(bdata[k],bdata[k-1],dfactor);
}
// and to the right
if (i < width-1)
{
bw[k] += differ(rdata[k],rdata[k+1],dfactor);
bw[k] += differ(gdata[k],gdata[k+1],dfactor);
bw[k] += differ(bdata[k],bdata[k+1],dfactor);
}
// and up one row
if (j > 0)
{
bw[k] += differ(rdata[k],rdata[k-width],dfactor);
bw[k] += differ(gdata[k],gdata[k-width],dfactor);
bw[k] += differ(bdata[k],bdata[k-width],dfactor);
}
// and down one row.
if (j < height-1)
{
bw[k] += differ(rdata[k],rdata[k+width],dfactor);
bw[k] += differ(gdata[k],gdata[k+width],dfactor);
bw[k] += differ(bdata[k],bdata[k+width],dfactor);
}
}
}
// Set all RGB components to the grayscale value.
for (k = 0; k < width*height; k++)
rdata[k] = gdata[k] = bdata[k] = (colorscale-bw[k]);
// Free unneeded memory.
delete bw;
}
};