mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-25 21:41:03 +00:00
Add path rasterizer
This commit is contained in:
parent
c446af41ae
commit
ab07343985
3 changed files with 707 additions and 0 deletions
|
@ -9,6 +9,7 @@ set(ZWIDGET_SOURCES
|
|||
src/core/timer.cpp
|
||||
src/core/widget.cpp
|
||||
src/core/utf8reader.cpp
|
||||
src/core/pathfill.cpp
|
||||
src/core/schrift/schrift.cpp
|
||||
src/core/schrift/schrift.h
|
||||
src/core/picopng/picopng.cpp
|
||||
|
@ -38,6 +39,7 @@ set(ZWIDGET_INCLUDES
|
|||
include/zwidget/core/font.h
|
||||
include/zwidget/core/image.h
|
||||
include/zwidget/core/rect.h
|
||||
include/zwidget/core/pathfill.h
|
||||
include/zwidget/core/span_layout.h
|
||||
include/zwidget/core/timer.h
|
||||
include/zwidget/core/widget.h
|
||||
|
|
93
libraries/ZWidget/include/zwidget/core/pathfill.h
Normal file
93
libraries/ZWidget/include/zwidget/core/pathfill.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "core/rect.h"
|
||||
|
||||
/*
|
||||
// 3x3 transform matrix
|
||||
class PathFillTransform
|
||||
{
|
||||
public:
|
||||
PathFillTransform() { for (int i = 0; i < 9; i++) matrix[i] = 0.0f; matrix[0] = matrix[4] = matrix[8] = 1.0; }
|
||||
PathFillTransform(const float* mat3x3) { for (int i = 0; i < 9; i++) matrix[i] = (double)mat3x3[i]; }
|
||||
PathFillTransform(const double* mat3x3) { for (int i = 0; i < 9; i++) matrix[i] = mat3x3[i]; }
|
||||
|
||||
double matrix[9];
|
||||
};
|
||||
*/
|
||||
|
||||
enum class PathFillMode
|
||||
{
|
||||
alternate,
|
||||
winding
|
||||
};
|
||||
|
||||
enum class PathFillCommand
|
||||
{
|
||||
line,
|
||||
quadradic,
|
||||
cubic
|
||||
};
|
||||
|
||||
class PathFillSubpath
|
||||
{
|
||||
public:
|
||||
PathFillSubpath() : points(1) { }
|
||||
|
||||
std::vector<Point> points;
|
||||
std::vector<PathFillCommand> commands;
|
||||
bool closed = false;
|
||||
};
|
||||
|
||||
class PathFillDesc
|
||||
{
|
||||
public:
|
||||
PathFillDesc() : subpaths(1) { }
|
||||
|
||||
PathFillMode fill_mode = PathFillMode::alternate;
|
||||
std::vector<PathFillSubpath> subpaths;
|
||||
|
||||
void MoveTo(const Point& point)
|
||||
{
|
||||
if (!subpaths.back().commands.empty())
|
||||
{
|
||||
subpaths.push_back(PathFillSubpath());
|
||||
}
|
||||
subpaths.back().points.front() = point;
|
||||
}
|
||||
|
||||
void LineTo(const Point& point)
|
||||
{
|
||||
auto& subpath = subpaths.back();
|
||||
subpath.points.push_back(point);
|
||||
subpath.commands.push_back(PathFillCommand::line);
|
||||
}
|
||||
|
||||
void BezierTo(const Point& control, const Point& point)
|
||||
{
|
||||
auto& subpath = subpaths.back();
|
||||
subpath.points.push_back(control);
|
||||
subpath.points.push_back(point);
|
||||
subpath.commands.push_back(PathFillCommand::quadradic);
|
||||
}
|
||||
|
||||
void BezierTo(const Point& control1, const Point& control2, const Point& point)
|
||||
{
|
||||
auto& subpath = subpaths.back();
|
||||
subpath.points.push_back(control1);
|
||||
subpath.points.push_back(control2);
|
||||
subpath.points.push_back(point);
|
||||
subpath.commands.push_back(PathFillCommand::cubic);
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
if (!subpaths.back().commands.empty())
|
||||
{
|
||||
subpaths.back().closed = true;
|
||||
subpaths.push_back(PathFillSubpath());
|
||||
}
|
||||
}
|
||||
|
||||
void Rasterize(uint8_t* dest, int width, int height, bool blend = false);
|
||||
};
|
612
libraries/ZWidget/src/core/pathfill.cpp
Normal file
612
libraries/ZWidget/src/core/pathfill.cpp
Normal file
|
@ -0,0 +1,612 @@
|
|||
|
||||
#include "core/pathfill.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
static const int AntialiasLevel = 8;
|
||||
static const int MaskBlockSize = 16;
|
||||
static const int MaskBufferSize = MaskBlockSize * MaskBlockSize;
|
||||
static const int ScanlineBlockSize = MaskBlockSize * AntialiasLevel;
|
||||
|
||||
class PathScanlineEdge
|
||||
{
|
||||
public:
|
||||
PathScanlineEdge() = default;
|
||||
PathScanlineEdge(double x, bool up_direction) : x(x), up_direction(up_direction) { }
|
||||
|
||||
double x = 0.0;
|
||||
bool up_direction = false;
|
||||
};
|
||||
|
||||
class PathScanline
|
||||
{
|
||||
public:
|
||||
std::vector<PathScanlineEdge> edges;
|
||||
std::vector<unsigned char> pixels;
|
||||
|
||||
void insert_sorted(PathScanlineEdge edge)
|
||||
{
|
||||
edges.push_back(edge);
|
||||
|
||||
for (size_t pos = edges.size() - 1; pos > 0 && edges[pos - 1].x >= edge.x; pos--)
|
||||
{
|
||||
PathScanlineEdge temp = edges[pos - 1];
|
||||
edges[pos - 1] = edges[pos];
|
||||
edges[pos] = temp;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PathRasterRange
|
||||
{
|
||||
public:
|
||||
void Begin(const PathScanline* scanline, PathFillMode mode);
|
||||
void Next();
|
||||
|
||||
bool found = false;
|
||||
int x0;
|
||||
int x1;
|
||||
|
||||
private:
|
||||
const PathScanline* scanline = nullptr;
|
||||
PathFillMode mode;
|
||||
size_t i = 0;
|
||||
int nonzero_rule = 0;
|
||||
};
|
||||
|
||||
enum class PathFillBlockResult
|
||||
{
|
||||
Empty,
|
||||
Partial,
|
||||
Full
|
||||
};
|
||||
|
||||
class PathMaskBuffer
|
||||
{
|
||||
public:
|
||||
void BeginRow(PathScanline* scanlines, PathFillMode mode);
|
||||
PathFillBlockResult FillBlock(int xpos);
|
||||
|
||||
unsigned char MaskBufferData[MaskBufferSize];
|
||||
|
||||
private:
|
||||
bool IsFullBlock(int xpos) const;
|
||||
|
||||
PathRasterRange Range[ScanlineBlockSize];
|
||||
};
|
||||
|
||||
class PathFillRasterizer
|
||||
{
|
||||
public:
|
||||
void Rasterize(const PathFillDesc& path, uint8_t* dest, int width, int height);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
||||
void Begin(double x, double y);
|
||||
void QuadraticBezier(double cp1_x, double cp1_y, double cp2_x, double cp2_y);
|
||||
void CubicBezier(double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y);
|
||||
void Line(double x, double y);
|
||||
void End(bool close);
|
||||
|
||||
void SubdivideBezier(int level, double cp0_x, double cp0_y, double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y, double t0, double t1);
|
||||
static Point PointOnBezier(double cp0_x, double cp0_y, double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y, double t);
|
||||
|
||||
void Fill(PathFillMode mode, uint8_t* dest, int dest_width, int dest_height);
|
||||
|
||||
struct Extent
|
||||
{
|
||||
Extent() : left(INT_MAX), right(0) {}
|
||||
int left;
|
||||
int right;
|
||||
};
|
||||
|
||||
Extent FindExtent(const PathScanline* scanline, int max_width);
|
||||
|
||||
double start_x = 0.0;
|
||||
double start_y = 0.0;
|
||||
double last_x = 0.0;
|
||||
double last_y = 0.0;
|
||||
|
||||
int first_scanline = 0;
|
||||
int last_scanline = 0;
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<PathScanline> scanlines;
|
||||
|
||||
PathMaskBuffer mask_blocks;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PathFillDesc::Rasterize(uint8_t* dest, int width, int height, bool blend)
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PathFillRasterizer::Rasterize(const PathFillDesc& path, uint8_t* dest, int dest_width, int dest_height)
|
||||
{
|
||||
Clear();
|
||||
|
||||
// For simplicity of the code, ensure the mask is always a multiple of MaskBlockSize
|
||||
int block_width = MaskBlockSize * ((dest_width + MaskBlockSize - 1) / MaskBlockSize);
|
||||
int block_height = MaskBlockSize * ((dest_height + MaskBlockSize - 1) / MaskBlockSize);
|
||||
|
||||
if (width != block_width || height != block_height)
|
||||
{
|
||||
width = block_width;
|
||||
height = block_height;
|
||||
|
||||
scanlines.resize(block_height * AntialiasLevel);
|
||||
first_scanline = scanlines.size();
|
||||
last_scanline = 0;
|
||||
}
|
||||
|
||||
for (const auto& subpath : path.subpaths)
|
||||
{
|
||||
Point start_point = subpath.points[0];
|
||||
Begin(start_point.x, start_point.y);
|
||||
|
||||
size_t i = 1;
|
||||
for (PathFillCommand command : subpath.commands)
|
||||
{
|
||||
if (command == PathFillCommand::line)
|
||||
{
|
||||
const Point& next_point = subpath.points[i];
|
||||
i++;
|
||||
|
||||
Line(next_point.x, next_point.y);
|
||||
}
|
||||
else if (command == PathFillCommand::quadradic)
|
||||
{
|
||||
const Point& control = subpath.points[i];
|
||||
const Point& next_point = subpath.points[i + 1];
|
||||
i += 2;
|
||||
|
||||
QuadraticBezier(control.x, control.y, next_point.x, next_point.y);
|
||||
}
|
||||
else if (command == PathFillCommand::cubic)
|
||||
{
|
||||
const Point& control1 = subpath.points[i];
|
||||
const Point& control2 = subpath.points[i + 1];
|
||||
const Point& next_point = subpath.points[i + 2];
|
||||
i += 3;
|
||||
|
||||
CubicBezier(control1.x, control1.y, control2.x, control2.y, next_point.x, next_point.y);
|
||||
}
|
||||
}
|
||||
|
||||
End(subpath.closed);
|
||||
}
|
||||
|
||||
Fill(path.fill_mode, dest, dest_width, dest_height);
|
||||
}
|
||||
|
||||
void PathFillRasterizer::Fill(PathFillMode mode, uint8_t* dest, int dest_width, int dest_height)
|
||||
{
|
||||
if (scanlines.empty()) return;
|
||||
|
||||
int start_y = first_scanline / ScanlineBlockSize * ScanlineBlockSize;
|
||||
int end_y = (last_scanline + ScanlineBlockSize - 1) / ScanlineBlockSize * ScanlineBlockSize;
|
||||
|
||||
for (int ypos = start_y; ypos < end_y; ypos += ScanlineBlockSize)
|
||||
{
|
||||
mask_blocks.BeginRow(&scanlines[ypos], mode);
|
||||
|
||||
Extent extent = FindExtent(&scanlines[ypos], width);
|
||||
for (int xpos = extent.left; xpos < extent.right; xpos += ScanlineBlockSize)
|
||||
{
|
||||
PathFillBlockResult result = mask_blocks.FillBlock(xpos);
|
||||
|
||||
int dest_x = xpos / AntialiasLevel;
|
||||
int dest_y = ypos / AntialiasLevel;
|
||||
int count_x = std::min(dest_x + MaskBlockSize, dest_width) - dest_x;
|
||||
int count_y = std::min(dest_y + MaskBlockSize, dest_height) - dest_y;
|
||||
|
||||
if (result == PathFillBlockResult::Full)
|
||||
{
|
||||
for (int i = 0; i < count_y; i++)
|
||||
{
|
||||
uint8_t* dline = dest + (dest_y + i) * dest_width;
|
||||
memset(dline, 255, count_x);
|
||||
}
|
||||
}
|
||||
else if (result == PathFillBlockResult::Partial)
|
||||
{
|
||||
for (int i = 0; i < count_y; i++)
|
||||
{
|
||||
const uint8_t* sline = mask_blocks.MaskBufferData + i * MaskBlockSize;
|
||||
uint8_t* dline = dest + (dest_y + i) * dest_width;
|
||||
for (int j = 0; j < count_x; j++)
|
||||
{
|
||||
dline[j] = std::min((int)dline[j] + (int)sline[j], 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathFillRasterizer::Extent PathFillRasterizer::FindExtent(const PathScanline* scanline, int max_width)
|
||||
{
|
||||
// Find scanline extents
|
||||
Extent extent;
|
||||
for (unsigned int cnt = 0; cnt < ScanlineBlockSize; cnt++, scanline++)
|
||||
{
|
||||
if (scanline->edges.empty())
|
||||
continue;
|
||||
|
||||
if (scanline->edges[0].x < extent.left)
|
||||
extent.left = scanline->edges[0].x;
|
||||
|
||||
if (scanline->edges[scanline->edges.size() - 1].x > extent.right)
|
||||
extent.right = scanline->edges[scanline->edges.size() - 1].x;
|
||||
}
|
||||
if (extent.left < 0)
|
||||
extent.left = 0;
|
||||
if (extent.right > max_width)
|
||||
extent.right = max_width;
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
void PathFillRasterizer::Clear()
|
||||
{
|
||||
for (size_t y = first_scanline; y < last_scanline; y++)
|
||||
{
|
||||
auto& scanline = scanlines[y];
|
||||
if (!scanline.edges.empty())
|
||||
{
|
||||
scanline.edges.clear();
|
||||
}
|
||||
}
|
||||
|
||||
first_scanline = scanlines.size();
|
||||
last_scanline = 0;
|
||||
}
|
||||
|
||||
void PathFillRasterizer::Begin(double x, double y)
|
||||
{
|
||||
start_x = last_x = x;
|
||||
start_y = last_y = y;
|
||||
}
|
||||
|
||||
void PathFillRasterizer::End(bool close)
|
||||
{
|
||||
if (close)
|
||||
{
|
||||
Line(start_x, start_y);
|
||||
}
|
||||
}
|
||||
|
||||
void PathFillRasterizer::QuadraticBezier(double qcp1_x, double qcp1_y, double qcp2_x, double qcp2_y)
|
||||
{
|
||||
double qcp0_x = last_x;
|
||||
double qcp0_y = last_y;
|
||||
|
||||
// Convert to cubic:
|
||||
double cp1_x = qcp0_x + 2.0 * (qcp1_x - qcp0_x) / 3.0;
|
||||
double cp1_y = qcp0_y + 2.0 * (qcp1_y - qcp0_y) / 3.0;
|
||||
double cp2_x = qcp1_x + (qcp2_x - qcp1_x) / 3.0;
|
||||
double cp2_y = qcp1_y + (qcp2_y - qcp1_y) / 3.0;
|
||||
double cp3_x = qcp2_x;
|
||||
double cp3_y = qcp2_y;
|
||||
CubicBezier(cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y);
|
||||
}
|
||||
|
||||
void PathFillRasterizer::CubicBezier(double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y)
|
||||
{
|
||||
double cp0_x = last_x;
|
||||
double cp0_y = last_y;
|
||||
|
||||
double estimated_length =
|
||||
std::sqrt((cp1_x - cp0_x) * (cp1_x - cp0_x) + (cp1_y - cp0_y) * (cp1_y - cp0_y)) +
|
||||
std::sqrt((cp1_x - cp0_x) * (cp1_x - cp0_x) + (cp1_y - cp0_y) * (cp1_y - cp0_y)) +
|
||||
std::sqrt((cp1_x - cp0_x) * (cp1_x - cp0_x) + (cp1_y - cp0_y) * (cp1_y - cp0_y));
|
||||
|
||||
double min_segs = 10.0;
|
||||
double segs = estimated_length / 5.0;
|
||||
int steps = (int)std::ceil(std::sqrt(segs * segs * 0.3f + min_segs));
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
//Point sp = PointOnBezier(cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, i / (double)steps);
|
||||
Point ep = PointOnBezier(cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, (i + 1) / (double)steps);
|
||||
Line(ep.x, ep.y);
|
||||
}
|
||||
|
||||
// http://ciechanowski.me/blog/2014/02/18/drawing-bezier-curves/
|
||||
// http://antigrain.com/research/adaptive_bezier/ (best method, unfortunately GPL example code)
|
||||
}
|
||||
|
||||
void PathFillRasterizer::SubdivideBezier(int level, double cp0_x, double cp0_y, double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y, double t0, double t1)
|
||||
{
|
||||
const double split_angle_cos = 0.99f;
|
||||
|
||||
double tc = (t0 + t1) * 0.5f;
|
||||
|
||||
Point sp = PointOnBezier(cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, t0);
|
||||
Point cp = PointOnBezier(cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, tc);
|
||||
Point ep = PointOnBezier(cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, t1);
|
||||
|
||||
Point sp2cp(cp.x - sp.x, cp.y - sp.y);
|
||||
Point cp2ep(ep.x - cp.x, ep.y - cp.y);
|
||||
|
||||
// Normalize
|
||||
double len_sp2cp = std::sqrt(sp2cp.x * sp2cp.x + sp2cp.y * sp2cp.y);
|
||||
double len_cp2ep = std::sqrt(cp2ep.x * cp2ep.x + cp2ep.y * cp2ep.y);
|
||||
if (len_sp2cp > 0.0) { sp2cp.x /= len_sp2cp; sp2cp.y /= len_sp2cp; }
|
||||
if (len_cp2ep > 0.0) { cp2ep.x /= len_cp2ep; cp2ep.y /= len_cp2ep; }
|
||||
|
||||
double dot = sp2cp.x * cp2ep.x + sp2cp.y * cp2ep.y;
|
||||
if (dot < split_angle_cos && level < 15)
|
||||
{
|
||||
SubdivideBezier(level + 1, cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, t0, tc);
|
||||
SubdivideBezier(level + 1, cp0_x, cp0_y, cp1_x, cp1_y, cp2_x, cp2_y, cp3_x, cp3_y, tc, t1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Line(ep.x, ep.y);
|
||||
}
|
||||
}
|
||||
|
||||
Point PathFillRasterizer::PointOnBezier(double cp0_x, double cp0_y, double cp1_x, double cp1_y, double cp2_x, double cp2_y, double cp3_x, double cp3_y, double t)
|
||||
{
|
||||
const int num_cp = 4;
|
||||
|
||||
double cp_x[4] = { cp0_x, cp1_x, cp2_x, cp3_x };
|
||||
double cp_y[4] = { cp0_y, cp1_y, cp2_y, cp3_y };
|
||||
|
||||
// Perform deCasteljau iterations:
|
||||
// (linear interpolate between the control points)
|
||||
double a = 1.0 - t;
|
||||
double b = t;
|
||||
for (int j = num_cp - 1; j > 0; j--)
|
||||
{
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
cp_x[i] = a * cp_x[i] + b * cp_x[i + 1];
|
||||
cp_y[i] = a * cp_y[i] + b * cp_y[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return Point(cp_x[0], cp_y[0]);
|
||||
}
|
||||
|
||||
void PathFillRasterizer::Line(double x1, double y1)
|
||||
{
|
||||
double x0 = last_x;
|
||||
double y0 = last_y;
|
||||
|
||||
last_x = x1;
|
||||
last_y = y1;
|
||||
|
||||
x0 *= static_cast<double>(AntialiasLevel);
|
||||
x1 *= static_cast<double>(AntialiasLevel);
|
||||
y0 *= static_cast<double>(AntialiasLevel);
|
||||
y1 *= static_cast<double>(AntialiasLevel);
|
||||
|
||||
bool up_direction = y1 < y0;
|
||||
double dy = y1 - y0;
|
||||
|
||||
constexpr const double epsilon = std::numeric_limits<double>::epsilon();
|
||||
if (dy < -epsilon || dy > epsilon)
|
||||
{
|
||||
int start_y = static_cast<int>(std::floor(std::min(y0, y1) + 0.5f));
|
||||
int end_y = static_cast<int>(std::floor(std::max(y0, y1) - 0.5f)) + 1;
|
||||
|
||||
start_y = std::max(start_y, 0);
|
||||
end_y = std::min(end_y, height * AntialiasLevel);
|
||||
|
||||
double rcp_dy = 1.0 / dy;
|
||||
|
||||
first_scanline = std::min(first_scanline, start_y);
|
||||
last_scanline = std::max(last_scanline, end_y);
|
||||
|
||||
for (int y = start_y; y < end_y; y++)
|
||||
{
|
||||
double ypos = y + 0.5f;
|
||||
double x = x0 + (x1 - x0) * (ypos - y0) * rcp_dy;
|
||||
scanlines[y].insert_sorted(PathScanlineEdge(x, up_direction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PathRasterRange::Begin(const PathScanline* new_scanline, PathFillMode new_mode)
|
||||
{
|
||||
scanline = new_scanline;
|
||||
mode = new_mode;
|
||||
found = false;
|
||||
i = 0;
|
||||
nonzero_rule = 0;
|
||||
Next();
|
||||
}
|
||||
|
||||
void PathRasterRange::Next()
|
||||
{
|
||||
if (i + 1 >= scanline->edges.size())
|
||||
{
|
||||
found = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == PathFillMode::alternate)
|
||||
{
|
||||
x0 = static_cast<int>(scanline->edges[i].x + 0.5f);
|
||||
x1 = static_cast<int>(scanline->edges[i + 1].x - 0.5f) + 1;
|
||||
i += 2;
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
x0 = static_cast<int>(scanline->edges[i].x + 0.5f);
|
||||
nonzero_rule += scanline->edges[i].up_direction ? 1 : -1;
|
||||
i++;
|
||||
|
||||
while (i < scanline->edges.size())
|
||||
{
|
||||
nonzero_rule += scanline->edges[i].up_direction ? 1 : -1;
|
||||
x1 = static_cast<int>(scanline->edges[i].x - 0.5f) + 1;
|
||||
i++;
|
||||
|
||||
if (nonzero_rule == 0)
|
||||
{
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PathMaskBuffer::BeginRow(PathScanline* scanlines, PathFillMode mode)
|
||||
{
|
||||
for (unsigned int cnt = 0; cnt < ScanlineBlockSize; cnt++)
|
||||
{
|
||||
Range[cnt].Begin(&scanlines[cnt], mode);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
PathFillBlockResult PathMaskBuffer::FillBlock(int xpos)
|
||||
{
|
||||
if (IsFullBlock(xpos))
|
||||
{
|
||||
return PathFillBlockResult::Full;
|
||||
}
|
||||
|
||||
const int block_size = MaskBlockSize / 16 * MaskBlockSize;
|
||||
__m128i block[block_size];
|
||||
|
||||
for (auto& elem : block)
|
||||
elem = _mm_setzero_si128();
|
||||
|
||||
for (unsigned int cnt = 0; cnt < ScanlineBlockSize; cnt++)
|
||||
{
|
||||
__m128i* line = &block[MaskBlockSize / 16 * (cnt / AntialiasLevel)];
|
||||
|
||||
while (range[cnt].found)
|
||||
{
|
||||
int x0 = range[cnt].x0;
|
||||
if (x0 >= xpos + ScanlineBlockSize)
|
||||
break;
|
||||
int x1 = range[cnt].x1;
|
||||
|
||||
x0 = max(x0, xpos);
|
||||
x1 = min(x1, xpos + ScanlineBlockSize);
|
||||
|
||||
if (x0 >= x1) // Done segment
|
||||
{
|
||||
range[cnt].next();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int sse_block = 0; sse_block < MaskBlockSize / 16; sse_block++)
|
||||
{
|
||||
for (int alias_cnt = 0; alias_cnt < (AntialiasLevel); alias_cnt++)
|
||||
{
|
||||
__m128i start = _mm_set1_epi8((x0 + alias_cnt - xpos) / AntialiasLevel - 16 * sse_block);
|
||||
__m128i end = _mm_set1_epi8((x1 + alias_cnt - xpos) / AntialiasLevel - 16 * sse_block);
|
||||
__m128i x = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
|
||||
|
||||
__m128i left = _mm_cmplt_epi8(x, start);
|
||||
__m128i right = _mm_cmplt_epi8(x, end);
|
||||
__m128i mask = _mm_andnot_si128(left, right);
|
||||
__m128i add_value = _mm_and_si128(mask, _mm_set1_epi8(256 / (AntialiasLevel * AntialiasLevel)));
|
||||
|
||||
line[sse_block] = _mm_adds_epu8(line[sse_block], add_value);
|
||||
}
|
||||
}
|
||||
|
||||
range[cnt].x0 = x1; // For next time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__m128i empty_status = _mm_setzero_si128();
|
||||
for (auto& elem : block)
|
||||
empty_status = _mm_or_si128(empty_status, elem);
|
||||
|
||||
bool empty_block = _mm_movemask_epi8(_mm_cmpeq_epi32(empty_status, _mm_setzero_si128())) == 0xffff;
|
||||
if (empty_block) return PathFillBlockResult::Empty;
|
||||
|
||||
for (unsigned int cnt = 0; cnt < MaskBlockSize; cnt++)
|
||||
{
|
||||
__m128i* input = &block[MaskBlockSize / 16 * cnt];
|
||||
__m128i* output = (__m128i*)(MaskBufferData + cnt * mask_texture_size);
|
||||
|
||||
for (int sse_block = 0; sse_block < MaskBlockSize / 16; sse_block++)
|
||||
_mm_storeu_si128(&output[sse_block], input[sse_block]);
|
||||
}
|
||||
|
||||
return PathFillBlockResult::Partial;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
PathFillBlockResult PathMaskBuffer::FillBlock(int xpos)
|
||||
{
|
||||
if (IsFullBlock(xpos))
|
||||
{
|
||||
return PathFillBlockResult::Full;
|
||||
}
|
||||
|
||||
memset(MaskBufferData, 0, MaskBufferSize);
|
||||
|
||||
bool empty_block = true;
|
||||
for (unsigned int cnt = 0; cnt < ScanlineBlockSize; cnt++)
|
||||
{
|
||||
unsigned char* line = MaskBufferData + MaskBlockSize * (cnt / AntialiasLevel);
|
||||
while (Range[cnt].found)
|
||||
{
|
||||
int x0 = Range[cnt].x0;
|
||||
if (x0 >= xpos + ScanlineBlockSize)
|
||||
break;
|
||||
int x1 = Range[cnt].x1;
|
||||
|
||||
x0 = std::max(x0, xpos);
|
||||
x1 = std::min(x1, xpos + ScanlineBlockSize);
|
||||
|
||||
if (x0 >= x1) // Done segment
|
||||
{
|
||||
Range[cnt].Next();
|
||||
}
|
||||
else
|
||||
{
|
||||
empty_block = false;
|
||||
for (int x = x0 - xpos; x < x1 - xpos; x++)
|
||||
{
|
||||
int pixel = line[x / AntialiasLevel];
|
||||
pixel = std::min(pixel + (256 / (AntialiasLevel * AntialiasLevel)), 255);
|
||||
line[x / AntialiasLevel] = pixel;
|
||||
}
|
||||
Range[cnt].x0 = x1; // For next time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty_block ? PathFillBlockResult::Empty : PathFillBlockResult::Partial;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool PathMaskBuffer::IsFullBlock(int xpos) const
|
||||
{
|
||||
for (auto& elem : Range)
|
||||
{
|
||||
if (!elem.found)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((elem.x0 > xpos) || (elem.x1 < (xpos + ScanlineBlockSize - 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
Loading…
Reference in a new issue