painting/qcosmeticstroker.cpp

Source codeSwitch to Preprocessed file
LineSource CodeCoverage
1/****************************************************************************-
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/****************************************************************************
2** -
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -
4** Contact: http://www.qt-project.org/legal -
5** -
6** This file is part of the QtGui module of the Qt Toolkit. -
7** -
8** $QT_BEGIN_LICENSE:LGPL$ -
9** Commercial License Usage -
10** Licensees holding valid commercial Qt licenses may use this file in -
11** accordance with the commercial license agreement provided with the -
12** Software or, alternatively, in accordance with the terms contained in -
13** a written agreement between you and Digia. For licensing terms and -
14** conditions see http://qt.digia.com/licensing. For further information -
15** use the contact form at http://qt.digia.com/contact-us. -
16** -
17** GNU Lesser General Public License Usage -
18** Alternatively, this file may be used under the terms of the GNU Lesser -
19** General Public License version 2.1 as published by the Free Software -
20** Foundation and appearing in the file LICENSE.LGPL included in the -
21** packaging of this file. Please review the following information to -
22** ensure the GNU Lesser General Public License version 2.1 requirements -
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -
24** -
25** In addition, as a special exception, Digia gives you certain additional -
26** rights. These rights are described in the Digia Qt LGPL Exception -
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -
28** -
29** GNU General Public License Usage -
30** Alternatively, this file may be used under the terms of the GNU -
31** General Public License version 3.0 as published by the Free Software -
32** Foundation and appearing in the file LICENSE.GPL included in the -
33** packaging of this file. Please review the following information to -
34** ensure the GNU General Public License version 3.0 requirements will be -
35** met: http://www.gnu.org/copyleft/gpl.html. -
36** -
37** -
38** $QT_END_LICENSE$ -
39** -
40****************************************************************************/ -
41 -
42#include "qcosmeticstroker_p.h" -
43#include "private/qpainterpath_p.h" -
44#include <qdebug.h> -
45#include <math.h> -
46 -
47QT_BEGIN_NAMESPACE -
48 -
49#if 0 -
50inline QString capString(int caps) -
51{ -
52 QString str; -
53 if (caps & QCosmeticStroker::CapBegin) { -
54 str += "CapBegin "; -
55 } -
56 if (caps & QCosmeticStroker::CapEnd) { -
57 str += "CapEnd "; -
58 } -
59 return str; -
60} -
61#endif -
62 -
63#define toF26Dot6(x) ((int)((x)*64.)) -
64 -
65static inline uint sourceOver(uint d, uint color) -
66{ -
67 return color + BYTE_MUL(d, qAlpha(~color)); -
68} -
69 -
70inline static int F16Dot16FixedDiv(int x, int y) -
71{ -
72 if (qAbs(x) > 0x7fff) -
73 return (((qlonglong)x) << 16) / y; -
74 return (x << 16) / y; -
75} -
76 -
77typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage); -
78 -
79namespace { -
80 -
81struct Dasher { -
82 QCosmeticStroker *stroker; -
83 int *pattern; -
84 int offset; -
85 int dashIndex; -
86 int dashOn; -
87 -
88 Dasher(QCosmeticStroker *s, bool reverse, int start, int stop) -
89 : stroker(s) -
90 { -
91 int delta = stop - start; -
92 if (reverse) { -
93 pattern = stroker->reversePattern; -
94 offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32); -
95 dashOn = 0; -
96 } else { -
97 pattern = stroker->pattern; -
98 offset = stroker->patternOffset - ((start & 63) - 32); -
99 dashOn = 1; -
100 } -
101 offset %= stroker->patternLength; -
102 if (offset < 0) -
103 offset += stroker->patternLength; -
104 -
105 dashIndex = 0; -
106 while (offset>= pattern[dashIndex]) -
107 ++dashIndex; -
108 -
109// qDebug() << " dasher" << offset/64. << reverse << dashIndex; -
110 stroker->patternOffset += delta; -
111 stroker->patternOffset %= stroker->patternLength; -
112 } -
113 -
114 bool on() const { -
115 return (dashIndex + dashOn) & 1; -
116 } -
117 void adjust() { -
118 offset += 64; -
119 if (offset >= pattern[dashIndex]) { -
120 ++dashIndex; -
121 dashIndex %= stroker->patternSize; -
122 } -
123 offset %= stroker->patternLength; -
124// qDebug() << "dasher.adjust" << offset/64. << dashIndex; -
125 } -
126}; -
127 -
128struct NoDasher { -
129 NoDasher(QCosmeticStroker *, bool, int, int) {} -
130 bool on() const { return true; } -
131 void adjust(int = 0) {} -
132}; -
133 -
134}; -
135 -
136template<DrawPixel drawPixel, class Dasher> -
137static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); -
138template<DrawPixel drawPixel, class Dasher> -
139static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); -
140 -
141inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage) -
142{ -
143 const QRect &cl = stroker->clip; -
144 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) -
145 return; -
146 -
147 int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ; -
148 int lasty = stroker->spans[stroker->current_span-1].y; -
149 -
150 if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) { -
151 stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData); -
152 stroker->current_span = 0; -
153 } -
154 -
155 stroker->spans[stroker->current_span].x = ushort(x); -
156 stroker->spans[stroker->current_span].len = 1; -
157 stroker->spans[stroker->current_span].y = y; -
158 stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8; -
159 ++stroker->current_span; -
160} -
161 -
162inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage) -
163{ -
164 const QRect &cl = stroker->clip; -
165 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) -
166 return; -
167 -
168 int offset = x + stroker->ppl*y; -
169 uint c = BYTE_MUL(stroker->color, coverage); -
170 stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c); -
171} -
172 -
173inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int) -
174{ -
175 const QRect &cl = stroker->clip; -
176 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) -
177 return; -
178 -
179 int offset = x + stroker->ppl*y; -
180 stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color); -
181} -
182 -
183enum StrokeSelection { -
184 Aliased = 0, -
185 AntiAliased = 1, -
186 Solid = 0, -
187 Dashed = 2, -
188 RegularDraw = 0, -
189 FastDraw = 4 -
190}; -
191 -
192static StrokeLine strokeLine(int strokeSelection) -
193{ -
194 StrokeLine stroke; -
195 -
196 switch (strokeSelection) { -
197 case Aliased|Solid|RegularDraw: -
198 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>; -
199 break; -
200 case Aliased|Solid|FastDraw: -
201 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>; -
202 break; -
203 case Aliased|Dashed|RegularDraw: -
204 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>; -
205 break; -
206 case Aliased|Dashed|FastDraw: -
207 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>; -
208 break; -
209 case AntiAliased|Solid|RegularDraw: -
210 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>; -
211 break; -
212 case AntiAliased|Solid|FastDraw: -
213 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>; -
214 break; -
215 case AntiAliased|Dashed|RegularDraw: -
216 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>; -
217 break; -
218 case AntiAliased|Dashed|FastDraw: -
219 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>; -
220 break; -
221 default: -
222 Q_ASSERT(false); -
223 stroke = 0; -
224 } -
225 return stroke; -
226} -
227 -
228void QCosmeticStroker::setup() -
229{ -
230 blend = state->penData.blend;
executed (the execution status of this line is deduced): blend = state->penData.blend;
-
231 if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) {
evaluated: state->clip
TRUEFALSE
yes
Evaluation Count:9194
yes
Evaluation Count:62506
partially evaluated: state->clip->enabled
TRUEFALSE
yes
Evaluation Count:9194
no
Evaluation Count:0
evaluated: state->clip->hasRectClip
TRUEFALSE
yes
Evaluation Count:8526
yes
Evaluation Count:668
partially evaluated: !state->clip->clipRect.isEmpty()
TRUEFALSE
yes
Evaluation Count:8526
no
Evaluation Count:0
0-62506
232 clip &= state->clip->clipRect;
executed (the execution status of this line is deduced): clip &= state->clip->clipRect;
-
233 blend = state->penData.unclipped_blend;
executed (the execution status of this line is deduced): blend = state->penData.unclipped_blend;
-
234 }
executed: }
Execution Count:8526
8526
235 -
236 int strokeSelection = 0;
executed (the execution status of this line is deduced): int strokeSelection = 0;
-
237 if (blend == state->penData.unclipped_blend
evaluated: blend == state->penData.unclipped_blend
TRUEFALSE
yes
Evaluation Count:8526
yes
Evaluation Count:63174
8526-63174
238 && state->penData.type == QSpanData::Solid
evaluated: state->penData.type == QSpanData::Solid
TRUEFALSE
yes
Evaluation Count:8520
yes
Evaluation Count:6
6-8520
239 && (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
evaluated: state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
TRUEFALSE
yes
Evaluation Count:442
yes
Evaluation Count:8078
442-8078
240 || state->penData.rasterBuffer->format == QImage::Format_RGB32)
evaluated: state->penData.rasterBuffer->format == QImage::Format_RGB32
TRUEFALSE
yes
Evaluation Count:29
yes
Evaluation Count:8049
29-8049
241 && state->compositionMode() == QPainter::CompositionMode_SourceOver)
partially evaluated: state->compositionMode() == QPainter::CompositionMode_SourceOver
TRUEFALSE
yes
Evaluation Count:471
no
Evaluation Count:0
0-471
242 strokeSelection |= FastDraw;
executed: strokeSelection |= FastDraw;
Execution Count:471
471
243 -
244 if (state->renderHints & QPainter::Antialiasing)
evaluated: state->renderHints & QPainter::Antialiasing
TRUEFALSE
yes
Evaluation Count:396
yes
Evaluation Count:71304
396-71304
245 strokeSelection |= AntiAliased;
executed: strokeSelection |= AntiAliased;
Execution Count:396
396
246 -
247 const QVector<qreal> &penPattern = state->lastPen.dashPattern();
executed (the execution status of this line is deduced): const QVector<qreal> &penPattern = state->lastPen.dashPattern();
-
248 if (penPattern.isEmpty()) {
evaluated: penPattern.isEmpty()
TRUEFALSE
yes
Evaluation Count:71677
yes
Evaluation Count:23
23-71677
249 Q_ASSERT(!pattern && !reversePattern);
executed (the execution status of this line is deduced): qt_noop();
-
250 pattern = 0;
executed (the execution status of this line is deduced): pattern = 0;
-
251 reversePattern = 0;
executed (the execution status of this line is deduced): reversePattern = 0;
-
252 patternLength = 0;
executed (the execution status of this line is deduced): patternLength = 0;
-
253 patternSize = 0;
executed (the execution status of this line is deduced): patternSize = 0;
-
254 } else {
executed: }
Execution Count:71677
71677
255 pattern = (int *)malloc(penPattern.size()*sizeof(int));
executed (the execution status of this line is deduced): pattern = (int *)malloc(penPattern.size()*sizeof(int));
-
256 reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
executed (the execution status of this line is deduced): reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
-
257 patternSize = penPattern.size();
executed (the execution status of this line is deduced): patternSize = penPattern.size();
-
258 -
259 patternLength = 0;
executed (the execution status of this line is deduced): patternLength = 0;
-
260 for (int i = 0; i < patternSize; ++i) {
evaluated: i < patternSize
TRUEFALSE
yes
Evaluation Count:46
yes
Evaluation Count:23
23-46
261 patternLength += (int) qMax(1. , penPattern.at(i)*64.);
executed (the execution status of this line is deduced): patternLength += (int) qMax(1. , penPattern.at(i)*64.);
-
262 pattern[i] = patternLength;
executed (the execution status of this line is deduced): pattern[i] = patternLength;
-
263 }
executed: }
Execution Count:46
46
264 patternLength = 0;
executed (the execution status of this line is deduced): patternLength = 0;
-
265 for (int i = 0; i < patternSize; ++i) {
evaluated: i < patternSize
TRUEFALSE
yes
Evaluation Count:46
yes
Evaluation Count:23
23-46
266 patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
executed (the execution status of this line is deduced): patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
-
267 reversePattern[i] = patternLength;
executed (the execution status of this line is deduced): reversePattern[i] = patternLength;
-
268 }
executed: }
Execution Count:46
46
269 strokeSelection |= Dashed;
executed (the execution status of this line is deduced): strokeSelection |= Dashed;
-
270// qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.; -
271 }
executed: }
Execution Count:23
23
272 -
273 stroke = strokeLine(strokeSelection);
executed (the execution status of this line is deduced): stroke = strokeLine(strokeSelection);
-
274 -
275 qreal width = state->lastPen.widthF();
executed (the execution status of this line is deduced): qreal width = state->lastPen.widthF();
-
276 if (width == 0)
evaluated: width == 0
TRUEFALSE
yes
Evaluation Count:1879
yes
Evaluation Count:69821
1879-69821
277 opacity = 256;
executed: opacity = 256;
Execution Count:1879
1879
278 else if (qt_pen_is_cosmetic(state->lastPen, state->renderHints))
evaluated: qt_pen_is_cosmetic(state->lastPen, state->renderHints)
TRUEFALSE
yes
Evaluation Count:990
yes
Evaluation Count:68831
990-68831
279 opacity = (int) 256*width;
executed: opacity = (int) 256*width;
Execution Count:990
990
280 else -
281 opacity = (int) 256*width*state->txscale;
executed: opacity = (int) 256*width*state->txscale;
Execution Count:68831
68831
282 opacity = qBound(0, opacity, 256);
executed (the execution status of this line is deduced): opacity = qBound(0, opacity, 256);
-
283 -
284 drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
executed (the execution status of this line is deduced): drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
-
285 -
286 if (strokeSelection & FastDraw) {
evaluated: strokeSelection & FastDraw
TRUEFALSE
yes
Evaluation Count:471
yes
Evaluation Count:71229
471-71229
287 color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
executed (the execution status of this line is deduced): color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
-
288 QRasterBuffer *buffer = state->penData.rasterBuffer;
executed (the execution status of this line is deduced): QRasterBuffer *buffer = state->penData.rasterBuffer;
-
289 pixels = (uint *)buffer->buffer();
executed (the execution status of this line is deduced): pixels = (uint *)buffer->buffer();
-
290 ppl = buffer->bytesPerLine()>>2;
executed (the execution status of this line is deduced): ppl = buffer->bytesPerLine()>>2;
-
291 }
executed: }
Execution Count:471
471
292 -
293 // line drawing produces different results with different clips, so -
294 // we need to clip consistently when painting to the same device -
295 -
296 // setup FP clip bounds -
297 xmin = clipdeviceRect.left() - 1;
executed (the execution status of this line is deduced): xmin = deviceRect.left() - 1;
-
298 xmax = clipdeviceRect.right() + 2;
executed (the execution status of this line is deduced): xmax = deviceRect.right() + 2;
-
299 ymin = clipdeviceRect.top() - 1;
executed (the execution status of this line is deduced): ymin = deviceRect.top() - 1;
-
300 ymax = clipdeviceRect.bottom() + 2;
executed (the execution status of this line is deduced): ymax = deviceRect.bottom() + 2;
-
301 -
302 lastPixel.x = -1;
executed (the execution status of this line is deduced): lastPixel.x = -1;
-
303}
executed: }
Execution Count:71700
71700
304 -
305// returns true if the whole line gets clipped away -
306bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2) -
307{ -
308 // basic/rough clipping is done in floating point coordinates to avoid -
309 // integer overflow problems. -
310 if (x1 < xmin) { -
311 if (x2 <= xmin) -
312 goto clipped; -
313 y1 += (y2 - y1)/(x2 - x1) * (xmin - x1); -
314 x1 = xmin; -
315 } else if (x1 > xmax) { -
316 if (x2 >= xmax) -
317 goto clipped; -
318 y1 += (y2 - y1)/(x2 - x1) * (xmax - x1); -
319 x1 = xmax; -
320 } -
321 if (x2 < xmin) { -
322 lastPixel.x = -1; -
323 y2 += (y2 - y1)/(x2 - x1) * (xmin - x2); -
324 x2 = xmin; -
325 } else if (x2 > xmax) { -
326 lastPixel.x = -1; -
327 y2 += (y2 - y1)/(x2 - x1) * (xmax - x2); -
328 x2 = xmax; -
329 } -
330 -
331 if (y1 < ymin) { -
332 if (y2 <= ymin) -
333 goto clipped; -
334 x1 += (x2 - x1)/(y2 - y1) * (ymin - y1); -
335 y1 = ymin; -
336 } else if (y1 > ymax) { -
337 if (y2 >= ymax) -
338 goto clipped; -
339 x1 += (x2 - x1)/(y2 - y1) * (ymax - y1); -
340 y1 = ymax; -
341 } -
342 if (y2 < ymin) { -
343 lastPixel.x = -1; -
344 x2 += (x2 - x1)/(y2 - y1) * (ymin - y2); -
345 y2 = ymin; -
346 } else if (y2 > ymax) { -
347 lastPixel.x = -1; -
348 x2 += (x2 - x1)/(y2 - y1) * (ymax - y2); -
349 y2 = ymax; -
350 } -
351 -
352 return false; -
353 -
354 clipped: -
355 lastPixel.x = -1; -
356 return true; -
357} -
358 -
359 -
360void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2) -
361{ -
362 if (p1 == p2) { -
363 drawPoints(&p1, 1); -
364 return; -
365 } -
366 -
367 QPointF start = p1 * state->matrix; -
368 QPointF end = p2 * state->matrix; -
369 -
370 patternOffset = state->lastPen.dashOffset()*64; -
371 lastPixel.x = -1; -
372 -
373 stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0); -
374 -
375 blend(current_span, spans, &state->penData); -
376 current_span = 0; -
377} -
378 -
379void QCosmeticStroker::drawPoints(const QPoint *points, int num) -
380{ -
381 const QPoint *end = points + num; -
382 while (points < end) { -
383 QPointF p = QPointF(*points) * state->matrix; -
384 drawPixel(this, qRound(p.x()), qRound(p.y()), 255); -
385 ++points; -
386 } -
387 -
388 blend(current_span, spans, &state->penData); -
389 current_span = 0; -
390} -
391 -
392void QCosmeticStroker::drawPoints(const QPointF *points, int num) -
393{ -
394 const QPointF *end = points + num; -
395 while (points < end) { -
396 QPointF p = (*points) * state->matrix; -
397 drawPixel(this, qRound(p.x()), qRound(p.y()), 255); -
398 ++points; -
399 } -
400 -
401 blend(current_span, spans, &state->penData); -
402 current_span = 0; -
403} -
404 -
405void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2) -
406{ -
407 // this is basically the same code as used in the aliased stroke method, -
408 // but it only determines the direction and last point of a line -
409 // -
410 // This is being used to have proper dropout control for closed contours -
411 // by calculating the direction and last pixel of the last segment in the contour. -
412 // the info is then used to perform dropout control when drawing the first line segment -
413 // of the contour -
414 lastPixel.x = -1; -
415 lastPixel.y = -1; -
416 -
417 if (clipLine(rx1, ry1, rx2, ry2)) -
418 return; -
419 -
420 const int half = legacyRounding ? 31 : 0; -
421 int x1 = toF26Dot6(rx1) + half; -
422 int y1 = toF26Dot6(ry1) + half; -
423 int x2 = toF26Dot6(rx2) + half; -
424 int y2 = toF26Dot6(ry2) + half; -
425 -
426 int dx = qAbs(x2 - x1); -
427 int dy = qAbs(y2 - y1); -
428 -
429 if (dx < dy) { -
430 // vertical -
431 bool swapped = false; -
432 if (y1 > y2) { -
433 swapped = true; -
434 qSwap(y1, y2); -
435 qSwap(x1, x2); -
436 } -
437 int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1); -
438 int x = x1 << 10; -
439 -
440 int y = (y1 + 32) >> 6; -
441 int ys = (y2 + 32) >> 6; -
442 -
443 if (y != ys) { -
444 x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6; -
445 -
446 if (swapped) { -
447 lastPixel.x = x >> 16; -
448 lastPixel.y = y; -
449 lastDir = QCosmeticStroker::BottomToTop; -
450 } else { -
451 lastPixel.x = (x + (ys - y - 1)*xinc) >> 16; -
452 lastPixel.y = ys - 1; -
453 lastDir = QCosmeticStroker::TopToBottom; -
454 } -
455 lastAxisAligned = qAbs(xinc) < (1 << 14); -
456 } -
457 } else { -
458 // horizontal -
459 if (!dx) -
460 return; -
461 -
462 bool swapped = false; -
463 if (x1 > x2) { -
464 swapped = true; -
465 qSwap(x1, x2); -
466 qSwap(y1, y2); -
467 } -
468 int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1); -
469 int y = y1 << 10; -
470 -
471 int x = (x1 + 32) >> 6; -
472 int xs = (x2 + 32) >> 6; -
473 -
474 if (x != xs) { -
475 y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6; -
476 -
477 if (swapped) { -
478 lastPixel.x = x; -
479 lastPixel.y = y >> 16; -
480 lastDir = QCosmeticStroker::RightToLeft; -
481 } else { -
482 lastPixel.x = xs - 1; -
483 lastPixel.y = (y + (xs - x - 1)*yinc) >> 16; -
484 lastDir = QCosmeticStroker::LeftToRight; -
485 } -
486 lastAxisAligned = qAbs(yinc) < (1 << 14); -
487 } -
488 } -
489// qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir; -
490} -
491 -
492static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, -
493 const qreal *points, bool *closed) -
494{ -
495 const QPainterPath::ElementType *start = t; -
496 ++t; -
497 -
498 // find out if the subpath is closed -
499 while (t < end) { -
500 if (*t == QPainterPath::MoveToElement) -
501 break; -
502 ++t; -
503 } -
504 -
505 int offset = t - start - 1; -
506// qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1]; -
507 *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]); -
508 -
509 return t; -
510} -
511 -
512void QCosmeticStroker::drawPath(const QVectorPath &path) -
513{ -
514// qDebug() << ">>>> drawpath" << path.convertToPainterPath() -
515// << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose(); -
516 if (path.isEmpty()) -
517 return; -
518 -
519 const qreal *points = path.points(); -
520 const QPainterPath::ElementType *type = path.elements(); -
521 -
522 if (type) { -
523 const QPainterPath::ElementType *end = type + path.elementCount(); -
524 -
525 while (type < end) { -
526 Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement); -
527 -
528 QPointF p = QPointF(points[0], points[1]) * state->matrix; -
529 patternOffset = state->lastPen.dashOffset()*64; -
530 lastPixel.x = -1; -
531 -
532 bool closed; -
533 const QPainterPath::ElementType *e = subPath(type, end, points, &closed); -
534 if (closed) { -
535 const qreal *p = points + 2*(e-type); -
536 QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix; -
537 QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix; -
538 calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y()); -
539 } -
540 int caps = (!closed & drawCaps) ? CapBegin : NoCaps; -
541// qDebug() << "closed =" << closed << capString(caps); -
542 -
543 points += 2; -
544 ++type; -
545 -
546 while (type < e) { -
547 QPointF p2 = QPointF(points[0], points[1]) * state->matrix; -
548 switch (*type) { -
549 case QPainterPath::MoveToElement: -
550 Q_ASSERT(!"Logic error"); -
551 break; -
552 -
553 case QPainterPath::LineToElement: -
554 if (!closed && drawCaps && type == e - 1) -
555 caps |= CapEnd; -
556 stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); -
557 p = p2; -
558 points += 2; -
559 ++type; -
560 break; -
561 -
562 case QPainterPath::CurveToElement: { -
563 if (!closed && drawCaps && type == e - 3) -
564 caps |= CapEnd; -
565 QPointF p3 = QPointF(points[2], points[3]) * state->matrix; -
566 QPointF p4 = QPointF(points[4], points[5]) * state->matrix; -
567 renderCubic(p, p2, p3, p4, caps); -
568 p = p4; -
569 type += 3; -
570 points += 6; -
571 break; -
572 } -
573 case QPainterPath::CurveToDataElement: -
574 Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type"); -
575 break; -
576 } -
577 caps = NoCaps; -
578 } -
579 } -
580 } else { // !type, simple polygon -
581 QPointF p = QPointF(points[0], points[1]) * state->matrix; -
582 QPointF movedTo = p; -
583 patternOffset = state->lastPen.dashOffset()*64; -
584 lastPixel.x = -1; -
585 -
586 const qreal *end = points + 2*path.elementCount(); -
587 // handle closed path case -
588 bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]); -
589 int caps = (!closed & drawCaps) ? CapBegin : NoCaps; -
590 if (closed) { -
591 QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix; -
592 calculateLastPoint(p2.x(), p2.y(), p.x(), p.y()); -
593 } -
594 -
595 points += 2; -
596 while (points < end) { -
597 QPointF p2 = QPointF(points[0], points[1]) * state->matrix; -
598 -
599 if (!closed && drawCaps && points == end - 2) -
600 caps |= CapEnd; -
601 -
602 stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); -
603 -
604 p = p2; -
605 points += 2; -
606 caps = NoCaps; -
607 } -
608 if (path.hasImplicitClose()) -
609 stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps); -
610 } -
611 -
612 -
613 blend(current_span, spans, &state->penData); -
614 current_span = 0; -
615} -
616 -
617void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps) -
618{ -
619// qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps); -
620 const int maxSubDivisions = 6; -
621 PointF points[3*maxSubDivisions + 4]; -
622 -
623 points[3].x = p1.x(); -
624 points[3].y = p1.y(); -
625 points[2].x = p2.x(); -
626 points[2].y = p2.y(); -
627 points[1].x = p3.x(); -
628 points[1].y = p3.y(); -
629 points[0].x = p4.x(); -
630 points[0].y = p4.y(); -
631 -
632 PointF *p = points; -
633 int level = maxSubDivisions; -
634 -
635 renderCubicSubdivision(p, level, caps); -
636} -
637 -
638static void splitCubic(QCosmeticStroker::PointF *points) -
639{ -
640 const qreal half = .5; -
641 qreal a, b, c, d; -
642 -
643 points[6].x = points[3].x; -
644 c = points[1].x; -
645 d = points[2].x; -
646 points[1].x = a = ( points[0].x + c ) * half; -
647 points[5].x = b = ( points[3].x + d ) * half; -
648 c = ( c + d ) * half; -
649 points[2].x = a = ( a + c ) * half; -
650 points[4].x = b = ( b + c ) * half; -
651 points[3].x = ( a + b ) * half; -
652 -
653 points[6].y = points[3].y; -
654 c = points[1].y; -
655 d = points[2].y; -
656 points[1].y = a = ( points[0].y + c ) * half; -
657 points[5].y = b = ( points[3].y + d ) * half; -
658 c = ( c + d ) * half; -
659 points[2].y = a = ( a + c ) * half; -
660 points[4].y = b = ( b + c ) * half; -
661 points[3].y = ( a + b ) * half; -
662} -
663 -
664void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps) -
665{ -
666 if (level) { -
667 qreal dx = points[3].x - points[0].x; -
668 qreal dy = points[3].y - points[0].y; -
669 qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy)); -
670 -
671 if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len || -
672 qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) { -
673 splitCubic(points); -
674 -
675 --level; -
676 renderCubicSubdivision(points + 3, level, caps & CapBegin); -
677 renderCubicSubdivision(points, level, caps & CapEnd); -
678 return; -
679 } -
680 } -
681 -
682 stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps); -
683} -
684 -
685static inline int swapCaps(int caps) -
686{ -
687 return ((caps & QCosmeticStroker::CapBegin) << 1) | -
688 ((caps & QCosmeticStroker::CapEnd) >> 1); -
689} -
690 -
691// adjust line by half a pixel -
692static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc) -
693{ -
694 if (caps & QCosmeticStroker::CapBegin) { -
695 x1 -= 32; -
696 y -= yinc >> 1; -
697 } -
698 if (caps & QCosmeticStroker::CapEnd) { -
699 x2 += 32; -
700 } -
701} -
702 -
703/* -
704 The hard part about this is dropout control and avoiding douple drawing of points when -
705 the drawing shifts from horizontal to vertical or back. -
706 */ -
707template<DrawPixel drawPixel, class Dasher> -
708static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) -
709{ -
710 if (stroker->clipLine(rx1, ry1, rx2, ry2)) -
711 return; -
712 -
713 const int half = stroker->legacyRounding ? 31 : 0; -
714 int x1 = toF26Dot6(rx1) + half; -
715 int y1 = toF26Dot6(ry1) + half; -
716 int x2 = toF26Dot6(rx2) + half; -
717 int y2 = toF26Dot6(ry2) + half; -
718 -
719 int dx = qAbs(x2 - x1); -
720 int dy = qAbs(y2 - y1); -
721 -
722 QCosmeticStroker::Point last = stroker->lastPixel; -
723 -
724// qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.; -
725 -
726 if (dx < dy) { -
727 // vertical -
728 QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom; -
729 -
730 bool swapped = false; -
731 if (y1 > y2) { -
732 swapped = true; -
733 qSwap(y1, y2); -
734 qSwap(x1, x2); -
735 caps = swapCaps(caps); -
736 dir = QCosmeticStroker::BottomToTop; -
737 } -
738 int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1); -
739 int x = x1 << 10; -
740 -
741 if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir) -
742 caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin; -
743 -
744 capAdjust(caps, y1, y2, x, xinc); -
745 -
746 int y = (y1 + 32) >> 6; -
747 int ys = (y2 + 32) >> 6; -
748 -
749 if (y != ys) { -
750 x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6; -
751 -
752 // calculate first and last pixel and perform dropout control -
753 QCosmeticStroker::Point first; -
754 first.x = x >> 16; -
755 first.y = y; -
756 last.x = (x + (ys - y - 1)*xinc) >> 16; -
757 last.y = ys - 1; -
758 if (swapped) -
759 qSwap(first, last); -
760 -
761 bool axisAligned = qAbs(xinc) < (1 << 14); -
762 if (stroker->lastPixel.x >= 0) { -
763 if (first.x == stroker->lastPixel.x && -
764 first.y == stroker->lastPixel.y) { -
765 // remove duplicated pixel -
766 if (swapped) { -
767 --ys; -
768 } else { -
769 ++y; -
770 x += xinc; -
771 } -
772 } else if (stroker->lastDir != dir && -
773 (((axisAligned && stroker->lastAxisAligned) && -
774 stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) || -
775 (qAbs(stroker->lastPixel.x - first.x) > 1 || -
776 qAbs(stroker->lastPixel.y - first.y) > 1))) { -
777 // have a missing pixel, insert it -
778 if (swapped) { -
779 ++ys; -
780 } else { -
781 --y; -
782 x -= xinc; -
783 } -
784 } -
785 } -
786 stroker->lastDir = dir; -
787 stroker->lastAxisAligned = axisAligned; -
788 -
789 Dasher dasher(stroker, swapped, y << 6, ys << 6); -
790 -
791 do { -
792 if (dasher.on()) -
793 drawPixel(stroker, x >> 16, y, 255); -
794 dasher.adjust(); -
795 x += xinc; -
796 } while (++y < ys); -
797 } -
798 } else { -
799 // horizontal -
800 if (!dx) -
801 return; -
802 -
803 QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight; -
804 -
805 bool swapped = false; -
806 if (x1 > x2) { -
807 swapped = true; -
808 qSwap(x1, x2); -
809 qSwap(y1, y2); -
810 caps = swapCaps(caps); -
811 dir = QCosmeticStroker::RightToLeft; -
812 } -
813 int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1); -
814 int y = y1 << 10; -
815 -
816 if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir) -
817 caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin; -
818 -
819 capAdjust(caps, x1, x2, y, yinc); -
820 -
821 int x = (x1 + 32) >> 6; -
822 int xs = (x2 + 32) >> 6; -
823 -
824 if (x != xs) { -
825 y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6; -
826 -
827 // calculate first and last pixel to perform dropout control -
828 QCosmeticStroker::Point first; -
829 first.x = x; -
830 first.y = y >> 16; -
831 last.x = xs - 1; -
832 last.y = (y + (xs - x - 1)*yinc) >> 16; -
833 if (swapped) -
834 qSwap(first, last); -
835 -
836 bool axisAligned = qAbs(yinc) < (1 << 14); -
837 if (stroker->lastPixel.x >= 0) { -
838 if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) { -
839 // remove duplicated pixel -
840 if (swapped) { -
841 --xs; -
842 } else { -
843 ++x; -
844 y += yinc; -
845 } -
846 } else if (stroker->lastDir != dir && -
847 (((axisAligned && stroker->lastAxisAligned) && -
848 stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) || -
849 (qAbs(stroker->lastPixel.x - first.x) > 1 || -
850 qAbs(stroker->lastPixel.y - first.y) > 1))) { -
851 // have a missing pixel, insert it -
852 if (swapped) { -
853 ++xs; -
854 } else { -
855 --x; -
856 y -= yinc; -
857 } -
858 } -
859 } -
860 stroker->lastDir = dir; -
861 stroker->lastAxisAligned = axisAligned; -
862 -
863 Dasher dasher(stroker, swapped, x << 6, xs << 6); -
864 -
865 do { -
866 if (dasher.on()) -
867 drawPixel(stroker, x, y >> 16, 255); -
868 dasher.adjust(); -
869 y += yinc; -
870 } while (++x < xs); -
871 } -
872 } -
873 stroker->lastPixel = last; -
874} -
875 -
876 -
877template<DrawPixel drawPixel, class Dasher> -
878static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) -
879{ -
880 if (stroker->clipLine(rx1, ry1, rx2, ry2)) -
881 return; -
882 -
883 int x1 = toF26Dot6(rx1); -
884 int y1 = toF26Dot6(ry1); -
885 int x2 = toF26Dot6(rx2); -
886 int y2 = toF26Dot6(ry2); -
887 -
888 int dx = x2 - x1; -
889 int dy = y2 - y1; -
890 -
891 if (qAbs(dx) < qAbs(dy)) { -
892 // vertical -
893 -
894 int xinc = F16Dot16FixedDiv(dx, dy); -
895 -
896 bool swapped = false; -
897 if (y1 > y2) { -
898 qSwap(y1, y2); -
899 qSwap(x1, x2); -
900 swapped = true; -
901 caps = swapCaps(caps); -
902 } -
903 -
904 int x = (x1 - 32) << 10; -
905 x -= ( ((y1 & 63) - 32) * xinc ) >> 6; -
906 -
907 capAdjust(caps, y1, y2, x, xinc); -
908 -
909 Dasher dasher(stroker, swapped, y1, y2); -
910 -
911 int y = y1 >> 6; -
912 int ys = y2 >> 6; -
913 -
914 int alphaStart, alphaEnd; -
915 if (y == ys) { -
916 alphaStart = y2 - y1; -
917 Q_ASSERT(alphaStart >= 0 && alphaStart < 64); -
918 alphaEnd = 0; -
919 } else { -
920 alphaStart = 64 - (y1 & 63); -
921 alphaEnd = (y2 & 63); -
922 } -
923// qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.; -
924// qDebug() << " x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys; -
925 -
926 // draw first pixel -
927 if (dasher.on()) { -
928 uint alpha = (quint8)(x >> 8); -
929 drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6); -
930 drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6); -
931 } -
932 dasher.adjust(); -
933 x += xinc; -
934 ++y; -
935 if (y < ys) { -
936 do { -
937 if (dasher.on()) { -
938 uint alpha = (quint8)(x >> 8); -
939 drawPixel(stroker, x>>16, y, (255-alpha)); -
940 drawPixel(stroker, (x>>16) + 1, y, alpha); -
941 } -
942 dasher.adjust(); -
943 x += xinc; -
944 } while (++y < ys); -
945 } -
946 // draw last pixel -
947 if (alphaEnd && dasher.on()) { -
948 uint alpha = (quint8)(x >> 8); -
949 drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6); -
950 drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6); -
951 } -
952 } else { -
953 // horizontal -
954 if (!dx) -
955 return; -
956 -
957 int yinc = F16Dot16FixedDiv(dy, dx); -
958 -
959 bool swapped = false; -
960 if (x1 > x2) { -
961 qSwap(x1, x2); -
962 qSwap(y1, y2); -
963 swapped = true; -
964 caps = swapCaps(caps); -
965 } -
966 -
967 int y = (y1 - 32) << 10; -
968 y -= ( ((x1 & 63) - 32) * yinc ) >> 6; -
969 -
970 capAdjust(caps, x1, x2, y, yinc); -
971 -
972 Dasher dasher(stroker, swapped, x1, x2); -
973 -
974 int x = x1 >> 6; -
975 int xs = x2 >> 6; -
976 -
977// qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.; -
978// qDebug() << " y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16); -
979 int alphaStart, alphaEnd; -
980 if (x == xs) { -
981 alphaStart = x2 - x1; -
982 Q_ASSERT(alphaStart >= 0 && alphaStart < 64); -
983 alphaEnd = 0; -
984 } else { -
985 alphaStart = 64 - (x1 & 63); -
986 alphaEnd = (x2 & 63); -
987 } -
988 -
989 // draw first pixel -
990 if (dasher.on()) { -
991 uint alpha = (quint8)(y >> 8); -
992 drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6); -
993 drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6); -
994 } -
995 dasher.adjust(); -
996 y += yinc; -
997 ++x; -
998 // draw line -
999 if (x < xs) { -
1000 do { -
1001 if (dasher.on()) { -
1002 uint alpha = (quint8)(y >> 8); -
1003 drawPixel(stroker, x, y>>16, (255-alpha)); -
1004 drawPixel(stroker, x, (y>>16) + 1, alpha); -
1005 } -
1006 dasher.adjust(); -
1007 y += yinc; -
1008 } while (++x < xs); -
1009 } -
1010 // draw last pixel -
1011 if (alphaEnd && dasher.on()) { -
1012 uint alpha = (quint8)(y >> 8); -
1013 drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6); -
1014 drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6); -
1015 } -
1016 } -
1017} -
1018 -
1019QT_END_NAMESPACE -
1020 -
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial