4 #include "v_ft_raster.h"
5 #include "v_ft_stroker.h"
9 #include "vtaskqueue.h"
18 if (mPointSize) delete[] ft.points;
19 if (mTagSize) delete[] ft.tags;
22 delete[] ft.contours_flag;
27 void convert(const VPath &path);
28 void convert(CapStyle, JoinStyle, float, float);
29 void moveTo(const VPointF &pt);
30 void lineTo(const VPointF &pt);
31 void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
34 void transform(const VMatrix &m);
40 SW_FT_Stroker_LineCap ftCap;
41 SW_FT_Stroker_LineJoin ftJoin;
43 SW_FT_Fixed ftMeterLimit;
46 void FTOutline::reset()
48 ft.n_points = ft.n_contours = 0;
52 void FTOutline::grow(int points, int segments)
56 int point_size = (points + segments);
57 int segment_size = (sizeof(short) * segments);
58 int tag_size = (sizeof(char) * (points + segments));
60 if (point_size > mPointSize) {
61 if (mPointSize) delete [] ft.points;
62 ft.points = new SW_FT_Vector[point_size];
63 mPointSize = point_size;
66 if (segment_size > mSegmentSize) {
68 delete [] ft.contours;
69 delete [] ft.contours_flag;
71 ft.contours = new short[segment_size];
72 ft.contours_flag = new char[segment_size];
73 mSegmentSize = segment_size;
76 if (tag_size > mTagSize) {
77 if (mTagSize) delete [] ft.tags;
78 ft.tags = new char[tag_size];
83 void FTOutline::convert(const VPath &path)
85 const std::vector<VPath::Element> &elements = path.elements();
86 const std::vector<VPointF> & points = path.points();
88 grow(points.size(), path.segments());
91 for (auto element : elements) {
93 case VPath::Element::MoveTo:
94 moveTo(points[index]);
97 case VPath::Element::LineTo:
98 lineTo(points[index]);
101 case VPath::Element::CubicTo:
102 cubicTo(points[index], points[index + 1], points[index + 2]);
105 case VPath::Element::Close:
115 void FTOutline::convert(CapStyle cap, JoinStyle join, float width,
118 // map strokeWidth to freetype. It uses as the radius of the pen not the
121 // convert to freetype co-ordinate
122 // IMP: stroker takes radius in 26.6 co-ordinate
123 ftWidth = SW_FT_Fixed(width * (1 << 6));
124 // IMP: stroker takes meterlimit in 16.16 co-ordinate
125 ftMeterLimit = SW_FT_Fixed(meterLimit * (1 << 16));
127 // map to freetype capstyle
129 case CapStyle::Square:
130 ftCap = SW_FT_STROKER_LINECAP_SQUARE;
132 case CapStyle::Round:
133 ftCap = SW_FT_STROKER_LINECAP_ROUND;
136 ftCap = SW_FT_STROKER_LINECAP_BUTT;
140 case JoinStyle::Bevel:
141 ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
143 case JoinStyle::Round:
144 ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
147 ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
152 #define TO_FT_COORD(x) ((x)*64) // to freetype 26.6 coordinate.
154 void FTOutline::moveTo(const VPointF &pt)
156 ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
157 ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
158 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
160 ft.contours[ft.n_contours] = ft.n_points - 1;
163 // mark the current contour as open
164 // will be updated if ther is a close tag at the end.
165 ft.contours_flag[ft.n_contours] = 1;
170 void FTOutline::lineTo(const VPointF &pt)
172 ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
173 ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
174 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
178 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2,
181 ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
182 ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
183 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
186 ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
187 ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
188 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
191 ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
192 ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
193 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
196 void FTOutline::close()
198 // mark the contour as a close path.
199 ft.contours_flag[ft.n_contours] = 0;
203 index = ft.contours[ft.n_contours - 1] + 1;
208 // make sure atleast 1 point exists in the segment.
209 if (ft.n_points == index) {
214 ft.points[ft.n_points].x = ft.points[index].x;
215 ft.points[ft.n_points].y = ft.points[index].y;
216 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
220 void FTOutline::end()
223 ft.contours[ft.n_contours] = ft.n_points - 1;
233 static void rleGenerationCb(int count, const SW_FT_Span *spans, void *user)
235 VRle * rle = (VRle *)user;
236 VRle::Span *rleSpan = (VRle::Span *)spans;
237 rle->addSpan(rleSpan, count);
240 static void bboxCb(int x, int y, int w, int h, void *user)
242 VRle * rle = (VRle *)user;
243 rle->setBoundingRect({x, y, w, h});
247 std::promise<VRle> sender;
257 VRle operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
258 void render(FTOutline &outRef);
261 void RleTask::render(FTOutline &outRef)
263 SW_FT_Raster_Params params;
265 params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
266 params.gray_spans = &rleGenerationCb;
267 params.bbox_cb = &bboxCb;
269 params.source = &outRef.ft;
272 params.flags |= SW_FT_RASTER_FLAG_CLIP;
274 params.clip_box.xMin = clip.left();
275 params.clip_box.yMin = clip.top();
276 params.clip_box.xMax = clip.right();
277 params.clip_box.yMax = clip.bottom();
280 sw_ft_grays_raster.raster_render(nullptr, ¶ms);
283 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
286 if (stroke) { // Stroke Task
287 outRef.convert(path);
288 outRef.convert(cap, join, width, meterLimit);
290 uint points, contors;
292 SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
293 outRef.ftMeterLimit);
294 SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
295 SW_FT_Stroker_GetCounts(stroker, &points, &contors);
297 outRef.grow(points, contors);
299 SW_FT_Stroker_Export(stroker, &outRef.ft);
301 } else { // Fill Task
302 outRef.convert(path);
303 int fillRuleFlag = SW_FT_OUTLINE_NONE;
305 case FillRule::EvenOdd:
306 fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
309 fillRuleFlag = SW_FT_OUTLINE_NONE;
312 outRef.ft.flags = fillRuleFlag;
319 return std::move(rle);
322 class RleTaskScheduler {
323 const unsigned _count{std::thread::hardware_concurrency()};
324 std::vector<std::thread> _threads;
325 std::vector<TaskQueue<RleTask>> _q{_count};
326 std::atomic<unsigned> _index{0};
331 * initalize per thread objects.
333 FTOutline outlineRef;
334 SW_FT_Stroker stroker;
335 SW_FT_Stroker_New(&stroker);
340 bool success = false;
342 for (unsigned n = 0; n != _count * 32; ++n) {
343 if (_q[(i + n) % _count].try_pop(task)) {
349 if (!success && !_q[i].pop(task)) break;
351 task.sender.set_value((task)(outlineRef, stroker));
355 SW_FT_Stroker_Done(stroker);
361 for (unsigned n = 0; n != _count; ++n) {
362 _threads.emplace_back([&, n] { run(n); });
368 for (auto &e : _q) e.done();
370 for (auto &e : _threads) e.join();
373 std::future<VRle> async(RleTask &&task)
375 auto receiver = std::move(task.sender.get_future());
378 for (unsigned n = 0; n != _count; ++n) {
379 if (_q[(i + n) % _count].try_push(std::move(task))) return receiver;
382 _q[i % _count].push(std::move(task));
387 std::future<VRle> strokeRle(VPath &&path, VRle &&rle, CapStyle cap, JoinStyle join,
388 float width, float meterLimit, const VRect &clip)
392 task.path = std::move(path);
393 task.rle = std::move(rle);
397 task.meterLimit = meterLimit;
399 return async(std::move(task));
402 std::future<VRle> fillRle(VPath &&path, VRle &&rle, FillRule fillRule, const VRect &clip)
405 task.path = std::move(path);
406 task.rle = std::move(rle);
407 task.fillRule = fillRule;
410 return async(std::move(task));
414 static RleTaskScheduler raster_scheduler;
416 std::future<VRle> VRaster::generateFillInfo(VPath &&path, VRle &&rle,
417 FillRule fillRule, const VRect &clip)
420 std::promise<VRle> promise;
421 promise.set_value(VRle());
422 return promise.get_future();
424 return raster_scheduler.fillRle(std::move(path), std::move(rle), fillRule, clip);
427 std::future<VRle> VRaster::generateStrokeInfo(VPath &&path, VRle &&rle, CapStyle cap,
428 JoinStyle join, float width,
429 float meterLimit, const VRect &clip)
432 std::promise<VRle> promise;
433 promise.set_value(VRle());
434 return promise.get_future();
436 return raster_scheduler.strokeRle(std::move(path), std::move(rle), cap, join, width, meterLimit, clip);