Add support for PGM format.

The code has been split into different source files.

This breaks for now rotation for tiled images and PPM format. The focus
is now on the PGM format.
This commit is contained in:
Fabien Freling 2014-07-20 16:34:45 +02:00
parent 53974a4116
commit 7e152c29fc
7 changed files with 612 additions and 469 deletions

2
.gitignore vendored
View file

@ -1,6 +1,8 @@
.DS_Store
*.dSYM
*.swp
*.o
rotated*.ppm
rotated*.pnm
*.png

View file

@ -1,14 +1,24 @@
CXX = clang++
CXXFLAGS = -std=c++11 -W -Wall -O3 -ffast-math -g -Werror
DEFINES = -DSIMD
LFLAGS = -flto
DEFINES = #-DSIMD
BUILD_DIR = /tmp
IMG = img/lena.ppm
SRC = rotation.cpp \
image.cpp \
pnm.cpp
HEADERS = image.h \
pnm.h
OBJS = $(patsubst %.cpp,%.o,$(SRC))
IMG = img/lena_3000.pgm
all: rotation.cpp
$(CXX) $(CXXFLAGS) $(DEFINES) $< -o $(BUILD_DIR)/rotation
all: $(OBJS)
$(CXX) $(CXXFLAGS) $(DEFINES) $(LFLAGS) $(OBJS) -o $(BUILD_DIR)/rotation
%.o: %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) $(DEFINES) $< -c -o $@
clean:
@rm -f *~ *.o .*.swp *.ppm cachegrind.out.*
@rm -f *~ *.o .*.swp *.ppm *.pgm *.pnm cachegrind.out.*
run: all
$(BUILD_DIR)/rotation $(IMG)
@ -17,4 +27,4 @@ debug: all
lldb $(BUILD_DIR)/rotation $(IMG)
cachegrind: all
valgrind --tool=cachegrind $(BUILD_DIR)/rotation $(IMG)
valgrind --tool=cachegrind --dsymutil=yes $(BUILD_DIR)/rotation $(IMG)

154
image.cpp Normal file
View file

@ -0,0 +1,154 @@
#include "image.h"
#include "pnm.h"
Image::Image()
: width(0)
, height(0)
, buffer(NULL)
, pixel_size(0)
{}
Image::~Image()
{
delete [] buffer;
}
Image::Image(unsigned int w, unsigned int h, pnm::Format type)
{
this->width = w;
this->height = h;
this->type = type;
switch (this->type)
{
case pnm::Format::PGM:
{
this->pixel_size = 1;
}
break;
case pnm::Format::PPM:
{
this->pixel_size = 4;
}
break;
}
buffer = new pvalue_t[width * height * pixel_size];
memset(buffer, 0, width * height * pixel_size * sizeof (pvalue_t));
}
Image::Image(string const& path)
: Image()
{
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();
}
switch (this->type)
{
case pnm::Format::PGM:
{
this->pixel_size = 1;
}
break;
case pnm::Format::PPM:
{
this->pixel_size = 4;
}
break;
}
if (!this->read_body(is))
{
delete buffer;
buffer = nullptr;
cerr << "Invalid header." << endl;
abort();
}
}
bool Image::save(string const& path) const
{
ofstream os(path);
if (!os.is_open())
{
cerr << "Cannot open file '" << path << "'" << endl;
return false;
}
pnm::write_header(os, this->type, this->width, this->height);
this->write_body(os);
return true;
}
bool Image::read_body(std::ifstream& istr)
{
unsigned int const nb_pixels = width * height;
buffer = new pvalue_t[nb_pixels * pixel_size];
pvalue_t* pixel = buffer;
for (unsigned int i = 0; i < nb_pixels; ++i)
{
switch (this->type)
{
case pnm::Format::PGM:
{
pixel[0] = istr.get();
}
break;
case pnm::Format::PPM:
{
pixel[0] = istr.get();
pixel[1] = istr.get();
pixel[2] = istr.get();
}
break;
}
pixel += pixel_size;
}
return true;
}
bool Image::write_body(std::ofstream& ostr) const
{
unsigned int const nb_pixels = width * height;
pvalue_t* pixel = buffer;
for (unsigned int i = 0; i < nb_pixels; ++i)
{
switch (this->type)
{
case pnm::Format::PGM:
{
ostr << (char) pixel[0];
}
break;
case pnm::Format::PPM:
{
ostr << (char) pixel[0];
ostr << (char) pixel[1];
ostr << (char) pixel[2];
}
break;
}
pixel += pixel_size;
}
return true;
}

