2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "v_ft_raster.h"
21 #include "v_ft_stroker.h"
25 #include "vtaskqueue.h"
34 if (mPointSize) delete[] ft.points;
35 if (mTagSize) delete[] ft.tags;
38 delete[] ft.contours_flag;
43 void convert(const VPath &path);
44 void convert(CapStyle, JoinStyle, float, float);
45 void moveTo(const VPointF &pt);
46 void lineTo(const VPointF &pt);
47 void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
50 void transform(const VMatrix &m);
56 SW_FT_Stroker_LineCap ftCap;
57 SW_FT_Stroker_LineJoin ftJoin;
59 SW_FT_Fixed ftMeterLimit;
62 void FTOutline::reset()
64 ft.n_points = ft.n_contours = 0;
68 void FTOutline::grow(int points, int segments)
72 int point_size = (points + segments);
73 int segment_size = (sizeof(short) * segments);
74 int tag_size = (sizeof(char) * (points + segments));
76 if (point_size > mPointSize) {
77 if (mPointSize) delete [] ft.points;
78 ft.points = new SW_FT_Vector[point_size];
79 mPointSize = point_size;
82 if (segment_size > mSegmentSize) {
84 delete [] ft.contours;
85 delete [] ft.contours_flag;
87 ft.contours = new short[segment_size];
88 ft.contours_flag = new char[segment_size];
89 mSegmentSize = segment_size;
92 if (tag_size > mTagSize) {
93 if (mTagSize) delete [] ft.tags;
94 ft.tags = new char[tag_size];
99 void FTOutline::convert(const VPath &path)
101 const std::vector<VPath::Element> &elements = path.elements();
102 const std::vector<VPointF> & points = path.points();
104 grow(points.size(), path.segments());
107 for (auto element : elements) {
109 case VPath::Element::MoveTo:
110 moveTo(points[index]);
113 case VPath::Element::LineTo:
114 lineTo(points[index]);
117 case VPath::Element::CubicTo:
118 cubicTo(points[index], points[index + 1], points[index + 2]);
121 case VPath::Element::Close:
131 void FTOutline::convert(CapStyle cap, JoinStyle join, float width,
134 // map strokeWidth to freetype. It uses as the radius of the pen not the
137 // convert to freetype co-ordinate
138 // IMP: stroker takes radius in 26.6 co-ordinate
139 ftWidth = SW_FT_Fixed(width * (1 << 6));
140 // IMP: stroker takes meterlimit in 16.16 co-ordinate
141 ftMeterLimit = SW_FT_Fixed(meterLimit * (1 << 16));
143 // map to freetype capstyle
145 case CapStyle::Square:
146 ftCap = SW_FT_STROKER_LINECAP_SQUARE;
148 case CapStyle::Round:
149 ftCap = SW_FT_STROKER_LINECAP_ROUND;
152 ftCap = SW_FT_STROKER_LINECAP_BUTT;
156 case JoinStyle::Bevel:
157 ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
159 case JoinStyle::Round:
160 ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
163 ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
168 #define TO_FT_COORD(x) ((x)*64) // to freetype 26.6 coordinate.
170 void FTOutline::moveTo(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;
176 ft.contours[ft.n_contours] = ft.n_points - 1;
179 // mark the current contour as open
180 // will be updated if ther is a close tag at the end.
181 ft.contours_flag[ft.n_contours] = 1;
186 void FTOutline::lineTo(const VPointF &pt)
188 ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
189 ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
190 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
194 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2,
197 ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
198 ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
199 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
202 ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
203 ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
204 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
207 ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
208 ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
209 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
212 void FTOutline::close()
214 // mark the contour as a close path.
215 ft.contours_flag[ft.n_contours] = 0;
219 index = ft.contours[ft.n_contours - 1] + 1;
224 // make sure atleast 1 point exists in the segment.
225 if (ft.n_points == index) {
230 ft.points[ft.n_points].x = ft.points[index].x;
231 ft.points[ft.n_points].y = ft.points[index].y;
232 ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
236 void FTOutline::end()
239 ft.contours[ft.n_contours] = ft.n_points - 1;
249 static void rleGenerationCb(int count, const SW_FT_Span *spans, void *user)
251 VRle * rle = (VRle *)user;
252 VRle::Span *rleSpan = (VRle::Span *)spans;
253 rle->addSpan(rleSpan, count);
256 static void bboxCb(int x, int y, int w, int h, void *user)
258 VRle * rle = (VRle *)user;
259 rle->setBoundingRect({x, y, w, h});
263 RleShare mRlePromise;
273 VRle operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
274 void render(FTOutline &outRef);
277 void RleTask::render(FTOutline &outRef)
279 SW_FT_Raster_Params params;
281 params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
282 params.gray_spans = &rleGenerationCb;
283 params.bbox_cb = &bboxCb;
285 params.source = &outRef.ft;
288 params.flags |= SW_FT_RASTER_FLAG_CLIP;
290 params.clip_box.xMin = clip.left();
291 params.clip_box.yMin = clip.top();
292 params.clip_box.xMax = clip.right();
293 params.clip_box.yMax = clip.bottom();
296 sw_ft_grays_raster.raster_render(nullptr, ¶ms);
299 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
302 if (stroke) { // Stroke Task
303 outRef.convert(path);
304 outRef.convert(cap, join, width, meterLimit);
306 uint points, contors;
308 SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
309 outRef.ftMeterLimit);
310 SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
311 SW_FT_Stroker_GetCounts(stroker, &points, &contors);
313 outRef.grow(points, contors);
315 SW_FT_Stroker_Export(stroker, &outRef.ft);
317 } else { // Fill Task
318 outRef.convert(path);
319 int fillRuleFlag = SW_FT_OUTLINE_NONE;
321 case FillRule::EvenOdd:
322 fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
325 fillRuleFlag = SW_FT_OUTLINE_NONE;
328 outRef.ft.flags = fillRuleFlag;
335 return std::move(rle);
338 class RleTaskScheduler {
339 const unsigned _count{std::thread::hardware_concurrency()};
340 std::vector<std::thread> _threads;
341 std::vector<TaskQueue<RleTask>> _q{_count};
342 std::atomic<unsigned> _index{0};
347 * initalize per thread objects.
349 FTOutline outlineRef;
350 SW_FT_Stroker stroker;
351 SW_FT_Stroker_New(&stroker);
356 bool success = false;
358 for (unsigned n = 0; n != _count * 32; ++n) {
359 if (_q[(i + n) % _count].try_pop(task)) {
365 if (!success && !_q[i].pop(task)) break;
367 task.mRlePromise->set_value((task)(outlineRef, stroker));
371 SW_FT_Stroker_Done(stroker);
376 for (unsigned n = 0; n != _count; ++n) {
377 _threads.emplace_back([&, n] { run(n); });
381 static RleTaskScheduler& instance()
383 static RleTaskScheduler singleton;
389 for (auto &e : _q) e.done();
391 for (auto &e : _threads) e.join();
394 void async(RleTask &&task)
398 for (unsigned n = 0; n != _count; ++n) {
399 if (_q[(i + n) % _count].try_push(std::move(task))) return;
402 _q[i % _count].push(std::move(task));
405 void strokeRle(RleShare &promise, VPath &&path, VRle &&rle, CapStyle cap, JoinStyle join,
406 float width, float meterLimit, const VRect &clip)
410 task.path = std::move(path);
411 task.rle = std::move(rle);
415 task.meterLimit = meterLimit;
417 task.mRlePromise = promise;
419 async(std::move(task));
422 void fillRle(RleShare &promise, VPath &&path, VRle &&rle, FillRule fillRule, const VRect &clip)
425 task.path = std::move(path);
426 task.rle = std::move(rle);
427 task.fillRule = fillRule;
430 task.mRlePromise = promise;
432 async(std::move(task));
436 void VRaster::generateFillInfo(RleShare &promise, VPath &&path, VRle &&rle,
437 FillRule fillRule, const VRect &clip)
440 promise->set_value(VRle());
443 return RleTaskScheduler::instance().fillRle(promise, std::move(path), std::move(rle), fillRule, clip);
446 void VRaster::generateStrokeInfo(RleShare &promise, VPath &&path, VRle &&rle, CapStyle cap,
447 JoinStyle join, float width,
448 float meterLimit, const VRect &clip)
451 promise->set_value(VRle());
454 return RleTaskScheduler::instance().strokeRle(promise, std::move(path), std::move(rle), cap, join, width, meterLimit, clip);