I'm creating an image class that can be used by other classes to create visualizations of data. The class doesn't need to be complicated as the classes will be editing the images pixel by pixel.
The image class will be used to create RGB images and export them to any of the netbpm image formats (pbm, pgm, ppm). I may expand the class to export in other formats and maybe load images, but for the moment, I just need the basic functionality of editing and exporting.
I'm mostly looking for feedback on the use of enum
values.
Image.h
#ifndef IMAGE_H
#define IMAGE_H
#include <string>
#include <vector>
#include <fstream>
#include <cctype>
#include <cmath>
using namespace std;
class Image {
public:
enum Enum_Colors {WHITE, BLACK, GREY, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA};
private:
const unsigned int NUM_PIXEL_VALUES = 3;
const unsigned int FILE_EXTENSION_LENGTH = 4;
struct Pixel {
unsigned int red;
unsigned int green;
unsigned int blue;
};
unsigned int maxValue;
vector<vector<Pixel>> imageData;
/**
* Creates a pixel representing the given color.
*
* @param color an enumerated color
* @return a pixel representing the given color
**/
Pixel ColorPixel(Enum_Colors color) const;
/**
* Creates an image file from the image data.
*
* The type of the created file is determined by the function name.
*
* @param fileName the name of the created file
* @return false if an error occurred
* @return true otherwise
**/
bool ExportPBM(string fileName);
bool ExportPGM(string fileName);
bool ExportPPM(string fileName);
public:
Image(unsigned int width, unsigned int height, unsigned int maxValue) : maxValue(maxValue),
imageData(vector<vector<Pixel>>(height, vector<Pixel>(width, ColorPixel(WHITE)))) {};
~Image() {imageData.clear();}
/**
* Returns the pixel width of the image.
*
* @return the pixel width of the image
**/
unsigned int Width();
/**
* Returns the pixel height of the image.
*
* @return the pixel height of the image
**/
unsigned int Height();
/**
* Returns the maximum intensity value of the image.
*
* @return the maximum intensity value of the image
**/
unsigned int MaxValue();
/**
* Sets the given pixel value to the given color.
*
* @param x the location along the width
* @param y the location along the height
* @param color the color to set the pixel to
*
* @return false if the indices are out of bounds
* @return true otherwise
**/
bool SetPixelColor(unsigned int x, unsigned int y, Enum_Colors color);
/**
* Sets the given pixel value to the given color.
*
* @param x the location along the width
* @param y the location along the height
* @param redVal the intensity of the red value to set the pixel to
* @param greenVal the intensity of the green value to set the pixel to
* @param blueVal the intensity of the blue value to set the pixel to
*
* @return false if the indices are out of bounds or the color values
are out of bounds
* @return true otherwise
**/
bool SetPixelValue(unsigned int x, unsigned int y, unsigned int redValue, unsigned int greenValue, unsigned int blueValue);
/**
* Creates an image file from the image data.
*
* The type of the created file is determined by the file name.
* (Supported extensions: .pbm, .pgm, .ppm)
*
* @param fileName the name of the created file
* @return false if the extension is not supported or an error occurred
* @return true otherwise
**/
bool ExportImage(string fileName);
};
#endif
Image.cpp
#include "Image.h"
Image::Pixel Image::ColorPixel(Enum_Colors color) const {
Pixel pixel;
switch (color) {
case WHITE:
pixel.red = maxValue;
pixel.green = maxValue;
pixel.blue = maxValue;
break;
case BLACK:
pixel.red = 0;
pixel.green = 0;
pixel.blue = 0;
break;
case GREY:
pixel.red = maxValue / 2;
pixel.green = maxValue / 2;
pixel.blue = maxValue / 2;
break;
case RED:
pixel.red = maxValue;
pixel.green = 0;
pixel.blue = 0;
break;
case GREEN:
pixel.red = 0;
pixel.green = maxValue;
pixel.blue = 0;
break;
case BLUE:
pixel.red = 0;
pixel.green = 0;
pixel.blue = maxValue;
break;
case YELLOW:
pixel.red = maxValue;
pixel.green = maxValue;
pixel.blue = 0;
break;
case CYAN:
pixel.red = 0;
pixel.green = maxValue;
pixel.blue = maxValue;
break;
case MAGENTA:
pixel.red = maxValue;
pixel.green = 0;
pixel.blue = maxValue;
break;
default:
pixel.red = maxValue;
pixel.green = maxValue;
pixel.blue = maxValue;
break;
}
return pixel;
}
bool Image::ExportPBM(string fileName) {
ofstream fout;
/* Attempt to create the file and check for errors */
fout.open(fileName);
if (fout.fail()) {
return false;
}
/* Write heading */
fout << "P1" << endl; // file type identifier
fout << "# CREATOR: C++ Image Class, by Jacob Bischoff" << endl;
fout << imageData.at(0).size() << " " << imageData.size() << endl; // width height
/* Write body */
for (unsigned int i = 0; i < imageData.size(); i++) {
/* Add end line in proper locations */
if (i > 0) {
fout << endl;
}
/* Output pixel values; 0 - White, 1 - Black */
for (unsigned int j = 0; j < imageData.at(i).size(); j++) {
Pixel pixel = imageData.at(i).at(j);
if (j > 0) {
fout << " ";
}
fout << 1 - (int)roundf((pixel.red + pixel.green + pixel.blue) / (double)NUM_PIXEL_VALUES / (double)maxValue);
}
}
return true;
}
bool Image::ExportPGM(string fileName) {
ofstream fout;
/* Attempt to create the file and check for errors */
fout.open(fileName);
if (fout.fail()) {
return false;
}
/* Write heading */
fout << "P2" << endl; // file type identifier
fout << "# CREATOR: C++ Image Class, by Jacob Bischoff" << endl;
fout << imageData.at(0).size() << " " << imageData.size() << endl; // width height
fout << maxValue << endl;
/* Write body */
for (unsigned int i = 0; i < imageData.size(); i++) {
/* Add end line in proper locations */
if (i > 0) {
fout << endl;
}
/* Output pixel values; 0 - black, maxValue - white */
for (unsigned int j = 0; j < imageData.at(i).size(); j++) {
Pixel pixel = imageData.at(i).at(j);
if (j > 0) {
fout << " ";
}
fout << (int)roundf((pixel.red + pixel.green + pixel.blue) / (double)NUM_PIXEL_VALUES);
}
}
return true;
}
bool Image::ExportPPM(string fileName) {
ofstream fout;
/* Attempt to create the file and check for errors */
fout.open(fileName);
if (fout.fail()) {
return false;
}
/* Write heading */
fout << "P3" << endl; // file type identifier
fout << "# CREATOR: C++ Image Class, by Jacob Bischoff" << endl;
fout << imageData.at(0).size() << " " << imageData.size() << endl; // width height
fout << maxValue << endl;
/* Write body */
for (unsigned int i = 0; i < imageData.size(); i++) {
/* Add end line in proper locations */
if (i > 0) {
fout << endl;
}
/* Output pixel values; 0 0 0 - black, maxValue maxValue maxValue - white */
for (unsigned int j = 0; j < imageData.at(i).size(); j++) {
Pixel pixel = imageData.at(i).at(j);
if (j > 0) {
fout << "\t";
}
fout << pixel.red << " " << pixel.green << " " << pixel.blue;
}
}
return true;
}
unsigned int Image::Width() {
/* prevent out of bounds error */
if (imageData.size() == 0) {
/* no second order elements */
return 0;
}
else {
/* return the second order size */
return imageData.at(0).size();
}
}
unsigned int Image::Height() {
/* return the first order size */
return imageData.size();
}
unsigned int Image::MaxValue() {
/* return the maximum intensity value */
return maxValue;
}
bool Image::SetPixelColor(unsigned int x, unsigned int y, Enum_Colors color) {
/* check if indices are withing bounds */
if ((y < imageData.size()) && (x < imageData.at(y).size())) {
imageData.at(y).at(x) = ColorPixel(color);
return true;
}
else {
return false;
}
}
bool Image::SetPixelValue(unsigned int x, unsigned int y, unsigned int redValue, unsigned int greenValue, unsigned int blueValue) {
/* check if indices are withing bounds */
if ((y < imageData.size()) && (x < imageData.at(y).size())) {
/* check if intensity values are larger than max */
if ((redValue <= maxValue) || (greenValue <= maxValue) || (blueValue <= maxValue)) {
imageData.at(y).at(x).red = redValue;
imageData.at(y).at(x).green = greenValue;
imageData.at(y).at(x).blue = blueValue;
return true;
}
else {return false;}
}
else {return false;}
}
bool Image::ExportImage(string fileName) {
string fileExtension = fileName.substr(fileName.size() - FILE_EXTENSION_LENGTH, FILE_EXTENSION_LENGTH);
/* Make extension all lowercase for ease of comparison */
for (unsigned int i = 0; i < fileExtension.size(); ++i) {
fileExtension.at(i) = tolower(fileExtension.at(i));
}
/* Evaluate extension to determine which file type to create. */
if (fileExtension == ".pbm") {
return ExportPBM(fileName);
}
else if (fileExtension == ".pgm") {
return ExportPGM(fileName);
}
else if (fileExtension == ".ppm") {
return ExportPPM(fileName);
}
else {
return false;
}
}