| 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 |
} |