├── test └── test.cpp ├── README.md ├── global_define.hpp ├── castle.hpp ├── time_stamp.hpp ├── example ├── trial_hpp.cpp ├── shape.cpp ├── Fractal.cpp ├── vid1.cpp ├── circle.cpp ├── julia.cpp ├── trial_of_sdf.cpp ├── circle_to_square.cpp ├── show_creation2.cpp └── transform.cpp ├── axis.hpp ├── render.hpp ├── picture.hpp ├── const.hpp ├── coordinate.hpp ├── .vscode └── settings.json ├── .gitignore ├── .clang-format ├── bmp_old.hpp ├── shape.hpp ├── color.hpp ├── bmp.hpp └── LICENSE /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "castle.hpp" 2 | int main() {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CASTLE 2 | 3 | CASTLE 是 C++ Animation (Standard Template Library) Engine 的缩写 (STL 是乱加的). 4 | 5 | ## 编译选项 6 | 7 | 多线程的程序 (如 `julia.cpp`) 在编译时要加上参数 `-lpthread`. 8 | 9 | 你需要使用 C++ 11 10 | -------------------------------------------------------------------------------- /global_define.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_DEFINE_HPP 2 | #define GLOBAL_DEFINE_HPP 3 | 4 | typedef unsigned char Byte; 5 | typedef unsigned short Word; 6 | typedef unsigned int Dword; 7 | typedef double Float; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /castle.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CASTLE_HPP 2 | #define CASTLE_HPP 3 | 4 | #include "axis.hpp" 5 | #include "bmp.hpp" 6 | #include "color.hpp" 7 | #include "const.hpp" 8 | #include "coordinate.hpp" 9 | #include "global_define.hpp" 10 | #include "picture.hpp" 11 | #include "render.hpp" 12 | #include "time_stamp.hpp" 13 | #include "shape.hpp" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /time_stamp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TIME_STAMP_HPP 2 | #define TIME_STAMP_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | //计时器 8 | //构造时启动,析构时停止并输出 9 | struct TimeStamp { 10 | std::chrono::high_resolution_clock::time_point t; 11 | std::string s; 12 | TimeStamp(std::string s = "") 13 | : s(s) 14 | , t(std::chrono::high_resolution_clock::now()) {} 15 | ~TimeStamp() { 16 | std::cout << s << (s.size() ? " " : "") << "Time Used:" 17 | << (std::chrono::high_resolution_clock::now() - t).count() 18 | / 1e6 19 | << "ms" << std::endl; 20 | } 21 | }; 22 | 23 | #endif // TIME_STAMP_HPP 24 | -------------------------------------------------------------------------------- /example/trial_hpp.cpp: -------------------------------------------------------------------------------- 1 | #include "bmp_old.hpp" 2 | 3 | const Dword Height = 4096u; 4 | const Dword Width = 4096u; 5 | 6 | Dword Func(Dword a) { return a * a + 111234567u * a + 212433u; } 7 | 8 | Byte BitMap[Height][Width][3]; 9 | 10 | void write_in(Dword i, Dword j) { 11 | Dword t = Func(i ^ j); 12 | BitMap[i][j][0] = (t & 255u); 13 | t >>= 8u; 14 | BitMap[i][j][1] = (t & 255u); 15 | t >>= 8u; 16 | BitMap[i][j][2] = (t & 255u); 17 | } 18 | 19 | int main() { 20 | for (Dword i = 0u; i < Height; ++i) 21 | for (Dword j = 0u; j < Width; ++j) 22 | write_in(i, j); 23 | 24 | BMPFile f(Height, Width, 24u); 25 | f.write(&BitMap[0][0][0]); 26 | f.output("xor.bmp"); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /axis.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AXIS_HPP 2 | #define AXIS_HPP 3 | #include "const.hpp" 4 | #include "coordinate.hpp" 5 | #include "global_define.hpp" 6 | #include 7 | class Axis { 8 | private: 9 | Float x_c, y_c; 10 | Float u_x, u_y; 11 | Float eq_u; 12 | 13 | public: 14 | Axis(Float x_c, Float y_c, Float u_x, Float u_y) 15 | : x_c(x_c) 16 | , y_c(y_c) 17 | , u_x(u_x) 18 | , u_y(u_y) { 19 | Float ratio = u_x / u_y; 20 | if (0.99 < ratio && ratio < 1.01) 21 | eq_u = sqrt(u_x * u_y); 22 | else 23 | eq_u = Const::NaN; 24 | } 25 | Coordinate to_point(Float x, Float y) { 26 | return Coordinate((x - x_c) / u_x, (y - y_c) / u_y); 27 | } 28 | Coordinate to_pixel(Float x, Float y) { 29 | return Coordinate(x_c + x * u_x, y_c + y * u_y); 30 | } 31 | Float x_unit() { return u_x; } 32 | Float y_unit() { return u_y; } 33 | Float unit() { return eq_u; } 34 | }; 35 | #endif 36 | -------------------------------------------------------------------------------- /render.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_HPP 2 | #define RENDER_HPP 3 | 4 | #include "global_define.hpp" 5 | #include "picture.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace Render { 11 | template 12 | void render(BasePicture &bitmap, std::function get_color, 13 | Dword thread_num = 1) { 14 | 15 | using namespace std; 16 | 17 | Dword height = bitmap.height(); 18 | Dword width = bitmap.width(); 19 | vector thd_pool(thread_num); 20 | 21 | // 渲染第 begin 行到第 end 行 (不含第 end 行) 22 | auto f = [width, get_color, &bitmap](Dword begin, Dword end) { 23 | for (Dword i = begin; i < end; ++i) 24 | for (Dword j = 0; j < width; ++j) { 25 | bitmap.data[i][j] = get_color(i, j); 26 | } 27 | }; 28 | 29 | // 将图片按行分成 thread_num 组, 各线程负责一组 30 | Dword p = height / thread_num; 31 | for (Dword i = 0; i < thread_num; ++i) 32 | thd_pool[i] = thread(f, i * p, (i + 1) * p); 33 | for (Dword i = 0; i < thread_num; ++i) 34 | thd_pool[i].join(); 35 | // 剩余的组在这里渲染 36 | f(thread_num * p, height); 37 | } 38 | 39 | } // namespace Render 40 | 41 | #endif // RENDER_HPP 42 | -------------------------------------------------------------------------------- /picture.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PICTURE_HPP 2 | #define PICTURE_HPP 3 | 4 | #include "color.hpp" 5 | #include "global_define.hpp" 6 | #include 7 | #include 8 | 9 | // 图片类的通用模板 10 | template struct BasePicture { 11 | std::vector> data; 12 | 13 | BasePicture(Dword height, Dword width, const T &c = T()) 14 | : data(std::vector>(height, std::vector(width, c))) {} 15 | 16 | // 图片类型转换 17 | template operator BasePicture

() const { 18 | BasePicture

pic(height(), width()); 19 | for (Dword i = 0; i < height(); i++) 20 | for (Dword j = 0; j < width(); j++) 21 | pic.data[i][j] = data[i][j]; 22 | return pic; 23 | } 24 | 25 | Dword height() const { return data.size(); } 26 | 27 | Dword width() const { return data.size() > 0 ? data.front().size() : 0; } 28 | }; 29 | 30 | typedef BasePicture ColorPicture; 31 | typedef BasePicture ColorExtPicture; 32 | typedef BasePicture> BytePicture; 33 | 34 | void compose(ColorExtPicture &a, const ColorExtPicture &b) { 35 | assert(a.height() == b.height() && a.width() == b.width()); 36 | for (Dword i = 0; i < a.height(); i++) 37 | for (Dword j = 0; j < a.width(); j++) 38 | a.data[i][j].compose(b.data[i][j]); 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /example/shape.cpp: -------------------------------------------------------------------------------- 1 | #include "castle.hpp" 2 | #include 3 | using namespace std; 4 | class CirclePixel { 5 | Coordinate C; 6 | Float r; 7 | 8 | public: 9 | CirclePixel() {} 10 | CirclePixel(Coordinate C, Float r) 11 | : C(C) 12 | , r(r) {} 13 | CirclePixel(Float Cx, Float Cy, Float r) 14 | : C(Cx, Cy) 15 | , r(r) {} 16 | Coordinate center() { return C; } 17 | Float radius() { return r; } 18 | Float sdf(Coordinate P) { return abs(P - C) - r; } 19 | }; 20 | template 21 | void render(ColorExtPicture &pic, Shape &shape, Float LineWidth, 22 | ColorExt InColor, ColorExt EdgeColor) { 23 | Dword height = pic.height(), width = pic.width(); 24 | Float HalfLW = LineWidth * 0.5; 25 | for (Dword i = 0; i < height; ++i) 26 | for (Dword j = 0; j < width; ++j) { 27 | Coordinate P(j, i); 28 | Float sdf = shape.sdf(P); 29 | // cout << sdf << endl; 30 | if (-HalfLW <= sdf && sdf <= HalfLW) { 31 | pic.data[i][j] = EdgeColor; 32 | } else if (sdf < 0) { 33 | pic.data[i][j] = InColor; 34 | } 35 | } 36 | } 37 | int main() { 38 | CirclePixel Circle(500.0, 500.0, 100.0); 39 | ColorExtPicture pic(1000, 1000); 40 | render(pic, Circle, 5, ColorExt(0.0, 1.0, 0.0), ColorExt(1.0, 1.0, 1.0)); 41 | BytePicture BP(pic); 42 | BMPFile f(BP); 43 | f.output("circle.bmp"); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /const.hpp: -------------------------------------------------------------------------------- 1 | // const.hpp: 定义常数与函数 2 | #ifndef CONST_HPP 3 | #define CONST_HPP 4 | 5 | #include "global_define.hpp" 6 | #include 7 | 8 | namespace Const { 9 | const Float pi = std::acos((Float)-1.0); 10 | const Float tau = 2.0 * pi; 11 | const Float half_pi = 0.5 * pi; 12 | const Float e = std::exp((Float)1.0); 13 | const Float NaN = nan(""); 14 | const Float inf = 1.0 / 0.0; 15 | } // namespace Const 16 | 17 | namespace Func { 18 | 19 | typedef Float (*Func)(Float); // 函数指针 20 | 21 | Float linear(Float t) { return t; } 22 | 23 | Float sigmoid(Float t) { return 1.0 / (1.0 + std::exp(-t)); } 24 | 25 | Float clip(Float t, Float a, Float b) { 26 | if (t < a) 27 | return a; 28 | if (t > b) 29 | return b; 30 | return t; 31 | } 32 | 33 | Float smooth(Float t) { 34 | const Float inflection = 10.0; 35 | Float error = sigmoid(-inflection * 0.5); 36 | return clip((sigmoid(inflection * (t - 0.5)) - error) / (1.0 - 2.0 * error), 37 | 0.0, 1.0); 38 | } 39 | 40 | Float rush_into(Float t) { return 2.0 * smooth(t * 0.5); } 41 | 42 | Float rush_from(Float t) { return 2.0 * smooth(t * 0.5 + 0.5) - 1.0; } 43 | 44 | Float slow_into(Float t) { return std::sqrt(1.0 - (1.0 - t) * (1.0 - t)); } 45 | 46 | Float double_smooth(Float t) { 47 | if (t < 0.5) 48 | return 0.5 * smooth(2.0 * t); 49 | else 50 | return 0.5 * (1 + smooth(2.0 * t - 1.0)); 51 | } 52 | 53 | } // namespace Func 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /example/Fractal.cpp: -------------------------------------------------------------------------------- 1 | #include "castle.hpp" 2 | #include 3 | using namespace std; 4 | Axis axis = Axis(1024, 1024, 2048, 2048); 5 | Float helper(Point a, Dword n) { 6 | if (n == 0u) { 7 | return static_cast(0.0); 8 | } 9 | a.x *= 3.0; 10 | a.y *= 3.0; 11 | if (a.x >= 1.0 && a.x <= 2.0 && a.y >= 1.0 && a.y <= 2.0) { 12 | return static_cast( 13 | min(min(a.x - 1.0, 2.0 - a.x), min(a.y - 1.0, 2.0 - a.y))); 14 | } 15 | while (a.x >= 1.0) 16 | a.x -= 1.0; 17 | while (a.y >= 1.0) 18 | a.y -= 1.0; 19 | return helper(a, n - 1u) * 0.5; 20 | } 21 | Float sdf(Point a) { 22 | if (a.x <= 0.0 || a.x >= 1.0 || a.y <= 0.0 || a.y >= 1.0) { 23 | if (a.x <= 0.0) { 24 | if (a.y <= 0.0) { 25 | return hypot(a.x, a.y); 26 | } 27 | if (a.y >= 1.0) { 28 | return hypot(a.x, a.y - 1.0); 29 | } else { 30 | return -a.x; 31 | } 32 | } else if (a.x >= 1.0) { 33 | if (a.y <= 0.0) { 34 | return hypot(a.x - 1.0, a.y); 35 | } else if (a.y >= 1.0) { 36 | return hypot(a.x - 1.0, a.y - 1.0); 37 | } else { 38 | return a.x - 1.0; 39 | } 40 | } else { 41 | if (a.y <= 0.0) { 42 | return -a.y; 43 | } else if (a.y >= 1.0) { 44 | return a.y - 1.0; 45 | } else { 46 | assert(0); 47 | return Const::NaN; 48 | } 49 | } 50 | } 51 | return helper(a, 6); 52 | } 53 | Color renderer(Dword i, Dword j) { 54 | if (sdf(axis.to_point(i, j)) <= 2.0 / axis.unit()) 55 | return Color(0, 0, 0); 56 | return Color(255, 255, 255); 57 | } 58 | int main() { 59 | ColorPicture pic(4096, 4096); 60 | Render::render(pic, function(renderer), 4u); 61 | BMPFile fractal(pic); 62 | fractal.output("fractal_square.bmp"); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /coordinate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COORDINATE_HPP 2 | #define COORDINATE_HPP 3 | 4 | #include "global_define.hpp" 5 | #include 6 | #include 7 | 8 | // 表示平面上的点 (或向量) 9 | template struct Coordinate { 10 | T x, y; 11 | 12 | Coordinate() 13 | : x() 14 | , y() {} // 用 x,y 的默认构造函数来初始化 15 | 16 | Coordinate(const T &x, const T &y) 17 | : x(x) 18 | , y(y) {} 19 | 20 | Coordinate(const std::complex &C) 21 | : x(C.real()) 22 | , y(C.imag()) {} 23 | 24 | operator std::complex() const { return std::complex(x, y); } 25 | 26 | Coordinate operator+(const Coordinate &P) const { 27 | return Coordinate(x + P.x, y + P.y); 28 | } 29 | 30 | Coordinate operator-(const Coordinate &P) const { 31 | return Coordinate(x - P.x, y - P.y); 32 | } 33 | 34 | // 数乘 35 | Coordinate operator*(const T &a) const { 36 | return Coordinate(x * a, y * a); 37 | } 38 | 39 | // 与 1/a 作数乘. 当心 a 是整型且 a == 0 的情形 40 | Coordinate operator/(const T &a) const { 41 | return Coordinate(x / a, y / a); 42 | } 43 | }; 44 | 45 | // 中点 46 | template 47 | Coordinate mid(const Coordinate &P, const Coordinate &Q) { 48 | return (P + Q) * 0.5; 49 | } 50 | 51 | // 模; 到原点的距离 52 | template T abs(const Coordinate &P) { 53 | return abs(std::complex(P)); 54 | } 55 | 56 | // 辐角 57 | template T arg(const Coordinate &P) { 58 | return arg(std::complex(P)); 59 | } 60 | typedef Coordinate Point; 61 | 62 | // 夹角 63 | template T angle(const Coordinate &P, const Coordinate &Q) { 64 | T angle = atan2(Q.y, Q.x) - atan2(P.y, P.x); 65 | return angle >= Const::pi 66 | ? angle - Const::tau 67 | : angle <= -Const::pi ? angle + Const::tau : angle; 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /example/vid1.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | uint ColorMap[256] = {16777215, 255}; 5 | const uint N = 1020; 6 | const double zoom = pow(1000.0, 1.0 / (N - 1)); 7 | double ZOOM = 1.0; 8 | const uint Height = 2048; 9 | const uint Width = 2048; 10 | uc BitMap[Height][Width]; 11 | uc BMPFILE[Height * Width + 2048]; 12 | double xmin = -5.0, xmax = 5.0, ymin = -1.1, ymax = 1.1; 13 | uint frame; 14 | void update() { 15 | xmin = -5.0 / ZOOM; 16 | xmax = 5.0 / ZOOM; 17 | } 18 | struct Point { 19 | double x, y; 20 | Point(double x = 0, double y = 0) 21 | : x(x) 22 | , y(y) {} 23 | }; 24 | inline Point to_Point(uint a, uint b) { 25 | return Point(xmin + (xmax - xmin) * b / (Width - 1), 26 | ymin + (ymax - ymin) * a / (Height - 1)); 27 | } 28 | inline uc get_color(double x, double y) { 29 | if (y < sin(1.0 / x)) 30 | return 1; 31 | return 0; 32 | } 33 | 34 | void row(uint s, uint t) { 35 | for (uint i = s; i < t; ++i) 36 | for (uint j = 0; j < Width; ++j) { 37 | Point P = to_Point(i, j); 38 | BitMap[i][j] = get_color(P.x, P.y); 39 | } 40 | } 41 | void process(uint thread_num = 1) { 42 | assert(Height % thread_num == 0); 43 | vector thd_pool(thread_num); 44 | uint p = Height / thread_num; 45 | for (uint i = 0; i < thread_num; ++i) 46 | thd_pool[i] = thread(row, i * p, i * p + p); 47 | for (uint i = 0; i < thread_num; ++i) 48 | thd_pool[i].join(); 49 | } 50 | int main() { 51 | for (frame = 1; frame <= N; ++frame) { 52 | update(); 53 | process(4); 54 | uint size 55 | = write_bmp(BMPFILE, Height, Width, 8, &BitMap[0][0], ColorMap); 56 | char filename[128]; 57 | sprintf(filename, "Vid1_%d.bmp", frame); 58 | output_bmp(filename, BMPFILE, size); 59 | printf("Vid1_%d.bmp Done!\n", frame); 60 | ZOOM *= zoom; 61 | } 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /example/circle.cpp: -------------------------------------------------------------------------------- 1 | #include "castle.hpp" 2 | #include "shape.hpp" 3 | #include "time_stamp.hpp" 4 | using namespace std; 5 | /* 6 | class CirclePixel { 7 | Coordinate C; 8 | Float r; 9 | 10 | public: 11 | CirclePixel() {} 12 | CirclePixel(Coordinate C, Float r) 13 | : C(C) 14 | , r(r) {} 15 | CirclePixel(Float Cx, Float Cy, Float r) 16 | : C(Cx, Cy) 17 | , r(r) {} 18 | Coordinate center() const { return C; } 19 | Float radius() const { return r; } 20 | Float sdf(Coordinate P) const { return abs(P - C) - r; } 21 | }; 22 | */ 23 | 24 | template 25 | void render_shape(ColorExtPicture &pic, const Shape &shape, 26 | function sdf_dealer, Dword thread_num = 1) { 27 | function f 28 | = [&shape, sdf_dealer](Dword i, Dword j) { 29 | return sdf_dealer(shape.sdf(Coordinate(j, i))); 30 | }; 31 | Render::render(pic, f, thread_num); 32 | } 33 | template 34 | void render_shape(ColorExtPicture &pic, const Shape &shape, ColorExt InColor, 35 | ColorExt EdgeColor, Float LineWidth, Dword thread_num = 1) { 36 | function sdf_dealer 37 | = [InColor, EdgeColor, LineWidth](Float sdf) { 38 | Float halfLW = 0.5 * LineWidth; 39 | if (-halfLW < sdf && sdf < halfLW) 40 | return EdgeColor; 41 | if (sdf < 0) 42 | return InColor; 43 | return ColorExt(0.0, 0.0, 0.0, 1.0); 44 | }; 45 | render_shape(pic, shape, sdf_dealer, thread_num); 46 | } 47 | int main() { 48 | TimeStamp _("Render"); 49 | ColorExtPicture pic(1000, 1000); 50 | using namespace CASTLE::Shape; 51 | using namespace CASTLE; 52 | CirclePixel shape(Point(500, 500), 200); 53 | // Segment shape(Point(400, 400), Point(600, 600)); 54 | // Polygon shape({Point(400,400), Point(600,400), Point(600,600), 55 | // Point(400,600)}); 56 | // Polygon shape({Point(400, 400), Point(600, 600), Point(600, 800), 57 | // Point(700, 700), Point(200, 700)}); 58 | render_shape( 59 | pic, shape, ColorExt(0.0, 1.0, 0.0), ColorExt(1.0, 1.0, 1.0), 10.0, 4); 60 | BMPFile bmp(pic); 61 | bmp.output("test.bmp"); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "files.autoSave": "onFocusChange", 4 | "files.associations": { 5 | "atomic": "cpp", 6 | "*.tcc": "cpp", 7 | "cctype": "cpp", 8 | "cfenv": "cpp", 9 | "chrono": "cpp", 10 | "cinttypes": "cpp", 11 | "clocale": "cpp", 12 | "cmath": "cpp", 13 | "codecvt": "cpp", 14 | "complex": "cpp", 15 | "condition_variable": "cpp", 16 | "csetjmp": "cpp", 17 | "csignal": "cpp", 18 | "cstdarg": "cpp", 19 | "cstddef": "cpp", 20 | "cstdint": "cpp", 21 | "cstdio": "cpp", 22 | "cstdlib": "cpp", 23 | "cstring": "cpp", 24 | "ctime": "cpp", 25 | "cuchar": "cpp", 26 | "cwchar": "cpp", 27 | "cwctype": "cpp", 28 | "deque": "cpp", 29 | "forward_list": "cpp", 30 | "list": "cpp", 31 | "unordered_map": "cpp", 32 | "unordered_set": "cpp", 33 | "vector": "cpp", 34 | "exception": "cpp", 35 | "algorithm": "cpp", 36 | "iterator": "cpp", 37 | "map": "cpp", 38 | "memory": "cpp", 39 | "memory_resource": "cpp", 40 | "numeric": "cpp", 41 | "optional": "cpp", 42 | "ratio": "cpp", 43 | "regex": "cpp", 44 | "set": "cpp", 45 | "string": "cpp", 46 | "string_view": "cpp", 47 | "system_error": "cpp", 48 | "tuple": "cpp", 49 | "utility": "cpp", 50 | "fstream": "cpp", 51 | "future": "cpp", 52 | "initializer_list": "cpp", 53 | "iomanip": "cpp", 54 | "iosfwd": "cpp", 55 | "iostream": "cpp", 56 | "limits": "cpp", 57 | "mutex": "cpp", 58 | "scoped_allocator": "cpp", 59 | "shared_mutex": "cpp", 60 | "stdexcept": "cpp", 61 | "thread": "cpp", 62 | "typeindex": "cpp", 63 | "typeinfo": "cpp", 64 | "array": "cpp", 65 | "bitset": "cpp", 66 | "functional": "cpp", 67 | "random": "cpp", 68 | "type_traits": "cpp", 69 | "istream": "cpp", 70 | "new": "cpp", 71 | "ostream": "cpp", 72 | "sstream": "cpp", 73 | "streambuf": "cpp", 74 | "valarray": "cpp", 75 | "charconv": "cpp" 76 | }, 77 | } -------------------------------------------------------------------------------- /example/julia.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | typedef complex Comp; 5 | const uint Height = 4096; 6 | const uint Width = 4096; 7 | uc BitMap[Height][Width][3]; 8 | uc BMPFILE[4 * Height * Width + 64]; 9 | const double xmin = -1.5; 10 | const double xmax = 1.5; 11 | const double ymin = -1.5; 12 | const double ymax = 1.5; 13 | struct Point { 14 | double x, y; 15 | Point(double x = 0, double y = 0) 16 | : x(x) 17 | , y(y) {} 18 | }; 19 | inline Point to_Point(uint a, uint b) { 20 | return Point(xmin + (xmax - xmin) * b / (Width - 1), 21 | ymin + (ymax - ymin) * a / (Height - 1)); 22 | } 23 | struct COLOR { 24 | uc r, g, b; 25 | COLOR(uc r = 0u, uc g = 0u, uc b = 0u) 26 | : r(r) 27 | , g(g) 28 | , b(b) {} 29 | }; 30 | void set_color(uc arr[3], COLOR c) { 31 | arr[0] = c.b; 32 | arr[1] = c.g; 33 | arr[2] = c.r; 34 | } 35 | const COLOR bgc = COLOR(255, 255, 255); 36 | struct COLOR_DOUBLE { 37 | double r, g, b; 38 | COLOR_DOUBLE(double r = 0.0, double g = 0.0, double b = 0.0) 39 | : r(r) 40 | , g(g) 41 | , b(b) {} 42 | uc scale(double x) { 43 | if (x < 0.0) 44 | x = -x; 45 | x -= (int)(0.5 * x) * 2.0; 46 | if (x > 1.0) 47 | x = 2.0 - x; 48 | return x * 255.0 + 0.5; 49 | } 50 | operator COLOR() { return COLOR(scale(r), scale(g), scale(b)); } 51 | }; 52 | inline COLOR Julia(double x, double y, double a, double b, int n = 100) { 53 | Comp z = Comp(x, y); 54 | Comp c = Comp(a, b); 55 | for (int i = 0; i < n; ++i) { 56 | z = z * z + c; 57 | if (abs(max(real(z), imag(z))) > 2.0) 58 | return bgc; 59 | } 60 | return COLOR(COLOR_DOUBLE(real(z), imag(z), abs(z))); 61 | } 62 | void row(uint s, uint t) { 63 | for (uint i = s; i < t; ++i) 64 | for (uint j = 0; j < Width; ++j) { 65 | Point P = to_Point(i, j); 66 | double x = P.x, y = P.y; 67 | set_color(BitMap[i][j], Julia(x, y, -0.163, 0.659, 1000)); 68 | } 69 | } 70 | void process(uint thread_num = 1) { 71 | assert(Height % thread_num == 0); 72 | vector thd_pool(thread_num); 73 | uint p = Height / thread_num; 74 | for (uint i = 0; i < thread_num; ++i) 75 | thd_pool[i] = thread(row, i * p, i * p + p); 76 | for (uint i = 0; i < thread_num; ++i) 77 | thd_pool[i].join(); 78 | } 79 | int main() { 80 | process(4); 81 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 82 | output_bmp("Julia_neg_0_163__0_659___4096_4096.bmp", BMPFILE, size); 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/c++,linux,macos,windows,visualstudiocode 2 | # Edit at https://www.gitignore.io/?templates=c++,linux,macos,windows,visualstudiocode 3 | 4 | ### C++ ### 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | 38 | ### Linux ### 39 | *~ 40 | 41 | # temporary files which can be created if a process still has a handle open of a deleted file 42 | .fuse_hidden* 43 | 44 | # KDE directory preferences 45 | .directory 46 | 47 | # Linux trash folder which might appear on any partition or disk 48 | .Trash-* 49 | 50 | # .nfs files are created when an open file is removed but is still being accessed 51 | .nfs* 52 | 53 | ### macOS ### 54 | # General 55 | .DS_Store 56 | .AppleDouble 57 | .LSOverride 58 | 59 | # Icon must end with two \r 60 | Icon 61 | 62 | # Thumbnails 63 | ._* 64 | 65 | # Files that might appear in the root of a volume 66 | .DocumentRevisions-V100 67 | .fseventsd 68 | .Spotlight-V100 69 | .TemporaryItems 70 | .Trashes 71 | .VolumeIcon.icns 72 | .com.apple.timemachine.donotpresent 73 | 74 | # Directories potentially created on remote AFP share 75 | .AppleDB 76 | .AppleDesktop 77 | Network Trash Folder 78 | Temporary Items 79 | .apdisk 80 | 81 | ### VisualStudioCode ### 82 | .vscode/* 83 | !.vscode/settings.json 84 | !.vscode/tasks.json 85 | !.vscode/launch.json 86 | !.vscode/extensions.json 87 | 88 | ### VisualStudioCode Patch ### 89 | # Ignore all local history of files 90 | .history 91 | 92 | ### Windows ### 93 | # Windows thumbnail cache files 94 | Thumbs.db 95 | Thumbs.db:encryptable 96 | ehthumbs.db 97 | ehthumbs_vista.db 98 | 99 | # Dump file 100 | *.stackdump 101 | 102 | # Folder config file 103 | [Dd]esktop.ini 104 | 105 | # Recycle Bin used on file shares 106 | $RECYCLE.BIN/ 107 | 108 | # Windows Installer files 109 | *.cab 110 | *.msi 111 | *.msix 112 | *.msm 113 | *.msp 114 | 115 | # Windows shortcuts 116 | *.lnk 117 | 118 | ### Audio ### 119 | *.aif 120 | *.iff 121 | *.m3u 122 | *.m4a 123 | *.mid 124 | *.mp3 125 | *.mpa 126 | *.ra 127 | *.wav 128 | *.wma 129 | 130 | ### Images ### 131 | # JPEG 132 | *.jpg 133 | *.jpeg 134 | *.jpe 135 | *.jif 136 | *.jfif 137 | *.jfi 138 | 139 | # JPEG 2000 140 | *.jp2 141 | *.j2k 142 | *.jpf 143 | *.jpx 144 | *.jpm 145 | *.mj2 146 | 147 | # JPEG XR 148 | *.jxr 149 | *.hdp 150 | *.wdp 151 | 152 | # Graphics Interchange Format 153 | *.gif 154 | 155 | # RAW 156 | *.raw 157 | 158 | # Web P 159 | *.webp 160 | 161 | # Portable Network Graphics 162 | *.png 163 | 164 | # Animated Portable Network Graphics 165 | *.apng 166 | 167 | # Multiple-image Network Graphics 168 | *.mng 169 | 170 | # Tagged Image File Format 171 | *.tiff 172 | *.tif 173 | 174 | # Scalable Vector Graphics 175 | *.svg 176 | *.svgz 177 | 178 | # Portable Document Format 179 | *.pdf 180 | 181 | # X BitMap 182 | *.xbm 183 | 184 | # BMP 185 | *.bmp 186 | *.dib 187 | 188 | # ICO 189 | *.ico 190 | 191 | # 3D Images 192 | *.3dm 193 | *.max 194 | 195 | ### Video ### 196 | *.3g2 197 | *.3gp 198 | *.asf 199 | *.asx 200 | *.avi 201 | *.flv 202 | *.mov 203 | *.mp4 204 | *.mpg 205 | *.rm 206 | *.swf 207 | *.vob 208 | *.wmv 209 | 210 | # End of https://www.gitignore.io/api/c++,linux,macos,windows,visualstudiocode -------------------------------------------------------------------------------- /example/trial_of_sdf.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | const uint Height = 4096; 5 | const uint Width = 4096; 6 | uc BitMap[Height][Width][3]; 7 | uc BMPFILE[4 * Height * Width + 64]; 8 | const double xmin = -1.2; 9 | const double xmax = 1.2; 10 | const double ymin = -1.2; 11 | const double ymax = 1.2; 12 | struct Point { 13 | double x, y; 14 | Point(double x = 0, double y = 0) 15 | : x(x) 16 | , y(y) {} 17 | }; 18 | double sdf1[Height][Width], sdf2[Height][Width]; 19 | inline Point to_Point(uint a, uint b) { 20 | return Point(xmin + (xmax - xmin) * b / (Width - 1), 21 | ymin + (ymax - ymin) * a / (Height - 1)); 22 | } 23 | const double cst_for_sdf1 = sqrt(4.0 / 3.141592653589793238462643); 24 | void get_sdf1(uint a, uint b) { 25 | Point P = to_Point(a, b); 26 | double x = P.x, y = P.y; 27 | sdf1[a][b] = hypot(x, y) - cst_for_sdf1; 28 | } 29 | void get_sdf2(uint a, uint b) { 30 | Point P = to_Point(a, b); 31 | double x = P.x, y = P.y; 32 | if (-x <= y && y <= x && -1.0 <= y && y <= 1.0) 33 | sdf2[a][b] = x - 1.0; 34 | else if (-y <= x && x <= y && -1.0 <= x && x <= 1.0) 35 | sdf2[a][b] = y - 1.0; 36 | else if (x <= y && y <= -x && -1.0 <= y && y <= 1.0) 37 | sdf2[a][b] = -1.0 - x; 38 | else if (y <= x && x <= -y && -1.0 <= x && x <= 1.0) 39 | sdf2[a][b] = -1.0 - y; 40 | else if (x >= 1.0 && y >= 1.0) 41 | sdf2[a][b] = hypot(x - 1.0, y - 1.0); 42 | else if (x <= -1.0 && y >= 1.0) 43 | sdf2[a][b] = hypot(x + 1.0, y - 1.0); 44 | else if (x <= -1.0 && y <= -1.0) 45 | sdf2[a][b] = hypot(x + 1.0, y + 1.0); 46 | else if (x >= 1.0 && y <= -1.0) 47 | sdf2[a][b] = hypot(x - 1.0, y + 1.0); 48 | else 49 | assert(0); 50 | } 51 | void get_sdf1() { 52 | for (uint i = 0; i < Height; ++i) 53 | for (uint j = 0; j < Width; ++j) 54 | get_sdf1(i, j); 55 | } 56 | void get_sdf2() { 57 | for (uint i = 0; i < Height; ++i) 58 | for (uint j = 0; j < Width; ++j) 59 | get_sdf2(i, j); 60 | } 61 | double sdf[Height][Width]; 62 | void get_sdf() { 63 | for (uint i = 0; i < Height; ++i) 64 | for (uint j = 0; j < Width; ++j) 65 | sdf[i][j] = min( 66 | max(sdf1[i][j], -sdf2[i][j]), max(-sdf1[i][j], sdf2[i][j])); 67 | } 68 | void make_sdf_in_scale() { 69 | for (uint i = 0; i < Height; ++i) 70 | for (uint j = 0; j < Width; ++j) 71 | sdf[i][j] = sdf[i][j] / (xmax - xmin) * (Width - 1); 72 | } 73 | struct COLOR { 74 | uc r, g, b, a; 75 | COLOR(uc r = 0u, uc g = 0u, uc b = 0u, uc a = 0u) 76 | : r(r) 77 | , g(g) 78 | , b(b) 79 | , a(a) {} 80 | }; 81 | 82 | void set_color(uc arr[3], COLOR c) { 83 | arr[0] = (arr[0] * c.a + c.b * (255 - c.a) + 127) / 255; 84 | arr[1] = (arr[1] * c.a + c.g * (255 - c.a) + 127) / 255; 85 | arr[2] = (arr[2] * c.a + c.r * (255 - c.a) + 127) / 255; 86 | } 87 | 88 | void RenderSdf( 89 | COLOR EdgeColor, COLOR InColor, COLOR OutColor, double LineWidth) { 90 | assert(LineWidth >= 0.0); 91 | double HLW = 0.5 * LineWidth; 92 | for (uint i = 0; i < Height; ++i) 93 | for (uint j = 0; j < Width; ++j) { 94 | if (-HLW <= sdf[i][j] && sdf[i][j] <= HLW) 95 | set_color(BitMap[i][j], EdgeColor); 96 | else if (sdf[i][j] < 0.0) 97 | set_color(BitMap[i][j], InColor); 98 | else if (sdf[i][j] > 0.0) 99 | set_color(BitMap[i][j], OutColor); 100 | } 101 | } 102 | int main() { 103 | get_sdf1(); 104 | get_sdf2(); 105 | get_sdf(); 106 | make_sdf_in_scale(); 107 | RenderSdf(COLOR(), COLOR(0, 255, 0), COLOR(255, 255, 255), 5.0); 108 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 109 | output_bmp("trial_render_edge_sdf_xor_4096x4096.bmp", BMPFILE, size); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: false 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: false 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: All 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: MultiLine 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: All 45 | BreakBeforeBraces: Custom 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeComma 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 80 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DerivePointerAlignment: false 61 | DisableFormat: false 62 | ExperimentalAutoDetectBinPacking: false 63 | FixNamespaceComments: true 64 | ForEachMacros: 65 | - foreach 66 | - Q_FOREACH 67 | - BOOST_FOREACH 68 | IncludeBlocks: Preserve 69 | IncludeCategories: 70 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 71 | Priority: 2 72 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 73 | Priority: 3 74 | - Regex: '.*' 75 | Priority: 1 76 | IncludeIsMainRegex: '(Test)?$' 77 | IndentCaseLabels: false 78 | IndentPPDirectives: None 79 | IndentWidth: 4 80 | IndentWrappedFunctionNames: false 81 | JavaScriptQuotes: Leave 82 | JavaScriptWrapImports: true 83 | KeepEmptyLinesAtTheStartOfBlocks: true 84 | MacroBlockBegin: '' 85 | MacroBlockEnd: '' 86 | MaxEmptyLinesToKeep: 1 87 | NamespaceIndentation: Inner 88 | ObjCBinPackProtocolList: Auto 89 | ObjCBlockIndentWidth: 4 90 | ObjCSpaceAfterProperty: true 91 | ObjCSpaceBeforeProtocolList: true 92 | PenaltyBreakAssignment: 2 93 | PenaltyBreakBeforeFirstCallParameter: 19 94 | PenaltyBreakComment: 300 95 | PenaltyBreakFirstLessLess: 120 96 | PenaltyBreakString: 1000 97 | PenaltyBreakTemplateDeclaration: 10 98 | PenaltyExcessCharacter: 1000000 99 | PenaltyReturnTypeOnItsOwnLine: 60 100 | PointerAlignment: Right 101 | ReflowComments: true 102 | SortIncludes: true 103 | SortUsingDeclarations: true 104 | SpaceAfterCStyleCast: false 105 | SpaceAfterLogicalNot: false 106 | SpaceAfterTemplateKeyword: true 107 | SpaceBeforeAssignmentOperators: true 108 | SpaceBeforeCpp11BracedList: true 109 | SpaceBeforeCtorInitializerColon: true 110 | SpaceBeforeInheritanceColon: true 111 | SpaceBeforeParens: ControlStatements 112 | SpaceBeforeRangeBasedForLoopColon: true 113 | SpaceInEmptyParentheses: false 114 | SpacesBeforeTrailingComments: 1 115 | SpacesInAngles: false 116 | SpacesInContainerLiterals: true 117 | SpacesInCStyleCastParentheses: false 118 | SpacesInParentheses: false 119 | SpacesInSquareBrackets: false 120 | Standard: Cpp11 121 | StatementMacros: 122 | - Q_UNUSED 123 | - QT_REQUIRE_VERSION 124 | TabWidth: 4 125 | UseTab: Always 126 | ... 127 | 128 | -------------------------------------------------------------------------------- /example/circle_to_square.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | const uint Height = 1500; 5 | const uint Width = 1500; 6 | uc BitMap[Height][Width][3]; 7 | uc BMPFILE[4 * Height * Width + 64]; 8 | const double xmin = -1.2; 9 | const double xmax = 1.2; 10 | const double ymin = -1.2; 11 | const double ymax = 1.2; 12 | struct Point { 13 | double x, y; 14 | Point(double x = 0, double y = 0) 15 | : x(x) 16 | , y(y) {} 17 | }; 18 | double sdf1[Height][Width], sdf2[Height][Width]; 19 | inline Point to_Point(uint a, uint b) { 20 | return Point(xmin + (xmax - xmin) * b / (Width - 1), 21 | ymin + (ymax - ymin) * a / (Height - 1)); 22 | } 23 | const double cst_for_sdf1 = sqrt(4.0 / 3.141592653589793238462643); 24 | void get_sdf1(uint a, uint b) { 25 | Point P = to_Point(a, b); 26 | double x = P.x, y = P.y; 27 | sdf1[a][b] = hypot(x, y) - cst_for_sdf1; 28 | } 29 | void get_sdf2(uint a, uint b) { 30 | Point P = to_Point(a, b); 31 | double x = P.x, y = P.y; 32 | if (-x <= y && y <= x && -1.0 <= y && y <= 1.0) 33 | sdf2[a][b] = x - 1.0; 34 | else if (-y <= x && x <= y && -1.0 <= x && x <= 1.0) 35 | sdf2[a][b] = y - 1.0; 36 | else if (x <= y && y <= -x && -1.0 <= y && y <= 1.0) 37 | sdf2[a][b] = -1.0 - x; 38 | else if (y <= x && x <= -y && -1.0 <= x && x <= 1.0) 39 | sdf2[a][b] = -1.0 - y; 40 | else if (x >= 1.0 && y >= 1.0) 41 | sdf2[a][b] = hypot(x - 1.0, y - 1.0); 42 | else if (x <= -1.0 && y >= 1.0) 43 | sdf2[a][b] = hypot(x + 1.0, y - 1.0); 44 | else if (x <= -1.0 && y <= -1.0) 45 | sdf2[a][b] = hypot(x + 1.0, y + 1.0); 46 | else if (x >= 1.0 && y <= -1.0) 47 | sdf2[a][b] = hypot(x - 1.0, y + 1.0); 48 | else 49 | assert(0); 50 | } 51 | void get_sdf1() { 52 | for (uint i = 0; i < Height; ++i) 53 | for (uint j = 0; j < Width; ++j) 54 | get_sdf1(i, j); 55 | } 56 | void get_sdf2() { 57 | for (uint i = 0; i < Height; ++i) 58 | for (uint j = 0; j < Width; ++j) 59 | get_sdf2(i, j); 60 | } 61 | uint frame; 62 | double sdf[Height][Width]; 63 | void get_sdf() { 64 | for (uint i = 0; i < Height; ++i) 65 | for (uint j = 0; j < Width; ++j) 66 | sdf[i][j] 67 | = sdf1[i][j] + (sdf2[i][j] - sdf1[i][j]) * (frame - 30) / 89.0; 68 | } 69 | 70 | struct COLOR { 71 | uc r, g, b, a; 72 | COLOR(uc r = 0u, uc g = 0u, uc b = 0u, uc a = 0u) 73 | : r(r) 74 | , g(g) 75 | , b(b) 76 | , a(a) {} 77 | }; 78 | 79 | void set_color(uc arr[3], COLOR c) { 80 | arr[0] = (arr[0] * c.a + c.b * (255 - c.a) + 127) / 255; 81 | arr[1] = (arr[1] * c.a + c.g * (255 - c.a) + 127) / 255; 82 | arr[2] = (arr[2] * c.a + c.r * (255 - c.a) + 127) / 255; 83 | } 84 | 85 | COLOR get_color() { 86 | return COLOR(255.0 * (1.0 - (frame - 30) / 89.0) + 0.5, 87 | 255.0 * (frame - 30) / 89.0 + 0.5, 255); 88 | } 89 | 90 | int main() { 91 | get_sdf1(); 92 | get_sdf2(); 93 | for (frame = 0; frame < 30u; ++frame) { 94 | COLOR c = COLOR(255, 0, 255); 95 | COLOR b = COLOR(); 96 | for (uint i = 0; i < Height; ++i) 97 | for (uint j = 0; j < Width; ++j) 98 | if (sdf1[i][j] <= 0.0) 99 | set_color(BitMap[i][j], c); 100 | else 101 | set_color(BitMap[i][j], b); 102 | char filename[64]; 103 | sprintf(filename, "circle_to_square%d.bmp", (int)frame + 1); 104 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 105 | output_bmp(filename, BMPFILE, size); 106 | } 107 | for (frame = 30; frame < 120u; ++frame) { 108 | get_sdf(); 109 | COLOR c = get_color(); 110 | COLOR b = COLOR(); 111 | for (uint i = 0; i < Height; ++i) 112 | for (uint j = 0; j < Width; ++j) 113 | if (sdf[i][j] <= 0.0) 114 | set_color(BitMap[i][j], c); 115 | else 116 | set_color(BitMap[i][j], b); 117 | char filename[64]; 118 | sprintf(filename, "circle_to_square%d.bmp", (int)frame + 1); 119 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 120 | output_bmp(filename, BMPFILE, size); 121 | } 122 | for (frame = 120; frame < 150u; ++frame) { 123 | COLOR c = COLOR(0, 255, 255); 124 | COLOR b = COLOR(); 125 | for (uint i = 0; i < Height; ++i) 126 | for (uint j = 0; j < Width; ++j) 127 | if (sdf2[i][j] <= 0.0) 128 | set_color(BitMap[i][j], c); 129 | else 130 | set_color(BitMap[i][j], b); 131 | char filename[64]; 132 | sprintf(filename, "circle_to_square%d.bmp", (int)frame + 1); 133 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 134 | output_bmp(filename, BMPFILE, size); 135 | } 136 | system("ffmpeg -y -i circle_to_square%d.bmp -pix_fmt yuv420p -r 30 " 137 | "circle_to_square.mp4"); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /bmp_old.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BMP_H 2 | #define BMP_H 3 | 4 | #include 5 | 6 | typedef unsigned char Byte; 7 | typedef unsigned short Word; 8 | typedef unsigned int Dword; 9 | 10 | class BMPFile { 11 | 12 | // 一个 bmp 文件由 header, colormap (可选), img 三部分组成 13 | Dword header_size; 14 | Dword colormap_size; 15 | Dword img_size; 16 | Dword buf_size; // 等于以上三者之和 17 | struct { 18 | // bmp header: 14 - 2 = 12 bytes 19 | // Byte type[2]; 去掉这一字段, 方便字节对齐 20 | Dword file_size; 21 | Word reserved1; 22 | Word reserved2; 23 | Dword offbits; 24 | 25 | // info header: 40 bytes 26 | Dword info_size; 27 | Dword width; 28 | Dword height; 29 | Word planes; 30 | Word bit_count; 31 | Dword compression; 32 | Dword img_size; 33 | Dword resolutionX; // 单位: pixel/meter 34 | Dword resolutionY; 35 | Dword color_used; 36 | Dword color_important; 37 | } header; 38 | Dword *colormap; 39 | Byte *buf; // 图像文件缓冲区 40 | 41 | public: 42 | BMPFile(Dword height, Dword width, Byte bpp, Dword *colormap = NULL) 43 | : header_size(sizeof(header) + 2) 44 | , colormap_size(colormap ? 1u << (bpp + 2) : 0) 45 | , img_size((((bpp * width + 31) >> 5) << 2) * height) 46 | , buf_size(header_size + colormap_size + img_size) 47 | , header() 48 | , colormap(colormap) 49 | , buf(new Byte[buf_size]) { 50 | // bmp header 51 | header.file_size = buf_size; 52 | header.reserved1 = 0; 53 | header.reserved2 = 0; 54 | header.offbits = header_size + colormap_size; 55 | 56 | // info header 57 | header.info_size = 40; 58 | header.width = width; 59 | header.height = height; 60 | header.planes = 1; 61 | header.bit_count = bpp; // bpp: bit per pixel 62 | header.compression = 0; 63 | header.img_size = img_size; 64 | header.resolutionX = 0; 65 | header.resolutionY = 0; 66 | header.color_used = 0; 67 | header.color_important = 0; 68 | } 69 | 70 | BMPFile(const char *filename) 71 | : header_size(sizeof(header) + 2) 72 | , colormap(NULL) { 73 | FILE *fp = fopen(filename, "r"); 74 | if (!fp) { 75 | perror(filename); 76 | return; 77 | } 78 | 79 | // type 80 | char type[2]; 81 | fread(type, sizeof(Byte), 2, fp); 82 | if (type[0] != 'B' || type[1] != 'M') { 83 | std::cerr << "BMPfile: " << filename << " not start with 'BM'\n"; 84 | return; 85 | } 86 | 87 | // header 88 | fread(&header, sizeof(Byte), sizeof(header), fp); 89 | colormap_size = header.offbits - header_size; 90 | img_size = header.img_size; 91 | buf_size = header.file_size; 92 | 93 | // colormap + img 94 | buf = new Byte[buf_size]; 95 | if (buf == NULL) { 96 | std::cerr << "BMPfile: failed to allocate memory\n"; 97 | return; 98 | } 99 | memcpy(buf, type, 2); 100 | memcpy(buf + 2, &header, sizeof(header)); 101 | fread(buf + header_size, sizeof(Byte), buf_size - header_size, fp); 102 | 103 | fclose(fp); 104 | } 105 | 106 | ~BMPFile() { 107 | if (buf) 108 | delete[] buf; 109 | buf = NULL; 110 | } 111 | 112 | void info() { 113 | std::cout << "file_size: " << header.file_size 114 | << "\nreserved1: " << header.reserved1 115 | << "\nreserved2: " << header.reserved2 116 | << "\noffbits: " << header.offbits 117 | << "\ninfo_size: " << header.info_size 118 | << "\nwidth: " << header.width 119 | << "\nheight: " << header.height 120 | << "\nplanes: " << header.planes 121 | << "\nbit_count: " << header.bit_count 122 | << "\ncompression: " << header.compression 123 | << "\nimg_size: " << header.img_size 124 | << "\nresolutionX: " << header.resolutionX 125 | << "\nresolutionY: " << header.resolutionY 126 | << "\ncolor_used: " << header.color_used 127 | << "\ncolor_important: " << header.color_important << "\n"; 128 | } 129 | 130 | Dword write(Byte *img, Dword *colormap = NULL) { 131 | 132 | Byte *ptr = buf; 133 | 134 | // header 135 | buf[0] = 'B'; 136 | buf[1] = 'M'; 137 | ptr += 2; 138 | memcpy(ptr, &header, sizeof(header)); 139 | ptr += sizeof(header); 140 | 141 | // colormap 142 | Byte bpp = header.bit_count; 143 | if (colormap) { 144 | Dword size = (1u << bpp) * sizeof(Dword); 145 | memcpy(ptr, colormap, size); 146 | ptr += size; 147 | } 148 | 149 | // img 150 | if (img) { 151 | Dword line_count = header.width * (bpp >> 3u); 152 | Dword zero_count = (4 - (((bpp >> 3) * header.width) & 3)) & 3; 153 | for (Dword i = 0; i < header.height; ++i) { 154 | // 从 img 数组拷贝 line_count 个字节 155 | memcpy(ptr, img, line_count); 156 | img += line_count; 157 | ptr += line_count; 158 | // 用零填充 zero_count 个字节 159 | memset(ptr, 0, zero_count); 160 | ptr += zero_count; 161 | } 162 | } 163 | return buf_size; 164 | } 165 | 166 | int output(const char *filename) { 167 | FILE *fp = fopen(filename, "wb"); 168 | if (!fp) { 169 | perror(filename); 170 | return -1; 171 | } 172 | fwrite(buf, sizeof(Byte), buf_size, fp); 173 | fclose(fp); 174 | return 0; 175 | } 176 | }; 177 | 178 | #endif // BMP_H 179 | -------------------------------------------------------------------------------- /shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPE_HPP 2 | #define SHAPE_HPP 3 | #include "coordinate.hpp" 4 | #include "global_define.hpp" 5 | #include 6 | #include 7 | namespace CASTLE { 8 | 9 | typedef Coordinate Point; 10 | 11 | namespace Shape { 12 | class Shape { 13 | protected: 14 | //坐标位置为左上角(右为x正方向,下为z正方向 15 | Point c; 16 | Shape() 17 | : c() {} 18 | Shape(Point c) 19 | : c(c) {} 20 | Shape(Float cx, Float cy) 21 | : c(cx, cy) {} 22 | 23 | public: 24 | virtual Float sdf(const Point &) const = 0; 25 | Point transform_center() const { return c; } 26 | }; 27 | class CirclePixel : public Shape { 28 | Point c; // 圆心 29 | Float r; // 半径 30 | 31 | public: 32 | CirclePixel() 33 | : Shape() 34 | , c() 35 | , r() {} 36 | CirclePixel(Point c, Float r) 37 | : Shape(c) 38 | , c(c) 39 | , r(r) {} 40 | CirclePixel(Float cx, Float cy, Float r) 41 | : Shape(cx, cy) 42 | , c(cx, cy) 43 | , r(r) {} 44 | Float radius() const { return r; } 45 | Float sdf(const Point &p) const { 46 | return abs(p - c) - r; 47 | } 48 | }; 49 | /* 50 | class Rectangle : public Shape { 51 | Float h, w; 52 | 53 | public: 54 | Rectangle() 55 | : Shape() 56 | , h() 57 | , w() {} 58 | Rectangle(Point c, Float h, Float w) 59 | : Shape(c) 60 | , h(h) 61 | , w(w) {} 62 | Float height() const { return h; } 63 | Float width() const { return w; } 64 | Float sdf(const Point &p) const {} //不会写 65 | }; 66 | */ 67 | 68 | class Line : public Shape { 69 | Float a, b, c; // a x + b y + c = 0 70 | 71 | public: 72 | Line(const Float &a, const Float &b, const Float &c) 73 | : a(a) 74 | , b(b) 75 | , c(c) {} 76 | 77 | Line(const Point &p1, const Point &p2) 78 | : a(p2.y - p1.y) 79 | , b(p1.x - p2.x) 80 | , c(p2.x * p1.y - p1.x * p2.y) {} 81 | 82 | Float sdf(const Point &P) const { 83 | return abs(a * P.x + b * P.y + c) / hypot(a, b); 84 | } 85 | }; 86 | 87 | class Segment : public Shape { 88 | Point p1, p2; 89 | 90 | public: 91 | Segment(const Point &p1, const Point &p2) 92 | : Shape(mid(p1, p2)) 93 | , p1(p1) 94 | , p2(p2) {} 95 | 96 | Float sdf(const Point &p) const { 97 | Point edge = p1 - p2, r1 = p1 - p, r2 = p2 - p; 98 | if (edge.x * r1.x + edge.y * r1.y <= 0) // p p1 p2 是钝角 99 | return abs(r1); 100 | if (edge.x * r2.x + edge.y * r2.y >= 0) // p p2 p1 是钝角 101 | return abs(r2); 102 | return abs(r1.x * edge.y - edge.x * r1.y) / abs(edge); 103 | } 104 | }; 105 | 106 | class Polygon : public Shape { 107 | std::vector vertices; // 沿边界逆时针方向 108 | enum PolygonMode { ALL_INSIDE, ALL_OUTSIDE, EVEN_ODD, NON_ZERO }; 109 | PolygonMode mode; 110 | 111 | static Point get_center(const std::vector &v) { 112 | if (v.empty()) 113 | std::cerr << "a polygon must have at least one vertex\n"; 114 | Point p(0,0); 115 | for (size_t i = 0; i < v.size(); ++i) { 116 | p = p + v[i]; 117 | } 118 | return p / v.size(); 119 | } 120 | 121 | public: 122 | Polygon(const std::vector &v) 123 | : Shape(get_center(v)) 124 | , vertices(v) 125 | , mode(EVEN_ODD) {} 126 | 127 | Polygon(const std::initializer_list &l) 128 | : Polygon(std::vector(l)) {} 129 | 130 | Float sdf(const Point &P) const { 131 | if (vertices.size() == 1) 132 | return abs(vertices.front() - P); 133 | 134 | // 求到每一边的最短距离 135 | Float dist = Segment(vertices.front(), vertices.back()).sdf(P); 136 | for (size_t i = 1; i < vertices.size(); ++i) { 137 | Float tmp = Segment(vertices[i - 1], vertices[i]).sdf(P); 138 | if (tmp < dist) 139 | dist = tmp; 140 | } 141 | 142 | if (mode == ALL_INSIDE) 143 | return -dist; 144 | if (mode == ALL_OUTSIDE) 145 | return dist; 146 | 147 | // 向径 148 | std::vector radius(vertices.size()); 149 | for (size_t i = 0; i < radius.size(); ++i) { 150 | radius[i] = vertices[i] - P; 151 | } 152 | 153 | // 求转过的角度 TODO: 比较费时, 待优化 154 | Float total_angle = angle(radius.back(), radius.front()); 155 | for (size_t i = 1; i < vertices.size(); ++i) { 156 | total_angle += angle(radius[i - 1], radius[i]); 157 | } 158 | 159 | // winding number = 角度 / 2pi 160 | int w = int(round(total_angle /= Const::tau)); 161 | 162 | if (mode == EVEN_ODD) 163 | return w % 2 == 0 ? dist : -dist; 164 | else // (mode == NON_ZERO) 165 | return w == 0 ? dist : -dist; 166 | } 167 | }; 168 | 169 | class BinaryOp : public Shape { 170 | 171 | protected: 172 | const Shape &a, &b; 173 | BinaryOp(const Shape &x, const Shape &y) 174 | : a(x) 175 | , b(y) { 176 | c = Point((a.transform_center().x + b.transform_center().x) / 2, 177 | (a.transform_center().y + b.transform_center().y) / 2); 178 | } 179 | }; 180 | class Union : public BinaryOp { 181 | public: 182 | Union(const Shape &x, const Shape &y) 183 | : BinaryOp(x, y) {} 184 | 185 | Float sdf(Point p) const { return std::min(a.sdf(p), b.sdf(p)); } 186 | }; 187 | class Difference : public BinaryOp { 188 | public: 189 | Difference(const Shape &x, const Shape &y) 190 | : BinaryOp(x, y) {} 191 | Float sdf(Point p) const { return std::max(a.sdf(p), -b.sdf(p)); } 192 | }; 193 | class Intersection : public BinaryOp { 194 | public: 195 | Intersection(const Shape &x, const Shape &y) 196 | : BinaryOp(x, y) {} 197 | Float sdf(Point p) const { return std::max(a.sdf(p), b.sdf(p)); } 198 | }; 199 | } // namespace Shape 200 | } // namespace CASTLE 201 | #endif 202 | -------------------------------------------------------------------------------- /color.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLOR_HPP 2 | #define COLOR_HPP 3 | 4 | #include "const.hpp" 5 | #include "global_define.hpp" 6 | #include 7 | #include 8 | 9 | struct Color { 10 | // red, green, blue 11 | Byte r, g, b; 12 | 13 | Color() 14 | : Color(0, 0, 0) {} 15 | Color(Byte r, Byte g, Byte b) 16 | : r(r) 17 | , g(g) 18 | , b(b) {} 19 | Color(const std::array &c) 20 | : Color(c[0], c[1], c[2]) {} 21 | 22 | operator std::array() const { 23 | return std::array({r, g, b}); 24 | } 25 | 26 | bool operator==(const Color &c) const { 27 | return r == c.r && g == c.g && b == c.b; 28 | } 29 | }; 30 | 31 | // 更高精度的颜色类 32 | class ColorExt { 33 | // red, green, blue, alpha 34 | // _a == 0: 不透明, _a == 1: 完全透明 35 | Float _r, _g, _b, _a; 36 | 37 | static Float fit(Float x) { return x < 0 ? 0.0 : (x > 1 ? 1.0 : x); } 38 | 39 | static Byte F2B(Float x) { 40 | if (x <= 0.0) 41 | return 0; 42 | if (x >= 1.0) 43 | return 255; 44 | return Byte(x * 256); 45 | } 46 | 47 | // 因为今后可能会 typedef double Float; 所以不能简单写成 255.0f 48 | static Float B2F(Byte x) { return x / static_cast(255.0); } 49 | 50 | public: 51 | ColorExt() { _r = _g = _b = _a = 0; } 52 | 53 | ColorExt(Float r, Float g, Float b, Float a = 0) 54 | : _r(fit(r)) 55 | , _g(fit(g)) 56 | , _b(fit(b)) 57 | , _a(fit(a)) {} 58 | 59 | ColorExt(const std::array &c) 60 | : ColorExt(c[0], c[1], c[2], c[3]) {} 61 | 62 | ColorExt(const std::array &c) 63 | : ColorExt(c[0], c[1], c[2]) {} 64 | 65 | ColorExt(const Color &c) 66 | : ColorExt(B2F(c.r), B2F(c.g), B2F(c.b)) {} 67 | 68 | // ColorExt(const std::array &c) 69 | // : ColorExt(B2F(c[0]), B2F(c[1]), B2F(c[2])) {} 70 | 71 | operator Color() const { return Color(F2B(_r), F2B(_g), F2B(_b)); } 72 | 73 | operator std::array() const { 74 | return std::array({_r, _g, _b, _a}); 75 | } 76 | 77 | operator std::array() const { 78 | return std::array({_r, _g, _b}); 79 | } 80 | 81 | // operator std::array() const { 82 | // return std::array({F2B(_r), F2B(_g), F2B(_b)}); 83 | //} 84 | 85 | Float r() const { return _r; } 86 | Float g() const { return _g; } 87 | Float b() const { return _b; } 88 | Float a() const { return _a; } 89 | 90 | // 叠加颜色 91 | void compose(const ColorExt &c) { 92 | if (c.a() == 0) { 93 | _r = c.r(); 94 | _g = c.g(); 95 | _b = c.b(); 96 | _a = 0; 97 | } else if (c.a() == 1) { 98 | // 99 | } else if (_a == 0) { 100 | _r = c.r() + (_r - c.r()) * c.a(); 101 | _g = c.g() + (_g - c.g()) * c.a(); 102 | _b = c.b() + (_b - c.b()) * c.a(); 103 | _a = 0.0; 104 | } else if (_a == 1) { 105 | _r = c.r(); 106 | _g = c.g(); 107 | _b = c.b(); 108 | _a = c.a(); 109 | } else { 110 | _r = (c.r() * (1.0 - c.a()) + c.a() * _r * (1.0 - _a)) 111 | / (1.0 - _a * c.a()); 112 | _g = (c.g() * (1.0 - c.a()) + c.a() * _g * (1.0 - _a)) 113 | / (1.0 - _a * c.a()); 114 | _b = (c.b() * (1.0 - c.a()) + c.a() * _b * (1.0 - _a)) 115 | / (1.0 - _a * c.a()); 116 | _a = _a * c.a(); 117 | } 118 | } 119 | }; 120 | 121 | void compose(ColorExt &a, const ColorExt &b) { a.compose(b); } 122 | 123 | class ColorHSV { 124 | Float _h, _s, _v, _a; 125 | static Float fit(Float x) { return x < 0 ? 0.0 : (x > 1 ? 1.0 : x); } 126 | 127 | public: 128 | ColorHSV() { _h = _s = _v = _a = 0; } 129 | ColorHSV(const ColorExt &); 130 | ColorHSV(Float h, Float s, Float v, Float a = 0) 131 | : _h(fit(h / Const::tau) * Const::tau) 132 | , _s(fit(s)) 133 | , _v(fit(v)) 134 | , _a(fit(a)) {} 135 | 136 | operator ColorExt() { 137 | const Float cst = Const::pi / 3.0; 138 | int hi = _h / cst; 139 | Float f, p, q, t; 140 | f = _h / cst - hi; 141 | p = _v * (1.0 - _s); 142 | q = _v * (1.0 - f * _s); 143 | t = _v * (1.0 - (1.0 - f) * _s); 144 | switch (hi) { 145 | case 0: 146 | return ColorExt(_v, t, p); 147 | case 1: 148 | return ColorExt(q, _v, p); 149 | case 2: 150 | return ColorExt(p, _v, t); 151 | case 3: 152 | return ColorExt(p, q, _v); 153 | case 4: 154 | return ColorExt(t, p, _v); 155 | case 5: 156 | return ColorExt(_v, p, q); 157 | } 158 | } 159 | }; 160 | 161 | ColorHSV::ColorHSV(const ColorExt &c) { 162 | Float r = c.r(), g = c.g(), b = c.b(); 163 | Float cmin, delta; 164 | const Float cst = Const::pi / 3.0; 165 | char vc, mc; 166 | if (r > g) { 167 | if (g > b) 168 | vc = 'r', mc = 'b'; 169 | else if (r > b) 170 | vc = 'r', mc = 'g'; 171 | else 172 | vc = 'b', mc = 'g'; 173 | } else { 174 | if (r > b) 175 | vc = 'g', mc = 'b'; 176 | else if (g > b) 177 | vc = 'g', mc = 'r'; 178 | else 179 | vc = 'b', mc = 'r'; 180 | } 181 | switch (vc) { 182 | case 'r': 183 | _v = r; 184 | break; 185 | case 'g': 186 | _v = g; 187 | break; 188 | case 'b': 189 | _v = b; 190 | break; 191 | } 192 | switch (mc) { 193 | case 'r': 194 | cmin = r; 195 | break; 196 | case 'g': 197 | cmin = g; 198 | break; 199 | case 'b': 200 | cmin = b; 201 | break; 202 | } 203 | delta = _v - cmin; 204 | if (delta > 0) { 205 | switch (vc) { 206 | case 'r': 207 | if (mc == 'b') 208 | _h = cst * ((g - b) / delta); 209 | else 210 | _h = cst * ((g - b) / delta + 6.0); 211 | case 'g': 212 | _h = cst * ((b - r) / delta + 2.0); 213 | break; 214 | case 'b': 215 | _h = cst * ((r - g) / delta + 4.0); 216 | break; 217 | } 218 | } 219 | if (_v > 0) { 220 | _s = delta / _v; 221 | } 222 | _a = c.a(); 223 | } 224 | #endif 225 | -------------------------------------------------------------------------------- /bmp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BMP_HPP 2 | #define BMP_HPP 3 | 4 | #include "color.hpp" 5 | #include "global_define.hpp" 6 | #include "picture.hpp" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //一般来说,RGB色深总是24,没有预料过RGB色深不是24的情况 13 | //请注意 14 | 15 | // 用于读写 bmp 文件 16 | class BMPFile { 17 | 18 | // 一个 bmp 文件由 header, colormap (可选), img 三部分组成 19 | const Dword header_size; 20 | Dword colormap_size; 21 | Dword img_size; 22 | 23 | // colorMap 以 vector 实现, 24 | // 为了更高效, img 以动态数组实现 25 | typedef std::vector ColorMap; 26 | ColorMap colormap; 27 | Byte *img; 28 | 29 | static Byte *pic2img(const BytePicture &pic) { 30 | Byte *res = new Byte[pic.height() * pic.width() * 3]; 31 | Dword pos = 0; 32 | for (Dword i = 0; i < pic.height(); i++) 33 | for (Dword j = 0; j < pic.width(); j++) { 34 | res[pos++] = pic.data[i][j][2]; 35 | res[pos++] = pic.data[i][j][1]; 36 | res[pos++] = pic.data[i][j][0]; 37 | } 38 | return res; 39 | } 40 | 41 | public: 42 | BMPFile(Byte *_img, Dword height, Dword width, Byte bpp = 24, 43 | const ColorMap &_colormap = ColorMap()) 44 | : // bpp: bit per pixel 45 | header_size(sizeof(header) + 2) 46 | , colormap_size(_colormap.size() ? 1 << (bpp + 2) : 0) 47 | , img_size((((bpp * width + 31) >> 5) << 2) * height) 48 | , colormap(_colormap) 49 | , img(_img) { 50 | // bmp header 51 | header.file_size = header_size + colormap_size + img_size; 52 | header.reserved1 = 0; 53 | header.reserved2 = 0; 54 | header.offbits = header_size + colormap_size; 55 | 56 | // info header 57 | header.info_size = 40; 58 | header.width = width; 59 | header.height = height; 60 | header.planes = 1; 61 | header.bit_count = bpp; 62 | header.compression = 0; 63 | header.img_size = img_size; 64 | header.resolutionX = 0; 65 | header.resolutionY = 0; 66 | header.color_used = 0; 67 | header.color_important = 0; 68 | } 69 | 70 | struct { 71 | // bmp header: 14 - 2 = 12 bytes 72 | // Byte type[2]; 去掉这一字段, 方便字节对齐 73 | Dword file_size; 74 | Word reserved1; 75 | Word reserved2; 76 | Dword offbits; 77 | 78 | // info header: 40 bytes 79 | Dword info_size; 80 | Dword width; 81 | Dword height; 82 | Word planes; 83 | Word bit_count; 84 | Dword compression; 85 | Dword img_size; 86 | Dword resolutionX; // 单位: pixel/meter 87 | Dword resolutionY; 88 | Dword color_used; 89 | Dword color_important; 90 | } header; 91 | 92 | BMPFile(const BytePicture &pic, Byte bpp = 24, 93 | const ColorMap &_colormap = ColorMap()) //委托构造函数 94 | : BMPFile(pic2img(pic), pic.height(), pic.width(), bpp, _colormap) {} 95 | 96 | BMPFile(const char *filename) 97 | : header_size(sizeof(header) + 2) 98 | , colormap(ColorMap()) 99 | , img(NULL) { 100 | // windows 下读写二进制文件要加上选项 "b" 101 | FILE *fp = fopen(filename, "rb"); 102 | if (!fp) { 103 | perror(filename); 104 | return; 105 | } 106 | 107 | // type 108 | char type[2]; 109 | fread(type, sizeof(Byte), 2, fp); 110 | if (type[0] != 'B' || type[1] != 'M') { 111 | std::cerr << "BMPfile: " << filename << " not start with 'BM'\n"; 112 | return; 113 | } 114 | 115 | // header 116 | fread(&header, sizeof(Byte), sizeof(header), fp); 117 | colormap_size = header.offbits - header_size; 118 | img_size = header.img_size; 119 | 120 | // colormap 121 | if (colormap_size) { 122 | Dword count = colormap_size / sizeof(Dword); 123 | try { 124 | colormap.resize(count); 125 | } catch (...) { 126 | std::cerr 127 | << "BMPfile: failed to allocate memory for colormap\n"; 128 | return; 129 | } 130 | fread(colormap.data(), sizeof(Dword), count, fp); 131 | } 132 | 133 | // img 134 | if (img_size) { 135 | try { 136 | img = new Byte[img_size]; 137 | } catch (...) { 138 | std::cerr << "BMPfile: failed to allocate memory for img\n"; 139 | return; 140 | } 141 | fread(img, sizeof(Byte), img_size, fp); 142 | 143 | // 将数据在内存中移动, 消除空白字节 144 | Byte bpp = header.bit_count; 145 | Dword zero_count = (4 - (((bpp >> 3) * header.width) & 3)) & 3; 146 | if (zero_count) { 147 | Dword line_count = header.width * (bpp >> 3); 148 | Dword count = line_count + zero_count; 149 | const Byte *r = img + count; 150 | Byte *w = img + line_count; 151 | for (; r < img + img_size; r += count, w += line_count) 152 | memmove(w, r, line_count); 153 | } 154 | } 155 | 156 | fclose(fp); 157 | } 158 | 159 | ~BMPFile() { 160 | if (img) { 161 | delete[] img; 162 | img = NULL; 163 | } 164 | } 165 | 166 | BMPFile(const BMPFile &f) 167 | : header_size(f.header_size) 168 | , colormap_size(f.colormap_size) 169 | , img_size(f.img_size) 170 | , colormap(f.colormap) 171 | , img(new Byte[img_size]) 172 | , header(f.header) { 173 | memcpy(img, f.img, sizeof(f.img)); 174 | } 175 | 176 | int output(const char *filename) const { 177 | FILE *fp = fopen(filename, "wb"); 178 | if (!fp) { 179 | perror(filename); 180 | return -1; 181 | } 182 | info(); 183 | Byte *buf = new Byte[header.file_size]; 184 | Byte *w = buf; 185 | 186 | // header 187 | buf[0] = 'B'; 188 | buf[1] = 'M'; 189 | w += 2; 190 | memcpy(w, &header, sizeof(header)); 191 | w += sizeof(header); 192 | 193 | // colormap 194 | Byte bpp = header.bit_count; 195 | if (colormap.size()) { 196 | memcpy(w, colormap.data(), colormap_size); 197 | w += colormap_size; 198 | } 199 | 200 | // img 201 | if (img) { 202 | Dword zero_count = (4 - (((bpp >> 3) * header.width) & 3)) & 3; 203 | Dword line_count = header.width * (bpp >> 3); 204 | const Byte *r = img; 205 | for (Dword i = 0; i < header.height; ++i) { 206 | // 从 img 数组拷贝 line_count 个字节 207 | memcpy(w, r, line_count); 208 | r += line_count; 209 | w += line_count; 210 | // 用零填充 zero_count 个字节 211 | memset(w, 0, zero_count); 212 | w += zero_count; 213 | } 214 | } 215 | fwrite(buf, sizeof(Byte), header.file_size, fp); 216 | fclose(fp); 217 | delete[] buf; 218 | return 0; 219 | } 220 | 221 | BytePicture to_pic() const { 222 | BytePicture pic(header.height, header.width); 223 | Dword pos = 0; 224 | for (Dword i = 0; i < pic.height(); i++) 225 | for (Dword j = 0; j < pic.width(); j++) { 226 | pic.data[i][j][2] = img[pos++]; 227 | pic.data[i][j][1] = img[pos++]; 228 | pic.data[i][j][0] = img[pos++]; 229 | } 230 | return pic; 231 | } 232 | 233 | Color get(Dword row, Dword col) const { 234 | Dword idx = (row * header.width + col) * 3; 235 | return Color {img[idx + 2], img[idx + 1], img[idx]}; 236 | } 237 | 238 | void set(Dword row, Dword col, const Color &color) { 239 | Dword idx = (row * header.width + col) * 3; 240 | img[idx + 2] = color.r; 241 | img[idx + 1] = color.g; 242 | img[idx] = color.b; 243 | } 244 | 245 | void info() const { 246 | std::cout << "BMP information:" 247 | << "\n file_size: " << header.file_size 248 | << "\n reserved1: " << header.reserved1 249 | << "\n reserved2: " << header.reserved2 250 | << "\n offbits: " << header.offbits 251 | << "\n info_size: " << header.info_size 252 | << "\n width: " << header.width 253 | << "\n height: " << header.height 254 | << "\n planes: " << header.planes 255 | << "\n bit_count: " << header.bit_count 256 | << "\n compression: " << header.compression 257 | << "\n img_size: " << header.img_size 258 | << "\n resolutionX: " << header.resolutionX 259 | << "\n resolutionY: " << header.resolutionY 260 | << "\n color_used: " << header.color_used 261 | << "\n color_important: " << header.color_important 262 | << "\n"; 263 | } 264 | }; 265 | 266 | #endif // BMP_HPP 267 | -------------------------------------------------------------------------------- /example/show_creation2.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | const uint Height = 1080; 5 | const uint Width = 1920; 6 | uc BitMap[Height][Width][3]; 7 | uc BMPFILE[(Height * Width << 2) + 256]; 8 | uc div255[65536]; 9 | struct COLOR { 10 | uc r, g, b, a; 11 | COLOR(uc r = 0u, uc g = 0u, uc b = 0u, uc a = 0u) 12 | : r(r) 13 | , g(g) 14 | , b(b) 15 | , a(a) {} 16 | bool operator==(COLOR c) { 17 | return r == c.r && g == c.g && b == c.b && a == c.a; 18 | } 19 | }; 20 | namespace FUNCTION { 21 | struct id { 22 | double operator()(double x) { return x; } 23 | }; 24 | struct sqr { 25 | double operator()(double x) { return x * x; } 26 | }; 27 | struct cube { 28 | double operator()(double x) { return x * x * x; } 29 | }; 30 | template struct anti { 31 | double operator()(double x) { 32 | FF f; 33 | return 1 - f(1 - x); 34 | } 35 | }; 36 | double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); } 37 | struct SMOOTH { 38 | double operator()(double x, double inflection) { 39 | double error = sigmoid(-inflection / 2); 40 | double temp 41 | = (sigmoid(inflection * (x - 0.5)) - error) / (1 - 2 * error); 42 | if (temp < 0.0) 43 | return 0.0; 44 | if (temp > 1.0) 45 | return 1.0; 46 | return temp; 47 | } 48 | }; 49 | struct smooth { 50 | double operator()(double x) { 51 | SMOOTH f; 52 | return f(x, 10.0); 53 | } 54 | }; 55 | template struct sfs { 56 | double operator()(double x) { 57 | FF f; 58 | if (x <= 0.5) 59 | return 0.5 * f(2.0 * x); 60 | return 1 - 0.5 * f(2.0 - 2.0 * x); 61 | } 62 | }; 63 | } // namespace FUNCTION 64 | void set_color(uc arr[3], COLOR c) { 65 | arr[0] = div255[arr[0] * c.a + c.b * (255 - c.a) + 127]; 66 | arr[1] = div255[arr[1] * c.a + c.g * (255 - c.a) + 127]; 67 | arr[2] = div255[arr[2] * c.a + c.r * (255 - c.a) + 127]; 68 | } 69 | void clear_all(COLOR bgc = COLOR(0u, 0u, 0u)) { 70 | for (uint i = 0; i < Height; ++i) 71 | for (uint j = 0; j < Width; ++j) 72 | set_color(BitMap[i][j], bgc); 73 | } 74 | struct PIC { 75 | uint height, width; 76 | uint size; 77 | COLOR *pic_file; 78 | PIC(uint h, uint w) 79 | : height(h) 80 | , width(w) 81 | , size(h * w) { 82 | pic_file = new COLOR[size]; 83 | } 84 | PIC(uint h, uint w, COLOR c) 85 | : height(h) 86 | , width(w) 87 | , size(h * w) { 88 | pic_file = new COLOR[h * w]; 89 | fill(pic_file, pic_file + h * w, c); 90 | } 91 | ~PIC() { delete[] pic_file; } 92 | void draw(int bl, int bg) { 93 | for (uint i = max(-bl, 0); (int)i < min((int)Height - bl, (int)height); 94 | ++i) { 95 | for (uint j = max(-bg, 0); 96 | (int)j < min((int)Width - bg, (int)width); ++j) { 97 | set_color(BitMap[i + bl][j + bg], pic_file[i * width + j]); 98 | } 99 | } 100 | } 101 | void turn_color(COLOR prev, COLOR cur) { 102 | for (uint i = 0; i < size; ++i) { 103 | if (pic_file[i] == prev) { 104 | pic_file[i] = cur; 105 | } 106 | } 107 | } 108 | void set_transparent_color(COLOR c) { turn_color(c, COLOR(0, 0, 0, 255)); } 109 | template void show(uint Duration, uint Current_frame) { 110 | --Duration; 111 | FUNC f; 112 | uint alpha = (1.0 - f((double)Current_frame / Duration)) * 255 + 0.5; 113 | for (uint i = 0; i < size; ++i) { 114 | pic_file[i].a = alpha; 115 | } 116 | } 117 | }; 118 | 119 | struct FIGURE { 120 | typedef pair PIXEL; 121 | vector Pixels; 122 | void draw(int bl, int br, COLOR c, uint num_visible, int th = 1) { 123 | int half_th = (th >> 1); 124 | for (uint i = 0; i < num_visible; ++i) { 125 | for (int j = -half_th; j < th - half_th; ++j) 126 | for (int k = -half_th; k < th - half_th; ++k) { 127 | int l = bl + Pixels[i].first + j; 128 | int r = br + Pixels[i].second + k; 129 | if (l >= 0 && l < (int)Height && r >= 0 && r < (int)Width) 130 | set_color(BitMap[l][r], c); 131 | } 132 | } 133 | } 134 | template 135 | uint Show_Creation(uint Duration, uint Current_frame) { 136 | FUNC f; 137 | return f((double)Current_frame / (Duration - 1)) * Pixels.size() + 0.5; 138 | } 139 | }; 140 | 141 | template 142 | COLOR color_transform( 143 | COLOR begin, COLOR end, uint Duration, uint Current_frame) { 144 | --Duration; 145 | COLOR ans; 146 | FUNC f; 147 | ans.r = begin.r + (end.r - begin.r) * f((double)Current_frame / Duration) 148 | + 0.5; 149 | ans.g = begin.g + (end.g - begin.g) * f((double)Current_frame / Duration) 150 | + 0.5; 151 | ans.b = begin.b + (end.r - begin.b) * f((double)Current_frame / Duration) 152 | + 0.5; 153 | return ans; 154 | } 155 | void to_file(const char *filename) { 156 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 157 | output_bmp(filename, BMPFILE, size); 158 | } 159 | void draw_rectangle(uint bline, uint brow, uint height, uint width, COLOR c) { 160 | for (uint i = 0; i < height; ++i) 161 | for (uint j = 0; j < width; ++j) 162 | set_color(BitMap[bline + i][brow + j], c); 163 | } 164 | void draw_rectangle(pair P, uint height, uint width, COLOR c) { 165 | draw_rectangle(P.first, P.second, height, width, c); 166 | } 167 | template 168 | pair move(pair begin, pair end, 169 | uint Duration, uint Current_frame) { 170 | --Duration; 171 | pair ans; 172 | FUNC f; 173 | ans.first = begin.first 174 | + (end.first - begin.first) * f((double)Current_frame / Duration) + 0.5; 175 | ans.second = begin.second 176 | + (end.second - begin.second) * f((double)Current_frame / Duration) 177 | + 0.5; 178 | return ans; 179 | } 180 | namespace CONSTANT { 181 | const COLOR RED = COLOR(255, 0, 0); 182 | const COLOR GREEN = COLOR(0, 255, 0); 183 | const COLOR BLUE = COLOR(0, 0, 255); 184 | const COLOR BLACK = COLOR(0, 0, 0); 185 | const COLOR WHITE = COLOR(255, 255, 255); 186 | const COLOR YELLOW = COLOR(255, 255, 0); 187 | const COLOR PURPLE = COLOR(255, 0, 255); 188 | const COLOR CYAN = COLOR(0, 255, 255); 189 | const COLOR TRANSPARENT = COLOR(0, 0, 0, 255); 190 | // const TRANS NORM_TRANS = TRANS(1.0, 0.0, 1.0, 0.0); 191 | } // namespace CONSTANT 192 | using namespace CONSTANT; 193 | int main() { 194 | system("del Show_Creation_Rectangle2_*.bmp"); 195 | for (uint i = 0; i < 65536; ++i) { 196 | div255[i] = uc(i / 255); 197 | } 198 | clock_t st = clock(); 199 | FIGURE fig; 200 | for (uint i = 300; i <= 800u; ++i) { 201 | fig.Pixels.push_back(make_pair(i, 700)); 202 | } 203 | for (uint i = 700; i <= 1200u; ++i) { 204 | fig.Pixels.push_back(make_pair(800, i)); 205 | } 206 | for (uint i = 800; i >= 300u; --i) { 207 | fig.Pixels.push_back(make_pair(i, 1200)); 208 | } 209 | for (uint i = 1200; i >= 700u; --i) { 210 | fig.Pixels.push_back(make_pair(300, i)); 211 | } 212 | clear_all(); 213 | PIC pic(495u, 495u, GREEN); 214 | for (uint frame = 0; frame < 45u; ++frame) { 215 | fig.draw( 216 | 0, 0, WHITE, fig.Show_Creation(45u, frame), 5u); 217 | char temp[64]; 218 | sprintf(temp, "Show_Creation_Rectangle2_%d.bmp", frame + 1); 219 | to_file(temp); 220 | printf("\r%.0lf%%", frame / 0.9); 221 | fflush(stdout); 222 | fig.draw( 223 | 0, 0, BLACK, fig.Show_Creation(45u, frame), 5u); 224 | } 225 | PIC blk(495u, 495u, BLACK); 226 | for (uint frame = 45u; frame < 60u; ++frame) { 227 | fig.draw(0, 0, WHITE, fig.Pixels.size(), 5u); 228 | pic.show(15u, frame - 45u); 229 | pic.draw(303, 703); 230 | char temp[64]; 231 | sprintf(temp, "Show_Creation_Rectangle2_%d.bmp", frame + 1); 232 | to_file(temp); 233 | printf("\r%.0lf%%", frame / 0.9); 234 | fflush(stdout); 235 | blk.draw(303, 703); 236 | } 237 | pic.draw(303, 703); 238 | for (uint frame = 60u; frame < 90u; ++frame) { 239 | char temp[64]; 240 | sprintf(temp, "Show_Creation_Rectangle2_%d.bmp", frame + 1); 241 | to_file(temp); 242 | printf("\r%.0lf%%", frame / 0.9); 243 | fflush(stdout); 244 | } 245 | // system("ffmpeg -y -i Show_Creation_Rectangle2_%d.bmp -r 30 -pix_fmt 246 | // yuv420p Show_Creation_Rectangle2.mp4"); 247 | printf("\n%ldms\n", clock() - st); 248 | return 0; 249 | } 250 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /example/transform.cpp: -------------------------------------------------------------------------------- 1 | #pragma GCC optimize(2) 2 | #include "bmp_old.h" 3 | using namespace std; 4 | const uint Height = 1080; 5 | const uint Width = 1920; 6 | uc BitMap[Height][Width][3]; 7 | uc BMPFILE[(Height * Width << 2) + 256]; 8 | struct PIXEL { 9 | uint line_num, row_num; 10 | PIXEL(uint l = 0, uint r = 0) 11 | : line_num(l) 12 | , row_num(r) {} 13 | bool operator==(PIXEL a) { 14 | return a.line_num == line_num && a.row_num == row_num; 15 | } 16 | bool operator<(PIXEL a) { 17 | return line_num < a.line_num 18 | || (line_num == a.line_num && row_num < a.row_num); 19 | } 20 | }; 21 | struct COOR { 22 | double x, y; 23 | COOR(double x = 0, double y = 0) 24 | : x(x) 25 | , y(y) {} 26 | // x = ax * row_num + bx 27 | // y = ay * line_num + by 28 | }; 29 | 30 | struct TRANS { 31 | double ax, bx, ay, by; 32 | TRANS(double ax, double bx, double ay, double by) 33 | : ax(ax) 34 | , bx(bx) 35 | , ay(ay) 36 | , by(by) {} 37 | COOR P2C(PIXEL P) { 38 | return COOR(ax * P.row_num + bx, ay * P.line_num + by); 39 | } 40 | PIXEL C2P(COOR C) { 41 | return PIXEL((C.y - by) / ay + 0.5, (C.x - bx) / ax + 0.5); 42 | } 43 | }; 44 | 45 | struct COLOR { 46 | uc r, g, b, a; 47 | COLOR(uc r = 0u, uc g = 0u, uc b = 0u, uc a = 0u) 48 | : r(r) 49 | , g(g) 50 | , b(b) 51 | , a(a) {} 52 | bool operator==(COLOR c) { 53 | return r == c.r && g == c.g && b == c.b && a == c.a; 54 | } 55 | }; 56 | 57 | namespace FUNCTION { 58 | struct id { 59 | double operator()(double x) { return x; } 60 | }; 61 | struct sqr { 62 | double operator()(double x) { return x * x; } 63 | }; 64 | struct cube { 65 | double operator()(double x) { return x * x * x; } 66 | }; 67 | template struct anti { 68 | double operator()(double x) { 69 | FF f; 70 | return 1 - f(1 - x); 71 | } 72 | }; 73 | double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); } 74 | struct SMOOTH { 75 | double operator()(double x, double inflection) { 76 | double error = sigmoid(-inflection / 2); 77 | double temp 78 | = (sigmoid(inflection * (x - 0.5)) - error) / (1 - 2 * error); 79 | if (temp < 0.0) 80 | return 0.0; 81 | if (temp > 1.0) 82 | return 1.0; 83 | return temp; 84 | } 85 | }; 86 | struct smooth { 87 | double operator()(double x) { 88 | SMOOTH f; 89 | return f(x, 10.0); 90 | } 91 | }; 92 | template struct sfs { 93 | double operator()(double x) { 94 | FF f; 95 | if (x <= 0.5) 96 | return 0.5 * f(2.0 * x); 97 | return 1 - 0.5 * f(2.0 - 2.0 * x); 98 | } 99 | }; 100 | } // namespace FUNCTION 101 | void set_color(uc arr[3], COLOR c) { 102 | arr[0] = (arr[0] * c.a + c.b * (255 - c.a) + 127) / 255; 103 | arr[1] = (arr[1] * c.a + c.g * (255 - c.a) + 127) / 255; 104 | arr[2] = (arr[2] * c.a + c.r * (255 - c.a) + 127) / 255; 105 | } 106 | void clear_all(COLOR bgc = COLOR(0u, 0u, 0u)) { 107 | for (uint i = 0; i < Height; ++i) 108 | for (uint j = 0; j < Width; ++j) 109 | set_color(BitMap[i][j], bgc); 110 | } 111 | struct PIC { 112 | uint height, width; 113 | uint size; 114 | COLOR *pic_file; 115 | PIC(uint h, uint w) 116 | : height(h) 117 | , width(w) 118 | , size(h * w) { 119 | pic_file = new COLOR[size]; 120 | } 121 | PIC(uint h, uint w, COLOR c) 122 | : height(h) 123 | , width(w) 124 | , size(h * w) { 125 | pic_file = new COLOR[h * w]; 126 | fill(pic_file, pic_file + h * w, c); 127 | } 128 | void draw(int bl, int bg) { 129 | for (uint i = max(-bl, 0); (int)i < min((int)Height - bl, (int)height); 130 | ++i) { 131 | for (uint j = max(-bg, 0); 132 | (int)j < min((int)Width - bg, (int)width); ++j) { 133 | set_color(BitMap[i + bl][j + bg], pic_file[i * width + j]); 134 | } 135 | } 136 | } 137 | void turn_color(COLOR prev, COLOR cur) { 138 | for (uint i = 0; i < size; ++i) { 139 | if (pic_file[i] == prev) { 140 | pic_file[i] = cur; 141 | } 142 | } 143 | } 144 | void set_transparent_color(COLOR c) { turn_color(c, COLOR(0, 0, 0, 255)); } 145 | template void show(uint Duration, uint Current_frame) { 146 | --Duration; 147 | FUNC f; 148 | uint alpha = (1.0 - f((double)Current_frame / Duration)) * 255 + 0.5; 149 | for (uint i = 0; i < size; ++i) { 150 | pic_file[i].a = alpha; 151 | } 152 | } 153 | }; 154 | 155 | struct FIGURE { 156 | vector Pixels; 157 | void draw(int bl, int br, COLOR c, uint num_visible, int th = 1) { 158 | int half_th = (th >> 1); 159 | for (uint i = 0; i < num_visible; ++i) { 160 | for (int j = -half_th; j < th - half_th; ++j) 161 | for (int k = -half_th; k < th - half_th; ++k) { 162 | int l = bl + Pixels[i].line_num + j; 163 | int r = br + Pixels[i].row_num + k; 164 | if (l >= 0 && l < Height && r >= 0 && r < Width) 165 | set_color(BitMap[l][r], c); 166 | } 167 | } 168 | } 169 | template 170 | uint Show_Creation(uint Duration, uint Current_frame) { 171 | FUNC f; 172 | return f((double)Current_frame / (Duration - 1)) * Pixels.size() + 0.5; 173 | } 174 | }; 175 | 176 | struct PARAEQ { 177 | TRANS trans; 178 | double (*X)(double); 179 | double (*Y)(double); 180 | PARAEQ(TRANS trans, double (*X)(double), double (*Y)(double)) 181 | : trans(trans) 182 | , X(X) 183 | , Y(Y) {} 184 | void draw(FIGURE &fig, double begin, double end, double xmin, double xmax, 185 | double ymin, double ymax, double eps1 = 1e-3, double eps2 = 1e-6) { 186 | double mid = (begin + end) * 0.5; 187 | if (end - begin > eps1) { 188 | draw(fig, begin, mid, xmin, xmax, ymin, ymax, eps1, eps2); 189 | draw(fig, mid, end, xmin, xmax, ymin, ymax, eps1, eps2); 190 | return; 191 | } 192 | COOR P(X(begin), Y(begin)), Q(X(end), Y(end)); 193 | if (P.x < xmin || P.x > xmax || P.x != P.x || P.y < ymin || P.y > ymax 194 | || P.y != P.y) 195 | if (Q.x < xmin || Q.x > xmax || Q.x != Q.x || Q.y < ymin 196 | || Q.y > ymax || Q.y != Q.y) 197 | return; 198 | else { 199 | draw(fig, mid, end, xmin, xmax, ymin, ymax, eps1, eps2); 200 | return; 201 | } 202 | else if (Q.x < xmin || Q.x > xmax || Q.x != Q.x || Q.y < ymin 203 | || Q.y > ymax || Q.y != Q.y) { 204 | draw(fig, begin, mid, xmin, xmax, ymin, ymax, eps1, eps2); 205 | return; 206 | } 207 | PIXEL B = trans.C2P(P), E = trans.C2P(Q); 208 | if (end - begin < eps2) { 209 | fig.Pixels.push_back(B); 210 | if (E.line_num != B.line_num || E.row_num != B.row_num) 211 | fig.Pixels.push_back(E); 212 | } 213 | int dx = B.line_num - E.line_num, dy = B.row_num - E.row_num; 214 | switch (dx * dx + dy * dy) { 215 | case 0: 216 | fig.Pixels.push_back(B); 217 | break; 218 | case 1: 219 | case 2: 220 | fig.Pixels.push_back(B); 221 | fig.Pixels.push_back(E); 222 | break; 223 | default: 224 | draw(fig, begin, mid, xmin, xmax, ymin, ymax, eps1, eps2); 225 | draw(fig, mid, end, xmin, xmax, ymin, ymax, eps1, eps2); 226 | } 227 | } 228 | }; 229 | double X(double t) { return t; } 230 | double Y(double t) { return t * t; } 231 | 232 | template 233 | COLOR color_transform( 234 | COLOR begin, COLOR end, uint Duration, uint Current_frame) { 235 | --Duration; 236 | COLOR ans; 237 | FUNC f; 238 | ans.r = begin.r + (end.r - begin.r) * f((double)Current_frame / Duration) 239 | + 0.5; 240 | ans.g = begin.g + (end.g - begin.g) * f((double)Current_frame / Duration) 241 | + 0.5; 242 | ans.b = begin.b + (end.r - begin.b) * f((double)Current_frame / Duration) 243 | + 0.5; 244 | return ans; 245 | } 246 | void to_file(const char *filename) { 247 | uint size = write_bmp(BMPFILE, Height, Width, 24, &BitMap[0][0][0]); 248 | output_bmp(filename, BMPFILE, size); 249 | } 250 | void get_normal_transform_sequence( 251 | vector *res, FIGURE &fig1, FIGURE &fig2) { 252 | if (fig1.Pixels.size() == 1) { 253 | res[0] = fig2.Pixels; 254 | return; 255 | } else if (fig2.Pixels.size() == 1) { 256 | for (uint i = 0; i < fig1.Pixels.size(); ++i) { 257 | res[i] = fig2.Pixels; 258 | } 259 | return; 260 | } 261 | ull temp11 = 0ull, temp12 = 0ull, temp21 = 0ull, temp22 = 0ull; 262 | for (uint i = 0; i < fig1.Pixels.size(); ++i) { 263 | temp11 += fig1.Pixels[i].line_num; 264 | temp12 += fig1.Pixels[i].row_num; 265 | } 266 | temp11 = (temp11 + fig1.Pixels.size() / 2) / fig1.Pixels.size(); 267 | temp12 = (temp12 + fig1.Pixels.size() / 2) / fig1.Pixels.size(); 268 | for (uint i = 0; i < fig2.Pixels.size(); ++i) { 269 | temp21 += fig2.Pixels[i].line_num; 270 | temp22 += fig2.Pixels[i].row_num; 271 | } 272 | temp21 = (temp21 + fig2.Pixels.size() / 2) / fig2.Pixels.size(); 273 | temp22 = (temp22 + fig2.Pixels.size() / 2) / fig2.Pixels.size(); 274 | PIXEL C1(temp11, temp12), C2(temp21, temp22); 275 | if (C1 == C2) 276 | C2 = PIXEL(C1.line_num, C1.row_num + 1); 277 | FIGURE fig11, fig12, fig13, fig21, fig22, fig23; 278 | for (uint i = 0; i < fig1.Pixels.size(); ++i) { 279 | if (fig1.Pixels[i].line_num * C1.row_num 280 | < fig1.Pixels[i].row_num * C1.line_num) { 281 | fig11.Pixels.push_back(fig1.Pixels[i]); 282 | } else if (fig1.Pixels[i].line_num * C1.row_num 283 | > fig1.Pixels[i].row_num * C1.line_num) { 284 | fig12.Pixels.push_back(fig1.Pixels[i]); 285 | } else { 286 | fig13.Pixels.push_back(fig1.Pixels[i]); 287 | } 288 | } 289 | uint a, b, c, c1; 290 | a = fig11.Pixels.size(); 291 | b = fig12.Pixels.size(); 292 | c = fig13.Pixels.size(); 293 | if (a > b) { 294 | if (a - b >= c) { 295 | c1 = 0; 296 | } else { 297 | c1 = (b + c - a) / 2; 298 | } 299 | } else { 300 | if (b - a >= c) { 301 | c1 = c; 302 | } else { 303 | c1 = (b + c - a) / 2; 304 | } 305 | } 306 | for (uint i = 0; i < c1; ++i) { 307 | fig11.Pixels.push_back(fig13.Pixels[i]); 308 | } 309 | for (uint i = c1; i < c; ++i) { 310 | fig12.Pixels.push_back(fig13.Pixels[i]); 311 | } 312 | if (fig11.Pixels.size() == 0) { 313 | fig11.Pixels.push_back(fig12.Pixels[fig12.Pixels.size() - 1]); 314 | fig12.Pixels.pop_back(); 315 | } 316 | if (fig12.Pixels.size() == 0) { 317 | fig12.Pixels.push_back(fig11.Pixels[fig11.Pixels.size() - 1]); 318 | fig11.Pixels.pop_back(); 319 | } 320 | for (uint i = 0; i < fig2.Pixels.size(); ++i) { 321 | if (fig2.Pixels[i].line_num * C2.row_num 322 | < fig2.Pixels[i].row_num * C2.line_num) { 323 | fig21.Pixels.push_back(fig2.Pixels[i]); 324 | } else if (fig2.Pixels[i].line_num * C2.row_num 325 | > fig2.Pixels[i].row_num * C2.line_num) { 326 | fig22.Pixels.push_back(fig2.Pixels[i]); 327 | } else { 328 | fig23.Pixels.push_back(fig2.Pixels[i]); 329 | } 330 | } 331 | a = fig21.Pixels.size(); 332 | b = fig22.Pixels.size(); 333 | c = fig23.Pixels.size(); 334 | if (a > b) { 335 | if (a - b >= c) { 336 | c1 = 0; 337 | } else { 338 | c1 = (b + c - a) / 2; 339 | } 340 | } else { 341 | if (b - a >= c) { 342 | c1 = c; 343 | } else { 344 | c1 = (b + c - a) / 2; 345 | } 346 | } 347 | for (uint i = 0; i < c1; ++i) { 348 | fig21.Pixels.push_back(fig23.Pixels[i]); 349 | } 350 | for (uint i = c1; i < c; ++i) { 351 | fig22.Pixels.push_back(fig23.Pixels[i]); 352 | } 353 | if (fig21.Pixels.size() == 0) { 354 | fig21.Pixels.push_back(fig22.Pixels[fig22.Pixels.size() - 1]); 355 | fig22.Pixels.pop_back(); 356 | } 357 | if (fig22.Pixels.size() == 0) { 358 | fig22.Pixels.push_back(fig21.Pixels[fig21.Pixels.size() - 1]); 359 | fig21.Pixels.pop_back(); 360 | } 361 | get_normal_transform_sequence(res, fig11, fig21); 362 | get_normal_transform_sequence(res + fig11.Pixels.size(), fig12, fig22); 363 | } 364 | template 365 | void fig_transform(FIGURE &ans, FIGURE &fig1, uint Duration, uint Current_frame, 366 | vector *tr) { 367 | ans.Pixels.clear(); 368 | --Duration; 369 | FUNC f; 370 | double nx = f((double)Current_frame / Duration); 371 | for (uint i = 0; i < fig1.Pixels.size(); ++i) 372 | for (uint j = 0; j < tr[i].size(); ++j) { 373 | PIXEL cur; 374 | cur.line_num = fig1.Pixels[i].line_num 375 | + nx * (tr[i][j].line_num - fig1.Pixels[i].line_num) + 0.5; 376 | cur.row_num = fig1.Pixels[i].row_num 377 | + nx * (tr[i][j].row_num - fig1.Pixels[i].row_num) + 0.5; 378 | ans.Pixels.push_back(cur); 379 | } 380 | } 381 | namespace CONSTANT { 382 | const COLOR RED = COLOR(255, 0, 0); 383 | const COLOR GREEN = COLOR(0, 255, 0); 384 | const COLOR BLUE = COLOR(0, 0, 255); 385 | const COLOR BLACK = COLOR(0, 0, 0); 386 | const COLOR WHITE = COLOR(255, 255, 255); 387 | const COLOR YELLOW = COLOR(255, 255, 0); 388 | const COLOR PURPLE = COLOR(255, 0, 255); 389 | const COLOR CYAN = COLOR(0, 255, 255); 390 | const COLOR TRANSPARENT = COLOR(0, 0, 0, 255); 391 | const TRANS NORM_TRANS = TRANS(1.0, 0.0, 1.0, 0.0); 392 | } // namespace CONSTANT 393 | const double neg_inf = -1.0 / 0.0; 394 | const double pos_inf = 1.0 / 0.0; 395 | using namespace CONSTANT; 396 | int main() { 397 | FIGURE fig1, fig2; 398 | for (uint i = 400; i < 600u; ++i) { 399 | for (uint j = 800; j < 1200u; ++j) { 400 | fig1.Pixels.push_back(PIXEL(i, j)); 401 | } 402 | } 403 | for (uint i = 500; i < 600u; ++i) { 404 | for (uint j = 1000; j < 1200u; ++j) { 405 | fig2.Pixels.push_back(PIXEL(i, j)); 406 | } 407 | } 408 | FIGURE ans; 409 | vector tr[fig1.Pixels.size()]; 410 | get_normal_transform_sequence(tr, fig1, fig2); 411 | for (uint i = 0; i < 120u; ++i) { 412 | fig_transform(ans, fig1, 120u, i, tr); 413 | ans.draw(0, 0, GREEN, ans.Pixels.size(), 1); 414 | char temp[64]; 415 | sprintf(temp, "transform%d.bmp", i + 1); 416 | to_file(temp); 417 | ans.draw(0, 0, BLACK, ans.Pixels.size(), 1); 418 | } 419 | return 0; 420 | } --------------------------------------------------------------------------------