289
image.h Normal file
View file

@ -0,0 +1,289 @@
#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)
{}
};
typedef TPoint<int> 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;
}
};

86
pnm.cpp Normal file
View file

@ -0,0 +1,86 @@
#include <fstream>
#include <string>
#include <iostream>
#include "pnm.h"
using namespace std;
namespace pnm
{
bool read_header(std::ifstream& istr, Format& type, unsigned int& width, unsigned int& height)
{
// check magic
if (istr.get() != 'P' )
{
return false;
}
char c_type = static_cast<char>(istr.get());
if (c_type == '6')
{
type = Format::PPM;
}
else if (c_type == '5')
{
type = Format::PGM;
}
else
{
cerr << "Invalid PNM format." << endl;
return false;
}
if (istr.get() != '\n')
{
return false;
}
// skip comments
while (istr.peek() == '#')
{
std::string line;
std::getline(istr, line);
}
// get size
istr >> width >> height;
if (width == 0 || height == 0)
{
return false;
}
// get maxvalue
if (istr.get() != '\n')
{
return false;
}
int max_value = -1;
istr >> max_value;
if (max_value > 255)
{
return false;
}
if (istr.get() != '\n')
{
return false;
}
return true;
}
bool write_header(std::ofstream& ostr, Format type, unsigned int width, unsigned int height)
{
if (type == Format::PGM)
ostr << "P5" << endl;
if (type == Format::PPM)
ostr << "P6" << endl;
ostr << width << " " << height << endl;
ostr << "255" << endl;
return true;
}
} // end of namespace pnm

16
pnm.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include <fstream>
namespace pnm
{
enum class Format {
PGM,
PPM
};
bool read_header(std::ifstream& istr, Format& type, unsigned int& width, unsigned int& height);
bool write_header(std::ofstream& ostr, Format type, unsigned int width, unsigned int height);
}

View file

