I have created a ImageStorageTraits.h
and a Image.hpp
file that inherits from it.
I have also written two files ppm.cpp
and ppm.h
with functionality for loading and saving an image, and a codec.h
for encoding/decoding the image.
Then I have my main.cpp
file for testing my functionality, that aims to model and implement an image and apply certain alterations upon it - I read my input from an input file that contains the image.
I want to complete the Image.hpp
class that will be inside a namespace
called graphics
.
This is my code:
ImageStorageTraits.h
:
#pragma once
namespace graphics
{
class ImageStorageTraits
{
protected:
// Data buffer to contain the raw pixel values of the image
unsigned char * data = nullptr;
// The width of the image, in pixels
int width = 0;
// The height of the image, in pixels
int height = 0;
/*! Returns the buffer location in "data" of the pixel at position (x,y)
*
* \return a pointer to the location in the buffer where the requested R,G,B pixel values start.
* The RED color value for the pixel is at the returned memory offset ret_addr, the GREEN value
* is at the next byte (ret_addr+1) and the BLUE value is next to the GREEN one (ret_addr+2)
*
* \param x is the horizontal coordinate of the pixel (zero-based). Valid values: [0,width-1].
* \param y is the vertical coordinate of the pixel (zero-based). Valid values: [0,height-1].
*
* Remark: No bounds are checked. x and y are assumed to be correctly within image extents.
*
*/
unsigned char * pixel(unsigned int x, unsigned int y)
{
return data + 3 * (width * y + x);
}
};
}
Image.hpp
(Incomplete).
#pragma once
#include "ppm.h"
namespace graphics {
class Image : public ImageStorageTraits {
protected:
public:
};
}
ppm.cpp
.
#include "ppm.h"
#include <string.h>
#include <fstream>
#include <iostream>
namespace graphics
{
void _skipComment(std::ifstream & file)
{
char c;
do
{ // remove rest of the line
c = file.get();
} while (c != '\n');
}
unsigned char * ReadPPM(const std::string & filename, int * w, int * h)
{
std::ifstream file(filename.c_str(), std::ifstream::in | std::ifstream::binary);
if (!file)
return nullptr;
int cur_token = 0;
char tokens[4][32];
int pos = 0;
while (cur_token < 4)
{
char c;
do
{ // remove empty space
c = file.get();
if (c == '#')
{
_skipComment(file);
break;
}
} while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
do
{ // write characters to token slot
tokens[cur_token][pos++] = c;
c = file.get();
if (c == '#')
{
_skipComment(file);
break;
}
} while (c != ' ' && c != '\t' && c != '\n' && c != '\r' && pos < 32);
tokens[cur_token][pos] = 0;
cur_token++;
pos = 0;
}
if (strcmp(tokens[0], "P6") != 0)
{
std::string msg;
msg = std::string(tokens[0]) + " - Error: Unsupported format.";
std::cerr << msg.c_str() << std::endl;
file.close();
return nullptr;
}
int width = atoi(tokens[1]);
if (width == 0)
{
std::cerr << "Error: Bad image width." << std::endl;
file.close();
return nullptr;
}
int height = atoi(tokens[2]);
if (height == 0)
{
std::cerr << "Error: Bad image height." << std::endl;
file.close();
return nullptr;
}
int max_val = atoi(tokens[3]);
if (max_val > 255)
{
std::cerr << "Error: Only 8bit per pixel images are supported." << std::endl;
file.close();
return nullptr;
}
// header ok, read image chunk
size_t sz = width * height * 3;
unsigned char * buffer = new unsigned char[sz];
file.read((char*)buffer, sz);
size_t count = size_t(file.gcount());
if (count < sz)
{
std::cerr << "Error: Could not read all pixels.\n";
file.close();
delete[] buffer;
return nullptr;
}
file.close();
*w = width;
*h = height;
return buffer;
}
bool WritePPM(const unsigned char * data, int w, int h, std::string filename)
{
std::ofstream file(filename.c_str(), std::ofstream::out | std::ofstream::binary);
if (!file)
return false;
// write header;
file << "P6" << " " << w << " " << h << " 255 ";
//prepare data for output
size_t sz = 3 * w * h;
file.write((char*)data, sz);
file.close();
return true;
}
}
main.cpp
.
#include <iostream>
#include <cstdlib>
#include <stdint.h>
#include <algorithm>
#include "codec.h"
#include "Image.h"
#define RAND0TO255 ((unsigned char)floorf(255.5f*rand()/(float)RAND_MAX))
//------------------- Unit Tests declaration ----------------------------
void Test1(unsigned long seed);
void Test2(unsigned long seed);
void Test3(unsigned long seed);
//-------------------- main function ------------------------------------
int main(int argc, char ** argv)
{
if (argc < 2)
{
std::cerr << "Please provide your student number (no leading letter) as an argument. E.g. 3015422\n";
exit(-1);
}
unsigned long seed = atoi(argv[1]);
Test1(seed);
Test2(seed);
Test3(seed);
std::cout << "\nFinished. Press a key to exit.\n";
getchar();
return 0;
}
//-------------------- Unit tests definitions ---------------------------
void Test1(unsigned long seed)
{
std::cout << "TEST 1: Basic funcionality.\n";
std::cout << "---------------------------\n";
srand(seed);
graphics::Image im1; // To be implemented
std::cout << "Checking default ctor... " << "[PASS]" << "\n";
const int dim = 256;
unsigned char icon[3 * dim * dim];
for (int i = 0; i < 3 * dim * dim; i++)
{
icon[i] = RAND0TO255;
}
graphics::Image im2(dim, dim); // To be implemented
unsigned char * memory = (reinterpret_cast<unsigned char**>(&im2))[0];
memory[3 * dim*dim - 1] ^= '$';
memory[3 * dim*dim - 1] ^= '$';
std::cout << "Checking custom ctor... " << "[PASS]" << "\n";
int pixel = rand() % (256*256);
unsigned char rgb[3] = { RAND0TO255, RAND0TO255, RAND0TO255 };
unsigned char * newpixel = im2(pixel % dim, pixel / dim); // To be implemented
newpixel[0] = rgb[0];
newpixel[1] = rgb[1];
newpixel[2] = rgb[2];
im1 = im2; // To be implemented
std::cout << "Checking operator() and operator = ... ";
bool resOK = true;
for (int i = 0; i < 3 * dim*dim; i++)
{
if (im1(0, 0)[i] != im2(0, 0)[i])
{
resOK = false;
break;
}
}
std::cout << (resOK ? "[PASS]" : "[FAIL]") << "\n";
std::cout << "Checking operator == ... ";
bool identical = (im1 == im2); // To be implemented
std::cout << ( identical? "[PASS]" : "[FAIL]") << "\n";
std::cout << "Checking copy ctor / dtor ... ";
graphics::Image * im3 = new graphics::Image(im2); // To be implemented
if ((*im3)(0, 0) == im2(0, 0))
{
std::cout << "[FAIL]: Shallow copy attempted. Use DEEP data buffer copy.\n";
}
else
{
identical = (im2 == *im3);
std::cout << (identical ? "[PASS]" : "[FAIL]: image content not identical.") << "\n";
}
delete im3; // To be implemented
std::cout << "\n";
}
void Test2(unsigned long seed)
{
std::cout << "TEST 2: Load / save operations.\n";
std::cout << "-------------------------------\n";
srand(seed);
graphics::Image im1;
std::cout << "Checking load ... ";
bool loadOK = im1.load("input.ppm"); // To be implemented
int w = 0, h = 0;
unsigned char * imbuf = graphics::ReadPPM("input.ppm", &w, &h);
if (!imbuf)
std::cout << "[FAIL] : could not find the input file.";
else if (imbuf && (!loadOK || !im1(0, 0)) )
std::cout << "[FAIL].";
else
{
bool resOK = true;
for (int i = 0; i < 3 * w*h; i++)
{
if (im1(0, 0)[i] != imbuf[i])
{
resOK = false;
break;
}
}
std::cout << (resOK? "[PASS]":"[FAIL].");
}
std::cout << "\n";
std::cout << "Checking getDimensions... ";
std::pair<int, int> dims = im1.getDimensions(); // To be implemented
std::cout << ((dims.first == w && dims.second == h) ? "[PASS]" : "[FAIL]") << "\n";
std::cout << "Checking save... ";
unsigned char * buffer = im1(0, 0);
for (int i = 0; i < 3 * w*h; i++)
{
buffer[i] = (unsigned char)std::max<int>(0,(int)(buffer[i]*0.5f));
}
im1.save("output.ppm"); // To be implemented
int sw = 0, sh = 0;
if (imbuf)
delete[] imbuf;
imbuf = graphics::ReadPPM("output.ppm", &sw, &sh);
bool saveOK = imbuf && sw == w && sh == h;
std::cout << (saveOK ? "[PASS]" : "[FAIL]") << "\n";
std::cout << "\n";
}
void Test3(unsigned long seed)
{
std::cout << "TEST 3: Conversion functionality.\n";
std::cout << "-------------------------------\n";
std::cout << "Checking encryption/decryption pipeline... ";
graphics::Image im1;
bool loadOK = im1.load("input.ppm");
if (!loadOK)
{
std::cout << "[FAIL] : could not find the input file.";
}
else
{
std::pair<int, int> dimensions = im1.getDimensions();
CoDec codec(seed, dimensions.first * dimensions.second * 3);
graphics::Image im2(dimensions.first, dimensions.second);
im2 << codec.encrypt(im1); // To be implemented
bool ok = im2.save("encrypted.ppm");
if (ok)
{
im2 << codec.decrypt(im2);
if (!(im1 == im2))
{
std::cout << "[FAIL].\n";
}
else
{
std::cout << "[PASS].\n";
}
}
else
{
std::cout << "[FAIL] : could not save encrypted file.\n";
}
}
std::cout << "Testing move assignment operator... ";
graphics::Image im2 = im1;
graphics::Image im3;
unsigned char * data_base_addr1 = im1(0, 0);
im3 = std::move(im1); // To be implemented
if ((im1 == im3) || !(im2==im3) || im3(0,0)!= data_base_addr1)
{
std::cout << "[FAIL]: Implement move assignment by \"stealing\" image resources, not copying them.\n";
}
else
{
std::cout << "[PASS].\n";
}
}
And the codec.h
:
#pragma once
//------------------- CoDec class required for Test3 --------------------
class CoDec
{
uint64_t key;
size_t length;
unsigned char * buffer;
public:
CoDec(uint64_t key, size_t stream_length);
~CoDec();
const unsigned char * const encrypt(unsigned char * stream);
const unsigned char * const decrypt(unsigned char * stream);
};
//-------------------- CoDec class definition ---------------------------
CoDec::CoDec(uint64_t key, size_t stream_length)
: key(key), length(stream_length), buffer(new unsigned char[stream_length])
{
// fill msb s with lsb bits on the key
key |= ~(key << 32);
}
const unsigned char * const CoDec::encrypt(unsigned char * input)
{
uint64_t local_key = key;
int offset = 0;
for (int i = 0; i < length; i++)
{
offset = i / 509;
buffer[i] = input[i] ^ ((unsigned char)(local_key & 0xff));
local_key = (local_key << (i + offset) % 64) | (local_key >> (64 - (i + offset) % 64));
}
local_key = key;
offset = 0;
const int range = 97;
unsigned char tmp[range];
for (int i = 0; i < length - 2* range; i += 2 * range)
{
offset = i / 313;
local_key = (local_key << (i + offset) % 64) | (local_key >> (64 - (i + offset) % 64));
if (local_key & 127)
{
memcpy(tmp, buffer + i, range);
memcpy(buffer + i, buffer + range + i, range);
memcpy(buffer + range + i, tmp, range);
}
}
return buffer;
}
const unsigned char * const CoDec::decrypt(unsigned char * input)
{
uint64_t local_key = key;
int offset = 0;
memcpy(buffer, input, length);
const int range = 97;
unsigned char tmp[range];
for (int i = 0; i < length - 2* range; i += 2 * range)
{
offset = i / 313;
local_key = (local_key << (i + offset) % 64) | (local_key >> (64 - (i + offset) % 64));
if (local_key & 127)
{
memcpy(tmp, buffer + i, range);
memcpy(buffer + i, buffer + range + i, range);
memcpy(buffer + range + i, tmp, range);
}
}
local_key = key;
offset = 0;
for (int i = 0; i < length; i++)
{
offset = i / 509;
buffer[i] = buffer[i] ^ ((unsigned char)(local_key & 0xff));
local_key = (local_key << (i + offset) % 64) | (local_key >> (64 - (i + offset) % 64));
}
return buffer;
}
CoDec::~CoDec()
{
delete[] buffer;
}
Aucun commentaire:
Enregistrer un commentaire