Created
June 10, 2024 15:05
-
-
Save 27Cobalter/33345eb5aca9c4d68610a130e7cdfa03 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bit> | |
#include <cmath> | |
#include <iostream> | |
#include <ranges> | |
#include <format> | |
#include <memory> | |
#include <numbers> | |
#include <chrono> | |
#include <opencv2/opencv.hpp> | |
#include <opencv2/highgui/highgui.hpp> | |
class SakuraFragment { | |
private: | |
cv::Mat lut_atan_; | |
cv::Mat lut_r_; | |
cv::Mat lut_d_; | |
float delta_theta_; | |
int32_t frame_max_; | |
int32_t half_width_; | |
int32_t half_height_; | |
public: | |
SakuraFragment(const int32_t width, const int32_t height, const int32_t frame_max); | |
float Frag(const int32_t x, const int32_t y, const int32_t time) const; | |
}; | |
SakuraFragment::SakuraFragment(const int32_t width, const int32_t height, | |
const int32_t frame_max) | |
: frame_max_(frame_max), half_width_(width >> 1), half_height_(height >> 1) { | |
constexpr float pi = std::numbers::pi_v<float>; | |
constexpr float coeff = 8192.0f / (pi * 2.0); | |
int32_t frame_size = frame_max_ + 1; | |
delta_theta_ = 2.0 * pi / frame_size * coeff; | |
lut_atan_ = cv::Mat::zeros(cv::Size(width, height), CV_32F); | |
lut_r_ = cv::Mat::zeros(cv::Size(width, height), CV_32F); | |
lut_d_ = cv::Mat::zeros(cv::Size(8192, 1), CV_32F); | |
float* laptr = lut_atan_.ptr<float>(0); | |
float* lrptr = lut_r_.ptr<float>(0); | |
for (int j = 0; j < height; j++) { | |
float* laptrj = laptr + j * width; | |
float* lrptrj = lrptr + j * width; | |
const int32_t y_denom = j - (height >> 1); | |
const float fy = static_cast<float>(y_denom) / height; | |
for (int i = 0; i < width; i++) { | |
const int32_t x_denom = (width >> 1) - i; | |
float fx = static_cast<float>(x_denom) / width; | |
laptrj[i] = std::atan2(fy, fx) * coeff; | |
lrptrj[i] = std::fma(fx, fx, fy * fy); | |
} | |
} | |
float* ldptr = lut_d_.ptr<float>(0); | |
for (int i = 0; i < 8192; i++) { | |
float theta = 2.0 * pi * i / 8192.0; | |
float v = | |
std::min(std::abs(std::cos(theta * 2.5)) + 0.4, std::abs(std::sin(theta * 2.5)) + 1.1) * | |
0.32; | |
ldptr[i] = v * v; | |
} | |
} | |
float SakuraFragment::Frag(const int32_t x, const int32_t y, const int32_t time) const { | |
constexpr float pi2 = 2.0 * std::numbers::pi_v<float>; | |
float theta_a = lut_atan_.ptr<float>(y)[x]; | |
int32_t theta = static_cast<int32_t>(theta_a + delta_theta_ * time); | |
// requires -4096 <= theta <= 8192 | |
// => if (theta < 0) { | |
// theta += 8192; | |
// } else if (theta >= 8192) { | |
// theta -= 8192; | |
// } => | |
theta = (theta + 8192) & 0x1FFF; | |
float d2 = lut_d_.ptr<float>(0)[theta]; | |
float r2 = lut_r_.ptr<float>(y)[x]; | |
if (r2 < d2) { | |
return 1.0f; | |
} else { | |
return 0.0f; | |
} | |
} | |
class ImageBuffer { | |
private: | |
std::vector<cv::Mat> buffer_; | |
const int32_t width_; | |
const int32_t height_; | |
const int32_t buffer_max_; | |
int32_t offset_x_; | |
int32_t offset_y_; | |
int32_t horizontal_; | |
int32_t vertical_; | |
int32_t current_frame_; | |
SakuraFragment sakura_; | |
private: | |
void PreGenerateFrame(); | |
public: | |
ImageBuffer(const int32_t width, const int32_t height, const int32_t buffer_size); | |
bool CreateSubArray(const int32_t offset_x, const int32_t offset_y, const int32_t horizontal, | |
const int32_t vertical); | |
uint8_t* GetFrame(); | |
}; | |
ImageBuffer::ImageBuffer(const int32_t width, const int32_t height, const int32_t buffer_max) | |
: width_(width), | |
height_(height), | |
buffer_max_(buffer_max), | |
offset_x_(0), | |
offset_y_(0), | |
horizontal_(width), | |
vertical_(height), | |
current_frame_(0), | |
sakura_(width, height, buffer_max) { | |
buffer_ = std::vector<cv::Mat>(buffer_max + 1); | |
std::cout << buffer_.size() << std::endl; | |
PreGenerateFrame(); | |
} | |
void ImageBuffer::PreGenerateFrame() { | |
std::cout << std::format("(ofx, ofy, h, v) = ({}, {}, {}, {})", offset_x_, offset_y_, | |
horizontal_, vertical_) | |
<< std::endl; | |
for (int32_t idx = 0; idx < buffer_.size(); idx++) { | |
buffer_[idx] = cv::Mat::zeros(cv::Size(horizontal_, vertical_), CV_8UC1); | |
uint8_t* dptr = buffer_[idx].ptr<uint8_t>(0); | |
for (int32_t y = 0; y < vertical_; y++) { | |
uint8_t* dptrj = dptr + horizontal_ * y; | |
int32_t frag_y = offset_y_ + y; | |
for (int32_t x = 0; x < horizontal_; x++) { | |
dptrj[x] = sakura_.Frag(offset_x_ + x, frag_y, idx) * 255; | |
} | |
} | |
} | |
} | |
bool ImageBuffer::CreateSubArray(const int32_t offset_x, const int32_t offset_y, | |
const int32_t horizontal, const int32_t vertical) { | |
if (offset_x + horizontal > width_ || offset_y + vertical > height_) { | |
return false; | |
} | |
offset_x_ = offset_x; | |
offset_y_ = offset_y; | |
horizontal_ = horizontal; | |
vertical_ = vertical; | |
PreGenerateFrame(); | |
return true; | |
} | |
uint8_t* ImageBuffer::GetFrame() { | |
uint8_t* ptr = buffer_[current_frame_].ptr<uint8_t>(0); | |
assert(std::bit_ceil<uint32_t>(buffer_max_) == (buffer_max_ + 1)); | |
current_frame_ = (current_frame_ + 1) & buffer_max_; | |
return ptr; | |
} | |
auto main() -> int32_t { | |
auto start = std::chrono::high_resolution_clock::now(); | |
constexpr int32_t image_size = 512; | |
constexpr int32_t buffer_max = 0x3F; | |
ImageBuffer buffer(image_size, image_size, buffer_max); | |
auto end = std::chrono::high_resolution_clock::now(); | |
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() | |
<< std::endl; | |
cv::namedWindow("Sakura", cv::WINDOW_GUI_NORMAL); | |
int32_t ui_offset_x = 0; | |
int32_t ui_offset_y = 0; | |
int32_t ui_horizontal = image_size; | |
int32_t ui_vertical = image_size; | |
cv::setWindowProperty("Sakura", cv::WND_PROP_ASPECT_RATIO, cv::WINDOW_AUTOSIZE); | |
cv::createTrackbar("OffsetX", "Sakura", &ui_offset_x, image_size - 1, NULL); | |
cv::setTrackbarMin("OffsetX", "Sakura", 0); | |
cv::createTrackbar("OffsetY", "Sakura", &ui_offset_y, image_size - 1, NULL); | |
cv::setTrackbarMin("OffsetY", "Sakura", 0); | |
cv::createTrackbar("Horizontal", "Sakura", &ui_horizontal, image_size, NULL); | |
cv::setTrackbarMin("Horizontal", "Sakura", 1); | |
cv::createTrackbar("Vertical", "Sakura", &ui_vertical, image_size, NULL); | |
cv::setTrackbarMin("Vertical", "Sakura", 1); | |
int32_t horizontal = image_size; | |
int32_t vertical = image_size; | |
for (;;) { | |
uint8_t* ptr = buffer.GetFrame(); | |
const cv::Mat mat = cv::Mat(cv::Size(horizontal, vertical), CV_8UC1, ptr); | |
cv::imshow("Sakura", mat); | |
char key = cv::waitKey(1); | |
switch (key) { | |
case ('q'): | |
return 0; | |
case ('c'): | |
if ((ui_offset_x + ui_horizontal) > image_size || | |
(ui_offset_y + ui_vertical) > image_size) { | |
continue; | |
} | |
horizontal = ui_horizontal; | |
vertical = ui_vertical; | |
buffer.CreateSubArray(ui_offset_x, ui_offset_y, horizontal, vertical); | |
cv::resizeWindow("Sakura", horizontal, vertical); | |
break; | |
} | |
if (key == 'q') return 0; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment