diff --git a/rotation.cpp b/rotation.cpp index 305409d..a6f69b4 100644 --- a/rotation.cpp +++ b/rotation.cpp @@ -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 +void rotate_pixel(TiledImage const& src, TiledImage& 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 +TiledImage rotate(TiledImage 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 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(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(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;