OXYGENHUB
◆ main openmsx / src / Paper.cc
2.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "Paper.hh"
2
3
#include "FileOperations.hh"
4
#include "PNG.hh"
5
6
#include "narrow.hh"
7
#include "small_buffer.hh"
8
#include "xrange.hh"
9
10
#include <algorithm>
11
#include <cassert>
12
#include <cmath>
13
#include <ranges>
14
15
namespace openmsx {
16
17
Paper::Paper(unsigned x, unsigned y, double dotSizeX, double dotSizeY)
18
	: buf(size_t(x) * size_t(y))
19
	, sizeX(x), sizeY(y)
20
{
21
	std::ranges::fill(buf, 255);
22
	setDotSize(dotSizeX, dotSizeY);
23
}
24
25
std::string Paper::save() const
26
{
27
	auto filename = FileOperations::getNextNumberedFileName(
28
		PRINT_DIR, "page", PRINT_EXTENSION);
29
	small_buffer<const uint8_t*, 4096> rowPointers(std::views::transform(xrange(sizeY),
30
		[&](size_t y) { return &buf[sizeX * y]; }));
31
	PNG::saveGrayscale(sizeX, rowPointers, filename);
32
	return filename;
33
}
34
35
void Paper::setDotSize(double dotSizeX, double dotSizeY)
36
{
37
	radiusX = dotSizeX * 0.5;
38
	radiusY = dotSizeY * 0.5;
39
40
	auto rx = int(16 * radiusX);
41
	auto ry = int(16 * radiusY);
42
	radius16 = ry;
43
44
	table.clear();
45
	table.resize(2 * (radius16 + 16), -(1 << 30));
46
47
	int offset = ry + 16;
48
	int rx2 = 2 * rx * rx;
49
	int ry2 = 2 * ry * ry;
50
51
	int x = 0;
52
	int y = ry;
53
	int de_x = ry * ry;
54
	int de_y = (1 - 2 * ry) * rx * rx;
55
	int e = 0;
56
	int sx = 0;
57
	int sy = rx2 * ry;
58
	while (sx <= sy) {
59
		table[offset - y - 1] = x;
60
		table[offset + y    ] = x;
61
		x    += 1;
62
		sx   += ry2;
63
		e    += de_x;
64
		de_x += ry2;
65
		if ((2 * e + de_y) > 0) {
66
			y    -= 1;
67
			sy   -= rx2;
68
			e    += de_y;
69
			de_y += rx2;
70
		}
71
	}
72
73
	x = rx;
74
	y = 0;
75
	de_x = (1 - 2 * rx) * ry * ry;
76
	de_y = rx * rx;
77
	e = 0;
78
	sx = ry2 * rx;
79
	sy = 0;
80
	while (sy <= sx) {
81
		table[offset - y - 1] = x;
82
		table[offset + y    ] = x;
83
		y    += 1;
84
		sy   += rx2;
85
		e    += de_y;
86
		de_y += rx2;
87
		if ((2 * e + de_x) > 0) {
88
			x -= 1;
89
			sx -= ry2;
90
			e += de_x;
91
			de_x += ry2;
92
		}
93
	}
94
}
95
96
void Paper::plot(double xPos, double yPos)
97
{
98
	int xx1 = std::max(int(floor(xPos - radiusX)), 0);
99
	int xx2 = std::min(int(ceil (xPos + radiusX)), int(sizeX));
100
	int yy1 = std::max(int(floor(yPos - radiusY)), 0);
101
	int yy2 = std::min(int(ceil (yPos + radiusY)), int(sizeY));
102
103
	int y = 16 * yy1 - int(16 * yPos) + 16 + radius16;
104
	for (auto yy : xrange(yy1, yy2)) {
105
		int x = 16 * xx1 - int(16 * xPos);
106
		for (auto xx : xrange(xx1, xx2)) {
107
			int sum = 0;
108
			for (auto i : xrange(16)) {
109
				int a = table[y + i];
110
				if (x < -a) {
111
					int t = 16 + a + x;
112
					if (t > 0) {
113
						sum += std::min(t, 2 * a);
114
					}
115
				} else {
116
					int t = a - x;
117
					if (t > 0) {
118
						sum += std::min(16, t);
119
					}
120
				}
121
			}
122
			dot(xx, yy) = narrow<uint8_t>(std::max(0, dot(xx, yy) - sum));
123
			x += 16;
124
		}
125
		y += 16;
126
	}
127
}
128
129
uint8_t& Paper::dot(unsigned x, unsigned y)
130
{
131
	assert(x < sizeX);
132
	assert(y < sizeY);
133
	return buf[y * sizeX + x];
134
}
135
136
} // namespace openmsx