4 #include "v_ft_raster.h"
5 #include "v_ft_stroker.h"
9 #include "vtaskqueue.h"
15 FTOutline() : mMemory(nullptr) {}
16 ~FTOutline() { delete[] mMemory; }
19 if (mMemory) delete[] mMemory;
26 void convert(const VPath &path);
27 void convert(CapStyle, JoinStyle, float, float);
28 void moveTo(const VPointF &pt);
29 void lineTo(const VPointF &pt);
30 void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
33 void transform(const VMatrix &m);
35 SW_FT_Vector * mMemory{nullptr};
39 SW_FT_Stroker_LineCap ftCap;
40 SW_FT_Stroker_LineJoin ftJoin;
42 SW_FT_Fixed ftMeterLimit;
46 void FTOutline::reset()
48 ft.n_points = ft.n_contours = 0;
52 void FTOutline::grow(int points, int segments)
55 if (mPointSize >= points && mSegmentSize >= segments) return;
60 // update book keeping
62 mSegmentSize = segments;
64 int point_size = (points + segments);
65 int contour_size = ((sizeof(short) * segments) / sizeof(SW_FT_Vector)) + 1;
67 ((sizeof(char) * (points + segments)) / sizeof(SW_FT_Vector)) + 1;
70 * Optimization, instead of allocating 3 different buffer
71 * allocate one big buffer and divide the buffer into 3 different
74 mMemory = new SW_FT_Vector[point_size + contour_size + tag_size];
75 ft.points = reinterpret_cast<SW_FT_Vector *>(mMemory);
76 ft.tags = reinterpret_cast<char *>(mMemory + point_size);
77 ft.contours = reinterpret_cast<short *>(mMemory + point_size + tag_size);
80 void FTOutline::convert(const VPath &path)
82 const std::vector<VPath::Element> &elements = path.elements();
83 const std::vector<VPointF> & points = path.points();
85 grow(points.size(), path.segments());
88 for (auto element : elements) {
90 case VPath::Element::MoveTo:
91 moveTo(points[index]);
94 case VPath::Element::LineTo:
95 lineTo(points[index]);
98 case VPath::Element::CubicTo:
99 cubicTo(points[index], points[index + 1], points[index + 2]);
102 case VPath::Element::Close:
112 void FTOutline::convert(CapStyle cap, JoinStyle join, float width,
115 ftClosed = (SW_FT_Bool)closed;
117 // map strokeWidth to freetype. It uses as the radius of the pen not the
120 // convert to freetype co-ordinate
121 // IMP: stroker takes radius in 26.6 co-ordinate
122 ftWidth = SW_FT_Fixed(width * (1 << 6));
123 // IMP: stroker takes meterlimit in 16.16 co-ordinate
124 ftMeterLimit = SW_FT_Fixed(meterLimit * (1 << 16));
126 // map to freetype capstyle
128 case CapStyle::Square:
129 ftCap = SW_FT_STROKER_LINECAP_SQUARE;
131 case CapStyle::Round:
132 ftCap = SW_FT_STROKER_LINECAP_ROUND;
135 ftCap = SW_FT_STROKER_LINECAP_BUTT;
139 case JoinStyle::Bevel:
140 ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
142 case JoinStyle::Round:
143 ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
146 ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
151 #define TO_FT_COORD(x) ((x)*64) // to freetype 26.6 coordinate.
153 void FTOutline::moveTo(const VPointF &pt)
155 ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
156 ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
157 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
159 ft.contours[ft.n_contours] = ft.n_points - 1;
166 void FTOutline::lineTo(const VPointF &pt)
168 ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
169 ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
170 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
175 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2,
178 ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
179 ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
180 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
183 ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
184 ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
185 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
188 ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
189 ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
190 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
194 void FTOutline::close()
198 index = ft.contours[ft.n_contours - 1] + 1;
203 // make sure atleast 1 point exists in the segment.
204 if (ft.n_points == index) {
209 ft.points[ft.n_points].x = ft.points[index].x;
210 ft.points[ft.n_points].y = ft.points[index].y;
211 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
216 void FTOutline::end()
219 ft.contours[ft.n_contours] = ft.n_points - 1;
229 static void rleGenerationCb(int count, const SW_FT_Span *spans, void *user)
231 VRle * rle = (VRle *)user;
232 VRle::Span *rleSpan = (VRle::Span *)spans;
233 rle->addSpan(rleSpan, count);
237 RleTask() { receiver = sender.get_future(); }
238 std::promise<VRle> sender;
239 std::future<VRle> receiver;
248 VRle operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
251 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
254 if (stroke) { // Stroke Task
255 outRef.convert(path);
256 outRef.convert(cap, join, width, meterLimit);
258 uint points, contors;
260 SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
261 outRef.ftMeterLimit);
262 SW_FT_Stroker_ParseOutline(stroker, &outRef.ft, !outRef.ftClosed);
263 SW_FT_Stroker_GetCounts(stroker, &points, &contors);
265 outRef.grow(points, contors);
267 SW_FT_Stroker_Export(stroker, &outRef.ft);
269 SW_FT_Raster_Params params;
271 params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
272 params.gray_spans = &rleGenerationCb;
274 params.source = &outRef;
276 sw_ft_grays_raster.raster_render(nullptr, ¶ms);
278 } else { // Fill Task
279 outRef.convert(path);
280 int fillRuleFlag = SW_FT_OUTLINE_NONE;
282 case FillRule::EvenOdd:
283 fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
286 fillRuleFlag = SW_FT_OUTLINE_NONE;
289 outRef.ft.flags = fillRuleFlag;
290 SW_FT_Raster_Params params;
292 params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
293 params.gray_spans = &rleGenerationCb;
295 params.source = &outRef.ft;
297 sw_ft_grays_raster.raster_render(nullptr, ¶ms);
299 return std::move(rle);
302 class RleTaskScheduler {
303 const unsigned _count{std::thread::hardware_concurrency()};
304 std::vector<std::thread> _threads;
305 std::vector<TaskQueue<RleTask>> _q{_count};
306 std::atomic<unsigned> _index{0};
311 * initalize per thread objects.
313 FTOutline outlineRef;
314 SW_FT_Stroker stroker;
315 SW_FT_Stroker_New(&stroker);
319 RleTask *task = nullptr;
321 for (unsigned n = 0; n != _count * 32; ++n) {
322 if (_q[(i + n) % _count].try_pop(task)) break;
324 if (!task && !_q[i].pop(task)) break;
326 task->sender.set_value((*task)(outlineRef, stroker));
331 SW_FT_Stroker_Done(stroker);
337 for (unsigned n = 0; n != _count; ++n) {
338 _threads.emplace_back([&, n] { run(n); });
344 for (auto &e : _q) e.done();
346 for (auto &e : _threads) e.join();
349 std::future<VRle> async(RleTask *task)
351 auto receiver = std::move(task->receiver);
354 for (unsigned n = 0; n != _count; ++n) {
355 if (_q[(i + n) % _count].try_push(task)) return receiver;
358 _q[i % _count].push(task);
363 std::future<VRle> strokeRle(const VPath &path, VRle &&rle, CapStyle cap, JoinStyle join,
364 float width, float meterLimit)
366 RleTask *task = new RleTask();
369 task->rle = std::move(rle);
373 task->meterLimit = meterLimit;
377 std::future<VRle> fillRle(const VPath &path, VRle &&rle, FillRule fillRule)
379 RleTask *task = new RleTask();
381 task->rle = std::move(rle);
382 task->fillRule = fillRule;
383 task->stroke = false;
388 static RleTaskScheduler raster_scheduler;
390 VRaster::VRaster() {}
392 VRaster::~VRaster() {}
394 std::future<VRle> VRaster::generateFillInfo(const VPath &path, VRle &&rle,
397 if (path.isEmpty()) {
398 std::promise<VRle> promise;
399 promise.set_value(VRle());
400 return promise.get_future();
402 return raster_scheduler.fillRle(path, std::move(rle), fillRule);
405 std::future<VRle> VRaster::generateStrokeInfo(const VPath &path, VRle &&rle, CapStyle cap,
406 JoinStyle join, float width,
409 if (path.isEmpty()) {
410 std::promise<VRle> promise;
411 promise.set_value(VRle());
412 return promise.get_future();
414 return raster_scheduler.strokeRle(path, std::move(rle), cap, join, width, meterLimit);