9f40d17ceab94116a176ac914077078efc57a8e6
[WebKit-https.git] / Source / WebCore / platform / graphics / skia / GraphicsContextSkia.cpp
1 /*
2  * Copyright (c) 2006, Google 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 are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 #include "config.h"
32 #include "GraphicsContext.h"
33
34 #include "AffineTransform.h"
35 #include "Color.h"
36 #include "FloatRect.h"
37 #include "Gradient.h"
38 #include "ImageBuffer.h"
39 #include "IntRect.h"
40 #include "KURL.h"
41 #include "NativeImageSkia.h"
42 #include "NotImplemented.h"
43 #include "PlatformContextSkia.h"
44
45 #include "SkAnnotation.h"
46 #include "SkBitmap.h"
47 #include "SkBlurMaskFilter.h"
48 #include "SkColorFilter.h"
49 #include "SkCornerPathEffect.h"
50 #include "SkData.h"
51 #include "SkLayerDrawLooper.h"
52 #include "SkShader.h"
53 #include "SkiaUtils.h"
54 #include "skia/ext/platform_canvas.h"
55
56 #include <math.h>
57 #include <wtf/Assertions.h>
58 #include <wtf/MathExtras.h>
59 #include <wtf/UnusedParam.h>
60
61 #if PLATFORM(CHROMIUM) && OS(DARWIN)
62 #include <ApplicationServices/ApplicationServices.h>
63 #endif
64
65 using namespace std;
66
67 namespace WebCore {
68
69 namespace {
70
71 // Return value % max, but account for value possibly being negative.
72 inline int fastMod(int value, int max)
73 {
74     bool isNeg = false;
75     if (value < 0) {
76         value = -value;
77         isNeg = true;
78     }
79     if (value >= max)
80         value %= max;
81     if (isNeg)
82         value = -value;
83     return value;
84 }
85
86 }  // namespace
87
88 // Local helper functions ------------------------------------------------------
89
90 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
91 {
92     SkIRect ir;
93     int rx = SkMin32(SkScalarRound(rect.width()), size.width());
94     int ry = SkMin32(SkScalarRound(rect.height()), size.height());
95
96     ir.set(-rx, -ry, rx, ry);
97     switch (startAngle) {
98     case 0:
99         ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
100         break;
101     case 90:
102         ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
103         break;
104     case 180:
105         ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
106         break;
107     case 270:
108         ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
109         break;
110     default:
111         ASSERT(0);
112     }
113
114     SkRect r;
115     r.set(ir);
116     path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
117 }
118
119 void draw2xMarker(SkBitmap* bitmap, int index)
120 {
121
122     static const SkPMColor lineColors[2] = {
123         SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
124         SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0), // Opaque gray.
125     };
126     static const SkPMColor antiColors1[2] = {
127         SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red
128         SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0), // Semitransparent gray
129     };
130     static const SkPMColor antiColors2[2] = {
131         SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
132         SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0), // More transparent gray
133     };
134
135     const SkPMColor lineColor = lineColors[index];
136     const SkPMColor antiColor1 = antiColors1[index];
137     const SkPMColor antiColor2 = antiColors2[index];
138
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);
143
144     // Pattern: X0o   o0X0o   o0
145     //          XX0o o0XXX0o o0X
146     //           o0XXX0o o0XXX0o
147     //            o0X0o   o0X0o
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 };
152
153     for (int x = 0; x < bitmap->width() + 8; x += 8) {
154         int count = min(bitmap->width() - x, 8);
155         if (count > 0) {
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));
160         }
161     }
162 }
163
164 void draw1xMarker(SkBitmap* bitmap, int index)
165 {
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.
169     };
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
173     };
174
175     const uint32_t lineColor = lineColors[index];
176     const uint32_t antiColor = antiColors[index];
177
178     // Pattern: X o   o X o   o X
179     //            o X o   o X o
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++) {
183         switch (x % 4) {
184         case 0:
185             row1[x] = lineColor;
186             break;
187         case 1:
188             row1[x] = antiColor;
189             row2[x] = antiColor;
190             break;
191         case 2:
192             row2[x] = lineColor;
193             break;
194         case 3:
195             row1[x] = antiColor;
196             row2[x] = antiColor;
197             break;
198         }
199     }
200 }
201
202 // -----------------------------------------------------------------------------
203
204 // This may be called with a NULL pointer to create a graphics context that has
205 // no painting.
206 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
207 {
208     if (gc)
209         gc->setGraphicsContext(this);
210
211     // the caller owns the gc
212     m_data = gc;
213     setPaintingDisabled(!gc || !gc->canvas());
214 }
215
216 void GraphicsContext::platformDestroy()
217 {
218 }
219
220 PlatformGraphicsContext* GraphicsContext::platformContext() const
221 {
222     ASSERT(!paintingDisabled());
223     return m_data;
224 }
225
226 // State saving ----------------------------------------------------------------
227
228 void GraphicsContext::savePlatformState()
229 {
230     if (paintingDisabled())
231         return;
232
233     // Save our private State.
234     platformContext()->save();
235 }
236
237 void GraphicsContext::restorePlatformState()
238 {
239     if (paintingDisabled())
240         return;
241
242     // Restore our private State.
243     platformContext()->restore();
244 }
245
246 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
247 {
248     if (paintingDisabled())
249         return;
250
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);
256
257     SkPaint layerPaint;
258     layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
259     layerPaint.setXfermodeMode(platformContext()->getXfermodeMode());
260
261     platformContext()->saveLayer(0, &layerPaint, saveFlags);
262 }
263
264 void GraphicsContext::endPlatformTransparencyLayer()
265 {
266     if (paintingDisabled())
267         return;
268     platformContext()->restoreLayer();
269 }
270
271 bool GraphicsContext::supportsTransparencyLayers()
272 {
273     return true;
274 }
275
276 // Graphics primitives ---------------------------------------------------------
277
278 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
279 {
280     if (paintingDisabled())
281         return;
282
283     SkRect r(rect);
284     SkPath path;
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);
293     }
294     platformContext()->clipPath(path, PlatformContextSkia::AntiAliased);
295 }
296
297 void GraphicsContext::clearPlatformShadow()
298 {
299     if (paintingDisabled())
300         return;
301     platformContext()->setDrawLooper(0);
302 }
303
304 void GraphicsContext::clearRect(const FloatRect& rect)
305 {
306     if (paintingDisabled())
307         return;
308
309     SkRect r = rect;
310     SkPaint paint;
311     platformContext()->setupPaintForFilling(&paint);
312     paint.setXfermodeMode(SkXfermode::kClear_Mode);
313     platformContext()->drawRect(r, paint);
314 }
315
316 void GraphicsContext::clip(const FloatRect& rect)
317 {
318     if (paintingDisabled())
319         return;
320
321     platformContext()->clipRect(rect);
322 }
323
324 void GraphicsContext::clip(const Path& path)
325 {
326     if (paintingDisabled() || path.isEmpty())
327         return;
328
329     platformContext()->clipPath(*path.platformPath(), PlatformContextSkia::AntiAliased);
330 }
331
332 void GraphicsContext::canvasClip(const Path& path)
333 {
334     if (paintingDisabled())
335         return;
336
337     platformContext()->clipPath(path.isNull() ? SkPath() : *path.platformPath());
338 }
339
340 void GraphicsContext::clipOut(const IntRect& rect)
341 {
342     if (paintingDisabled())
343         return;
344
345     platformContext()->clipRect(rect, PlatformContextSkia::NotAntiAliased, SkRegion::kDifference_Op);
346 }
347
348 void GraphicsContext::clipOut(const Path& p)
349 {
350     if (paintingDisabled())
351         return;
352
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);
357 }
358
359 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
360 {
361     if (paintingDisabled())
362         return;
363
364     const SkPath* path = pathToClip.platformPath();
365     SkPath::FillType ftype = (clipRule == RULE_EVENODD) ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
366     SkPath storage;
367     if (!path)
368         path = &storage;
369     else if (path->getFillType() != ftype) {
370         storage = *path;
371         storage.setFillType(ftype);
372         path = &storage;
373     }
374     platformContext()->clipPath(*path, PlatformContextSkia::AntiAliased);
375 }
376
377 void GraphicsContext::concatCTM(const AffineTransform& affine)
378 {
379     if (paintingDisabled())
380         return;
381
382     platformContext()->concat(affine);
383 }
384
385 void GraphicsContext::setCTM(const AffineTransform& affine)
386 {
387     if (paintingDisabled())
388         return;
389
390     platformContext()->setMatrix(affine);
391 }
392
393 static void setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
394 {
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()));
401     }
402
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.
407         See crbug.com 108605
408     */
409     SkPath::Convexity convexity = SkPath::kConvex_Convexity;
410     if (numPoints == 4)
411         convexity = SkPath::kUnknown_Convexity;
412     path->setConvexity(convexity);
413 }
414
415 void GraphicsContext::drawConvexPolygon(size_t numPoints,
416                                         const FloatPoint* points,
417                                         bool shouldAntialias)
418 {
419     if (paintingDisabled())
420         return;
421
422     if (numPoints <= 1)
423         return;
424
425     SkPath path;
426     setPathFromConvexPoints(&path, numPoints, points);
427
428     SkPaint paint;
429     platformContext()->setupPaintForFilling(&paint);
430     paint.setAntiAlias(shouldAntialias);
431     platformContext()->drawPath(path, paint);
432
433     if (strokeStyle() != NoStroke) {
434         paint.reset();
435         platformContext()->setupPaintForStroking(&paint, 0, 0);
436         platformContext()->drawPath(path, paint);
437     }
438 }
439
440 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
441 {
442     if (paintingDisabled())
443         return;
444
445     if (numPoints <= 1)
446         return;
447
448     SkPath path;
449     setPathFromConvexPoints(&path, numPoints, points);
450     platformContext()->clipPath(path, antialiased
451         ? PlatformContextSkia::AntiAliased
452         : PlatformContextSkia::NotAntiAliased);
453 }
454
455 // This method is only used to draw the little circles used in lists.
456 void GraphicsContext::drawEllipse(const IntRect& elipseRect)
457 {
458     if (paintingDisabled())
459         return;
460
461     SkRect rect = elipseRect;
462     SkPaint paint;
463     platformContext()->setupPaintForFilling(&paint);
464     platformContext()->drawOval(rect, paint);
465
466     if (strokeStyle() != NoStroke) {
467         paint.reset();
468         platformContext()->setupPaintForStroking(&paint, &rect, 0);
469         platformContext()->drawOval(rect, paint);
470     }
471 }
472
473 void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
474 {
475     // FIXME: implement
476 }
477
478 static inline void drawOuterPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
479 {
480 #if PLATFORM(CHROMIUM) && OS(DARWIN)
481     paint.setAlpha(64);
482     paint.setStrokeWidth(width);
483     paint.setPathEffect(new SkCornerPathEffect((width - 1) * 0.5f))->unref();
484 #else
485     paint.setStrokeWidth(1);
486     paint.setPathEffect(new SkCornerPathEffect(1))->unref();
487 #endif
488     context->drawPath(path, paint);
489 }
490
491 static inline void drawInnerPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
492 {
493 #if PLATFORM(CHROMIUM) && OS(DARWIN)
494     paint.setAlpha(128);
495     paint.setStrokeWidth(width * 0.5f);
496     context->drawPath(path, paint);
497 #endif
498 }
499
500 static inline int getFocusRingOutset(int offset)
501 {
502 #if PLATFORM(CHROMIUM) && OS(DARWIN)
503     return offset + 2;
504 #else
505     return 0;
506 #endif
507 }
508
509 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
510 {
511     if (paintingDisabled())
512         return;
513
514     unsigned rectCount = rects.size();
515     if (!rectCount)
516         return;
517
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);
524     }
525
526     SkPath path;
527     SkPaint paint;
528     paint.setAntiAlias(true);
529     paint.setStyle(SkPaint::kStroke_Style);
530
531     paint.setColor(color.rgb());
532     focusRingRegion.getBoundaryPath(&path);
533     drawOuterPath(platformContext(), path, paint, width);
534     drawInnerPath(platformContext(), path, paint, width);
535 }
536
537 // This is only used to draw borders.
538 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
539 {
540     if (paintingDisabled())
541         return;
542
543     StrokeStyle penStyle = strokeStyle();
544     if (penStyle == NoStroke)
545         return;
546
547     SkPaint paint;
548     FloatPoint p1 = point1;
549     FloatPoint p2 = point2;
550     bool isVerticalLine = (p1.x() == p2.x());
551     int width = roundf(strokeThickness());
552
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);
559
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.
563
564         SkRect r1, r2;
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);
567
568         if (isVerticalLine) {
569             r1.offset(-width / 2, 0);
570             r2.offset(-width / 2, -width);
571         } else {
572             r1.offset(0, -width / 2);
573             r2.offset(-width, -width / 2);
574         }
575         SkPaint fillPaint;
576         fillPaint.setColor(paint.getColor());
577         platformContext()->drawRect(r1, fillPaint);
578         platformContext()->drawRect(r2, fillPaint);
579     }
580
581     adjustLineToPixelBoundaries(p1, p2, width, penStyle);
582     SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 };
583
584     platformContext()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
585 }
586
587 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
588 {
589     if (paintingDisabled())
590         return;
591
592     int deviceScaleFactor = SkScalarRoundToInt(WebCoreFloatToSkScalar(platformContext()->deviceScaleFactor()));
593     ASSERT(deviceScaleFactor == 1 || deviceScaleFactor == 2);
594
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();
609
610         misspellBitmap[index]->eraseARGB(0, 0, 0, 0);
611         const uint32_t transparentColor = 0x00000000;
612
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 }
617             };
618
619             // Pattern: a b a   a b a
620             //          c d c   c d c
621             //          e f e   e f e
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;
628             }
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 }
635             };
636
637             // Pattern: a b c c b a
638             //          d e f f e d
639             //          g h j j h g
640             //          k l m m l k
641             //          n o p p o n
642             //          q r s s r q
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;
653             }
654         } else
655             ASSERT_NOT_REACHED();
656 #else
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
659         // page for it.
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();
665
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);
671         else
672             ASSERT_NOT_REACHED();
673 #endif
674     }
675
676 #if PLATFORM(CHROMIUM) && OS(DARWIN)
677     SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
678     SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
679
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;
685 #else
686     SkScalar originX = WebCoreFloatToSkScalar(pt.x());
687
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;
692 #endif
693
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);
699     SkMatrix matrix;
700     matrix.setTranslate(originX, originY);
701     shader->setLocalMatrix(matrix);
702
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
705     // out of scope.
706     SkPaint paint;
707     paint.setShader(shader)->unref();
708
709     SkRect rect;
710     rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
711
712     if (deviceScaleFactor == 2) {
713         platformContext()->save();
714         platformContext()->scale(SK_ScalarHalf, SK_ScalarHalf);
715     }
716     platformContext()->drawRect(rect, paint);
717     if (deviceScaleFactor == 2)
718         platformContext()->restore();
719 }
720
721 void GraphicsContext::drawLineForText(const FloatPoint& pt,
722                                       float width,
723                                       bool printing)
724 {
725     if (paintingDisabled())
726         return;
727
728     if (width <= 0)
729         return;
730
731     int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
732     SkRect r;
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);
738
739     SkPaint paint;
740     platformContext()->setupPaintForFilling(&paint);
741     // Text lines are drawn using the stroke color.
742     paint.setColor(platformContext()->effectiveStrokeColor());
743     platformContext()->drawRect(r, paint);
744 }
745
746 // Draws a filled rectangle with a stroked border.
747 void GraphicsContext::drawRect(const IntRect& rect)
748 {
749     if (paintingDisabled())
750         return;
751
752     ASSERT(!rect.isEmpty());
753     if (rect.isEmpty())
754         return;
755
756     platformContext()->drawRect(rect);
757 }
758
759 void GraphicsContext::fillPath(const Path& pathToFill)
760 {
761     if (paintingDisabled() || pathToFill.isEmpty())
762         return;
763
764     const GraphicsContextState& state = m_state;
765     SkPath::FillType ftype = state.fillRule == RULE_EVENODD ?
766         SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
767
768     const SkPath* path = pathToFill.platformPath();
769     SkPath storage;
770     if (path->getFillType() != ftype) {
771         storage = *path;
772         storage.setFillType(ftype);
773         path = &storage;
774     }
775
776     SkPaint paint;
777     platformContext()->setupPaintForFilling(&paint);
778
779     platformContext()->drawPath(*path, paint);
780 }
781
782 void GraphicsContext::fillRect(const FloatRect& rect)
783 {
784     if (paintingDisabled())
785         return;
786
787     SkRect r = rect;
788
789     SkPaint paint;
790     platformContext()->setupPaintForFilling(&paint);
791     platformContext()->drawRect(r, paint);
792 }
793
794 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
795 {
796     if (paintingDisabled())
797         return;
798
799     SkRect r = rect;
800     SkPaint paint;
801     platformContext()->setupPaintCommon(&paint);
802     paint.setColor(color.rgb());
803     platformContext()->drawRect(r, paint);
804 }
805
806 void GraphicsContext::fillRoundedRect(const IntRect& rect,
807                                       const IntSize& topLeft,
808                                       const IntSize& topRight,
809                                       const IntSize& bottomLeft,
810                                       const IntSize& bottomRight,
811                                       const Color& color,
812                                       ColorSpace colorSpace)
813 {
814     if (paintingDisabled())
815         return;
816
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);
825         return;
826     }
827
828     SkRect r = rect;
829     SkPath path;
830     addCornerArc(&path, r, topRight, 270);
831     addCornerArc(&path, r, bottomRight, 0);
832     addCornerArc(&path, r, bottomLeft, 90);
833     addCornerArc(&path, r, topLeft, 180);
834
835     SkPaint paint;
836     platformContext()->setupPaintForFilling(&paint);
837     paint.setColor(color.rgb());
838     platformContext()->drawPath(path, paint);
839 }
840
841 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
842 {
843     if (paintingDisabled())
844         return AffineTransform();
845
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()));
853 }
854
855 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
856 {
857     return rect;
858 }
859
860 void GraphicsContext::scale(const FloatSize& size)
861 {
862     if (paintingDisabled())
863         return;
864
865     platformContext()->scale(WebCoreFloatToSkScalar(size.width()),
866         WebCoreFloatToSkScalar(size.height()));
867 }
868
869 void GraphicsContext::setAlpha(float alpha)
870 {
871     if (paintingDisabled())
872         return;
873
874     platformContext()->setAlpha(alpha);
875 }
876
877 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
878 {
879     if (paintingDisabled())
880         return;
881
882     platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
883 }
884
885 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
886 {
887     return platformContext()->interpolationQuality();
888 }
889
890 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q)
891 {
892     platformContext()->setInterpolationQuality(q);
893 }
894
895 void GraphicsContext::setLineCap(LineCap cap)
896 {
897     if (paintingDisabled())
898         return;
899     switch (cap) {
900     case ButtCap:
901         platformContext()->setLineCap(SkPaint::kButt_Cap);
902         break;
903     case RoundCap:
904         platformContext()->setLineCap(SkPaint::kRound_Cap);
905         break;
906     case SquareCap:
907         platformContext()->setLineCap(SkPaint::kSquare_Cap);
908         break;
909     default:
910         ASSERT(0);
911         break;
912     }
913 }
914
915 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
916 {
917     if (paintingDisabled())
918         return;
919
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();
923     if (!dashLength) {
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);
928         return;
929     }
930
931     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
932     SkScalar* intervals = new SkScalar[count];
933
934     for (unsigned int i = 0; i < count; i++)
935         intervals[i] = dashes[i % dashLength];
936
937     platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
938
939     delete[] intervals;
940 }
941
942 void GraphicsContext::setLineJoin(LineJoin join)
943 {
944     if (paintingDisabled())
945         return;
946     switch (join) {
947     case MiterJoin:
948         platformContext()->setLineJoin(SkPaint::kMiter_Join);
949         break;
950     case RoundJoin:
951         platformContext()->setLineJoin(SkPaint::kRound_Join);
952         break;
953     case BevelJoin:
954         platformContext()->setLineJoin(SkPaint::kBevel_Join);
955         break;
956     default:
957         ASSERT(0);
958         break;
959     }
960 }
961
962 void GraphicsContext::setMiterLimit(float limit)
963 {
964     if (paintingDisabled())
965         return;
966     platformContext()->setMiterLimit(limit);
967 }
968
969 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
970 {
971     if (paintingDisabled())
972         return;
973
974     platformContext()->setFillColor(color.rgb());
975 }
976
977 void GraphicsContext::setPlatformShadow(const FloatSize& size,
978                                         float blurFloat,
979                                         const Color& color,
980                                         ColorSpace colorSpace)
981 {
982     if (paintingDisabled())
983         return;
984
985     // Detect when there's no effective shadow and clear the looper.
986     if (!size.width() && !size.height() && !blurFloat) {
987         platformContext()->setDrawLooper(0);
988         return;
989     }
990
991     double width = size.width();
992     double height = size.height();
993     double blur = blurFloat;
994
995     uint32_t mfFlags = SkBlurMaskFilter::kHighQuality_BlurFlag;
996     SkXfermode::Mode colorMode = SkXfermode::kSrc_Mode;
997
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;
1004
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;
1010
1011         // CG uses natural orientation for Y axis, but the HTML5 canvas spec
1012         // does not.
1013         // So we now flip the height since it was flipped in
1014         // CanvasRenderingContext in order to work with CG.
1015         height = -height;
1016     }
1017
1018     SkColor c;
1019     if (color.isValid())
1020         c = color.rgb();
1021     else
1022         c = SkColorSetARGB(0xFF/3, 0, 0, 0);    // "std" apple shadow color.
1023
1024     // TODO(tc): Should we have a max value for the blur?  CG clamps at 1000.0
1025     // for perf reasons.
1026
1027     SkLayerDrawLooper* dl = new SkLayerDrawLooper;
1028     SkAutoUnref aur(dl);
1029
1030     // top layer, we just draw unchanged
1031     dl->addLayer();
1032
1033     // lower layer contains our offset, blur, and colorfilter
1034     SkLayerDrawLooper::LayerInfo info;
1035
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;
1041
1042     SkMaskFilter* mf = SkBlurMaskFilter::Create(blur / 2, SkBlurMaskFilter::kNormal_BlurStyle, mfFlags);
1043
1044     SkColorFilter* cf = SkColorFilter::CreateModeFilter(c, SkXfermode::kSrcIn_Mode);
1045
1046     SkPaint* paint = dl->addLayer(info);
1047     SkSafeUnref(paint->setMaskFilter(mf));
1048     SkSafeUnref(paint->setColorFilter(cf));
1049
1050     // dl is now built, just install it
1051     platformContext()->setDrawLooper(dl);
1052 }
1053
1054 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace)
1055 {
1056     if (paintingDisabled())
1057         return;
1058
1059     platformContext()->setStrokeColor(strokecolor.rgb());
1060 }
1061
1062 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke)
1063 {
1064     if (paintingDisabled())
1065         return;
1066
1067     platformContext()->setStrokeStyle(stroke);
1068 }
1069
1070 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1071 {
1072     if (paintingDisabled())
1073         return;
1074
1075     platformContext()->setStrokeThickness(thickness);
1076 }
1077
1078 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1079 {
1080     if (paintingDisabled())
1081         return;
1082
1083     platformContext()->setTextDrawingMode(mode);
1084 }
1085
1086 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1087 {
1088     if (paintingDisabled())
1089         return;
1090
1091     SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1092     SkAnnotateRectWithURL(platformContext()->canvas(), destRect, url.get());
1093 }
1094
1095 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1096 {
1097     if (paintingDisabled())
1098         return;
1099
1100     platformContext()->setUseAntialiasing(enable);
1101 }
1102
1103 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1104 {
1105     if (paintingDisabled())
1106         return;
1107
1108     SkPaint paint;
1109     SkRect oval = r;
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()));
1116     } else
1117         platformContext()->setupPaintForStroking(&paint, 0, 0);
1118
1119     // We do this before converting to scalar, so we don't overflow SkFixed.
1120     startAngle = fastMod(startAngle, 360);
1121     angleSpan = fastMod(angleSpan, 360);
1122
1123     SkPath path;
1124     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1125     platformContext()->drawPath(path, paint);
1126 }
1127
1128 void GraphicsContext::strokePath(const Path& pathToStroke)
1129 {
1130     if (paintingDisabled() || pathToStroke.isEmpty())
1131         return;
1132
1133     const SkPath& path = *pathToStroke.platformPath();
1134     SkPaint paint;
1135     platformContext()->setupPaintForStroking(&paint, 0, 0);
1136     platformContext()->drawPath(path, paint);
1137 }
1138
1139 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1140 {
1141     if (paintingDisabled())
1142         return;
1143
1144     SkPaint paint;
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
1150     SkRect r(rect);
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.
1158         SkPath path;
1159         path.moveTo(r.fLeft, r.fTop);
1160         path.lineTo(r.fRight, r.fBottom);
1161         path.close();
1162         platformContext()->drawPath(path, paint);
1163     }
1164 }
1165
1166 void GraphicsContext::rotate(float angleInRadians)
1167 {
1168     if (paintingDisabled())
1169         return;
1170
1171     platformContext()->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1172 }
1173
1174 void GraphicsContext::translate(float w, float h)
1175 {
1176     if (paintingDisabled())
1177         return;
1178
1179     platformContext()->translate(WebCoreFloatToSkScalar(w), WebCoreFloatToSkScalar(h));
1180 }
1181
1182 bool GraphicsContext::isAcceleratedContext() const
1183 {
1184     return platformContext()->isAccelerated();
1185 }
1186
1187 #if PLATFORM(CHROMIUM) && OS(DARWIN)
1188 CGColorSpaceRef deviceRGBColorSpaceRef()
1189 {
1190     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
1191     return deviceSpace;
1192 }
1193 #endif
1194
1195 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1196 {
1197     if (paintingDisabled())
1198         return;
1199
1200     SkRect rect = ellipse;
1201     SkPaint paint;
1202     platformContext()->setupPaintForFilling(&paint);
1203     platformContext()->drawOval(rect, paint);
1204 }
1205
1206 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1207 {
1208     if (paintingDisabled())
1209         return;
1210
1211     SkRect rect(ellipse);
1212     SkPaint paint;
1213     platformContext()->setupPaintForStroking(&paint, 0, 0);
1214     platformContext()->drawOval(rect, paint);
1215 }
1216
1217 }  // namespace WebCore