Skip to content

Instantly share code, notes, and snippets.

@27Cobalter
Created June 10, 2024 15:05
Show Gist options
  • Save 27Cobalter/33345eb5aca9c4d68610a130e7cdfa03 to your computer and use it in GitHub Desktop.
Save 27Cobalter/33345eb5aca9c4d68610a130e7cdfa03 to your computer and use it in GitHub Desktop.
#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