2
* Copyright (c) 2006, Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are
8
* * Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* * Redistributions in binary form must reproduce the above
11
* copyright notice, this list of conditions and the following disclaimer
12
* in the documentation and/or other materials provided with the
14
* * Neither the name of Google Inc. nor the names of its
15
* contributors may be used to endorse or promote products derived from
16
* this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
#include "GraphicsContext.h"
34
#include "AffineTransform.h"
36
#include "FloatRect.h"
38
#include "ImageBuffer.h"
41
#include "NativeImageSkia.h"
42
#include "NotImplemented.h"
43
#include "PlatformContextSkia.h"
45
#include "SkAnnotation.h"
47
#include "SkBlurMaskFilter.h"
48
#include "SkColorFilter.h"
49
#include "SkCornerPathEffect.h"
51
#include "SkLayerDrawLooper.h"
53
#include "SkiaUtils.h"
54
#include "skia/ext/platform_canvas.h"
57
#include <wtf/Assertions.h>
58
#include <wtf/MathExtras.h>
59
#include <wtf/UnusedParam.h>
61
#if PLATFORM(CHROMIUM) && OS(DARWIN)
62
#include <ApplicationServices/ApplicationServices.h>
71
// Return value % max, but account for value possibly being negative.
72
inline int fastMod(int value, int max)
88
// Local helper functions ------------------------------------------------------
90
void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
93
int rx = SkMin32(SkScalarRound(rect.width()), size.width());
94
int ry = SkMin32(SkScalarRound(rect.height()), size.height());
96
ir.set(-rx, -ry, rx, ry);
99
ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
102
ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
105
ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
108
ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
116
path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
119
void draw2xMarker(SkBitmap* bitmap, int index)
122
static const SkPMColor lineColors[2] = {
123
SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
124
SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0), // Opaque gray.
126
static const SkPMColor antiColors1[2] = {
127
SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red
128
SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0), // Semitransparent gray
130
static const SkPMColor antiColors2[2] = {
131
SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
132
SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0), // More transparent gray
135
const SkPMColor lineColor = lineColors[index];
136
const SkPMColor antiColor1 = antiColors1[index];
137
const SkPMColor antiColor2 = antiColors2[index];
139
uint32_t* row1 = bitmap->getAddr32(0, 0);
140
uint32_t* row2 = bitmap->getAddr32(0, 1);
141
uint32_t* row3 = bitmap->getAddr32(0, 2);
142
uint32_t* row4 = bitmap->getAddr32(0, 3);
144
// Pattern: X0o o0X0o o0
148
const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
149
const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
150
const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
151
const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
153
for (int x = 0; x < bitmap->width() + 8; x += 8) {
154
int count = min(bitmap->width() - x, 8);
156
memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
157
memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
158
memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
159
memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
164
void draw1xMarker(SkBitmap* bitmap, int index)
166
static const uint32_t lineColors[2] = {
167
0xFF << SK_A32_SHIFT | 0xFF << SK_R32_SHIFT, // Opaque red.
168
0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Opaque gray.
170
static const uint32_t antiColors[2] = {
171
0x60 << SK_A32_SHIFT | 0x60 << SK_R32_SHIFT, // Semitransparent red
172
0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Semitransparent gray
175
const uint32_t lineColor = lineColors[index];
176
const uint32_t antiColor = antiColors[index];
178
// Pattern: X o o X o o X
180
uint32_t* row1 = bitmap->getAddr32(0, 0);
181
uint32_t* row2 = bitmap->getAddr32(0, 1);
182
for (int x = 0; x < bitmap->width(); x++) {
202
// -----------------------------------------------------------------------------
204
// This may be called with a NULL pointer to create a graphics context that has
206
void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
209
gc->setGraphicsContext(this);
211
// the caller owns the gc
213
setPaintingDisabled(!gc || !gc->canvas());
216
void GraphicsContext::platformDestroy()
220
PlatformGraphicsContext* GraphicsContext::platformContext() const
222
ASSERT(!paintingDisabled());
226
// State saving ----------------------------------------------------------------
228
void GraphicsContext::savePlatformState()
230
if (paintingDisabled())
233
// Save our private State.
234
platformContext()->save();
237
void GraphicsContext::restorePlatformState()
239
if (paintingDisabled())
242
// Restore our private State.
243
platformContext()->restore();
246
void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
248
if (paintingDisabled())
251
// We need the "alpha" layer flag here because the base layer is opaque
252
// (the surface of the page) but layers on top may have transparent parts.
253
// Without explicitly setting the alpha flag, the layer will inherit the
254
// opaque setting of the base and some things won't work properly.
255
SkCanvas::SaveFlags saveFlags = static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag);
258
layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
259
layerPaint.setXfermodeMode(platformContext()->getXfermodeMode());
261
platformContext()->saveLayer(0, &layerPaint, saveFlags);
264
void GraphicsContext::endPlatformTransparencyLayer()
266
if (paintingDisabled())
268
platformContext()->restoreLayer();
271
bool GraphicsContext::supportsTransparencyLayers()
276
// Graphics primitives ---------------------------------------------------------
278
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
280
if (paintingDisabled())
285
path.addOval(r, SkPath::kCW_Direction);
286
// only perform the inset if we won't invert r
287
if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
288
// Adding one to the thickness doesn't make the border too thick as
289
// it's painted over afterwards. But without this adjustment the
290
// border appears a little anemic after anti-aliasing.
291
r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
292
path.addOval(r, SkPath::kCCW_Direction);
294
platformContext()->clipPath(path, PlatformContextSkia::AntiAliased);
297
void GraphicsContext::clearPlatformShadow()
299
if (paintingDisabled())
301
platformContext()->setDrawLooper(0);
304
void GraphicsContext::clearRect(const FloatRect& rect)
306
if (paintingDisabled())
311
platformContext()->setupPaintForFilling(&paint);
312
paint.setXfermodeMode(SkXfermode::kClear_Mode);
313
platformContext()->drawRect(r, paint);
316
void GraphicsContext::clip(const FloatRect& rect)
318
if (paintingDisabled())
321
platformContext()->clipRect(rect);
324
void GraphicsContext::clip(const Path& path)
326
if (paintingDisabled() || path.isEmpty())
329
platformContext()->clipPath(*path.platformPath(), PlatformContextSkia::AntiAliased);
332
void GraphicsContext::canvasClip(const Path& path)
334
if (paintingDisabled())
337
platformContext()->canvasClipPath(path.isNull() ? SkPath() : *path.platformPath());
340
void GraphicsContext::clipOut(const IntRect& rect)
342
if (paintingDisabled())
345
platformContext()->clipRect(rect, PlatformContextSkia::NotAntiAliased, SkRegion::kDifference_Op);
348
void GraphicsContext::clipOut(const Path& p)
350
if (paintingDisabled())
353
// We must make a copy of the path, to mark it as inverse-filled.
354
SkPath path(p.isNull() ? SkPath() : *p.platformPath());
355
path.toggleInverseFillType();
356
platformContext()->clipPath(path, PlatformContextSkia::AntiAliased);
359
void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
361
if (paintingDisabled())
364
const SkPath* path = pathToClip.platformPath();
365
SkPath::FillType ftype = (clipRule == RULE_EVENODD) ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
369
else if (path->getFillType() != ftype) {
371
storage.setFillType(ftype);
374
platformContext()->clipPath(*path, PlatformContextSkia::AntiAliased);
377
void GraphicsContext::concatCTM(const AffineTransform& affine)
379
if (paintingDisabled())
382
platformContext()->concat(affine);
385
void GraphicsContext::setCTM(const AffineTransform& affine)
387
if (paintingDisabled())
390
platformContext()->setMatrix(affine);
393
static void setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
395
path->incReserve(numPoints);
396
path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
397
WebCoreFloatToSkScalar(points[0].y()));
398
for (size_t i = 1; i < numPoints; ++i) {
399
path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
400
WebCoreFloatToSkScalar(points[i].y()));
403
/* The code used to just blindly call this
404
path->setIsConvex(true);
405
But webkit can sometimes send us non-convex 4-point values, so we mark the path's
406
convexity as unknown, so it will get computed by skia at draw time.
409
SkPath::Convexity convexity = SkPath::kConvex_Convexity;
411
convexity = SkPath::kUnknown_Convexity;
412
path->setConvexity(convexity);
415
void GraphicsContext::drawConvexPolygon(size_t numPoints,
416
const FloatPoint* points,
417
bool shouldAntialias)
419
if (paintingDisabled())
426
setPathFromConvexPoints(&path, numPoints, points);
429
platformContext()->setupPaintForFilling(&paint);
430
paint.setAntiAlias(shouldAntialias);
431
platformContext()->drawPath(path, paint);
433
if (strokeStyle() != NoStroke) {
435
platformContext()->setupPaintForStroking(&paint, 0, 0);
436
platformContext()->drawPath(path, paint);
440
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
442
if (paintingDisabled())
449
setPathFromConvexPoints(&path, numPoints, points);
450
platformContext()->clipPath(path, antialiased
451
? PlatformContextSkia::AntiAliased
452
: PlatformContextSkia::NotAntiAliased);
455
// This method is only used to draw the little circles used in lists.
456
void GraphicsContext::drawEllipse(const IntRect& elipseRect)
458
if (paintingDisabled())
461
SkRect rect = elipseRect;
463
platformContext()->setupPaintForFilling(&paint);
464
platformContext()->drawOval(rect, paint);
466
if (strokeStyle() != NoStroke) {
468
platformContext()->setupPaintForStroking(&paint, &rect, 0);
469
platformContext()->drawOval(rect, paint);
473
void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
478
static inline void drawOuterPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
480
#if PLATFORM(CHROMIUM) && OS(DARWIN)
482
paint.setStrokeWidth(width);
483
paint.setPathEffect(new SkCornerPathEffect((width - 1) * 0.5f))->unref();
485
paint.setStrokeWidth(1);
486
paint.setPathEffect(new SkCornerPathEffect(1))->unref();
488
context->drawPath(path, paint);
491
static inline void drawInnerPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
493
#if PLATFORM(CHROMIUM) && OS(DARWIN)
495
paint.setStrokeWidth(width * 0.5f);
496
context->drawPath(path, paint);
500
static inline int getFocusRingOutset(int offset)
502
#if PLATFORM(CHROMIUM) && OS(DARWIN)
509
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
511
if (paintingDisabled())
514
unsigned rectCount = rects.size();
518
SkRegion focusRingRegion;
519
const int focusRingOutset = getFocusRingOutset(offset);
520
for (unsigned i = 0; i < rectCount; i++) {
521
SkIRect r = rects[i];
522
r.inset(-focusRingOutset, -focusRingOutset);
523
focusRingRegion.op(r, SkRegion::kUnion_Op);
528
paint.setAntiAlias(true);
529
paint.setStyle(SkPaint::kStroke_Style);
531
paint.setColor(color.rgb());
532
focusRingRegion.getBoundaryPath(&path);
533
drawOuterPath(platformContext(), path, paint, width);
534
drawInnerPath(platformContext(), path, paint, width);
537
// This is only used to draw borders.
538
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
540
if (paintingDisabled())
543
StrokeStyle penStyle = strokeStyle();
544
if (penStyle == NoStroke)
548
FloatPoint p1 = point1;
549
FloatPoint p2 = point2;
550
bool isVerticalLine = (p1.x() == p2.x());
551
int width = roundf(strokeThickness());
553
// We know these are vertical or horizontal lines, so the length will just
554
// be the sum of the displacement component vectors give or take 1 -
555
// probably worth the speed up of no square root, which also won't be exact.
556
FloatSize disp = p2 - p1;
557
int length = SkScalarRound(disp.width() + disp.height());
558
platformContext()->setupPaintForStroking(&paint, 0, length);
560
if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
561
// Do a rect fill of our endpoints. This ensures we always have the
562
// appearance of being a border. We then draw the actual dotted/dashed line.
565
r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
566
r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
568
if (isVerticalLine) {
569
r1.offset(-width / 2, 0);
570
r2.offset(-width / 2, -width);
572
r1.offset(0, -width / 2);
573
r2.offset(-width, -width / 2);
576
fillPaint.setColor(paint.getColor());
577
platformContext()->drawRect(r1, fillPaint);
578
platformContext()->drawRect(r2, fillPaint);
581
adjustLineToPixelBoundaries(p1, p2, width, penStyle);
582
SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 };
584
platformContext()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
587
void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
589
if (paintingDisabled())
592
int deviceScaleFactor = SkScalarRoundToInt(WebCoreFloatToSkScalar(platformContext()->deviceScaleFactor()));
593
ASSERT(deviceScaleFactor == 1 || deviceScaleFactor == 2);
595
// Create the pattern we'll use to draw the underline.
596
int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
597
static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
598
static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
599
SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
600
if (!misspellBitmap[index]) {
601
#if PLATFORM(CHROMIUM) && OS(DARWIN)
602
// Match the artwork used by the Mac.
603
const int rowPixels = 4 * deviceScaleFactor;
604
const int colPixels = 3 * deviceScaleFactor;
605
misspellBitmap[index] = new SkBitmap;
606
misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config,
607
rowPixels, colPixels);
608
misspellBitmap[index]->allocPixels();
610
misspellBitmap[index]->eraseARGB(0, 0, 0, 0);
611
const uint32_t transparentColor = 0x00000000;
613
if (deviceScaleFactor == 1) {
614
const uint32_t colors[2][6] = {
615
{ 0x2A2A0600, 0x57571000, 0xA8A81B00, 0xBFBF1F00, 0x70701200, 0xE0E02400 },
616
{ 0x2A001503, 0x57002A08, 0xA800540D, 0xBF005F0F, 0x70003809, 0xE0007012 }
619
// Pattern: a b a a b a
622
for (int x = 0; x < colPixels; ++x) {
623
uint32_t* row = misspellBitmap[index]->getAddr32(0, x);
624
row[0] = colors[index][x * 2];
625
row[1] = colors[index][x * 2 + 1];
626
row[2] = colors[index][x * 2];
627
row[3] = transparentColor;
629
} else if (deviceScaleFactor == 2) {
630
const uint32_t colors[2][18] = {
631
{ 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
632
0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
633
{ 0x0a000400, 0x33031b06, 0x55062f0b, 0x37041e06, 0x6e083d0d, 0x6e083d0d, 0x7608410e, 0x8d094e11, 0x8d094e11,
634
0x960a5313, 0xb30d6417, 0xb30d6417, 0x6d073c0d, 0xd90f781c, 0xd90f781c, 0x19010d03, 0x7c094510, 0xce0f731a }
637
// Pattern: a b c c b a
643
for (int x = 0; x < colPixels; ++x) {
644
uint32_t* row = misspellBitmap[index]->getAddr32(0, x);
645
row[0] = colors[index][x * 3];
646
row[1] = colors[index][x * 3 + 1];
647
row[2] = colors[index][x * 3 + 2];
648
row[3] = colors[index][x * 3 + 2];
649
row[4] = colors[index][x * 3 + 1];
650
row[5] = colors[index][x * 3];
651
row[6] = transparentColor;
652
row[7] = transparentColor;
655
ASSERT_NOT_REACHED();
657
// We use a 2-pixel-high misspelling indicator because that seems to be
658
// what WebKit is designed for, and how much room there is in a typical
660
const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
661
const int colPixels = 2 * deviceScaleFactor;
662
misspellBitmap[index] = new SkBitmap;
663
misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config, rowPixels, colPixels);
664
misspellBitmap[index]->allocPixels();
666
misspellBitmap[index]->eraseARGB(0, 0, 0, 0);
667
if (deviceScaleFactor == 1)
668
draw1xMarker(misspellBitmap[index], index);
669
else if (deviceScaleFactor == 2)
670
draw2xMarker(misspellBitmap[index], index);
672
ASSERT_NOT_REACHED();
676
#if PLATFORM(CHROMIUM) && OS(DARWIN)
677
SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
678
SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
680
// Make sure to draw only complete dots.
681
int rowPixels = misspellBitmap[index]->width();
682
float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
683
if (rowPixels - widthMod > deviceScaleFactor)
684
width -= widthMod / deviceScaleFactor;
686
SkScalar originX = WebCoreFloatToSkScalar(pt.x());
688
// Offset it vertically by 1 so that there's some space under the text.
689
SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
690
originX *= deviceScaleFactor;
691
originY *= deviceScaleFactor;
694
// Make a shader for the bitmap with an origin of the box we'll draw. This
695
// shader is refcounted and will have an initial refcount of 1.
696
SkShader* shader = SkShader::CreateBitmapShader(
697
*misspellBitmap[index], SkShader::kRepeat_TileMode,
698
SkShader::kRepeat_TileMode);
700
matrix.setTranslate(originX, originY);
701
shader->setLocalMatrix(matrix);
703
// Assign the shader to the paint & release our reference. The paint will
704
// now own the shader and the shader will be destroyed when the paint goes
707
paint.setShader(shader)->unref();
710
rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
712
if (deviceScaleFactor == 2) {
713
platformContext()->save();
714
platformContext()->scale(SK_ScalarHalf, SK_ScalarHalf);
716
platformContext()->drawRect(rect, paint);
717
if (deviceScaleFactor == 2)
718
platformContext()->restore();
721
void GraphicsContext::drawLineForText(const FloatPoint& pt,
725
if (paintingDisabled())
731
int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
733
r.fLeft = WebCoreFloatToSkScalar(pt.x());
734
// Avoid anti-aliasing lines. Currently, these are always horizontal.
735
r.fTop = WebCoreFloatToSkScalar(floorf(pt.y()));
736
r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
737
r.fBottom = r.fTop + SkIntToScalar(thickness);
740
platformContext()->setupPaintForFilling(&paint);
741
// Text lines are drawn using the stroke color.
742
paint.setColor(platformContext()->effectiveStrokeColor());
743
platformContext()->drawRect(r, paint);
746
// Draws a filled rectangle with a stroked border.
747
void GraphicsContext::drawRect(const IntRect& rect)
749
if (paintingDisabled())
752
ASSERT(!rect.isEmpty());
756
platformContext()->drawRect(rect);
759
void GraphicsContext::fillPath(const Path& pathToFill)
761
if (paintingDisabled() || pathToFill.isEmpty())
764
const GraphicsContextState& state = m_state;
765
SkPath::FillType ftype = state.fillRule == RULE_EVENODD ?
766
SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
768
const SkPath* path = pathToFill.platformPath();
770
if (path->getFillType() != ftype) {
772
storage.setFillType(ftype);
777
platformContext()->setupPaintForFilling(&paint);
779
platformContext()->drawPath(*path, paint);
782
void GraphicsContext::fillRect(const FloatRect& rect)
784
if (paintingDisabled())
790
platformContext()->setupPaintForFilling(&paint);
791
platformContext()->drawRect(r, paint);
794
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
796
if (paintingDisabled())
801
platformContext()->setupPaintCommon(&paint);
802
paint.setColor(color.rgb());
803
platformContext()->drawRect(r, paint);
806
void GraphicsContext::fillRoundedRect(const IntRect& rect,
807
const IntSize& topLeft,
808
const IntSize& topRight,
809
const IntSize& bottomLeft,
810
const IntSize& bottomRight,
812
ColorSpace colorSpace)
814
if (paintingDisabled())
817
if (topLeft.width() + topRight.width() > rect.width()
818
|| bottomLeft.width() + bottomRight.width() > rect.width()
819
|| topLeft.height() + bottomLeft.height() > rect.height()
820
|| topRight.height() + bottomRight.height() > rect.height()) {
821
// Not all the radii fit, return a rect. This matches the behavior of
822
// Path::createRoundedRectangle. Without this we attempt to draw a round
823
// shadow for a square box.
824
fillRect(rect, color, colorSpace);
830
addCornerArc(&path, r, topRight, 270);
831
addCornerArc(&path, r, bottomRight, 0);
832
addCornerArc(&path, r, bottomLeft, 90);
833
addCornerArc(&path, r, topLeft, 180);
836
platformContext()->setupPaintForFilling(&paint);
837
paint.setColor(color.rgb());
838
platformContext()->drawPath(path, paint);
841
AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
843
if (paintingDisabled())
844
return AffineTransform();
846
const SkMatrix& m = platformContext()->getTotalMatrix();
847
return AffineTransform(SkScalarToDouble(m.getScaleX()),
848
SkScalarToDouble(m.getSkewY()),
849
SkScalarToDouble(m.getSkewX()),
850
SkScalarToDouble(m.getScaleY()),
851
SkScalarToDouble(m.getTranslateX()),
852
SkScalarToDouble(m.getTranslateY()));
855
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
860
void GraphicsContext::scale(const FloatSize& size)
862
if (paintingDisabled())
865
platformContext()->scale(WebCoreFloatToSkScalar(size.width()),
866
WebCoreFloatToSkScalar(size.height()));
869
void GraphicsContext::setAlpha(float alpha)
871
if (paintingDisabled())
874
platformContext()->setAlpha(alpha);
877
void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
879
if (paintingDisabled())
882
platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
885
InterpolationQuality GraphicsContext::imageInterpolationQuality() const
887
return platformContext()->interpolationQuality();
890
void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q)
892
platformContext()->setInterpolationQuality(q);
895
void GraphicsContext::setLineCap(LineCap cap)
897
if (paintingDisabled())
901
platformContext()->setLineCap(SkPaint::kButt_Cap);
904
platformContext()->setLineCap(SkPaint::kRound_Cap);
907
platformContext()->setLineCap(SkPaint::kSquare_Cap);
915
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
917
if (paintingDisabled())
920
// FIXME: This is lifted directly off SkiaSupport, lines 49-74
921
// so it is not guaranteed to work correctly.
922
size_t dashLength = dashes.size();
924
// If no dash is set, revert to solid stroke
925
// FIXME: do we need to set NoStroke in some cases?
926
platformContext()->setStrokeStyle(SolidStroke);
927
platformContext()->setDashPathEffect(0);
931
size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
932
SkScalar* intervals = new SkScalar[count];
934
for (unsigned int i = 0; i < count; i++)
935
intervals[i] = dashes[i % dashLength];
937
platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
942
void GraphicsContext::setLineJoin(LineJoin join)
944
if (paintingDisabled())
948
platformContext()->setLineJoin(SkPaint::kMiter_Join);
951
platformContext()->setLineJoin(SkPaint::kRound_Join);
954
platformContext()->setLineJoin(SkPaint::kBevel_Join);
962
void GraphicsContext::setMiterLimit(float limit)
964
if (paintingDisabled())
966
platformContext()->setMiterLimit(limit);
969
void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
971
if (paintingDisabled())
974
platformContext()->setFillColor(color.rgb());
977
void GraphicsContext::setPlatformShadow(const FloatSize& size,
980
ColorSpace colorSpace)
982
if (paintingDisabled())
985
// Detect when there's no effective shadow and clear the looper.
986
if (!size.width() && !size.height() && !blurFloat) {
987
platformContext()->setDrawLooper(0);
991
double width = size.width();
992
double height = size.height();
993
double blur = blurFloat;
995
uint32_t mfFlags = SkBlurMaskFilter::kHighQuality_BlurFlag;
996
SkXfermode::Mode colorMode = SkXfermode::kSrc_Mode;
998
if (m_state.shadowsIgnoreTransforms) {
999
// Currently only the GraphicsContext associated with the
1000
// CanvasRenderingContext for HTMLCanvasElement have shadows ignore
1001
// Transforms. So with this flag set, we know this state is associated
1002
// with a CanvasRenderingContext.
1003
mfFlags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
1005
// CSS wants us to ignore the original's alpha, but Canvas wants us to
1006
// modulate with it. Using shadowsIgnoreTransforms to tell us that we're
1007
// in a Canvas, we change the colormode to kDst_Mode, so we don't overwrite
1008
// it with our layer's (default opaque-black) color.
1009
colorMode = SkXfermode::kDst_Mode;
1011
// CG uses natural orientation for Y axis, but the HTML5 canvas spec
1013
// So we now flip the height since it was flipped in
1014
// CanvasRenderingContext in order to work with CG.
1019
if (color.isValid())
1022
c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color.
1024
// TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0
1025
// for perf reasons.
1027
SkLayerDrawLooper* dl = new SkLayerDrawLooper;
1028
SkAutoUnref aur(dl);
1030
// top layer, we just draw unchanged
1033
// lower layer contains our offset, blur, and colorfilter
1034
SkLayerDrawLooper::LayerInfo info;
1036
info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; // our blur
1037
info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
1038
info.fColorMode = colorMode;
1039
info.fOffset.set(width, height);
1040
info.fPostTranslate = m_state.shadowsIgnoreTransforms;
1042
SkMaskFilter* mf = SkBlurMaskFilter::Create(blur / 2, SkBlurMaskFilter::kNormal_BlurStyle, mfFlags);
1044
SkColorFilter* cf = SkColorFilter::CreateModeFilter(c, SkXfermode::kSrcIn_Mode);
1046
SkPaint* paint = dl->addLayer(info);
1047
SkSafeUnref(paint->setMaskFilter(mf));
1048
SkSafeUnref(paint->setColorFilter(cf));
1050
// dl is now built, just install it
1051
platformContext()->setDrawLooper(dl);
1054
void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace)
1056
if (paintingDisabled())
1059
platformContext()->setStrokeColor(strokecolor.rgb());
1062
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke)
1064
if (paintingDisabled())
1067
platformContext()->setStrokeStyle(stroke);
1070
void GraphicsContext::setPlatformStrokeThickness(float thickness)
1072
if (paintingDisabled())
1075
platformContext()->setStrokeThickness(thickness);
1078
void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1080
if (paintingDisabled())
1083
platformContext()->setTextDrawingMode(mode);
1086
void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1088
if (paintingDisabled())
1091
SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1092
SkAnnotateRectWithURL(platformContext()->canvas(), destRect, url.get());
1095
void GraphicsContext::setPlatformShouldAntialias(bool enable)
1097
if (paintingDisabled())
1100
platformContext()->setUseAntialiasing(enable);
1103
void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1105
if (paintingDisabled())
1110
if (strokeStyle() == NoStroke) {
1111
// Stroke using the fill color.
1112
// TODO(brettw) is this really correct? It seems unreasonable.
1113
platformContext()->setupPaintForFilling(&paint);
1114
paint.setStyle(SkPaint::kStroke_Style);
1115
paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness()));
1117
platformContext()->setupPaintForStroking(&paint, 0, 0);
1119
// We do this before converting to scalar, so we don't overflow SkFixed.
1120
startAngle = fastMod(startAngle, 360);
1121
angleSpan = fastMod(angleSpan, 360);
1124
path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1125
platformContext()->drawPath(path, paint);
1128
void GraphicsContext::strokePath(const Path& pathToStroke)
1130
if (paintingDisabled() || pathToStroke.isEmpty())
1133
const SkPath& path = *pathToStroke.platformPath();
1135
platformContext()->setupPaintForStroking(&paint, 0, 0);
1136
platformContext()->drawPath(path, paint);
1139
void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1141
if (paintingDisabled())
1145
platformContext()->setupPaintForStroking(&paint, 0, 0);
1146
paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1147
// strokerect has special rules for CSS when the rect is degenerate:
1148
// if width==0 && height==0, do nothing
1149
// if width==0 || height==0, then just draw line for the other dimension
1151
bool validW = r.width() > 0;
1152
bool validH = r.height() > 0;
1153
if (validW && validH) {
1154
platformContext()->drawRect(r, paint);
1155
} else if (validW || validH) {
1156
// we are expected to respect the lineJoin, so we can't just call
1157
// drawLine -- we have to create a path that doubles back on itself.
1159
path.moveTo(r.fLeft, r.fTop);
1160
path.lineTo(r.fRight, r.fBottom);
1162
platformContext()->drawPath(path, paint);
1166
void GraphicsContext::rotate(float angleInRadians)
1168
if (paintingDisabled())
1171
platformContext()->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1174
void GraphicsContext::translate(float w, float h)
1176
if (paintingDisabled())
1179
platformContext()->translate(WebCoreFloatToSkScalar(w), WebCoreFloatToSkScalar(h));
1182
bool GraphicsContext::isAcceleratedContext() const
1184
return platformContext()->isAccelerated();
1187
#if PLATFORM(CHROMIUM) && OS(DARWIN)
1188
CGColorSpaceRef deviceRGBColorSpaceRef()
1190
static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
1195
void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1197
if (paintingDisabled())
1200
SkRect rect = ellipse;
1202
platformContext()->setupPaintForFilling(&paint);
1203
platformContext()->drawOval(rect, paint);
1206
void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1208
if (paintingDisabled())
1211
SkRect rect(ellipse);
1213
platformContext()->setupPaintForStroking(&paint, 0, 0);
1214
platformContext()->drawOval(rect, paint);
1217
} // namespace WebCore