@ -12,428 +12,10 @@
#include <xmmintrin.h>
#include <emmintrin.h>
#include "image.h"
using namespace std;
//
//
// Point
//
template <typename T>
struct TPoint {
T x;
T y;
TPoint(T a, T b)
: x(a)
, y(b)
{}
};
typedef TPoint<int> 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
#define PIXEL_SIZE 4
//
//
// Image
//
struct Image {
unsigned int width;
unsigned int height;
pvalue_t* buffer;
Image()
: width(0)
, height(0)
, buffer(NULL)
{}
virtual ~Image()
{
delete [] buffer;
}
Image(unsigned int w, unsigned int h)
{
this->width = w;
this->height = h;
buffer = new pvalue_t[width * height * PIXEL_SIZE];
memset(buffer, 0, width * height * PIXEL_SIZE * sizeof (pvalue_t));
}
Image(string const& path)
: Image()
{
ifstream is(path);
if (!is.is_open())
{
cerr << "Cannot open file '" << path << "'" << endl;
abort();
}
if (!this->read_header(is))
{
cerr << "Invalid header." << endl;
abort();
}
if (!this->read_body(is))
{
delete buffer;
buffer = nullptr;
cerr << "Invalid header." << endl;
abort();
}
}
bool save(string const& path) const
{
ofstream os(path);
if (!os.is_open())
{
cerr << "Cannot open file '" << path << "'" << endl;
return false;
}
this->write_header(os);
this->write_body(os);
return true;
}
protected:
bool read_header(std::ifstream& istr)
{
// check magic
if (istr.get() != 'P' )
{
return false;
}
char type = static_cast<char>(istr.get());
if (type != '6')
{
return false;
}
if (istr.get() != '\n')
{
return false;
}
// skip comments
while (istr.peek() == '#')
{
std::string line;
std::getline(istr, line);
}
// get size
istr >> width >> height;
if (width == 0 || height == 0)
{
return false;
}
// get maxvalue
if (istr.get() != '\n')
{
return false;
}
int max_value = -1;
istr >> max_value;
if (max_value > 255)
{
return false;
}
if (istr.get() != '\n')
{
return false;
}
// cout << "width: " << width << endl;
// cout << "height: " << height << endl;
return true;
}
bool write_header(std::ofstream& ostr) const
{
ostr << "P6" << endl;
ostr << width << " " << height << endl;
ostr << "255" << endl;
return true;
}
virtual bool read_body(std::ifstream& istr)
{
unsigned int const nb_pixels = width * height;
buffer = new pvalue_t[nb_pixels * PIXEL_SIZE];
pvalue_t* pixel = buffer;
for (unsigned int i = 0; i < nb_pixels; ++i)
{
pixel[0] = istr.get();
pixel[1] = istr.get();
pixel[2] = istr.get();
pixel += PIXEL_SIZE;
}
return true;
}
virtual bool write_body(std::ofstream& ostr) const
{
unsigned int const nb_pixels = width * height;
pvalue_t* pixel = buffer;
for (unsigned int i = 0; i < nb_pixels; ++i)
{
ostr << (char) pixel[0];
ostr << (char) pixel[1];
ostr << (char) pixel[2];
pixel += PIXEL_SIZE;
}
return true;
}
};
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 (!this->read_header(is))
{
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;
}
};
//
@ -589,12 +171,12 @@ void rotate_pixel(Image const& src,
int const src_x = src_rotated_point.x >> 3;
int const src_y = src_rotated_point.y >> 3;
unsigned int src_index = (src_y * src.width + src_x) * PIXEL_SIZE;
unsigned int src_index = (src_y * src.width + src_x) * src.pixel_size;
// Bilinear interpolation
unsigned int src_index_1 = src_index;
unsigned int src_index_3 = src_index_1 + PIXEL_SIZE * src.width;
unsigned int src_index_4 = src_index_3 + PIXEL_SIZE;
unsigned int src_index_3 = src_index_1 + src.pixel_size * src.width;
unsigned int src_index_4 = src_index_3 + src.pixel_size;
// Out-of-bounds check
if (src_index_4 >= src_limit)
@ -607,14 +189,14 @@ void rotate_pixel(Image const& src,
#ifndef SIMD
unsigned int src_index_2 = src_index_1 + PIXEL_SIZE;
unsigned int src_index_2 = src_index_1 + src.pixel_size;
rotate_buffer[rot_index] = ((src.buffer[src_index_1] * inv_x + src.buffer[src_index_2] * x_delta) * inv_y
+ (src.buffer[src_index_3] * inv_x + src.buffer[src_index_4] * x_delta) * y_delta) >> 6;
rotate_buffer[rot_index + 1] = ((src.buffer[src_index_1 + 1] * inv_x + src.buffer[src_index_2 + 1] * x_delta) * inv_y
+ (src.buffer[src_index_3 + 1] * inv_x + src.buffer[src_index_4 + 1] * x_delta) * y_delta) >> 6;
rotate_buffer[rot_index + 2] = ((src.buffer[src_index_1 + 2] * inv_x + src.buffer[src_index_2 + 2] * x_delta) * inv_y
+ (src.buffer[src_index_3 + 2] * inv_x + src.buffer[src_index_4 + 2] * x_delta) * y_delta) >> 6;
// rotate_buffer[rot_index + 1] = ((src.buffer[src_index_1 + 1] * inv_x + src.buffer[src_index_2 + 1] * x_delta) * inv_y
// + (src.buffer[src_index_3 + 1] * inv_x + src.buffer[src_index_4 + 1] * x_delta) * y_delta) >> 6;
// rotate_buffer[rot_index + 2] = ((src.buffer[src_index_1 + 2] * inv_x + src.buffer[src_index_2 + 2] * x_delta) * inv_y
// + (src.buffer[src_index_3 + 2] * inv_x + src.buffer[src_index_4 + 2] * x_delta) * y_delta) >> 6;
#else
@ -635,8 +217,8 @@ void rotate_pixel(Image const& src,
top = _mm_srli_epi16(top, 6);
rotate_buffer[rot_index] = _mm_extract_epi16(top, 0) + _mm_extract_epi16(top, 4);
rotate_buffer[rot_index + 1] = _mm_extract_epi16(top, 1) + _mm_extract_epi16(top, 5);
rotate_buffer[rot_index + 2] = _mm_extract_epi16(top, 2) + _mm_extract_epi16(top, 6);
// rotate_buffer[rot_index + 1] = _mm_extract_epi16(top, 1) + _mm_extract_epi16(top, 5);
// rotate_buffer[rot_index + 2] = _mm_extract_epi16(top, 2) + _mm_extract_epi16(top, 6);
#endif // ! SIMD
@ -648,7 +230,7 @@ Image* rotate(Image const& src, double angle)
unsigned int w = 0;
unsigned int h = 0;
compute_output_size(src, rotation, w, h);
Image* rotated = new Image(w, h);
Image* rotated = new Image(w, h, src.type);
// corner points in rotated image
// TODO: add one ligne for smooth border
@ -672,7 +254,7 @@ Image* rotate(Image const& src, double angle)
round_if_very_small(src_delta_y.x);
round_if_very_small(src_delta_y.y);
unsigned int const src_limit = src.width * src.height * PIXEL_SIZE;
unsigned int const src_limit = src.width * src.height * src.pixel_size;
DPoint const rot_origin_in_src_grid = get_mapped_point(*rotated, Point(0, 0), -rotation);
DPoint const rot_origin_in_src = convert_img_coord_precision(src, rot_origin_in_src_grid);
@ -702,7 +284,7 @@ Image* rotate(Image const& src, double angle)
buffer, buffer_index);
}
buffer_index += PIXEL_SIZE;
buffer_index += rotated->pixel_size;
}
}
@ -725,7 +307,7 @@ void rotate_pixel(TiledImage<W, H> const& src,
int const src_y = src_rotated_point.y >> 3;
pvalue_t const* src_index_1 = src.access_pixel(src_x, src_y);
pvalue_t const* src_index_3 = src_index_1 + (W + 1) * PIXEL_SIZE;
pvalue_t const* src_index_3 = src_index_1 + (W + 1) * src.pixel_size;
unsigned int x_delta = src_rotated_point.x & 0x07;;
unsigned int y_delta = src_rotated_point.y & 0x07;
@ -734,8 +316,8 @@ void rotate_pixel(TiledImage<W, H> const& src,
#ifndef SIMD
pvalue_t const* src_index_2 = src_index_1 + PIXEL_SIZE;
pvalue_t const* src_index_4 = src_index_3 + PIXEL_SIZE;
pvalue_t const* src_index_2 = src_index_1 + src.pixel_size;
pvalue_t const* src_index_4 = src_index_3 + src.pixel_size;
rot_tile[0] = ((src_index_1[0] * inv_x + src_index_2[0] * x_delta) * inv_y
+ (src_index_3[0] * inv_x + src_index_4[0] * x_delta) * y_delta) >> 6;
@ -824,11 +406,11 @@ rotate(TiledImage<W, H> const& src, double angle)
rotate_pixel(src, src_runner, runner);
}
runner += PIXEL_SIZE;
runner += rotated->pixel_size;
}
// Jump overlapping pixel
runner += PIXEL_SIZE;
runner += rotated->pixel_size;
}
}
}
@ -847,7 +429,7 @@ rotate(TiledImage<W, H> const& src, double angle)
bool check_points()
{
Image five(5, 5);
Image five(5, 5, pnm::Format::PGM);
Point origin(0, 0);
DPoint d1 = convert_grid_coord(five, origin);
assert(d1.x == -2);
@ -858,7 +440,7 @@ bool check_points()
bool check_trigo()
{
Image square(500, 500);
Image square(500, 500, pnm::Format::PGM);
double const ratio = compute_ratio(square);
double const sigma = 1.0e-2;
if (!fequal(ratio, 1 / 707.106, sigma))
@ -903,7 +485,7 @@ bool check_trigo()
}
Image rotated(w, h);
Image rotated(w, h, pnm::Format::PGM);
DPoint const a_p45 = convert_abs_coord(angle + rotation, ratio);
Point const p45 = convert_img_coord(rotated, a_p45);
@ -953,9 +535,9 @@ bool check_90(string const& path)
{
for (unsigned int x = 0; x < rotated->width; ++x)
{
unsigned rot_index = (y * rotated->width + x) * 3;
unsigned src_index = (x * src.width + (src.width - 1 - y)) * 3;
if (memcmp(&rotated->buffer[rot_index], &src.buffer[src_index], 3 * sizeof (uint8_t)) != 0)
unsigned rot_index = (y * rotated->width + x) * rotated->pixel_size;
unsigned src_index = (x * src.width + (src.width - 1 - y)) * src.pixel_size;
if (memcmp(&rotated->buffer[rot_index], &src.buffer[src_index], src.pixel_size * sizeof (pvalue_t)) != 0)
{
Point r(x, y);
Point s((src.width - 1 - y), x);
@ -988,7 +570,7 @@ string get_save_path(string const& base, unsigned int i)
filename << "0";
if (i < 10)
filename << "0";
filename << i << ".ppm";
filename << i << ".pnm";
return filename.str();
}
@ -1021,6 +603,7 @@ int main(int argc, char* argv[])
double const step = 15;
bool save_output_img = false;
bool print_each_run = false;
bool test_tile = false;
// No tile
Image img(argv[1]);
@ -1048,29 +631,32 @@ int main(int argc, char* argv[])
cout << " average: " << average / i << "ms" << endl << endl;
// Tile
TiledImage<32, 32> tiled_img(argv[1]);
average = 0.0;
i = 0;
cout << "Tiled image" << endl;
for (double rotation = 0; rotation < 360; rotation += step)
if (test_tile)
{
auto const before = chrono::high_resolution_clock::now();
auto const rotated = rotate(tiled_img, rotation);
auto const after = chrono::high_resolution_clock::now();
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
average += duration_ms.count();
TiledImage<32, 32> tiled_img(argv[1]);
average = 0.0;
i = 0;
cout << "Tiled image" << endl;
for (double rotation = 0; rotation < 360; rotation += step)
{
auto const before = chrono::high_resolution_clock::now();
auto const rotated = rotate(tiled_img, rotation);
auto const after = chrono::high_resolution_clock::now();
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
average += duration_ms.count();
if (print_each_run)
cout << "rotate tiled(" << rotation << "): " << duration_ms.count() << " ms" << endl;
if (print_each_run)
cout << "rotate tiled(" << rotation << "): " << duration_ms.count() << " ms" << endl;
if (save_output_img)
rotated->save(get_save_path("rotated_tiled", rotation));
if (save_output_img)
rotated->save(get_save_path("rotated_tiled", rotation));
delete rotated;
++i;
delete rotated;
++i;
}
cout << "---------" << endl;
cout << " average: " << average / i << "ms" << endl;
}
cout << "---------" << endl;
cout << " average: " << average / i << "ms" << endl;
return 0;
}