lundi 8 février 2021

Implementing header class

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