blob: 3918ba47e9effa2ff14fcddeb8debbdd03d08912 [file] [log] [blame]
Mike Reed0fdc53f2018-08-27 13:00:37 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPathMeasure.h"
9#include "SkCanvas.h"
10#include "SkMatrix.h"
11#include "SkPaint.h"
12
13#include "SkTextToPathIter.h"
14
15static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
16 SkPathMeasure& meas, const SkMatrix& matrix) {
17 for (int i = 0; i < count; i++) {
18 SkPoint pos;
19 SkVector tangent;
20
21 matrix.mapXY(src[i].fX, src[i].fY, &pos);
22 SkScalar sx = pos.fX;
23 SkScalar sy = pos.fY;
24
25 if (!meas.getPosTan(sx, &pos, &tangent)) {
26 // set to 0 if the measure failed, so that we just set dst == pos
27 tangent.set(0, 0);
28 }
29
30 /* This is the old way (that explains our approach but is way too slow
31 SkMatrix matrix;
32 SkPoint pt;
33
34 pt.set(sx, sy);
35 matrix.setSinCos(tangent.fY, tangent.fX);
36 matrix.preTranslate(-sx, 0);
37 matrix.postTranslate(pos.fX, pos.fY);
38 matrix.mapPoints(&dst[i], &pt, 1);
39 */
40 dst[i].set(pos.fX - tangent.fY * sy, pos.fY + tangent.fX * sy);
41 }
42}
43
44/* TODO
45
46 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
47 determine that, but we need it. I guess a cheap answer is let the caller tell us,
48 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
49 */
50static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
51 const SkMatrix& matrix) {
52 SkPath::Iter iter(src, false);
53 SkPoint srcP[4], dstP[3];
54 SkPath::Verb verb;
55
56 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
57 switch (verb) {
58 case SkPath::kMove_Verb:
59 morphpoints(dstP, srcP, 1, meas, matrix);
60 dst->moveTo(dstP[0]);
61 break;
62 case SkPath::kLine_Verb:
63 // turn lines into quads to look bendy
64 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
65 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
66 morphpoints(dstP, srcP, 2, meas, matrix);
67 dst->quadTo(dstP[0], dstP[1]);
68 break;
69 case SkPath::kQuad_Verb:
70 morphpoints(dstP, &srcP[1], 2, meas, matrix);
71 dst->quadTo(dstP[0], dstP[1]);
72 break;
73 case SkPath::kConic_Verb:
74 morphpoints(dstP, &srcP[1], 2, meas, matrix);
75 dst->conicTo(dstP[0], dstP[1], iter.conicWeight());
76 break;
77 case SkPath::kCubic_Verb:
78 morphpoints(dstP, &srcP[1], 3, meas, matrix);
79 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
80 break;
81 case SkPath::kClose_Verb:
82 dst->close();
83 break;
84 default:
85 SkDEBUGFAIL("unknown verb");
86 break;
87 }
88 }
89}
90
91void SkVisitTextOnPath(const void* text, size_t byteLength, const SkPaint& paint,
92 const SkPath& follow, const SkMatrix* matrix,
93 const std::function<void(const SkPath&)>& visitor) {
94 if (byteLength == 0) {
95 return;
96 }
97
98 SkTextToPathIter iter((const char*)text, byteLength, paint, false);
99 SkPathMeasure meas(follow, false);
100 SkScalar hOffset = 0;
101
102 // need to measure first
103 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
104 SkScalar pathLen = meas.getLength();
105 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
106 pathLen = SkScalarHalf(pathLen);
107 }
108 hOffset += pathLen;
109 }
110
111 const SkPath* iterPath;
112 SkScalar xpos;
113 SkMatrix scaledMatrix;
114 SkScalar scale = iter.getPathScale();
115
116 scaledMatrix.setScale(scale, scale);
117
118 while (iter.next(&iterPath, &xpos)) {
119 if (iterPath) {
120 SkPath tmp;
121 SkMatrix m(scaledMatrix);
122
123 tmp.setIsVolatile(true);
124 m.postTranslate(xpos + hOffset, 0);
125 if (matrix) {
126 m.postConcat(*matrix);
127 }
128 morphpath(&tmp, *iterPath, meas, m);
129 visitor(tmp);
130 }
131 }
132}
133
134void SkDrawTextOnPath(const void* text, size_t byteLength, const SkPaint& paint,
135 const SkPath& follow, const SkMatrix* matrix, SkCanvas* canvas) {
136 SkVisitTextOnPath(text, byteLength, paint, follow, matrix, [canvas, paint](const SkPath& path) {
137 canvas->drawPath(path, paint);
138 });
139}
140