318 lines
6.7 KiB
C++
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;
|
|
}
|
|
|
|
};
|