rotate-me-fast/image.h

318 lines
6.7 KiB
C++

#pragma once
#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include "pnm.h"
using namespace std;
//
//
// Point
//
template <typename T>
struct TPoint {
T x;
T y;
TPoint(T a, T b)
: x(a)
, y(b)
{}
TPoint& operator+=(TPoint const& rhs)
{
x += rhs.x;
y += rhs.y;
return *this;
}
TPoint& operator-=(TPoint const& rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
TPoint& operator*=(TPoint const& rhs)
{
x *= rhs.x;
y *= rhs.y;
return *this;
}
TPoint& operator/=(TPoint const& rhs)
{
x /= rhs.x;
y /= rhs.y;
return *this;
}
};
typedef TPoint<int32_t> Point;
typedef TPoint<double> DPoint; // absolute point, can be negative
template<typename Elem, typename Traits, typename T>
std::basic_ostream<Elem, Traits>& operator << (std::basic_ostream<Elem, Traits>& o, TPoint<T> const& p)
{
o << "(" << p.x << ", " << p.y << ")";
return o;
}
//
//
// Pixel
//
typedef uint16_t pvalue_t; // pixel value type
//
//
// Image
//
struct Image {
unsigned int width;
unsigned int height;
pvalue_t* buffer;
pnm::Format type;
size_t pixel_size;
Image();
Image(unsigned int w, unsigned int h, pnm::Format type);
Image(string const& path);
virtual ~Image();
bool save(string const& path) const;
protected:
virtual bool read_body(std::ifstream& istr);
virtual bool write_body(std::ofstream& ostr) const;
};
template<unsigned int W, unsigned int H>
struct TiledImage : public Image {
pvalue_t* tiles;
unsigned int static const tile_w = W;
unsigned int static const tile_h = H;
// two lines overlap, bottom + right
unsigned int static const tile_size = (W + 1) * (H + 1);
unsigned int nb_col_tile;
unsigned int nb_row_tile;
TiledImage()
: Image()
, tiles(NULL)
, nb_col_tile(0)
, nb_row_tile(0)
{}
~TiledImage()
{
delete [] tiles;
}
TiledImage(unsigned int w, unsigned int h)
{
allocate_memory(w, h);
}
TiledImage(string const& path)
: TiledImage()
{
ifstream is(path);
if (!is.is_open())
{
cerr << "Cannot open file '" << path << "'" << endl;
abort();
}
if (!pnm::read_header(is, this->type, this->width, this->height))
{
cerr << "Invalid header." << endl;
abort();
}
if (!this->read_body(is))
{
// TODO: delete tiles
cerr << "Invalid header." << endl;
abort();
}
}
pvalue_t const*
get_tile(unsigned int index) const
{
if (index >= nb_col_tile * nb_row_tile)
return nullptr;
return tiles + index * tile_size * pixel_size;
}
pvalue_t*
get_tile(unsigned int index)
{
if (index >= nb_col_tile * nb_row_tile)
return nullptr;
return tiles + index * tile_size * pixel_size;
}
pvalue_t*
access_pixel(unsigned int x, unsigned int y)
{
if (x >= width || y >= height)
return nullptr;
unsigned int const tile_width = (tile_w + 1) * pixel_size;
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
pvalue_t* tile = this->get_tile(tile_index);
unsigned int const tile_j = y % tile_h;
unsigned int const tile_i = x % tile_w;
return tile + tile_j * tile_width + (tile_i * pixel_size);
}
pvalue_t const*
access_pixel(unsigned int x, unsigned int y) const
{
if (x >= width || y >= height)
return nullptr;
unsigned int const tile_width = (tile_w + 1) * pixel_size;
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
const pvalue_t* tile = this->get_tile(tile_index);
unsigned int const tile_j = y % tile_h;
unsigned int const tile_i = x % tile_w;
return tile + tile_j * tile_width + (tile_i * pixel_size);
}
void
print_tile(unsigned int index) const
{
cout << "Tile[" << index << "]" << endl;
pvalue_t const* tile = this->get_tile(index);
unsigned int const tile_width = (tile_w + 1) * pixel_size;
for (unsigned int j = 0; j < tile_h + 1; ++j)
{
for (unsigned int i = 0; i < tile_w + 1; ++i)
{
if (i != 0)
cout << ", ";
pvalue_t const* p = tile + j * tile_width + i * pixel_size;
cout << (int) *p << " " << (int) *(p + 1) << " " << (int) *(p + 2);
}
cout << endl;
}
cout << endl;
}
void fill_overlap()
{
unsigned int const tile_width = (W + 1) * pixel_size;
for (int j = nb_row_tile - 1; j >= 0; --j)
for (unsigned int i = 0; i < nb_col_tile; ++i)
{
// copy last line overlap
if (j != (int) nb_row_tile - 1)
{
pvalue_t const* tile_src = this->access_pixel(i * W, (j + 1) * H);
pvalue_t* tile_dst = this->access_pixel(i * W, j * H);
tile_dst += H * tile_width;
memcpy(tile_dst, tile_src, tile_width * sizeof (pvalue_t));
}
// copy last col overlap
if (i != nb_col_tile - 1)
{
pvalue_t* tile_src = this->get_tile(i + 1 + j * nb_col_tile);
pvalue_t* tile_dst = this->get_tile(i + j * nb_col_tile);
tile_dst += W * pixel_size;
for (unsigned int y = 0; y < H; ++y)
{
memcpy(tile_dst, tile_src, pixel_size * sizeof (pvalue_t));
tile_src += tile_width;
tile_dst += tile_width;
}
}
}
}
protected:
void allocate_memory(unsigned int w, unsigned int h)
{
width = w;
height = h;
nb_col_tile = width / tile_w;
if (width % tile_w != 0)
++nb_col_tile;
nb_row_tile = height / tile_h;
if (height % tile_h != 0)
++nb_row_tile;
unsigned int const nb_tiles = nb_col_tile * nb_row_tile;
tiles = new pvalue_t[nb_tiles * tile_size * pixel_size];
memset(tiles, 0, nb_tiles * tile_size * pixel_size * sizeof (pvalue_t));
}
virtual bool read_body(std::ifstream& istr) override
{
this->allocate_memory(width, height);
// Pixel loading
for (unsigned int j = 0; j < height; ++j)
for (unsigned int i = 0; i < width; ++i)
{
pvalue_t* tile = this->access_pixel(i, j);
tile[0] = istr.get();
tile[1] = istr.get();
tile[2] = istr.get();
tile += pixel_size;
}
this->fill_overlap();
return true;
}
virtual bool write_body(std::ofstream& ostr) const override
{
for (unsigned int j = 0; j < height; ++j)
for (unsigned int i = 0; i < width; ++i)
{
pvalue_t const* tile = this->access_pixel(i, j);
ostr << (char) tile[0];
ostr << (char) tile[1];
ostr << (char) tile[2];
tile += pixel_size;
}
return true;
}
};