2011-01-30 Simon Fraser <simon.fraser@apple.com>
[WebKit-https.git] / Source / WebCore / platform / graphics / GraphicsContext.cpp
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "GraphicsContext.h"
28
29 #include "BidiResolver.h"
30 #include "Font.h"
31 #include "Generator.h"
32 #include "ImageBuffer.h"
33 #include "IntRect.h"
34 #include "RoundedIntRect.h"
35 #include "TextRun.h"
36
37 using namespace std;
38
39 namespace WebCore {
40
41 class TextRunIterator {
42 public:
43     TextRunIterator()
44         : m_textRun(0)
45         , m_offset(0)
46     {
47     }
48
49     TextRunIterator(const TextRun* textRun, unsigned offset)
50         : m_textRun(textRun)
51         , m_offset(offset)
52     {
53     }
54
55     TextRunIterator(const TextRunIterator& other)
56         : m_textRun(other.m_textRun)
57         , m_offset(other.m_offset)
58     {
59     }
60
61     unsigned offset() const { return m_offset; }
62     void increment() { m_offset++; }
63     bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
64     UChar current() const { return (*m_textRun)[m_offset]; }
65     WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
66
67     bool operator==(const TextRunIterator& other)
68     {
69         return m_offset == other.m_offset && m_textRun == other.m_textRun;
70     }
71
72     bool operator!=(const TextRunIterator& other) { return !operator==(other); }
73
74 private:
75     const TextRun* m_textRun;
76     int m_offset;
77 };
78
79 GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
80     : m_updatingControlTints(false)
81 {
82     platformInit(platformGraphicsContext);
83 }
84
85 GraphicsContext::~GraphicsContext()
86 {
87     platformDestroy();
88 }
89
90 void GraphicsContext::save()
91 {
92     if (paintingDisabled())
93         return;
94
95     m_stack.append(m_state);
96
97     savePlatformState();
98 }
99
100 void GraphicsContext::restore()
101 {
102     if (paintingDisabled())
103         return;
104
105     if (m_stack.isEmpty()) {
106         LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
107         return;
108     }
109     m_state = m_stack.last();
110     m_stack.removeLast();
111
112     restorePlatformState();
113 }
114
115 void GraphicsContext::setStrokeThickness(float thickness)
116 {
117     m_state.strokeThickness = thickness;
118     setPlatformStrokeThickness(thickness);
119 }
120
121 void GraphicsContext::setStrokeStyle(StrokeStyle style)
122 {
123     m_state.strokeStyle = style;
124     setPlatformStrokeStyle(style);
125 }
126
127 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
128 {
129     m_state.strokeColor = color;
130     m_state.strokeColorSpace = colorSpace;
131     m_state.strokeGradient.clear();
132     m_state.strokePattern.clear();
133     setPlatformStrokeColor(color, colorSpace);
134 }
135
136 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
137 {
138     m_state.shadowOffset = offset;
139     m_state.shadowBlur = blur;
140     m_state.shadowColor = color;
141     m_state.shadowColorSpace = colorSpace;
142     setPlatformShadow(offset, blur, color, colorSpace);
143 }
144
145 void GraphicsContext::clearShadow()
146 {
147     m_state.shadowOffset = FloatSize();
148     m_state.shadowBlur = 0;
149     m_state.shadowColor = Color();
150     m_state.shadowColorSpace = ColorSpaceDeviceRGB;
151     clearPlatformShadow();
152 }
153
154 bool GraphicsContext::hasShadow() const
155 {
156     return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
157            && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
158 }
159
160 bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
161 {
162     offset = m_state.shadowOffset;
163     blur = m_state.shadowBlur;
164     color = m_state.shadowColor;
165     colorSpace = m_state.shadowColorSpace;
166
167     return hasShadow();
168 }
169
170 float GraphicsContext::strokeThickness() const
171 {
172     return m_state.strokeThickness;
173 }
174
175 StrokeStyle GraphicsContext::strokeStyle() const
176 {
177     return m_state.strokeStyle;
178 }
179
180 Color GraphicsContext::strokeColor() const
181 {
182     return m_state.strokeColor;
183 }
184
185 ColorSpace GraphicsContext::strokeColorSpace() const
186 {
187     return m_state.strokeColorSpace;
188 }
189
190 WindRule GraphicsContext::fillRule() const
191 {
192     return m_state.fillRule;
193 }
194
195 void GraphicsContext::setFillRule(WindRule fillRule)
196 {
197     m_state.fillRule = fillRule;
198 }
199
200 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
201 {
202     m_state.fillColor = color;
203     m_state.fillColorSpace = colorSpace;
204     m_state.fillGradient.clear();
205     m_state.fillPattern.clear();
206     setPlatformFillColor(color, colorSpace);
207 }
208
209 Color GraphicsContext::fillColor() const
210 {
211     return m_state.fillColor;
212 }
213
214 ColorSpace GraphicsContext::fillColorSpace() const
215 {
216     return m_state.fillColorSpace;
217 }
218
219 void GraphicsContext::setShouldAntialias(bool b)
220 {
221     m_state.shouldAntialias = b;
222     setPlatformShouldAntialias(b);
223 }
224
225 bool GraphicsContext::shouldAntialias() const
226 {
227     return m_state.shouldAntialias;
228 }
229
230 void GraphicsContext::setShouldSmoothFonts(bool b)
231 {
232     m_state.shouldSmoothFonts = b;
233     setPlatformShouldSmoothFonts(b);
234 }
235
236 bool GraphicsContext::shouldSmoothFonts() const
237 {
238     return m_state.shouldSmoothFonts;
239 }
240
241 const GraphicsContextState& GraphicsContext::state() const
242 {
243     return m_state;
244 }
245
246 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
247 {
248     ASSERT(pattern);
249     if (!pattern) {
250         setStrokeColor(Color::black, ColorSpaceDeviceRGB);
251         return;
252     }
253     m_state.strokeGradient.clear();
254     m_state.strokePattern = pattern;
255     setPlatformStrokePattern(m_state.strokePattern.get());
256 }
257
258 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
259 {
260     ASSERT(pattern);
261     if (!pattern) {
262         setFillColor(Color::black, ColorSpaceDeviceRGB);
263         return;
264     }
265     m_state.fillGradient.clear();
266     m_state.fillPattern = pattern;
267     setPlatformFillPattern(m_state.fillPattern.get());
268 }
269
270 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
271 {
272     ASSERT(gradient);
273     if (!gradient) {
274         setStrokeColor(Color::black, ColorSpaceDeviceRGB);
275         return;
276     }
277     m_state.strokeGradient = gradient;
278     m_state.strokePattern.clear();
279     setPlatformStrokeGradient(m_state.strokeGradient.get());
280 }
281
282 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
283 {
284     ASSERT(gradient);
285     if (!gradient) {
286         setFillColor(Color::black, ColorSpaceDeviceRGB);
287         return;
288     }
289     m_state.fillGradient = gradient;
290     m_state.fillPattern.clear();
291     setPlatformFillGradient(m_state.fillGradient.get());
292 }
293
294 Gradient* GraphicsContext::fillGradient() const
295 {
296     return m_state.fillGradient.get();
297 }
298
299 Gradient* GraphicsContext::strokeGradient() const
300 {
301     return m_state.strokeGradient.get();
302 }
303
304 Pattern* GraphicsContext::fillPattern() const
305 {
306     return m_state.fillPattern.get();
307 }
308
309 Pattern* GraphicsContext::strokePattern() const
310 {
311     return m_state.strokePattern.get();
312 }
313
314 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
315 {
316     m_state.shadowsIgnoreTransforms = ignoreTransforms;
317 }
318
319 bool GraphicsContext::shadowsIgnoreTransforms() const
320 {
321     return m_state.shadowsIgnoreTransforms;
322 }
323
324 bool GraphicsContext::updatingControlTints() const
325 {
326     return m_updatingControlTints;
327 }
328
329 void GraphicsContext::setUpdatingControlTints(bool b)
330 {
331     setPaintingDisabled(b);
332     m_updatingControlTints = b;
333 }
334
335 void GraphicsContext::setPaintingDisabled(bool f)
336 {
337     m_state.paintingDisabled = f;
338 }
339
340 bool GraphicsContext::paintingDisabled() const
341 {
342     return m_state.paintingDisabled;
343 }
344
345 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
346 {
347     drawImage(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
348 }
349
350 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
351 {
352     drawImage(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
353 }
354
355 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
356 {
357     drawImage(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
358 }
359
360 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
361 {
362     drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
363 }
364
365 #if !OS(WINCE) || PLATFORM(QT)
366 void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to)
367 {
368     if (paintingDisabled())
369         return;
370
371     font.drawText(this, run, point, from, to);
372 }
373 #endif
374
375 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const IntPoint& point, int from, int to)
376 {
377     if (paintingDisabled())
378         return;
379
380     font.drawEmphasisMarks(this, run, mark, point, from, to);
381 }
382
383 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point)
384 {
385     if (paintingDisabled())
386         return;
387
388     BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
389     WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
390
391     bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
392
393     bidiResolver.setPosition(TextRunIterator(&run, 0));
394     bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
395
396     if (!bidiResolver.runCount())
397         return;
398
399     FloatPoint currPoint = point;
400     BidiCharacterRun* bidiRun = bidiResolver.firstRun();
401     while (bidiRun) {
402
403         TextRun subrun = run;
404         subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start());
405         subrun.setRTL(bidiRun->level() % 2);
406         subrun.setDirectionalOverride(bidiRun->dirOverride(false));
407
408         font.drawText(this, subrun, currPoint);
409
410         bidiRun = bidiRun->next();
411         // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
412         if (bidiRun)
413             currPoint.move(font.floatWidth(subrun), 0.f);
414     }
415
416     bidiResolver.deleteRuns();
417 }
418
419 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
420 {
421     if (paintingDisabled())
422         return;
423
424     fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
425 }
426
427 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
428 {
429     if (paintingDisabled() || !image)
430         return;
431
432     float tsw = src.width();
433     float tsh = src.height();
434     float tw = dest.width();
435     float th = dest.height();
436
437     if (tsw == -1)
438         tsw = image->width();
439     if (tsh == -1)
440         tsh = image->height();
441
442     if (tw == -1)
443         tw = image->width();
444     if (th == -1)
445         th = image->height();
446
447     if (useLowQualityScale) {
448         InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
449         // FIXME: Should be InterpolationLow
450         setImageInterpolationQuality(InterpolationNone);
451         image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
452         setImageInterpolationQuality(previousInterpolationQuality);
453     } else
454         image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
455 }
456
457 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale)
458 {
459     if (paintingDisabled() || !image)
460         return;
461
462     if (useLowQualityScale) {
463         InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
464         setImageInterpolationQuality(InterpolationLow);
465         image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
466         setImageInterpolationQuality(previousInterpolationQuality);
467     } else
468         image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
469 }
470
471 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
472 {
473     if (paintingDisabled() || !image)
474         return;
475
476     if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
477         // Just do a scale.
478         drawImage(image, styleColorSpace, dest, srcRect, op);
479         return;
480     }
481
482     if (useLowQualityScale) {
483         InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
484         setImageInterpolationQuality(InterpolationLow);
485         image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
486         setImageInterpolationQuality(previousInterpolationQuality);
487     } else
488         image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
489 }
490
491 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
492 {
493     drawImageBuffer(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
494 }
495
496 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
497 {
498     drawImageBuffer(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
499 }
500
501 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
502 {
503     drawImageBuffer(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
504 }
505
506 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
507 {
508     drawImageBuffer(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
509 }
510
511 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
512 {
513     if (paintingDisabled() || !image)
514         return;
515
516     float tsw = src.width();
517     float tsh = src.height();
518     float tw = dest.width();
519     float th = dest.height();
520
521     if (tsw == -1)
522         tsw = image->width();
523     if (tsh == -1)
524         tsh = image->height();
525
526     if (tw == -1)
527         tw = image->width();
528     if (th == -1)
529         th = image->height();
530
531     if (useLowQualityScale) {
532         InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
533         // FIXME: Should be InterpolationLow
534         setImageInterpolationQuality(InterpolationNone);
535         image->draw(this, styleColorSpace, dest, src, op, useLowQualityScale);
536         setImageInterpolationQuality(previousInterpolationQuality);
537     } else
538         image->draw(this, styleColorSpace, dest, src, op, useLowQualityScale);
539 }
540
541 void GraphicsContext::addRoundedRectClip(const RoundedIntRect& rect)
542 {
543     if (paintingDisabled())
544         return;
545
546     Path path;
547     path.addRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight());
548     clip(path);
549 }
550
551 void GraphicsContext::clipOutRoundedRect(const RoundedIntRect& rect)
552 {
553     if (paintingDisabled())
554         return;
555
556     Path path;
557     path.addRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight());
558     clipOut(path);
559 }
560
561 void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
562 {
563     if (paintingDisabled())
564         return;
565     buffer->clip(this, rect);
566 }
567
568 #if !PLATFORM(CG)
569 IntRect GraphicsContext::clipBounds() const
570 {
571     ASSERT_NOT_REACHED();
572     return IntRect();
573 }
574 #endif
575
576 TextDrawingModeFlags GraphicsContext::textDrawingMode() const
577 {
578     return m_state.textDrawingMode;
579 }
580
581 void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
582 {
583     m_state.textDrawingMode = mode;
584     if (paintingDisabled())
585         return;
586     setPlatformTextDrawingMode(mode);
587 }
588
589 void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator)
590 {
591     if (paintingDisabled())
592         return;
593     generator.fill(this, rect);
594 }
595
596 void GraphicsContext::fillRoundedRect(const RoundedIntRect& rect, const Color& color, ColorSpace colorSpace)
597 {
598     fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace);
599 }
600
601 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation)
602 {
603     m_state.compositeOperator = compositeOperation;
604     setPlatformCompositeOperation(compositeOperation);
605 }
606
607 CompositeOperator GraphicsContext::compositeOperation() const
608 {
609     return m_state.compositeOperator;
610 }
611
612 #if !PLATFORM(SKIA)
613 void GraphicsContext::setPlatformFillGradient(Gradient*)
614 {
615 }
616
617 void GraphicsContext::setPlatformFillPattern(Pattern*)
618 {
619 }
620
621 void GraphicsContext::setPlatformStrokeGradient(Gradient*)
622 {
623 }
624
625 void GraphicsContext::setPlatformStrokePattern(Pattern*)
626 {
627 }
628 #endif
629
630 #if !PLATFORM(CG) && !PLATFORM(SKIA)
631 // Implement this if you want to go ahead and push the drawing mode into your native context
632 // immediately.
633 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
634 {
635 }
636 #endif
637
638 #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !PLATFORM(SKIA) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG)
639 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
640 {
641 }
642 #endif
643
644 #if !PLATFORM(CG)
645 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
646 {
647 }
648 #endif
649
650 #if !PLATFORM(SKIA)
651 void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&)
652 {
653 }
654
655 void GraphicsContext::syncSoftwareCanvas()
656 {
657 }
658
659 void GraphicsContext::markDirtyRect(const IntRect&)
660 {
661 }
662 #endif
663
664
665 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
666 {
667     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
668     // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
669     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
670     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
671     if (penStyle == DottedStroke || penStyle == DashedStroke) {
672         if (p1.x() == p2.x()) {
673             p1.setY(p1.y() + strokeWidth);
674             p2.setY(p2.y() - strokeWidth);
675         } else {
676             p1.setX(p1.x() + strokeWidth);
677             p2.setX(p2.x() - strokeWidth);
678         }
679     }
680
681     if (static_cast<int>(strokeWidth) % 2) { //odd
682         if (p1.x() == p2.x()) {
683             // We're a vertical line.  Adjust our x.
684             p1.setX(p1.x() + 0.5f);
685             p2.setX(p2.x() + 0.5f);
686         } else {
687             // We're a horizontal line. Adjust our y.
688             p1.setY(p1.y() + 0.5f);
689             p2.setY(p2.y() + 0.5f);
690         }
691     }
692 }
693
694 }