1 | |
2 | #include "../msdfgen.h" |
3 | |
4 | #include <vector> |
5 | #include "edge-selectors.h" |
6 | #include "contour-combiners.h" |
7 | #include "ShapeDistanceFinder.h" |
8 | |
9 | namespace msdfgen { |
10 | |
11 | template <typename DistanceType> |
12 | class DistancePixelConversion; |
13 | |
14 | template <> |
15 | class DistancePixelConversion<double> { |
16 | double invRange; |
17 | public: |
18 | typedef BitmapRef<float, 1> BitmapRefType; |
19 | inline explicit DistancePixelConversion(double range) : invRange(1/range) { } |
20 | inline void operator()(float *pixels, double distance) const { |
21 | *pixels = float(invRange*distance+.5); |
22 | } |
23 | }; |
24 | |
25 | template <> |
26 | class DistancePixelConversion<MultiDistance> { |
27 | double invRange; |
28 | public: |
29 | typedef BitmapRef<float, 3> BitmapRefType; |
30 | inline explicit DistancePixelConversion(double range) : invRange(1/range) { } |
31 | inline void operator()(float *pixels, const MultiDistance &distance) const { |
32 | pixels[0] = float(invRange*distance.r+.5); |
33 | pixels[1] = float(invRange*distance.g+.5); |
34 | pixels[2] = float(invRange*distance.b+.5); |
35 | } |
36 | }; |
37 | |
38 | template <> |
39 | class DistancePixelConversion<MultiAndTrueDistance> { |
40 | double invRange; |
41 | public: |
42 | typedef BitmapRef<float, 4> BitmapRefType; |
43 | inline explicit DistancePixelConversion(double range) : invRange(1/range) { } |
44 | inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const { |
45 | pixels[0] = float(invRange*distance.r+.5); |
46 | pixels[1] = float(invRange*distance.g+.5); |
47 | pixels[2] = float(invRange*distance.b+.5); |
48 | pixels[3] = float(invRange*distance.a+.5); |
49 | } |
50 | }; |
51 | |
52 | template <class ContourCombiner> |
53 | void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) { |
54 | DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range); |
55 | #ifdef MSDFGEN_USE_OPENMP |
56 | #pragma omp parallel |
57 | #endif |
58 | { |
59 | ShapeDistanceFinder<ContourCombiner> distanceFinder(shape); |
60 | bool rightToLeft = false; |
61 | #ifdef MSDFGEN_USE_OPENMP |
62 | #pragma omp for |
63 | #endif |
64 | for (int y = 0; y < output.height; ++y) { |
65 | int row = shape.inverseYAxis ? output.height-y-1 : y; |
66 | for (int col = 0; col < output.width; ++col) { |
67 | int x = rightToLeft ? output.width-col-1 : col; |
68 | Point2 p = projection.unproject(Point2(x+.5, y+.5)); |
69 | typename ContourCombiner::DistanceType distance = distanceFinder.distance(p); |
70 | distancePixelConversion(output(x, row), distance); |
71 | } |
72 | rightToLeft = !rightToLeft; |
73 | } |
74 | } |
75 | } |
76 | |
77 | void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { |
78 | if (config.overlapSupport) |
79 | generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); |
80 | else |
81 | generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); |
82 | } |
83 | |
84 | void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { |
85 | if (config.overlapSupport) |
86 | generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range); |
87 | else |
88 | generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range); |
89 | } |
90 | |
91 | void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { |
92 | if (config.overlapSupport) |
93 | generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); |
94 | else |
95 | generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); |
96 | msdfErrorCorrection(output, shape, projection, range, config); |
97 | } |
98 | |
99 | void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { |
100 | if (config.overlapSupport) |
101 | generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); |
102 | else |
103 | generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); |
104 | msdfErrorCorrection(output, shape, projection, range, config); |
105 | } |
106 | |
107 | // Legacy API |
108 | |
109 | void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { |
110 | generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); |
111 | } |
112 | |
113 | void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { |
114 | generatePseudoSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); |
115 | } |
116 | |
117 | void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { |
118 | generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); |
119 | } |
120 | |
121 | void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { |
122 | generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); |
123 | } |
124 | |
125 | // Legacy version |
126 | |
127 | void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { |
128 | #ifdef MSDFGEN_USE_OPENMP |
129 | #pragma omp parallel for |
130 | #endif |
131 | for (int y = 0; y < output.height; ++y) { |
132 | int row = shape.inverseYAxis ? output.height-y-1 : y; |
133 | for (int x = 0; x < output.width; ++x) { |
134 | double dummy; |
135 | Point2 p = Vector2(x+.5, y+.5)/scale-translate; |
136 | SignedDistance minDistance; |
137 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) |
138 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { |
139 | SignedDistance distance = (*edge)->signedDistance(p, dummy); |
140 | if (distance < minDistance) |
141 | minDistance = distance; |
142 | } |
143 | *output(x, row) = float(minDistance.distance/range+.5); |
144 | } |
145 | } |
146 | } |
147 | |
148 | void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { |
149 | #ifdef MSDFGEN_USE_OPENMP |
150 | #pragma omp parallel for |
151 | #endif |
152 | for (int y = 0; y < output.height; ++y) { |
153 | int row = shape.inverseYAxis ? output.height-y-1 : y; |
154 | for (int x = 0; x < output.width; ++x) { |
155 | Point2 p = Vector2(x+.5, y+.5)/scale-translate; |
156 | SignedDistance minDistance; |
157 | const EdgeHolder *nearEdge = NULL; |
158 | double nearParam = 0; |
159 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) |
160 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { |
161 | double param; |
162 | SignedDistance distance = (*edge)->signedDistance(p, param); |
163 | if (distance < minDistance) { |
164 | minDistance = distance; |
165 | nearEdge = &*edge; |
166 | nearParam = param; |
167 | } |
168 | } |
169 | if (nearEdge) |
170 | (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam); |
171 | *output(x, row) = float(minDistance.distance/range+.5); |
172 | } |
173 | } |
174 | } |
175 | |
176 | void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { |
177 | #ifdef MSDFGEN_USE_OPENMP |
178 | #pragma omp parallel for |
179 | #endif |
180 | for (int y = 0; y < output.height; ++y) { |
181 | int row = shape.inverseYAxis ? output.height-y-1 : y; |
182 | for (int x = 0; x < output.width; ++x) { |
183 | Point2 p = Vector2(x+.5, y+.5)/scale-translate; |
184 | |
185 | struct { |
186 | SignedDistance minDistance; |
187 | const EdgeHolder *nearEdge; |
188 | double nearParam; |
189 | } r, g, b; |
190 | r.nearEdge = g.nearEdge = b.nearEdge = NULL; |
191 | r.nearParam = g.nearParam = b.nearParam = 0; |
192 | |
193 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) |
194 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { |
195 | double param; |
196 | SignedDistance distance = (*edge)->signedDistance(p, param); |
197 | if ((*edge)->color&RED && distance < r.minDistance) { |
198 | r.minDistance = distance; |
199 | r.nearEdge = &*edge; |
200 | r.nearParam = param; |
201 | } |
202 | if ((*edge)->color&GREEN && distance < g.minDistance) { |
203 | g.minDistance = distance; |
204 | g.nearEdge = &*edge; |
205 | g.nearParam = param; |
206 | } |
207 | if ((*edge)->color&BLUE && distance < b.minDistance) { |
208 | b.minDistance = distance; |
209 | b.nearEdge = &*edge; |
210 | b.nearParam = param; |
211 | } |
212 | } |
213 | |
214 | if (r.nearEdge) |
215 | (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam); |
216 | if (g.nearEdge) |
217 | (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam); |
218 | if (b.nearEdge) |
219 | (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam); |
220 | output(x, row)[0] = float(r.minDistance.distance/range+.5); |
221 | output(x, row)[1] = float(g.minDistance.distance/range+.5); |
222 | output(x, row)[2] = float(b.minDistance.distance/range+.5); |
223 | } |
224 | } |
225 | |
226 | errorCorrectionConfig.distanceCheckMode = ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE; |
227 | msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig)); |
228 | } |
229 | |
230 | void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { |
231 | #ifdef MSDFGEN_USE_OPENMP |
232 | #pragma omp parallel for |
233 | #endif |
234 | for (int y = 0; y < output.height; ++y) { |
235 | int row = shape.inverseYAxis ? output.height-y-1 : y; |
236 | for (int x = 0; x < output.width; ++x) { |
237 | Point2 p = Vector2(x+.5, y+.5)/scale-translate; |
238 | |
239 | SignedDistance minDistance; |
240 | struct { |
241 | SignedDistance minDistance; |
242 | const EdgeHolder *nearEdge; |
243 | double nearParam; |
244 | } r, g, b; |
245 | r.nearEdge = g.nearEdge = b.nearEdge = NULL; |
246 | r.nearParam = g.nearParam = b.nearParam = 0; |
247 | |
248 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) |
249 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { |
250 | double param; |
251 | SignedDistance distance = (*edge)->signedDistance(p, param); |
252 | if (distance < minDistance) |
253 | minDistance = distance; |
254 | if ((*edge)->color&RED && distance < r.minDistance) { |
255 | r.minDistance = distance; |
256 | r.nearEdge = &*edge; |
257 | r.nearParam = param; |
258 | } |
259 | if ((*edge)->color&GREEN && distance < g.minDistance) { |
260 | g.minDistance = distance; |
261 | g.nearEdge = &*edge; |
262 | g.nearParam = param; |
263 | } |
264 | if ((*edge)->color&BLUE && distance < b.minDistance) { |
265 | b.minDistance = distance; |
266 | b.nearEdge = &*edge; |
267 | b.nearParam = param; |
268 | } |
269 | } |
270 | |
271 | if (r.nearEdge) |
272 | (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam); |
273 | if (g.nearEdge) |
274 | (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam); |
275 | if (b.nearEdge) |
276 | (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam); |
277 | output(x, row)[0] = float(r.minDistance.distance/range+.5); |
278 | output(x, row)[1] = float(g.minDistance.distance/range+.5); |
279 | output(x, row)[2] = float(b.minDistance.distance/range+.5); |
280 | output(x, row)[3] = float(minDistance.distance/range+.5); |
281 | } |
282 | } |
283 | |
284 | errorCorrectionConfig.distanceCheckMode = ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE; |
285 | msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig)); |
286 | } |
287 | |
288 | } |
289 | |