136#ifndef CANVAS_ITY_HPP
137#define CANVAS_ITY_HPP
220 rgba(
float,
float,
float,
float);
239 std::vector<std::optional<float>>
hints;
249 std::vector<unsigned char>
data;
358 void transform(
float a,
float b,
float c,
float d,
float e,
float f);
637 std::optional<float> hint = {});
665 unsigned char const*
image,
751 float control_1_x,
float control_1_y,
float control_2_x,
float control_2_y,
float x,
float y);
779 void arc_to(
float vertex_x,
float vertex_y,
float x,
float y,
float radius);
802 void arc(
float x,
float y,
float radius,
float start_angle,
float end_angle,
bool counter_clockwise =
false);
1010 void fill_text(
char const* text,
float x,
float y,
float maximum_width = 1.0e30f);
1032 void stroke_text(
char const* text,
float x,
float y,
float maximum_width = 1.0e30f);
1231#ifdef CANVAS_ITY_IMPLEMENTATION
1232#define LINEARIZE_RGB 2
1242 const float pi = 3.14159265f;
1243 xy::xy() : x(0.0f), y(0.0f)
1246 xy::xy(
float new_x,
float new_y) : x(new_x), y(new_y)
1255 static xy& operator-=(xy& left, xy right)
1261 static xy& operator*=(xy& left,
float right)
1267 static xy
const operator+(xy left, xy right)
1269 return left += right;
1271 static xy
const operator-(xy left, xy right)
1273 return left -= right;
1275 static xy
const operator*(
float left, xy right)
1277 return right *= left;
1279 static xy
const operator*(affine_matrix
const& left, xy right)
1281 return xy(left.a * right.x + left.c * right.y + left.e, left.b * right.x + left.d * right.y + left.f);
1283 static float dot(xy left, xy right)
1285 return left.x * right.x + left.y * right.y;
1287 static float length(xy that)
1289 return sqrtf(dot(that, that));
1291 static float direction(xy that)
1293 return atan2f(that.y, that.x);
1295 static xy
const normalized(xy that)
1297 return 1.0f / std::max(1.0e-6f, length(that)) * that;
1299 static xy
const perpendicular(xy that)
1301 return xy(-that.y, that.x);
1303 static xy
const lerp(xy from, xy to,
float ratio)
1305 return from + ratio * (to - from);
1308 float normalize_angle(
float angle)
1310 return fmodf(angle, 360) + (angle < 0 ? 360 : 0);
1317 rgba::rgba(
float new_r,
float new_g,
float new_b,
float new_a) : r(new_r), g(new_g),
b(new_b),
a(new_a)
1320 static rgba&
operator+=(rgba& left, rgba right)
1331 static rgba& operator*=(rgba& left,
float right)
1339 static rgba
const operator+(rgba left, rgba right)
1341 return left += right;
1345 static rgba
const operator*(
float left, rgba right)
1347 return right *= left;
1349#if (CANVAS_ITY_IMPLEMENTATION + 0) & LINEARIZE_RGB
1350 static float linearized(
float value)
1352 return value < 0.04045f ?
value / 12.92f : powf((
value + 0.055f) / 1.055f, 2.4f);
1354 static float delinearized(
float value)
1356 return value < 0.0031308f ? 12.92f *
value : 1.055f * powf(
value, 1.0f / 2.4f) - 0.055f;
1359 static float linearized(
float value)
1363 static float delinearized(
float value)
1368 static rgba
const linearized(rgba that)
1370 return rgba(linearized(that.r), linearized(that.g), linearized(that.b), that.a);
1372 static rgba
const premultiplied(rgba that)
1374 return rgba(that.r * that.a, that.g * that.a, that.b * that.a, that.a);
1376 static rgba
const delinearized(rgba that)
1378 return rgba(delinearized(that.r), delinearized(that.g), delinearized(that.b), that.a);
1380 static rgba
const unpremultiplied(rgba that)
1382 static float const threshold = 1.0f / 8160.0f;
1383 return that.a < threshold
1384 ? rgba(0.0f, 0.0f, 0.0f, 0.0f)
1385 : rgba(1.0f / that.
a * that.r, 1.0f / that.
a * that.g, 1.0f / that.
a * that.
b, that.
a);
1387 static rgba
const clamped(rgba that)
1389 return rgba(std::min(std::max(that.r, 0.0f), 1.0f), std::min(std::max(that.g, 0.0f), 1.0f),
1390 std::min(std::max(that.b, 0.0f), 1.0f), std::min(std::max(that.a, 0.0f), 1.0f));
1394 static int unsigned_8(std::vector<unsigned char>&
data,
int index)
1396 return data[
static_cast<size_t>(
index)];
1398 static int signed_8(std::vector<unsigned char>&
data,
int index)
1400 size_t place =
static_cast<size_t>(
index);
1401 return static_cast<signed char>(
data[place]);
1403 static int unsigned_16(std::vector<unsigned char>&
data,
int index)
1405 size_t place =
static_cast<size_t>(
index);
1406 return data[place] << 8 |
data[place + 1];
1408 static int signed_16(std::vector<unsigned char>&
data,
int index)
1410 size_t place =
static_cast<size_t>(
index);
1411 return static_cast<short>(
data[place] << 8 |
data[place + 1]);
1413 static int signed_32(std::vector<unsigned char>&
data,
int index)
1415 size_t place =
static_cast<size_t>(
index);
1416 return (
data[place + 0] << 24 |
data[place + 1] << 16 |
data[place + 2] << 8 |
data[place + 3] << 0);
1436 static float const tolerance = 0.125f;
1437 float flatness = tolerance * tolerance;
1438 xy edge_1 = control_1 - point_1;
1439 xy edge_2 = control_2 - control_1;
1440 xy edge_3 = point_2 - control_2;
1441 xy segment = point_2 - point_1;
1442 float squared_1 = dot(edge_1, edge_1);
1443 float squared_2 = dot(edge_2, edge_2);
1444 float squared_3 = dot(edge_3, edge_3);
1445 static float const epsilon = 1.0e-4f;
1446 float length_squared = std::max(epsilon, dot(segment, segment));
1447 float projection_1 = dot(edge_1, segment) / length_squared;
1448 float projection_2 = dot(edge_3, segment) / length_squared;
1449 float clamped_1 = std::min(std::max(projection_1, 0.0f), 1.0f);
1450 float clamped_2 = std::min(std::max(projection_2, 0.0f), 1.0f);
1451 xy to_line_1 = point_1 + clamped_1 * segment - control_1;
1452 xy to_line_2 = point_2 - clamped_2 * segment - control_2;
1453 float cosine = 1.0f;
1454 if (angular > -1.0f)
1456 if (squared_1 * squared_3 != 0.0f)
1457 cosine = dot(edge_1, edge_3) / sqrtf(squared_1 * squared_3);
1458 else if (squared_1 * squared_2 != 0.0f)
1459 cosine = dot(edge_1, edge_2) / sqrtf(squared_1 * squared_2);
1460 else if (squared_2 * squared_3 != 0.0f)
1461 cosine = dot(edge_2, edge_3) / sqrtf(squared_2 * squared_3);
1463 if ((dot(to_line_1, to_line_1) <= flatness && dot(to_line_2, to_line_2) <= flatness && cosine >= angular) ||
1466 if (angular > -1.0f && squared_1 != 0.0f)
1468 if (angular > -1.0f && squared_2 != 0.0f)
1470 if (angular == -1.0f || squared_3 != 0.0f)
1474 xy left_1 = lerp(point_1, control_1, 0.5f);
1475 xy
middle = lerp(control_1, control_2, 0.5f);
1476 xy right_2 = lerp(control_2, point_2, 0.5f);
1477 xy left_2 = lerp(left_1,
middle, 0.5f);
1478 xy right_1 = lerp(
middle, right_2, 0.5f);
1479 xy split = lerp(left_2, right_1, 0.5f);
1493 void canvas::add_bezier(xy point_1, xy control_1, xy control_2, xy point_2,
float angular)
1495 xy edge_1 = control_1 - point_1;
1496 xy edge_2 = control_2 - control_1;
1497 xy edge_3 = point_2 - control_2;
1498 if (dot(edge_1, edge_1) == 0.0f && dot(edge_3, edge_3) == 0.0f)
1503 float at[7] = {0.0f, 1.0f};
1505 xy extrema_a = -9.0f * edge_2 + 3.0f * (point_2 - point_1);
1506 xy extrema_b = 6.0f * (point_1 + control_2) - 12.0f * control_1;
1507 xy extrema_c = 3.0f * edge_1;
1508 static float const epsilon = 1.0e-4f;
1509 if (fabsf(extrema_a.x) > epsilon)
1511 float discriminant = extrema_b.x * extrema_b.x - 4.0f * extrema_a.x * extrema_c.x;
1512 if (discriminant >= 0.0f)
1514 float sign = extrema_b.x > 0.0f ? 1.0f : -1.0f;
1515 float term = -extrema_b.x -
sign * sqrtf(discriminant);
1516 float extremum_1 = term / (2.0f * extrema_a.x);
1517 at[cuts++] = extremum_1;
1518 at[cuts++] = extrema_c.x / (extrema_a.x * extremum_1);
1521 else if (fabsf(extrema_b.x) > epsilon)
1522 at[cuts++] = -extrema_c.x / extrema_b.x;
1523 if (fabsf(extrema_a.y) > epsilon)
1525 float discriminant = extrema_b.y * extrema_b.y - 4.0f * extrema_a.y * extrema_c.y;
1526 if (discriminant >= 0.0f)
1528 float sign = extrema_b.y > 0.0f ? 1.0f : -1.0f;
1529 float term = -extrema_b.y -
sign * sqrtf(discriminant);
1530 float extremum_1 = term / (2.0f * extrema_a.y);
1531 at[cuts++] = extremum_1;
1532 at[cuts++] = extrema_c.y / (extrema_a.y * extremum_1);
1535 else if (fabsf(extrema_b.y) > epsilon)
1536 at[cuts++] = -extrema_c.y / extrema_b.y;
1537 float determinant_1 = dot(perpendicular(edge_1), edge_2);
1538 float determinant_2 = dot(perpendicular(edge_1), edge_3);
1539 float determinant_3 = dot(perpendicular(edge_2), edge_3);
1540 float curve_a = determinant_1 - determinant_2 + determinant_3;
1541 float curve_b = -2.0f * determinant_1 + determinant_2;
1542 if (fabsf(curve_a) > epsilon && fabsf(curve_b) > epsilon)
1543 at[cuts++] = -0.5f * curve_b / curve_a;
1547 int sorted =
index - 1;
1548 for (; 0 <= sorted &&
value <
at[sorted]; --sorted)
1549 at[sorted + 1] = at[sorted];
1552 xy split_point_1 = point_1;
1558 xy partial_1 = lerp(point_1, control_1, at[
index + 1]);
1559 xy partial_2 = lerp(control_1, control_2, at[
index + 1]);
1560 xy partial_3 = lerp(control_2, point_2, at[
index + 1]);
1561 xy partial_4 = lerp(partial_1, partial_2, at[
index + 1]);
1562 xy partial_5 = lerp(partial_2, partial_3, at[
index + 1]);
1563 xy partial_6 = lerp(partial_1, partial_4, ratio);
1564 xy split_point_2 = lerp(partial_4, partial_5, at[
index + 1]);
1565 xy split_control_2 = lerp(partial_4, split_point_2, ratio);
1566 xy split_control_1 = lerp(partial_6, split_control_2, ratio);
1567 add_tessellation(split_point_1, split_control_1, split_control_2, split_point_2, angular, 20);
1568 split_point_1 = split_point_2;
1580 static float const tolerance = 0.125f;
1581 float ratio = tolerance / std::max(0.5f *
line_width, tolerance);
1582 float angular = stroking ? (ratio - 2.0f) * ratio * 2.0f + 1.0f : -1.0f;
1587 for (
size_t subpath = 0; subpath <
path.
subpaths.size(); ++subpath)
1598 add_bezier(point_1, control_1, control_2, point_2, angular);
1619 : unsigned_16(
face.
data,
face.loca + glyph * 2 + 2) * 2);
1636 offset += flags & 1 ? 8 : 6;
1637 float a = flags & 200 ?
static_cast<float>(signed_16(
face.
data,
offset)) / 16384.0f : 1.0f;
1638 float b = flags & 128 ?
static_cast<float>(signed_16(
face.
data,
offset + 2)) / 16384.0f : 0.0f;
1639 float c = flags & 128 ?
static_cast<float>(signed_16(
face.
data,
offset + 4)) / 16384.0f : 0.0f;
1640 float d = flags & 8 ?
a
1641 : flags & 64 ?
static_cast<float>(signed_16(
face.
data,
offset + 2)) / 16384.0f
1642 : flags & 128 ? static_cast<float>(signed_16(
face.
data,
offset + 6)) / 16384.0f
1644 offset += flags & 8 ? 2 : flags & 64 ? 4 : flags & 128 ? 8 : 0;
1645 affine_matrix saved_forward =
forward;
1646 affine_matrix saved_inverse =
inverse;
1656 int left_side_bearing = glyph < hmetrics ? signed_16(
face.
data,
face.
hmtx + glyph * 4 + 2)
1657 : signed_16(
face.
data,
face.hmtx + hmetrics * 2 + glyph * 2);
1659 int points = unsigned_16(
face.
data,
offset + 8 + contours * 2) + 1;
1660 int instructions = unsigned_16(
face.
data,
offset + 10 + contours * 2);
1661 int flags_array =
offset + 12 + contours * 2 + instructions;
1666 int flags = unsigned_8(
face.
data, flags_array + flags_size++);
1667 int repeated = flags & 8 ? unsigned_8(
face.
data, flags_array + flags_size++) + 1 : 1;
1668 x_size += repeated * (flags & 2 ? 1 : flags & 16 ? 0 : 2);
1671 int x_array = flags_array + flags_size;
1672 int y_array = x_array + x_size;
1673 int x = left_side_bearing - x_min;
1678 for (
int contour = 0; contour < contours; ++contour)
1680 int beginning =
index;
1682 xy begin_point = xy(0.0f, 0.0f);
1683 bool begin_on =
false;
1684 xy end_point = xy(0.0f, 0.0f);
1685 bool end_on =
false;
1693 flags = unsigned_8(
face.
data, flags_array++);
1695 repeated = unsigned_8(
face.
data, flags_array++);
1698 x += (unsigned_8(
face.
data, x_array) * (flags & 16 ? 1 : -1));
1699 else if (!(flags & 16))
1700 x += signed_16(
face.
data, x_array);
1702 y += (unsigned_8(
face.
data, y_array) * (flags & 32 ? 1 : -1));
1703 else if (!(flags & 32))
1704 y += signed_16(
face.
data, y_array);
1705 x_array += flags & 2 ? 1 : flags & 16 ? 0 : 2;
1706 y_array += flags & 4 ? 1 : flags & 32 ? 0 : 2;
1707 xy point =
forward * xy(
static_cast<float>(x),
static_cast<float>(y));
1708 int on_curve = flags & 1;
1709 if (
index == beginning)
1711 begin_point = point;
1712 begin_on = on_curve;
1718 xy point_2 = on_curve ? point : lerp(end_point, point, 0.5f);
1719 if (
lines.
points.size() == first || (end_on && on_curve))
1721 else if (!end_on || on_curve)
1724 xy control_1 = lerp(point_1, end_point, 2.0f / 3.0f);
1725 xy control_2 = lerp(point_2, end_point, 2.0f / 3.0f);
1726 add_bezier(point_1, control_1, control_2, point_2, angular);
1732 if (begin_on ^ end_on)
1736 xy control = end_on ? begin_point : end_point;
1737 xy control_1 = lerp(point_1, control, 2.0f / 3.0f);
1738 xy control_2 = lerp(point_2, control, 2.0f / 3.0f);
1739 add_bezier(point_1, control_1, control_2, point_2, angular);
1741 else if (!begin_on && !end_on)
1744 xy split = lerp(begin_point, end_point, 0.5f);
1746 xy left_1 = lerp(point_1, end_point, 2.0f / 3.0f);
1747 xy left_2 = lerp(split, end_point, 2.0f / 3.0f);
1748 xy right_1 = lerp(split, begin_point, 2.0f / 3.0f);
1749 xy right_2 = lerp(point_2, begin_point, 2.0f / 3.0f);
1750 add_bezier(point_1, left_1, left_2, split, angular);
1751 add_bezier(split, right_1, right_2, point_2, angular);
1772 int bytes = ((text[
index] & 0x80) == 0x00 ? 1
1773 : (text[
index] & 0xe0) == 0xc0 ? 2
1774 : (text[
index] & 0xf0) == 0xe0 ? 3
1775 : (text[
index] & 0xf8) == 0xf0 ? 4
1777 int const masks[] = {0x0, 0x7f, 0x1f, 0x0f, 0x07};
1781 if ((text[
index] & 0xc0) == 0x80)
1782 codepoint = codepoint << 6 | (text[
index++] & 0x3f);
1788 if (codepoint ==
'\t' || codepoint ==
'\v' || codepoint ==
'\f' || codepoint ==
'\r' || codepoint ==
'\n')
1800 if (platform == 3 && encoding == 10 &&
format == 12)
1802 else if (platform == 3 && encoding == 1 &&
format == 4)
1809 int groups = signed_32(
face.
data, format_12 + 12);
1810 for (
int group = 0; group < groups; ++group)
1812 int start = signed_32(
face.
data, format_12 + 16 + group * 12);
1813 int end = signed_32(
face.
data, format_12 + 20 + group * 12);
1814 int glyph = signed_32(
face.
data, format_12 + 24 + group * 12);
1815 if (
start <= codepoint && codepoint <= end)
1816 return codepoint -
start + glyph;
1821 int segments = unsigned_16(
face.
data, format_4 + 6);
1822 int end_array = format_4 + 14;
1823 int start_array = end_array + 2 + segments;
1824 int delta_array = start_array + segments;
1825 int range_array = delta_array + segments;
1826 for (
int segment = 0; segment < segments; segment += 2)
1829 int end = unsigned_16(
face.
data, end_array + segment);
1830 int delta = signed_16(
face.
data, delta_array + segment);
1831 int range = unsigned_16(
face.
data, range_array + segment);
1832 if (
start <= codepoint && codepoint <= end)
1833 return range ? unsigned_16(
face.
data, range_array + segment + (codepoint -
start) * 2 + range)
1834 : (codepoint + delta) & 0xffff;
1837 else if (format_0 && 0 <= codepoint && codepoint < 256)
1838 return unsigned_8(
face.
data, format_0 + 6 + codepoint);
1855 float sxHeight = os2ver >= 2 ? (float)signed_16(
face.
data,
face.
os_2 + 86) : 0;
1857 ascent = (int)ceil(sTypoAscender *
face.
scale);
1858 descent = (int)ceil(-sTypoDescender *
face.
scale);
1868 x_height = (int)ceil(sxHeight *
face.
scale);
1880 static float const tolerance = 0.125f;
1881 float ratio = tolerance / std::max(0.5f *
line_width, tolerance);
1882 float angular = stroking ? (ratio - 2.0f) * ratio * 2.0f + 1.0f : -1.0f;
1885 if (
face.
data.empty() || !text || maximum_width <= 0.0f)
1888 float reduction = maximum_width / std::max(maximum_width,
width);
1890 position.x -=
width * reduction;
1892 position.x -= 0.5f *
width * reduction;
1893 xy scaling =
face.
scale * xy(reduction, 1.0f);
1894 float units_per_em =
static_cast<float>(unsigned_16(
face.
data,
face.
head + 18));
1896 float descender =
static_cast<float>(signed_16(
face.
data,
face.
os_2 + 70));
1900 position.y += (ascender - descender) * 0.5f *
face.
scale;
1904 position.y += 0.6f *
face.
scale * units_per_em;
1905 affine_matrix saved_forward =
forward;
1906 affine_matrix saved_inverse =
inverse;
1913 transform(scaling.x, 0.0f, 0.0f, -scaling.y, position.x +
static_cast<float>(place) * scaling.x,
1916 int entry = std::min(glyph, hmetrics - 1);
1952 for (
size_t subpath = 0; subpath <
scratch.
subpaths.size(); ++subpath)
1957 size_t segment =
start;
1958 bool emit = ~start & 1;
1961 bool merge_emit = emit;
1972 lines.
points.push_back(lerp(from, to, next / line));
1979 segment = segment + 1 <
line_dash.size() ? segment + 1 : 0;
1997 std::rotate((
lines.
points.begin() +
static_cast<ptrdiff_t
>(merge_point)),
2024 xy in_direction = xy(0.0f, 0.0f);
2025 float in_length = 0.0f;
2027 size_t finish = beginning;
2028 size_t index = beginning;
2032 xy out_direction = normalized(next - point);
2033 float out_length = length(next - point);
2034 static float const epsilon = 1.0e-4f;
2035 if (in_length != 0.0f && out_length >= epsilon)
2037 if (closed && finish == beginning)
2039 xy side_in = point + half * perpendicular(in_direction);
2040 xy side_out = point + half * perpendicular(out_direction);
2041 float turn = dot(perpendicular(in_direction), out_direction);
2042 if (fabsf(turn) < epsilon)
2044 xy
offset = turn == 0.0f ? xy(0.0f, 0.0f) : half / turn * (out_direction - in_direction);
2045 bool tight = (dot(
offset, in_direction) < -in_length && dot(
offset, out_direction) > out_length);
2046 if (turn > 0.0f && tight)
2048 std::swap(side_in, side_out);
2049 std::swap(in_direction, out_direction);
2058 float cosine = dot(in_direction, out_direction);
2059 float angle = acosf(std::min(std::max(cosine, -1.0f), 1.0f));
2060 float alpha = 4.0f / 3.0f * tanf(0.25f * angle);
2063 forward * (side_out - alpha * half * out_direction),
forward * side_out, -1.0f);
2070 if (turn > 0.0f && tight)
2075 std::swap(in_direction, out_direction);
2078 if (out_length >= epsilon)
2080 in_direction = out_direction;
2081 in_length = out_length;
2085 }
while (
index != finish);
2086 if (closed || in_length == 0.0f)
2088 xy ahead = half * in_direction;
2089 xy side = perpendicular(ahead);
2102 static float const alpha = 0.55228475f;
2105 forward * (point + ahead + alpha * side),
forward * (point + ahead), -1.0f);
2107 forward * (point - side + alpha * ahead),
forward * (point - side), -1.0f);
2131 for (
size_t subpath = 0; subpath <
scratch.
subpaths.size(); ++subpath)
2133 size_t beginning =
ending;
2135 if (
ending - beginning < 2)
2160 static float const epsilon = 2.0e-5f;
2161 if (fabsf(to.y - from.y) < epsilon)
2163 float sign = to.y > from.y ? 1.0f : -1.0f;
2165 std::swap(from, to);
2167 xy pixel = xy(floorf(
now.x), floorf(
now.y));
2168 xy corner = pixel + xy(1.0f, to.y > from.y ? 1.0f : 0.0f);
2169 xy slope = xy((to.x - from.x) / (to.y - from.y), (to.y - from.y) / (to.x - from.x));
2170 xy next_x = (to.x - from.x < epsilon) ? to : xy(corner.x,
now.y + (corner.x -
now.x) * slope.y);
2171 xy next_y = xy(
now.x + (corner.y -
now.y) * slope.x, corner.y);
2172 if ((from.y < to.y && to.y < next_y.y) || (from.y > to.y && to.y > next_y.y))
2174 float y_step = to.y > from.y ? 1.0f : -1.0f;
2178 while (next_x.x < next_y.x)
2180 float strip = std::min(std::max((next_x.y -
now.y) * y_step, 0.0f), 1.0f);
2181 float mid = (next_x.x +
now.x) * 0.5f;
2182 float area = (mid - pixel.x) * strip;
2183 pixel_run piece = {
static_cast<unsigned short>(pixel.x),
static_cast<unsigned short>(pixel.y),
2185 runs.push_back(piece);
2189 next_x.y = (next_x.x - from.x) * slope.y + from.y;
2192 float strip = std::min(std::max((next_y.y -
now.y) * y_step, 0.0f), 1.0f);
2193 float mid = (next_y.x +
now.x) * 0.5f;
2194 float area = (mid - pixel.x) * strip;
2195 pixel_run piece_1 = {
static_cast<unsigned short>(pixel.x),
static_cast<unsigned short>(pixel.y),
2197 pixel_run piece_2 = {
static_cast<unsigned short>(pixel.x + 1.0f),
static_cast<unsigned short>(pixel.y),
2199 runs.push_back(piece_1);
2200 runs.push_back(piece_2);
2203 next_y.x = (next_y.y - from.y) * slope.x + from.x;
2205 if ((from.y < to.y && to.y < next_y.y) || (from.y > to.y && to.y > next_y.y))
2207 }
while (
now.y != to.y);
2210 static bool operator<(pixel_run left, pixel_run right)
2212 return (left.y < right.y ?
true
2213 : left.y > right.y ? false
2214 : left.x < right.x ? true
2215 : left.x > right.x ? false
2216 : fabsf(left.delta) < fabsf(right.delta));
2232 float width =
static_cast<float>(
size_x + padding);
2235 for (
size_t subpath = 0; subpath <
lines.
subpaths.size(); ++subpath)
2237 size_t beginning =
ending;
2242 for (
int edge = 0; edge < 4; ++edge)
2244 xy normal = xy(edge == 0 ? 1.0f
2250 float place = edge == 2 ?
width : edge == 3 ?
height : 0.0f;
2256 float from_side = dot(from, normal) + place;
2257 float to_side = dot(to, normal) + place;
2258 if (from_side * to_side < 0.0f)
2259 scratch.
points.push_back(lerp(from, to, from_side / (from_side - to_side)));
2260 if (to_side >= 0.0f)
2270 add_runs(xy(std::min(std::max(from.x, 0.0f),
width), std::min(std::max(from.y, 0.0f),
height)),
2271 xy(std::min(std::max(to.x, 0.0f),
width), std::min(std::max(to.y, 0.0f),
height)));
2276 std::sort(
runs.begin(),
runs.end());
2278 for (
size_t from = 1; from <
runs.size(); ++from)
2280 runs[to].delta +=
runs[from].delta;
2281 else if (
runs[from].delta != 0.0f)
2283 runs.erase(
runs.begin() +
static_cast<ptrdiff_t
>(to) + 1,
runs.end());
2298 if (brush.colors.empty())
2299 return rgba(0.0f, 0.0f, 0.0f, 0.0f);
2301 return brush.colors.front();
2305 float width =
static_cast<float>(brush.width);
2306 float height =
static_cast<float>(brush.height);
2307 if (((brush.repetition & 2) && (point.x < 0.0f ||
width <= point.x)) ||
2308 ((brush.repetition & 1) && (point.y < 0.0f ||
height <= point.y)))
2309 return rgba(0.0f, 0.0f, 0.0f, 0.0f);
2312 scale_x = std::max(1.0f, std::min(scale_x,
width * 0.25f));
2313 scale_y = std::max(1.0f, std::min(scale_y,
height * 0.25f));
2314 float reciprocal_x = 1.0f / scale_x;
2315 float reciprocal_y = 1.0f / scale_y;
2316 point -= xy(0.5f, 0.5f);
2317 int left =
static_cast<int>(ceilf(point.x - scale_x * 2.0f));
2318 int top =
static_cast<int>(ceilf(point.y - scale_y * 2.0f));
2319 int right =
static_cast<int>(ceilf(point.x + scale_x * 2.0f));
2320 int bottom =
static_cast<int>(ceilf(point.y + scale_y * 2.0f));
2321 rgba total_color = rgba(0.0f, 0.0f, 0.0f, 0.0f);
2322 float total_weight = 0.0f;
2323 for (
int pattern_y =
top; pattern_y <
bottom; ++pattern_y)
2325 float y = fabsf(reciprocal_y * (
static_cast<float>(pattern_y) - point.y));
2327 (y < 1.0f ? (1.5f * y - 2.5f) * y * y + 1.0f : ((-0.5f * y + 2.5f) * y - 4.0f) * y + 2.0f);
2328 int wrapped_y = pattern_y % brush.height;
2330 wrapped_y += brush.height;
2332 wrapped_y = std::min(std::max(pattern_y, 0), brush.height - 1);
2333 for (
int pattern_x = left; pattern_x < right; ++pattern_x)
2335 float x = fabsf(reciprocal_x * (
static_cast<float>(pattern_x) - point.x));
2337 (x < 1.0f ? (1.5f * x - 2.5f) * x * x + 1.0f : ((-0.5f * x + 2.5f) * x - 4.0f) * x + 2.0f);
2338 int wrapped_x = pattern_x % brush.width;
2340 wrapped_x += brush.width;
2342 wrapped_x = std::min(std::max(pattern_x, 0), brush.width - 1);
2343 float weight = weight_x * weight_y;
2344 size_t index =
static_cast<size_t>(wrapped_y * brush.width + wrapped_x);
2345 total_color += weight * brush.colors[
index];
2346 total_weight += weight;
2349 return (1.0f / total_weight) * total_color;
2352 xy relative = point - brush.start;
2353 xy line = brush.end - brush.start;
2354 float gradient = dot(relative, line);
2355 float span = dot(line, line);
2359 return rgba(0.0f, 0.0f, 0.0f, 0.0f);
2364 float initial = brush.start_radius;
2365 float change = brush.end_radius - initial;
2366 float a =
span - change * change;
2367 float b = -2.0f * (gradient + initial * change);
2368 float c = dot(relative, relative) - initial * initial;
2369 float discriminant =
b *
b - 4.0f *
a * c;
2370 if (discriminant < 0.0f || (
span == 0.0f && change == 0.0f))
2371 return rgba(0.0f, 0.0f, 0.0f, 0.0f);
2372 float root = sqrtf(discriminant);
2373 float reciprocal = 1.0f / (2.0f *
a);
2374 float offset_1 = (-
b - root) * reciprocal;
2375 float offset_2 = (-
b + root) * reciprocal;
2376 float radius_1 = initial + change * offset_1;
2377 float radius_2 = initial + change * offset_2;
2378 if (radius_2 >= 0.0f)
2380 else if (radius_1 >= 0.0f)
2383 return rgba(0.0f, 0.0f, 0.0f, 0.0f);
2387 if (brush.css_radius.x == 0 || brush.css_radius.y == 0)
2391 xy rel = {relative.x / brush.css_radius.x, relative.y / brush.css_radius.y};
2397 float angle = 90 + direction(relative) * 180 /
pi;
2398 offset = normalize_angle(angle - brush.angle) / 360;
2401 static_cast<size_t>(std::upper_bound(brush.stops.begin(), brush.stops.end(),
offset) - brush.stops.begin());
2403 return premultiplied(brush.colors.front());
2404 if (
index == brush.stops.size())
2405 return premultiplied(brush.colors.back());
2410 std::optional<float> hint;
2411 } A = {brush.colors[
index - 1], brush.stops[
index - 1], brush.hints[
index - 1]},
2412 B = {brush.colors[
index], brush.stops[
index], {}};
2416 float H = !A.hint ? .5f : (*A.hint - A.stop) / (B.stop - A.stop);
2419 float P = (
offset - A.stop) / (B.stop - A.stop);
2421 float C = pow(P,
log(.5f) /
log(H));
2424 return premultiplied((1 - C) * A.color + C * B.color);
2448 size_t radius =
static_cast<size_t>(0.5f * sqrtf(4.0f * sigma_squared + 1.0f) - 0.5f);
2449 int border = 3 * (
static_cast<int>(radius) + 1);
2452 int left =
size_x + 2 * border;
2458 left = std::min(left,
static_cast<int>(
runs[
index].x));
2459 right = std::max(right,
static_cast<int>(
runs[
index].x));
2463 left = std::max(left - border, 0);
2464 right = std::min(right + border,
size_x + 2 * border) + 1;
2465 top = std::max(
top - border, 0);
2467 size_t width =
static_cast<size_t>(std::max(right - left, 0));
2472 static float const threshold = 1.0f / 8160.0f;
2480 float coverage = std::min(fabsf(sum), 1.0f);
2481 int to = next.y == y ? next.x : x + 1;
2482 if (coverage >= threshold)
2484 shadow[
static_cast<size_t>(y -
top) *
width +
static_cast<size_t>(x - left)] =
2486 paint_pixel(xy(
static_cast<float>(x) + 0.5f,
static_cast<float>(y) + 0.5f) -
offset, brush)
2495 float alpha =
static_cast<float>(2 * radius + 1) * (
static_cast<float>(radius * (radius + 1)) - sigma_squared) /
2496 (2.0f * sigma_squared -
static_cast<float>(6 * (radius + 1) * (radius + 1)));
2497 float divisor = 2.0f * (alpha +
static_cast<float>(radius)) + 1.0f;
2498 float weight_1 = alpha /
divisor;
2499 float weight_2 = (1.0f - alpha) / divisor;
2500 for (
size_t y = 0; y <
height; ++y)
2501 for (
int pass = 0; pass < 3; ++pass)
2503 for (
size_t x = 0; x <
width; ++x)
2505 float running = weight_1 *
shadow[working + radius + 1];
2506 for (
size_t x = 0; x <= radius; ++x)
2507 running += (weight_1 + weight_2) *
shadow[working + x];
2509 for (
size_t x = 1; x <
width; ++x)
2511 if (x >= radius + 1)
2512 running -= weight_2 *
shadow[working + x - radius - 1];
2513 if (x >= radius + 2)
2514 running -= weight_1 *
shadow[working + x - radius - 2];
2515 if (x + radius <
width)
2516 running += weight_2 *
shadow[working + x + radius];
2517 if (x + radius + 1 <
width)
2518 running += weight_1 *
shadow[working + x + radius + 1];
2522 for (
size_t x = 0; x <
width; ++x)
2523 for (
int pass = 0; pass < 3; ++pass)
2525 for (
size_t y = 0; y <
height; ++y)
2527 float running = weight_1 *
shadow[working + radius + 1];
2528 for (
size_t y = 0; y <= radius; ++y)
2529 running += (weight_1 + weight_2) *
shadow[working + y];
2531 for (
size_t y = 1; y <
height; ++y)
2533 if (y >= radius + 1)
2534 running -= weight_2 *
shadow[working + y - radius - 1];
2535 if (y >= radius + 2)
2536 running -= weight_1 *
shadow[working + y - radius - 2];
2538 running += weight_2 *
shadow[working + y + radius];
2539 if (y + radius + 1 <
height)
2540 running += weight_1 *
shadow[working + y + radius + 1];
2551 float visibility = std::min(fabsf(sum), 1.0f);
2552 int to = std::min(next.y == y ? next.x : x + 1, right - border);
2553 if (visibility >= threshold &&
top <= y + border && y + border <
bottom)
2559 shadow[
static_cast<size_t>(y + border -
top) *
width +
static_cast<size_t>(x + border - left)] *
2561 float mix_fore = operation & 1 ? back.
a : 0.0f;
2563 mix_fore = 1.0f - mix_fore;
2564 float mix_back = operation & 4 ? fore.a : 0.0f;
2566 mix_back = 1.0f - mix_back;
2567 rgba blend = mix_fore * fore + mix_back * back;
2568 blend.a = std::min(blend.a, 1.0f);
2573 x = std::max(
static_cast<int>(next.x), left - border);
2598 float path_sum = 0.0f;
2599 float clip_sum = 0.0f;
2600 size_t path_index = 0;
2601 size_t clip_index = 0;
2602 while (clip_index <
mask.size())
2604 bool which = (path_index <
runs.size() &&
runs[path_index] <
mask[clip_index]);
2605 pixel_run next = which ?
runs[path_index] :
mask[clip_index];
2606 float coverage = std::min(fabsf(path_sum), 1.0f);
2607 float visibility = std::min(fabsf(clip_sum), 1.0f);
2608 int to = next.y == y ? next.x : x + 1;
2609 static float const threshold = 1.0f / 8160.0f;
2610 if ((coverage >= threshold || ~operation & 8) && visibility >= threshold)
2615 paint_pixel(xy(
static_cast<float>(x) + 0.5f,
static_cast<float>(y) + 0.5f), brush);
2616 float mix_fore = operation & 1 ? back.a : 0.0f;
2618 mix_fore = 1.0f - mix_fore;
2619 float mix_back = operation & 4 ? fore.a : 0.0f;
2621 mix_back = 1.0f - mix_back;
2622 rgba blend = mix_fore * fore + mix_back * back;
2623 blend.a = std::min(blend.a, 1.0f);
2634 path_sum +=
runs[path_index++].delta;
2636 clip_sum +=
mask[clip_index++].delta;
2641 : global_composite_operation(
source_over), shadow_offset_x(0.0f), shadow_offset_y(0.0f), line_cap(
butt),
2643 size_y(height), global_alpha(1.0f), shadow_blur(0.0f), line_width(1.0f), miter_limit(10.0f), fill_brush(),
2644 stroke_brush(), image_brush(), face(), bitmap(new rgba[width * height]), saves(0)
2646 affine_matrix identity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
2651 for (
unsigned short y = 0; y <
size_y; ++y)
2653 pixel_run piece_1 = {0, y, 1.0f};
2654 pixel_run piece_2 = {
static_cast<unsigned short>(
size_x), y, -1.0f};
2655 mask.push_back(piece_1);
2656 mask.push_back(piece_2);
2681 transform(x, 0.0f, 0.0f, y, 0.0f, 0.0f);
2686 float cosine = cosf(angle);
2687 float sine = sinf(angle);
2688 transform(cosine, sine, -sine, cosine, 0.0f, 0.0f);
2693 transform(1.0f, 0.0f, 0.0f, 1.0f, x, y);
2705 float determinant =
a * d -
b * c;
2706 float scaling = determinant != 0.0f ? 1.0f / determinant : 0.0f;
2707 affine_matrix new_forward = {
a,
b, c, d, e, f};
2708 affine_matrix new_inverse = {
2709 scaling * d, scaling * -
b, scaling * -c, scaling *
a, scaling * (c * f - d * e), scaling * (
b * e -
a * f)};
2716 if (0.0f <= alpha && alpha <= 1.0f)
2746 if (segments && segments[
index] < 0.0f)
2762 brush.colors.clear();
2763 brush.colors.push_back(premultiplied(linearized(clamped(rgba(
red,
green,
blue, alpha)))));
2770 brush.colors.clear();
2771 brush.stops.clear();
2772 brush.hints.clear();
2773 brush.start = xy(start_x, start_y);
2774 brush.end = xy(end_x, end_y);
2778 brush_type type,
float start_x,
float start_y,
float start_radius,
float end_x,
float end_y,
float end_radius)
2780 if (start_radius < 0.0f || end_radius < 0.0f)
2784 brush.colors.clear();
2785 brush.stops.clear();
2786 brush.hints.clear();
2787 brush.start = xy(start_x, start_y);
2788 brush.end = xy(end_x, end_y);
2789 brush.start_radius = start_radius;
2790 brush.end_radius = end_radius;
2795 if (radius_x < 0.0f || radius_y < 0.0f)
2799 brush.colors.clear();
2800 brush.stops.clear();
2801 brush.hints.clear();
2802 brush.start = {x, y};
2803 brush.css_radius = {radius_x, radius_y};
2813 brush.start = {x, y};
2814 brush.angle = angle;
2825 ptrdiff_t
index = std::upper_bound(brush.stops.begin(), brush.stops.end(),
offset) - brush.
stops.begin();
2827 brush.colors.insert(brush.colors.begin() +
index,
color);
2828 brush.stops.insert(brush.stops.begin() +
index,
offset);
2829 brush.hints.insert(brush.hints.begin() +
index, hint);
2839 brush.colors.clear();
2840 for (
int y = 0; y <
height; ++y)
2841 for (
int x = 0; x <
width; ++x)
2843 int index = y * stride + x * 4;
2846 brush.colors.push_back(premultiplied(linearized(
color)));
2848 brush.width =
width;
2850 brush.repetition = repetition;
2866 subpath_data subpath = {1,
false};
2876 affine_matrix saved_forward =
forward;
2877 affine_matrix identity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
2893 xy point_2 =
forward * xy(x, y);
2894 if (dot(point_2 - point_1, point_2 - point_1) == 0.0f)
2905 move_to(control_x, control_y);
2907 xy control =
forward * xy(control_x, control_y);
2908 xy point_2 =
forward * xy(x, y);
2909 xy control_1 = lerp(point_1, control, 2.0f / 3.0f);
2910 xy control_2 = lerp(point_2, control, 2.0f / 3.0f);
2918 float control_1_x,
float control_1_y,
float control_2_x,
float control_2_y,
float x,
float y)
2921 move_to(control_1_x, control_1_y);
2922 xy control_1 =
forward * xy(control_1_x, control_1_y);
2923 xy control_2 =
forward * xy(control_2_x, control_2_y);
2924 xy point_2 =
forward * xy(x, y);
2931 void canvas::arc_to(
float vertex_x,
float vertex_y,
float x,
float y,
float radius)
2938 xy vertex = xy(vertex_x, vertex_y);
2939 xy point_2 = xy(x, y);
2940 xy edge_1 = normalized(point_1 - vertex);
2941 xy edge_2 = normalized(point_2 - vertex);
2942 float sine = fabsf(dot(perpendicular(edge_1), edge_2));
2943 static float const epsilon = 1.0e-4f;
2949 xy
offset = radius / sine * (edge_1 + edge_2);
2951 float angle_1 = direction(dot(
offset, edge_1) * edge_1 -
offset);
2952 float angle_2 = direction(dot(
offset, edge_2) * edge_2 -
offset);
2953 bool reverse =
static_cast<int>(floorf((angle_2 - angle_1) / pi)) & 1;
2957 void canvas::arc(
float x,
float y,
float radius,
float start_angle,
float end_angle,
bool counter_clockwise)
2961 static float const tau = 2 *
pi;
2962 float winding = counter_clockwise ? -1.0f : 1.0f;
2963 float from = fmodf(start_angle, tau);
2964 float span = fmodf(end_angle, tau) - from;
2965 if ((end_angle - start_angle) * winding >= tau)
2966 span = tau * winding;
2967 else if (
span * winding < 0.0f)
2968 span += tau * winding;
2969 xy centered_1 = radius * xy(cosf(from), sinf(from));
2970 line_to(x + centered_1.x, y + centered_1.y);
2973 int steps =
static_cast<int>(std::max(1.0f, roundf(16.0f / tau *
span * winding)));
2974 float segment =
span /
static_cast<float>(steps);
2975 float alpha = 4.0f / 3.0f * tanf(0.25f * segment);
2976 for (
int step = 0; step < steps; ++step)
2978 float angle = from +
static_cast<float>(step + 1) * segment;
2979 xy centered_2 = radius * xy(cosf(angle), sinf(angle));
2980 xy point_1 = xy(x, y) + centered_1;
2981 xy point_2 = xy(x, y) + centered_2;
2982 xy control_1 = point_1 + alpha * perpendicular(centered_1);
2983 xy control_2 = point_2 - alpha * perpendicular(centered_2);
2984 bezier_curve_to(control_1.x, control_1.y, control_2.x, control_2.y, point_2.x, point_2.y);
2985 centered_1 = centered_2;
3000 move_to(points[0].x, points[0].y);
3001 for (
auto pt : points)
3023 size_t part =
runs.size();
3031 size_t index_2 = part;
3032 while (index_1 < part && index_2 <
runs.size())
3034 bool which =
runs[index_1] <
runs[index_2];
3035 pixel_run next = which ?
runs[index_1] :
runs[index_2];
3044 sum_1 +=
runs[index_1++].delta;
3046 sum_2 +=
runs[index_2++].delta;
3047 float visibility = (std::min(fabsf(sum_1), 1.0f) * std::min(fabsf(sum_2), 1.0f));
3048 if (visibility == last)
3050 if (!
mask.empty() &&
mask.back().x == next.x &&
mask.back().y == next.y)
3054 pixel_run piece = {next.x, next.y,
visibility - last};
3055 mask.push_back(piece);
3066 size_t beginning = 0;
3077 if ((from.y < y && y <= to.y) || (to.y < y && y <= from.y))
3079 float side = dot(perpendicular(to - from), xy(x, y) - from);
3082 winding += side > 0.0f ? 1 : -1;
3084 else if (from.y == y && y == to.y && ((from.x <= x && x <= to.x) || (to.x <= x && x <= from.x)))
3117 subpath_data entry = {4,
true};
3132 subpath_data entry = {2,
false};
3142 subpath_data entry = {5,
true};
3164 int version = (
font[0] << 24 |
font[1] << 16 |
font[2] << 8 |
font[3] << 0);
3165 int tables =
font[4] << 8 |
font[5];
3166 if ((version != 0x00010000 && version != 0x74727565) ||
bytes < tables * 16 + 12)
3179 int place =
static_cast<int>(
face.
data.size());
3180 if (tag == 0x636d6170)
3182 else if (tag == 0x676c7966)
3184 else if (tag == 0x68656164)
3186 else if (tag == 0x68686561)
3188 else if (tag == 0x686d7478)
3190 else if (tag == 0x6c6f6361)
3192 else if (tag == 0x6d617870)
3194 else if (tag == 0x4f532f32)
3210 face.
scale = size /
static_cast<float>(units_per_em);
3236 int entry = std::min(glyph, hmetrics - 1);
3251 if (!
image ||
width <= 0 ||
height <= 0 || to_width == 0.0f || to_height == 0.0f)
3262 subpath_data entry = {4,
true};
3264 affine_matrix saved_forward =
forward;
3265 affine_matrix saved_inverse =
inverse;
3266 translate(x + std::min(0.0f, to_width), y + std::min(0.0f, to_height));
3267 scale(fabsf(to_width) /
static_cast<float>(
width), fabsf(to_height) /
static_cast<float>(
height));
3277 static float const bayer[][4] = {{0.03125f, 0.53125f, 0.15625f, 0.65625f},
3278 {0.78125f, 0.28125f, 0.90625f, 0.40625f},
3279 {0.21875f, 0.71875f, 0.09375f, 0.59375f},
3280 {0.96875f, 0.46875f, 0.84375f, 0.34375f}};
3281 for (
int image_y = 0; image_y <
height; ++image_y)
3282 for (
int image_x = 0; image_x <
width; ++image_x)
3284 int index = image_y * stride + image_x * 4;
3285 int canvas_x = x + image_x;
3286 int canvas_y = y + image_y;
3287 rgba
color = rgba(0.0f, 0.0f, 0.0f, 0.0f);
3288 if (0 <= canvas_x && canvas_x <
size_x && 0 <= canvas_y && canvas_y <
size_y)
3290 float threshold = bayer[canvas_y & 3][canvas_x & 3];
3291 color = rgba(threshold, threshold, threshold, threshold) +
3292 255.0f * delinearized(clamped(unpremultiplied(
color)));
3304 for (
int image_y = 0; image_y <
height; ++image_y)
3305 for (
int image_x = 0; image_x <
width; ++image_x)
3307 int index = image_y * stride + image_x * 4;
3308 int canvas_x = x + image_x;
3309 int canvas_y = y + image_y;
3310 if (canvas_x < 0 ||
size_x <= canvas_x || canvas_y < 0 ||
size_y <= canvas_y)
3341 state->saves =
saves;
Definition canvas_ity.hpp:276
void rectangle(float x, float y, float width, float height)
Add a closed subpath in the shape of a rectangle.
void set_global_alpha(float alpha)
Set the degree of opacity applied to all drawing operations.
void put_image_data(unsigned char const *image, int width, int height, int stride, int x, int y)
Replace a rectangle of pixels on the canvas with an image.
void stroke_text(char const *text, float x, float y, float maximum_width=1.0e30f)
Draw a line of text by stroking its outline.
void render_shadow(paint_brush const &)
paint_brush image_brush
Definition canvas_ity.hpp:1181
void get_image_data(unsigned char *image, int width, int height, int stride, int x, int y)
Fetch a rectangle of pixels from the canvas to an image.
void set_pattern(brush_type type, unsigned char const *image, int width, int height, int stride, repetition_style repetition)
Set filling or stroking to draw with an image pattern.
bool set_font(unsigned char const *font, int bytes, float size)
Set the font to use for text drawing.
canvas * saves
Definition canvas_ity.hpp:1189
void stroke_rectangle(float x, float y, float width, float height)
Stroke a rectangular area.
canvas()
Definition canvas_ity.hpp:295
void set_shadow_blur(float level)
Set the level of Gaussian blurring on the shadow.
void move_to(float x, float y)
Create a new subpath.
float miter_limit
Definition canvas_ity.hpp:1177
cap_style line_cap
Cap style for the ends of open subpaths and dash segments.
Definition canvas_ity.hpp:481
void set_miter_limit(float limit)
Set the limit on maximum pointiness allowed for miter joins.
std::vector< float > line_dash
Definition canvas_ity.hpp:1178
void clip()
Restrict the clip region by the current path.
baseline_style text_baseline
Vertical position of the text relative to the anchor point.
Definition canvas_ity.hpp:968
rgba paint_pixel(xy, paint_brush const &)
void rotate(float angle)
Rotate the current transform.
void set_line_width(float width)
Set the width of the lines when stroking.
void set_css_radial_gradient(brush_type type, float x, float y, float radius_x, float radius_y)
void polygon(std::vector< xy > points)
void draw_image(unsigned char const *image, int width, int height, int stride, float x, float y, float to_width, float to_height)
Draw an image onto the canvas.
void set_shadow_color(float red, float green, float blue, float alpha)
Set the color and opacity of the shadow.
void text_to_lines(char const *, xy, float, bool)
void get_font_metrics(int &ascent, int &descent, int &height, int &x_height)
pixel_runs runs
Definition canvas_ity.hpp:1185
void save()
Save the current state as though to a stack.
void add_bezier(xy, xy, xy, xy, float)
void bezier_curve_to(float control_1_x, float control_1_y, float control_2_x, float control_2_y, float x, float y)
Extend the current subpath with a cubic Bezier curve.
float global_alpha
Definition canvas_ity.hpp:1172
void scale(float x, float y)
Scale the current transform.
void arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise=false)
Extend the current subpath with an arc between two angles.
align_style text_align
Horizontal position of the text relative to the anchor point.
Definition canvas_ity.hpp:955
void restore()
Restore a previously saved state as though from a stack.
void add_glyph(int, float)
void set_line_dash(float const *segments, int count)
Set or clear the line dash pattern.
void set_conic_gradient(brush_type type, float x, float y, float angle)
void render_main(paint_brush const &)
canvas(int width, int height, rgba color)
paint_brush fill_brush
Definition canvas_ity.hpp:1179
float shadow_blur
Definition canvas_ity.hpp:1174
~canvas()
Destroy the canvas and release all associated memory.
void lines_to_runs(xy, int)
paint_brush stroke_brush
Definition canvas_ity.hpp:1180
float line_width
Definition canvas_ity.hpp:1176
void add_color_stop(brush_type type, float offset, float red, float green, float blue, float alpha, std::optional< float > hint={})
Add a color stop to a linear or radial gradient.
join_style line_join
Join style for connecting lines within the paths.
Definition canvas_ity.hpp:492
void transform(float a, float b, float c, float d, float e, float f)
Add an arbitrary transform to the current transform.
void fill_text(char const *text, float x, float y, float maximum_width=1.0e30f)
Draw a line of text by filling its outline.
void begin_path()
Reset the current path.
float line_dash_offset
Offset where each subpath starts the dash pattern.
Definition canvas_ity.hpp:516
void fill_rectangle(float x, float y, float width, float height)
Fill a rectangular area.
int size_x
Definition canvas_ity.hpp:1168
void add_half_stroke(size_t, size_t, bool)
void close_path()
Close the current subpath.
std::vector< float > shadow
Definition canvas_ity.hpp:1175
void translate(float x, float y)
Translate the current transform.
void add_tessellation(xy, xy, xy, xy, float, int)
void set_color(brush_type type, rgba c)
Definition canvas_ity.hpp:554
void set_color(brush_type type, float red, float green, float blue, float alpha)
Set filling or stroking to use a constant color and opacity.
affine_matrix forward
Definition canvas_ity.hpp:1170
int character_to_glyph(char const *, int &)
bool is_point_in_path(float x, float y)
Tests whether a point is in or on the current path.
void line_to(float x, float y)
Extend the current subpath with a straight line.
int height()
Definition canvas_ity.hpp:1143
float measure_text(char const *text)
Measure the width in pixels of a line of text.
rgba shadow_color
Definition canvas_ity.hpp:1173
int width()
Definition canvas_ity.hpp:1139
void set_radial_gradient(brush_type type, float start_x, float start_y, float start_radius, float end_x, float end_y, float end_radius)
Set filling or stroking to use a radial gradient.
void set_linear_gradient(brush_type type, float start_x, float start_y, float end_x, float end_y)
Set filling or stroking to use a linear gradient.
void arc_to(float vertex_x, float vertex_y, float x, float y, float radius)
Extend the current subpath with an arc tangent to two lines.
affine_matrix inverse
Definition canvas_ity.hpp:1171
int size_y
Definition canvas_ity.hpp:1169
font_face face
Definition canvas_ity.hpp:1187
void set_transform(float a, float b, float c, float d, float e, float f)
Replace the current transform.
void quadratic_curve_to(float control_x, float control_y, float x, float y)
Extend the current subpath with a quadratic Bezier curve.
canvas & operator=(canvas const &)
bezier_path path
Definition canvas_ity.hpp:1182
float shadow_offset_y
Vertical offset of the shadow in pixels.
Definition canvas_ity.hpp:447
composite_operation global_composite_operation
Compositing operation for blending new drawing and old pixels.
Definition canvas_ity.hpp:413
void stroke()
Draw the edges of the current path using the stroke style.
void fill()
Draw the interior of the current path using the fill style.
canvas(int width, int height)
Construct a new canvas.
pixel_runs mask
Definition canvas_ity.hpp:1186
line_path scratch
Definition canvas_ity.hpp:1184
rgba * bitmap
Definition canvas_ity.hpp:1188
line_path lines
Definition canvas_ity.hpp:1183
float shadow_offset_x
Horizontal offset of the shadow in pixels.
Definition canvas_ity.hpp:440
void clear_rectangle(float x, float y, float width, float height)
Clear a rectangular area back to transparent black.
std::basic_string< Char > format(const text_style &ts, const S &format_str, const Args &... args)
Definition color.h:646
constexpr auto count() -> size_t
Definition core.h:1538
type
Definition core.h:681
#define offset(member)
Definition css_properties.cpp:5
brush_type
Definition canvas_ity.hpp:180
@ fill_style
Definition canvas_ity.hpp:181
@ stroke_style
Definition canvas_ity.hpp:182
baseline_style
Definition canvas_ity.hpp:200
@ middle
Definition canvas_ity.hpp:203
@ alphabetic
Definition canvas_ity.hpp:201
@ hanging
Definition canvas_ity.hpp:205
@ ideographic
Definition canvas_ity.hpp:206
@ top
Definition canvas_ity.hpp:202
@ bottom
Definition canvas_ity.hpp:204
align_style
Definition canvas_ity.hpp:192
@ leftward
Definition canvas_ity.hpp:193
@ rightward
Definition canvas_ity.hpp:194
@ center
Definition canvas_ity.hpp:195
@ start
Definition canvas_ity.hpp:196
@ ending
Definition canvas_ity.hpp:197
composite_operation
Definition canvas_ity.hpp:148
@ exclusive_or
Definition canvas_ity.hpp:165
@ source_out
Definition canvas_ity.hpp:151
@ lighter
Definition canvas_ity.hpp:160
@ source_over
Definition canvas_ity.hpp:164
@ destination_out
Definition canvas_ity.hpp:162
@ source_atop
Definition canvas_ity.hpp:163
@ source_copy
Definition canvas_ity.hpp:150
@ destination_atop
Definition canvas_ity.hpp:153
@ source_in
Definition canvas_ity.hpp:149
@ destination_over
Definition canvas_ity.hpp:161
@ destination_in
Definition canvas_ity.hpp:152
cap_style
Definition canvas_ity.hpp:168
@ square
Definition canvas_ity.hpp:170
@ circle
Definition canvas_ity.hpp:171
@ butt
Definition canvas_ity.hpp:169
join_style
Definition canvas_ity.hpp:174
@ miter
Definition canvas_ity.hpp:175
@ bevel
Definition canvas_ity.hpp:176
@ rounded
Definition canvas_ity.hpp:177
repetition_style
Definition canvas_ity.hpp:185
@ repeat_y
Definition canvas_ity.hpp:188
@ repeat
Definition canvas_ity.hpp:186
@ repeat_x
Definition canvas_ity.hpp:187
@ no_repeat
Definition canvas_ity.hpp:189
std::vector< pixel_run > pixel_runs
Definition canvas_ity.hpp:273
uint32_t divisor
Definition format-inl.h:268
const T & first(const T &value, const Tail &...)
Definition compile.h:179
const float pi
Definition gradient.cpp:350
bool operator<(const css_selector &v1, const css_selector &v2)
Definition css_selector.h:237
const T & at(const vector< T > &vec, int index)
Definition html.h:50
encoding
Definition encodings.h:9
vector< T > & operator+=(vector< T > &vec, const vector< T > &x)
Definition html.h:88
visibility
Definition types.h:624
bool end(const css_token_vector &tokens, int index)
Definition gradient.cpp:78
text_align
Definition types.h:572
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
Definition os-inl.h:76
void log(source_loc source, level::level_enum lvl, format_string_t< Args... > fmt, Args &&... args)
Definition spdlog.h:145
Definition canvas_ity.hpp:223
float a
Definition canvas_ity.hpp:224
float b
Definition canvas_ity.hpp:224
float e
Definition canvas_ity.hpp:224
float f
Definition canvas_ity.hpp:224
float d
Definition canvas_ity.hpp:224
float c
Definition canvas_ity.hpp:224
Definition canvas_ity.hpp:259
std::vector< xy > points
Definition canvas_ity.hpp:260
std::vector< subpath_data > subpaths
Definition canvas_ity.hpp:261
Definition canvas_ity.hpp:248
int maxp
Definition canvas_ity.hpp:250
std::vector< unsigned char > data
Definition canvas_ity.hpp:249
int hhea
Definition canvas_ity.hpp:250
int loca
Definition canvas_ity.hpp:250
int os_2
Definition canvas_ity.hpp:250
int hmtx
Definition canvas_ity.hpp:250
int cmap
Definition canvas_ity.hpp:250
int glyf
Definition canvas_ity.hpp:250
int head
Definition canvas_ity.hpp:250
float scale
Definition canvas_ity.hpp:251
Definition canvas_ity.hpp:264
std::vector< xy > points
Definition canvas_ity.hpp:265
std::vector< subpath_data > subpaths
Definition canvas_ity.hpp:266
Definition canvas_ity.hpp:227
xy end
Definition canvas_ity.hpp:240
xy start
Definition canvas_ity.hpp:240
int height
Definition canvas_ity.hpp:244
xy css_radius
Definition canvas_ity.hpp:242
float angle
Definition canvas_ity.hpp:243
float end_radius
Definition canvas_ity.hpp:241
int width
Definition canvas_ity.hpp:244
float start_radius
Definition canvas_ity.hpp:241
repetition_style repetition
Definition canvas_ity.hpp:245
std::vector< float > stops
Definition canvas_ity.hpp:238
std::vector< rgba > colors
Definition canvas_ity.hpp:237
types
Definition canvas_ity.hpp:229
@ radial
Definition canvas_ity.hpp:233
@ pattern
Definition canvas_ity.hpp:231
@ conic
Definition canvas_ity.hpp:235
@ linear
Definition canvas_ity.hpp:232
@ css_radial
Definition canvas_ity.hpp:234
@ color
Definition canvas_ity.hpp:230
std::vector< std::optional< float > > hints
Definition canvas_ity.hpp:239
enum canvas_ity::paint_brush::types type
Definition canvas_ity.hpp:269
float delta
Definition canvas_ity.hpp:271
unsigned short x
Definition canvas_ity.hpp:270
unsigned short y
Definition canvas_ity.hpp:270
Definition canvas_ity.hpp:217
float r
Definition canvas_ity.hpp:218
rgba(float, float, float, float)
float a
Definition canvas_ity.hpp:218
float g
Definition canvas_ity.hpp:218
float b
Definition canvas_ity.hpp:218
Definition canvas_ity.hpp:254
bool closed
Definition canvas_ity.hpp:256
size_t count
Definition canvas_ity.hpp:255
Definition canvas_ity.hpp:211
float x
Definition canvas_ity.hpp:212
float y
Definition canvas_ity.hpp:212
byte a
Definition Bitmap.h:11
byte b
Definition Bitmap.h:11
byte r
Definition Bitmap.h:11
byte g
Definition Bitmap.h:11
b
Definition tag_strings.h:61
a
Definition tag_strings.h:43
image
Definition tag_strings.h:74
area
Definition tag_strings.h:87
head
Definition tag_strings.h:5
span
Definition tag_strings.h:69
annotation font
Definition tag_strings.h:148
canvas
Definition tag_strings.h:85
annotation table
Definition tag_strings.h:100