Fix tiled rotation.

- Add TiledImage::access_pixel().
- Add TiledImage::print_tile().
- Compare implementations.
master
Fabien Freling 2014-07-02 21:54:52 +02:00
parent 007ad2284e
commit 7e33e85909
1 changed files with 227 additions and 28 deletions

View File

@ -250,6 +250,59 @@ struct TiledImage : public Image {
}
}
uint8_t*
access_pixel(unsigned int x, unsigned int y)
{
if (x >= width || y >= height)
return nullptr;
unsigned int const tile_width = tile_w * 3;
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
uint8_t* tile = tiles[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 * 3);
}
uint8_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 * 3;
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
//cout << "tile index: " << tile_index << endl;
uint8_t* tile = tiles[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 * 3);
}
void
print_tile(unsigned int index) const
{
cout << "Tile[" << index << "]" << endl;
uint8_t const* tile = tiles[index];
unsigned int const tile_width = tile_w * 3;
for (unsigned int j = 0; j < tile_h; ++j)
{
for (unsigned int i = 0; i < tile_w; ++i)
{
if (i != 0)
cout << ", ";
uint8_t const* p = tile + j * tile_width + i * 3;
cout << (int) *p << " " << (int) *(p + 1) << " " << (int) *(p + 2);
}
cout << endl;
}
cout << endl;
}
protected:
void allocate_memory(unsigned int w, unsigned int h)
{
@ -277,19 +330,11 @@ struct TiledImage : public Image {
{
this->allocate_memory(width, height);
unsigned int const tile_width = tile_w * 3;
// Pixel loading
for (unsigned int j = 0; j < height; ++j)
for (unsigned int i = 0; i < width; ++i)
{
unsigned int const tile_index = (j / tile_h) * nb_col_tile + (i / tile_w);
uint8_t* tile = tiles[tile_index];
unsigned int const tile_j = j % tile_h;
unsigned int const tile_i = i % tile_w;
// if (i % 3 == 0)
// cout << "pixel (" << i / 3 << ", " << j << ") goes in tile (" << i / (tile_w * 3) << ", " << j / tile_h << ")" << endl;
tile += tile_j * tile_width + (tile_i * 3);
uint8_t* tile = this->access_pixel(i, j);
*(tile++) = istr.get();
*(tile++) = istr.get();
*(tile++) = istr.get();
@ -300,16 +345,10 @@ struct TiledImage : public Image {
virtual bool write_body(std::ofstream& ostr) const override
{
unsigned int const tile_width = tile_w * 3;
for (unsigned int j = 0; j < height; ++j)
for (unsigned int i = 0; i < width; ++i)
{
unsigned int const tile_index = (j / tile_h) * nb_col_tile + (i / tile_w);
uint8_t* tile = tiles[tile_index];
unsigned int const tile_j = j % tile_h;
unsigned int const tile_i = i % tile_w;
tile += tile_j * tile_width + (tile_i * 3);
uint8_t const* tile = this->access_pixel(i, j);
ostr << (char) *(tile++);
ostr << (char) *(tile++);
ostr << (char) *(tile++);
@ -532,6 +571,9 @@ void round_if_very_small(double& d)
{
if (abs(d) < 1.0e-10)
d = 0.0;
if (abs(d - 1) < 1.0e-10)
d = 1.0;
}
@ -576,6 +618,13 @@ void rotate_pixel(Image const& src, Image& rotated,
double y_delta = src_rotated_point.y - floor(src_rotated_point.y);
round_if_very_small(y_delta);
// special case if we can directly map the src to the dest
if (x_delta == 0 && y_delta == 0)
{
memcpy(&rotated.buffer[rot_index], &src.buffer[src_index], 3 * sizeof (uint8_t));
return;
}
// SIMD
__m128 const x_d = _mm_set_ps1(x_delta);
__m128 const inv_x_d = _mm_set_ps1(1 - x_delta);
@ -692,6 +741,136 @@ Image rotate(Image const& src, double angle)
return rotated;
}
//
//
// Tile rotation
//
template<unsigned int W, unsigned int H>
void rotate_pixel(TiledImage<W, H> const& src, TiledImage<W, H>& rotated,
DPoint const& src_rotated_point,
unsigned int rot_tile_index, unsigned int rot_index)
{
uint8_t const* src_index_1 = src.access_pixel((int) src_rotated_point.x, (int) src_rotated_point.y);
double x_delta = src_rotated_point.x - (int) src_rotated_point.x;
round_if_very_small(x_delta);
double y_delta = src_rotated_point.y - (int) src_rotated_point.y;
round_if_very_small(y_delta);
// special case if we can directly map the src to the dest
if (x_delta == 0 && y_delta == 0)
{
// cout << "we can directly map, w00t" << endl;
uint8_t* rot_tile = rotated.tiles[rot_tile_index];
memcpy(&rot_tile[rot_index], src_index_1, 3 * sizeof (uint8_t));
return;
}
// cout << "src rotated point: " << src_rotated_point << endl;
// cout << "src rotated point y: " << src_rotated_point.y << endl;
// cout << "src rotated point y int: " << (int) src_rotated_point.y << endl;
// cout << "x delta = " << x_delta << endl;
// cout << "y delta = " << y_delta << endl;
uint8_t const* src_index_2 = src.access_pixel((int) src_rotated_point.x + 1, (int) src_rotated_point.y);
uint8_t const* src_index_3 = src.access_pixel((int) src_rotated_point.x, (int) src_rotated_point.y + 1);
uint8_t const* src_index_4 = src.access_pixel((int) src_rotated_point.x + 1, (int) src_rotated_point.y + 1);
// FIXME: deal with image border
if (!src_index_1 || !src_index_2 || !src_index_3 || !src_index_4)
return;
// SIMD
__m128 const x_d = _mm_set_ps1(x_delta);
__m128 const inv_x_d = _mm_set_ps1(1 - x_delta);
__m128 top_left = _mm_set_ps(*src_index_1, *(src_index_1 + 1), *(src_index_1 + 2), 0.0);
__m128 top_right = _mm_set_ps(*src_index_2, *(src_index_2 + 1), *(src_index_2 + 2), 0.0);
top_left = _mm_mul_ps(top_left, inv_x_d);
top_right = _mm_mul_ps(top_right, x_d);
top_left = _mm_add_ps(top_left, top_right);
__m128 bottom_left = _mm_set_ps(*src_index_3, *(src_index_3 + 1), *(src_index_3 + 2), 0.0);
__m128 bottom_right = _mm_set_ps(*src_index_4, *(src_index_4 + 1), *(src_index_4 + 2), 0.0);
bottom_left = _mm_mul_ps(bottom_left, inv_x_d);
bottom_right = _mm_mul_ps(bottom_right, x_d);
bottom_left = _mm_add_ps(bottom_left, bottom_right);
__m128 const y_d = _mm_set_ps1(y_delta);
__m128 const inv_y_d = _mm_set_ps1(1 - y_delta);
top_left = _mm_mul_ps(top_left, inv_y_d);
bottom_left = _mm_mul_ps(bottom_left, y_d);
top_left = _mm_add_ps(top_left, bottom_left);
// convert float values to uint8_t
uint8_t* rot_tile = rotated.tiles[rot_tile_index];
rot_tile[rot_index] = top_left[3];
rot_tile[rot_index + 1] = top_left[2];
rot_tile[rot_index + 2] = top_left[1];
}
template<unsigned int W, unsigned int H>
TiledImage<W, H> rotate(TiledImage<W, H> const& src, double angle)
{
double const rotation = (angle / 180.0f) * M_PI;
unsigned int w = 0;
unsigned int h = 0;
compute_output_size(src, rotation, w, h);
TiledImage<W, H> rotated(w, h);
DPoint src_origin = get_mapped_point(rotated, Point(0, 0), -rotation);
DPoint src_delta_x = get_mapped_point(rotated, Point(1, 0), -rotation);
DPoint src_delta_y = get_mapped_point(rotated, Point(0, 1), -rotation);
src_delta_x.x = src_delta_x.x - src_origin.x;
src_delta_x.y = src_delta_x.y - src_origin.y;
round_if_very_small(src_delta_x.x);
round_if_very_small(src_delta_x.y);
src_delta_y.x = src_delta_y.x - src_origin.x;
src_delta_y.y = src_delta_y.y - src_origin.y;
round_if_very_small(src_delta_y.x);
round_if_very_small(src_delta_y.y);
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);
for (unsigned int y = 0; y < rotated.nb_row_tile; ++y)
{
for (unsigned int x = 0; x < rotated.nb_col_tile; ++x)
{
unsigned int const rot_tile_index = y * rotated.nb_col_tile + x;
for (unsigned int j = 0; j < H; ++j)
{
int const y_index = y * H + j;
for (unsigned int i = 0; i < W; ++i)
{
// cout << "rotated: tile[" << x << ", " << y << "] point(" << i << ", " << j << ")" << endl;
unsigned int const rot_index = (j * W + i) * 3;
int const x_index = x * W + i;
Point const rot_point(x_index, y_index);
DPoint src_rotated_point(rot_origin_in_src.x + x_index * src_delta_x.x + y_index * src_delta_y.x,
rot_origin_in_src.y + x_index * src_delta_x.y + y_index * src_delta_y.y);
// cout << "rotated tile index: " << rot_tile_index << endl;
// cout << "src point: " << src_rotated_point << endl;
if (src_rotated_point.x < 0 || src_rotated_point.x > src.width
|| src_rotated_point.y < 0 || src_rotated_point.y > src.height)
continue;
rotate_pixel(src, rotated,
src_rotated_point,
rot_tile_index, rot_index);
}
}
}
}
return rotated;
}
//
@ -851,38 +1030,58 @@ int main(int argc, char* argv[])
if (!check_90(argv[1]))
{
cerr << __LINE__ << " | 90 degrees check failed" << endl;
cerr << __LINE__ << " | 90 degrees check failed" << endl << endl;
// return 1;
}
}
TiledImage<8, 8> tiled_img(argv[1]);
tiled_img.save("tiled.ppm");
return 0;
Image img(argv[1]);
TiledImage<16, 16> tiled_img(argv[1]);
for (double rotation = 0; rotation < 360; rotation += 5)
for (double rotation = 0; rotation < 360; rotation += 450)
{
// double rotation = 45;
// No tile
auto const before = chrono::high_resolution_clock::now();
Image rotated = rotate(img, rotation);
Image const rotated = rotate(img, rotation);
auto const after = chrono::high_resolution_clock::now();
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
// Tile
auto const before_tiled = chrono::high_resolution_clock::now();
auto const rotated_tiled = rotate(tiled_img, rotation);
auto const after_tiled = chrono::high_resolution_clock::now();
auto const duration_ms_tiled = std::chrono::duration_cast<std::chrono::milliseconds>(after_tiled - before_tiled);
cout << "rotate(" << rotation << "): " << duration_ms.count() << " ms" << endl;
// TODO: compare implementation
cout << "tiled: " << duration_ms_tiled.count() << " ms" << endl;
cout << "speedup: " << (int) (((float) duration_ms.count() / duration_ms_tiled.count() - 1) * 100) << "%" << endl << endl;
// tile check
//tiled_img.print_tile(0);
//rotated_tiled.print_tile(0);
stringstream filename;
// filename << "/tmp/";
// filename << "/tmp/";
filename << "rotated_";
stringstream filename_tiled;
// filename_tiled << "/tmp/";
filename_tiled << "tiled_rotated_";
if (rotation < 100)
{
filename << "0";
filename_tiled << "0";
}
if (rotation < 10)
{
filename << "0";
filename_tiled << "0";
}
filename << rotation << ".ppm";
filename_tiled << rotation << ".ppm";
rotated.save(filename.str());
rotated_tiled.save(filename_tiled.str());
}
return 0;