diff --git a/.gitignore b/.gitignore index 91e3764..dd09683 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .DS_Store *.dSYM *.swp +*.o rotated*.ppm +rotated*.pnm *.png diff --git a/Makefile b/Makefile index 6b1eb5d..bf5e679 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/image.cpp b/image.cpp new file mode 100644 index 0000000..55e9bc6 --- /dev/null +++ b/image.cpp @@ -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; +} diff --git a/image.h b/image.h new file mode 100644 index 0000000..09d6d90 --- /dev/null +++ b/image.h @@ -0,0 +1,289 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pnm.h" + +using namespace std; + + +// +// +// Point +// + +template +struct TPoint { + T x; + T y; + + TPoint(T a, T b) + : x(a) + , y(b) + {} +}; + +typedef TPoint Point; +typedef TPoint DPoint; // absolute point, can be negative +template + +std::basic_ostream& operator << (std::basic_ostream& o, TPoint 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 +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; + } + +}; diff --git a/pnm.cpp b/pnm.cpp new file mode 100644 index 0000000..a2c5d42 --- /dev/null +++ b/pnm.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#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(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 diff --git a/pnm.h b/pnm.h new file mode 100644 index 0000000..af90ea4 --- /dev/null +++ b/pnm.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +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); + +} diff --git a/rotation.cpp b/rotation.cpp index 1acccde..57f2830 100644 --- a/rotation.cpp +++ b/rotation.cpp @@ -12,428 +12,10 @@ #include #include +#include "image.h" + using namespace std; -// -// -// Point -// - -template -struct TPoint { - T x; - T y; - - TPoint(T a, T b) - : x(a) - , y(b) - {} -}; - -typedef TPoint Point; -typedef TPoint DPoint; // absolute point, can be negative -template - -std::basic_ostream& operator << (std::basic_ostream& o, TPoint 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(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 -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 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 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 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 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(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(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; }