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
37using namespace std;
38
39namespace WebCore {
40
41class TextRunIterator {
42public:
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
74private:
75    const TextRun* m_textRun;
76    int m_offset;
77};
78
79GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
80    : m_updatingControlTints(false)
81{
82    platformInit(platformGraphicsContext);
83}
84
85GraphicsContext::~GraphicsContext()
86{
87    platformDestroy();
88}
89
90void GraphicsContext::save()
91{
92    if (paintingDisabled())
93        return;
94
95    m_stack.append(m_state);
96
97    savePlatformState();
98}
99
100void 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
115void GraphicsContext::setStrokeThickness(float thickness)
116{
117    m_state.strokeThickness = thickness;
118    setPlatformStrokeThickness(thickness);
119}
120
121void GraphicsContext::setStrokeStyle(StrokeStyle style)
122{
123    m_state.strokeStyle = style;
124    setPlatformStrokeStyle(style);
125}
126
127void 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
136void 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
145void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
146{
147    m_state.shadowOffset = offset;
148    m_state.shadowBlur = blur;
149    m_state.shadowColor = color;
150    m_state.shadowColorSpace = colorSpace;
151#if USE(CG)
152    m_state.shadowsUseLegacyRadius = true;
153#endif
154    setPlatformShadow(offset, blur, color, colorSpace);
155}
156
157void GraphicsContext::clearShadow()
158{
159    m_state.shadowOffset = FloatSize();
160    m_state.shadowBlur = 0;
161    m_state.shadowColor = Color();
162    m_state.shadowColorSpace = ColorSpaceDeviceRGB;
163    clearPlatformShadow();
164}
165
166bool GraphicsContext::hasShadow() const
167{
168    return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
169           && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
170}
171
172bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
173{
174    offset = m_state.shadowOffset;
175    blur = m_state.shadowBlur;
176    color = m_state.shadowColor;
177    colorSpace = m_state.shadowColorSpace;
178
179    return hasShadow();
180}
181
182float GraphicsContext::strokeThickness() const
183{
184    return m_state.strokeThickness;
185}
186
187StrokeStyle GraphicsContext::strokeStyle() const
188{
189    return m_state.strokeStyle;
190}
191
192Color GraphicsContext::strokeColor() const
193{
194    return m_state.strokeColor;
195}
196
197ColorSpace GraphicsContext::strokeColorSpace() const
198{
199    return m_state.strokeColorSpace;
200}
201
202WindRule GraphicsContext::fillRule() const
203{
204    return m_state.fillRule;
205}
206
207void GraphicsContext::setFillRule(WindRule fillRule)
208{
209    m_state.fillRule = fillRule;
210}
211
212void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
213{
214    m_state.fillColor = color;
215    m_state.fillColorSpace = colorSpace;
216    m_state.fillGradient.clear();
217    m_state.fillPattern.clear();
218    setPlatformFillColor(color, colorSpace);
219}
220
221Color GraphicsContext::fillColor() const
222{
223    return m_state.fillColor;
224}
225
226ColorSpace GraphicsContext::fillColorSpace() const
227{
228    return m_state.fillColorSpace;
229}
230
231void GraphicsContext::setShouldAntialias(bool b)
232{
233    m_state.shouldAntialias = b;
234    setPlatformShouldAntialias(b);
235}
236
237bool GraphicsContext::shouldAntialias() const
238{
239    return m_state.shouldAntialias;
240}
241
242void GraphicsContext::setShouldSmoothFonts(bool b)
243{
244    m_state.shouldSmoothFonts = b;
245    setPlatformShouldSmoothFonts(b);
246}
247
248bool GraphicsContext::shouldSmoothFonts() const
249{
250    return m_state.shouldSmoothFonts;
251}
252
253const GraphicsContextState& GraphicsContext::state() const
254{
255    return m_state;
256}
257
258void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
259{
260    ASSERT(pattern);
261    if (!pattern) {
262        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
263        return;
264    }
265    m_state.strokeGradient.clear();
266    m_state.strokePattern = pattern;
267    setPlatformStrokePattern(m_state.strokePattern.get());
268}
269
270void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
271{
272    ASSERT(pattern);
273    if (!pattern) {
274        setFillColor(Color::black, ColorSpaceDeviceRGB);
275        return;
276    }
277    m_state.fillGradient.clear();
278    m_state.fillPattern = pattern;
279    setPlatformFillPattern(m_state.fillPattern.get());
280}
281
282void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
283{
284    ASSERT(gradient);
285    if (!gradient) {
286        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
287        return;
288    }
289    m_state.strokeGradient = gradient;
290    m_state.strokePattern.clear();
291    setPlatformStrokeGradient(m_state.strokeGradient.get());
292}
293
294void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
295{
296    ASSERT(gradient);
297    if (!gradient) {
298        setFillColor(Color::black, ColorSpaceDeviceRGB);
299        return;
300    }
301    m_state.fillGradient = gradient;
302    m_state.fillPattern.clear();
303    setPlatformFillGradient(m_state.fillGradient.get());
304}
305
306Gradient* GraphicsContext::fillGradient() const
307{
308    return m_state.fillGradient.get();
309}
310
311Gradient* GraphicsContext::strokeGradient() const
312{
313    return m_state.strokeGradient.get();
314}
315
316Pattern* GraphicsContext::fillPattern() const
317{
318    return m_state.fillPattern.get();
319}
320
321Pattern* GraphicsContext::strokePattern() const
322{
323    return m_state.strokePattern.get();
324}
325
326void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
327{
328    m_state.shadowsIgnoreTransforms = ignoreTransforms;
329}
330
331bool GraphicsContext::shadowsIgnoreTransforms() const
332{
333    return m_state.shadowsIgnoreTransforms;
334}
335
336bool GraphicsContext::updatingControlTints() const
337{
338    return m_updatingControlTints;
339}
340
341void GraphicsContext::setUpdatingControlTints(bool b)
342{
343    setPaintingDisabled(b);
344    m_updatingControlTints = b;
345}
346
347void GraphicsContext::setPaintingDisabled(bool f)
348{
349    m_state.paintingDisabled = f;
350}
351
352bool GraphicsContext::paintingDisabled() const
353{
354    return m_state.paintingDisabled;
355}
356
357void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
358{
359    drawImage(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
360}
361
362void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
363{
364    drawImage(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
365}
366
367void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
368{
369    drawImage(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
370}
371
372void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
373{
374    drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
375}
376
377#if !OS(WINCE) || PLATFORM(QT)
378void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
379{
380    if (paintingDisabled())
381        return;
382
383    font.drawText(this, run, point, from, to);
384}
385#endif
386
387void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
388{
389    if (paintingDisabled())
390        return;
391
392    font.drawEmphasisMarks(this, run, mark, point, from, to);
393}
394
395void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point)
396{
397    if (paintingDisabled())
398        return;
399
400    // FIXME: This ownership should be reversed. We should pass BidiRunList
401    // to BidiResolver in createBidiRunsForLine.
402    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
403    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
404
405    WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
406
407    bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
408
409    bidiResolver.setPosition(TextRunIterator(&run, 0));
410    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
411
412    if (!bidiRuns.runCount())
413        return;
414
415    FloatPoint currPoint = point;
416    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
417    while (bidiRun) {
418
419        TextRun subrun = run;
420        subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start());
421        subrun.setRTL(bidiRun->level() % 2);
422        subrun.setDirectionalOverride(bidiRun->dirOverride(false));
423
424        font.drawText(this, subrun, currPoint);
425
426        bidiRun = bidiRun->next();
427        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
428        if (bidiRun)
429            currPoint.move(font.width(subrun), 0);
430    }
431
432    bidiRuns.deleteRuns();
433}
434
435#if !PLATFORM(ANDROID)
436void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
437{
438    if (paintingDisabled())
439        return;
440
441    fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
442}
443#endif
444
445void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
446{
447    if (paintingDisabled() || !image)
448        return;
449
450    float tsw = src.width();
451    float tsh = src.height();
452    float tw = dest.width();
453    float th = dest.height();
454
455    if (tsw == -1)
456        tsw = image->width();
457    if (tsh == -1)
458        tsh = image->height();
459
460    if (tw == -1)
461        tw = image->width();
462    if (th == -1)
463        th = image->height();
464
465    if (useLowQualityScale) {
466        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
467        // FIXME: Should be InterpolationLow
468        setImageInterpolationQuality(InterpolationNone);
469        image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
470        setImageInterpolationQuality(previousInterpolationQuality);
471    } else
472        image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
473}
474
475void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale)
476{
477    if (paintingDisabled() || !image)
478        return;
479
480    if (useLowQualityScale) {
481        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
482        setImageInterpolationQuality(InterpolationLow);
483        image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
484        setImageInterpolationQuality(previousInterpolationQuality);
485    } else
486        image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
487}
488
489void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
490{
491    if (paintingDisabled() || !image)
492        return;
493
494    if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
495        // Just do a scale.
496        drawImage(image, styleColorSpace, dest, srcRect, op);
497        return;
498    }
499
500    if (useLowQualityScale) {
501        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
502        setImageInterpolationQuality(InterpolationLow);
503        image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
504        setImageInterpolationQuality(previousInterpolationQuality);
505    } else
506        image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
507}
508
509void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
510{
511    drawImageBuffer(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
512}
513
514void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
515{
516    drawImageBuffer(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
517}
518
519void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
520{
521    drawImageBuffer(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
522}
523
524void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
525{
526    drawImageBuffer(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
527}
528
529void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
530{
531    if (paintingDisabled() || !image)
532        return;
533
534    float tsw = src.width();
535    float tsh = src.height();
536    float tw = dest.width();
537    float th = dest.height();
538
539    if (tsw == -1)
540        tsw = image->width();
541    if (tsh == -1)
542        tsh = image->height();
543
544    if (tw == -1)
545        tw = image->width();
546    if (th == -1)
547        th = image->height();
548
549    if (useLowQualityScale) {
550        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
551        // FIXME: Should be InterpolationLow
552        setImageInterpolationQuality(InterpolationNone);
553        image->draw(this, styleColorSpace, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op, useLowQualityScale);
554        setImageInterpolationQuality(previousInterpolationQuality);
555    } else
556        image->draw(this, styleColorSpace, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op, useLowQualityScale);
557}
558
559#if !PLATFORM(QT)
560void GraphicsContext::clip(const IntRect& rect)
561{
562    clip(FloatRect(rect));
563}
564#endif
565
566void GraphicsContext::addRoundedRectClip(const RoundedIntRect& rect)
567{
568    if (paintingDisabled())
569        return;
570
571    Path path;
572    path.addRoundedRect(rect);
573    clip(path);
574}
575
576void GraphicsContext::clipOutRoundedRect(const RoundedIntRect& rect)
577{
578    if (paintingDisabled())
579        return;
580
581    Path path;
582    path.addRoundedRect(rect);
583    clipOut(path);
584}
585
586void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
587{
588    if (paintingDisabled())
589        return;
590    buffer->clip(this, rect);
591}
592
593#if !USE(CG)
594IntRect GraphicsContext::clipBounds() const
595{
596    ASSERT_NOT_REACHED();
597    return IntRect();
598}
599#endif
600
601TextDrawingModeFlags GraphicsContext::textDrawingMode() const
602{
603    return m_state.textDrawingMode;
604}
605
606void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
607{
608    m_state.textDrawingMode = mode;
609    if (paintingDisabled())
610        return;
611    setPlatformTextDrawingMode(mode);
612}
613
614void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator)
615{
616    if (paintingDisabled())
617        return;
618    generator.fill(this, rect);
619}
620
621void GraphicsContext::fillRoundedRect(const RoundedIntRect& rect, const Color& color, ColorSpace colorSpace)
622{
623    fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace);
624}
625
626#if !USE(CG)
627void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedIntRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
628{
629    if (paintingDisabled())
630        return;
631
632    Path path;
633    path.addRect(rect);
634
635    if (!roundedHoleRect.radii().isZero())
636        path.addRoundedRect(roundedHoleRect);
637    else
638        path.addRect(roundedHoleRect.rect());
639
640    WindRule oldFillRule = fillRule();
641    Color oldFillColor = fillColor();
642    ColorSpace oldFillColorSpace = fillColorSpace();
643
644    setFillRule(RULE_EVENODD);
645    setFillColor(color, colorSpace);
646
647    fillPath(path);
648
649    setFillRule(oldFillRule);
650    setFillColor(oldFillColor, oldFillColorSpace);
651}
652#endif
653
654void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation)
655{
656    m_state.compositeOperator = compositeOperation;
657    setPlatformCompositeOperation(compositeOperation);
658}
659
660CompositeOperator GraphicsContext::compositeOperation() const
661{
662    return m_state.compositeOperator;
663}
664
665#if !USE(SKIA)
666void GraphicsContext::setPlatformFillGradient(Gradient*)
667{
668}
669
670void GraphicsContext::setPlatformFillPattern(Pattern*)
671{
672}
673
674void GraphicsContext::setPlatformStrokeGradient(Gradient*)
675{
676}
677
678void GraphicsContext::setPlatformStrokePattern(Pattern*)
679{
680}
681#endif
682
683#if !USE(CG) && !(USE(SKIA) && !PLATFORM(ANDROID))
684// Implement this if you want to go ahead and push the drawing mode into your native context
685// immediately.
686void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
687{
688}
689#endif
690
691#if !PLATFORM(QT) && !USE(CAIRO) && !USE(SKIA) && !PLATFORM(OPENVG)
692void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
693{
694}
695#endif
696
697#if !USE(CG)
698void GraphicsContext::setPlatformShouldSmoothFonts(bool)
699{
700}
701#endif
702
703#if !USE(SKIA)
704void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&)
705{
706}
707
708void GraphicsContext::syncSoftwareCanvas()
709{
710}
711
712void GraphicsContext::markDirtyRect(const IntRect&)
713{
714}
715#endif
716
717
718void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
719{
720    // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
721    // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
722    // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
723    // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
724    if (penStyle == DottedStroke || penStyle == DashedStroke) {
725        if (p1.x() == p2.x()) {
726            p1.setY(p1.y() + strokeWidth);
727            p2.setY(p2.y() - strokeWidth);
728        } else {
729            p1.setX(p1.x() + strokeWidth);
730            p2.setX(p2.x() - strokeWidth);
731        }
732    }
733
734    if (static_cast<int>(strokeWidth) % 2) { //odd
735        if (p1.x() == p2.x()) {
736            // We're a vertical line.  Adjust our x.
737            p1.setX(p1.x() + 0.5f);
738            p2.setX(p2.x() + 0.5f);
739        } else {
740            // We're a horizontal line. Adjust our y.
741            p1.setY(p1.y() + 0.5f);
742            p2.setY(p2.y() + 0.5f);
743        }
744    }
745}
746
747}
748