1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "platform/graphics/GraphicsContext.h"
29
30 #include "platform/TraceEvent.h"
31 #include "platform/geometry/IntRect.h"
32 #include "platform/geometry/RoundedRect.h"
33 #include "platform/graphics/BitmapImage.h"
34 #include "platform/graphics/DisplayList.h"
35 #include "platform/graphics/Gradient.h"
36 #include "platform/graphics/ImageBuffer.h"
37 #include "platform/text/BidiResolver.h"
38 #include "platform/text/TextRunIterator.h"
39 #include "platform/weborigin/KURL.h"
40 #include "third_party/skia/include/core/SkAnnotation.h"
41 #include "third_party/skia/include/core/SkColorFilter.h"
42 #include "third_party/skia/include/core/SkData.h"
43 #include "third_party/skia/include/core/SkDevice.h"
44 #include "third_party/skia/include/core/SkPicture.h"
45 #include "third_party/skia/include/core/SkRRect.h"
46 #include "third_party/skia/include/core/SkRefCnt.h"
47 #include "third_party/skia/include/core/SkSurface.h"
48 #include "third_party/skia/include/effects/SkBlurMaskFilter.h"
49 #include "third_party/skia/include/effects/SkCornerPathEffect.h"
50 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
51 #include "third_party/skia/include/gpu/GrRenderTarget.h"
52 #include "third_party/skia/include/gpu/GrTexture.h"
53 #include "wtf/Assertions.h"
54 #include "wtf/MathExtras.h"
55
56 using namespace std;
57 using blink::WebBlendMode;
58
59 namespace WebCore {
60
61 namespace {
62
63 class CompatibleImageBufferSurface : public ImageBufferSurface {
64 WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED;
65 public:
CompatibleImageBufferSurface(PassRefPtr<SkSurface> surface,const IntSize & size,OpacityMode opacityMode)66 CompatibleImageBufferSurface(PassRefPtr<SkSurface> surface, const IntSize& size, OpacityMode opacityMode)
67 : ImageBufferSurface(size, opacityMode)
68 , m_surface(surface)
69 {
70 }
~CompatibleImageBufferSurface()71 virtual ~CompatibleImageBufferSurface() { }
72
canvas() const73 virtual SkCanvas* canvas() const OVERRIDE { return m_surface ? m_surface->getCanvas() : 0; }
isValid() const74 virtual bool isValid() const OVERRIDE { return m_surface; }
isAccelerated() const75 virtual bool isAccelerated() const OVERRIDE { return isValid() && m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); }
getBackingTexture() const76 virtual Platform3DObject getBackingTexture() const OVERRIDE
77 {
78 ASSERT(isAccelerated());
79 GrRenderTarget* renderTarget = m_surface->getCanvas()->getTopDevice()->accessRenderTarget();
80 if (renderTarget) {
81 return renderTarget->asTexture()->getTextureHandle();
82 }
83 return 0;
84 };
85
86 private:
87 RefPtr<SkSurface> m_surface;
88 };
89
90 } // unnamed namespace
91
92 struct GraphicsContext::CanvasSaveState {
CanvasSaveStateWebCore::GraphicsContext::CanvasSaveState93 CanvasSaveState(bool pendingSave, int count)
94 : m_pendingSave(pendingSave), m_restoreCount(count) { }
95
96 bool m_pendingSave;
97 int m_restoreCount;
98 };
99
100 struct GraphicsContext::RecordingState {
RecordingStateWebCore::GraphicsContext::RecordingState101 RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList)
102 : m_savedCanvas(currentCanvas)
103 , m_displayList(displayList)
104 , m_savedMatrix(currentMatrix)
105 {
106 }
107
108 SkCanvas* m_savedCanvas;
109 RefPtr<DisplayList> m_displayList;
110 const SkMatrix m_savedMatrix;
111 };
112
GraphicsContext(SkCanvas * canvas,DisabledMode disableContextOrPainting)113 GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting)
114 : m_canvas(canvas)
115 , m_paintStateStack()
116 , m_paintStateIndex(0)
117 , m_pendingCanvasSave(false)
118 , m_annotationMode(0)
119 #if ASSERT_ENABLED
120 , m_annotationCount(0)
121 , m_layerCount(0)
122 , m_disableDestructionChecks(false)
123 #endif
124 , m_disabledState(disableContextOrPainting)
125 , m_trackOpaqueRegion(false)
126 , m_trackTextRegion(false)
127 , m_useHighResMarker(false)
128 , m_updatingControlTints(false)
129 , m_accelerated(false)
130 , m_isCertainlyOpaque(true)
131 , m_printing(false)
132 , m_antialiasHairlineImages(false)
133 {
134 if (!canvas)
135 m_disabledState |= PaintingDisabled;
136
137 // FIXME: Do some tests to determine how many states are typically used, and allocate
138 // several here.
139 m_paintStateStack.append(GraphicsContextState::create());
140 m_paintState = m_paintStateStack.last().get();
141 }
142
~GraphicsContext()143 GraphicsContext::~GraphicsContext()
144 {
145 #if ASSERT_ENABLED
146 if (!m_disableDestructionChecks) {
147 ASSERT(!m_paintStateIndex);
148 ASSERT(!m_paintState->saveCount());
149 ASSERT(!m_annotationCount);
150 ASSERT(!m_layerCount);
151 ASSERT(m_recordingStateStack.isEmpty());
152 ASSERT(m_canvasStateStack.isEmpty());
153 }
154 #endif
155 }
156
save()157 void GraphicsContext::save()
158 {
159 if (contextDisabled())
160 return;
161
162 m_paintState->incrementSaveCount();
163
164 m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount()));
165 m_pendingCanvasSave = true;
166 }
167
restore()168 void GraphicsContext::restore()
169 {
170 if (contextDisabled())
171 return;
172
173 if (!m_paintStateIndex && !m_paintState->saveCount()) {
174 WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
175 return;
176 }
177
178 if (m_paintState->saveCount()) {
179 m_paintState->decrementSaveCount();
180 } else {
181 m_paintStateIndex--;
182 m_paintState = m_paintStateStack[m_paintStateIndex].get();
183 }
184
185 CanvasSaveState savedState = m_canvasStateStack.last();
186 m_canvasStateStack.removeLast();
187 m_pendingCanvasSave = savedState.m_pendingSave;
188 m_canvas->restoreToCount(savedState.m_restoreCount);
189 }
190
saveLayer(const SkRect * bounds,const SkPaint * paint)191 void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint)
192 {
193 if (contextDisabled())
194 return;
195
196 realizeCanvasSave();
197
198 m_canvas->saveLayer(bounds, paint);
199 if (m_trackOpaqueRegion)
200 m_opaqueRegion.pushCanvasLayer(paint);
201 }
202
restoreLayer()203 void GraphicsContext::restoreLayer()
204 {
205 if (contextDisabled())
206 return;
207
208 m_canvas->restore();
209 if (m_trackOpaqueRegion)
210 m_opaqueRegion.popCanvasLayer(this);
211 }
212
beginAnnotation(const char * rendererName,const char * paintPhase,const String & elementId,const String & elementClass,const String & elementTag)213 void GraphicsContext::beginAnnotation(const char* rendererName, const char* paintPhase,
214 const String& elementId, const String& elementClass, const String& elementTag)
215 {
216 if (contextDisabled())
217 return;
218
219 canvas()->beginCommentGroup("GraphicsContextAnnotation");
220
221 GraphicsContextAnnotation annotation(rendererName, paintPhase, elementId, elementClass, elementTag);
222 AnnotationList annotations;
223 annotation.asAnnotationList(annotations);
224
225 AnnotationList::const_iterator end = annotations.end();
226 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it)
227 canvas()->addComment(it->first, it->second.ascii().data());
228
229 #if ASSERT_ENABLED
230 ++m_annotationCount;
231 #endif
232 }
233
endAnnotation()234 void GraphicsContext::endAnnotation()
235 {
236 if (contextDisabled())
237 return;
238
239 canvas()->endCommentGroup();
240
241 ASSERT(m_annotationCount > 0);
242 #if ASSERT_ENABLED
243 --m_annotationCount;
244 #endif
245 }
246
setStrokePattern(PassRefPtr<Pattern> pattern)247 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
248 {
249 if (contextDisabled())
250 return;
251
252 ASSERT(pattern);
253 if (!pattern) {
254 setStrokeColor(Color::black);
255 return;
256 }
257 mutableState()->setStrokePattern(pattern);
258 }
259
setStrokeGradient(PassRefPtr<Gradient> gradient)260 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
261 {
262 if (contextDisabled())
263 return;
264
265 ASSERT(gradient);
266 if (!gradient) {
267 setStrokeColor(Color::black);
268 return;
269 }
270 mutableState()->setStrokeGradient(gradient);
271 }
272
setFillPattern(PassRefPtr<Pattern> pattern)273 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
274 {
275 if (contextDisabled())
276 return;
277
278 ASSERT(pattern);
279 if (!pattern) {
280 setFillColor(Color::black);
281 return;
282 }
283
284 mutableState()->setFillPattern(pattern);
285 }
286
setFillGradient(PassRefPtr<Gradient> gradient)287 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
288 {
289 if (contextDisabled())
290 return;
291
292 ASSERT(gradient);
293 if (!gradient) {
294 setFillColor(Color::black);
295 return;
296 }
297
298 mutableState()->setFillGradient(gradient);
299 }
300
setShadow(const FloatSize & offset,float blur,const Color & color,DrawLooperBuilder::ShadowTransformMode shadowTransformMode,DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode)301 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color,
302 DrawLooperBuilder::ShadowTransformMode shadowTransformMode,
303 DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode)
304 {
305 if (contextDisabled())
306 return;
307
308 if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) {
309 clearShadow();
310 return;
311 }
312
313 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
314 drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode);
315 drawLooperBuilder->addUnmodifiedContent();
316 setDrawLooper(drawLooperBuilder.release());
317 }
318
setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)319 void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
320 {
321 if (contextDisabled())
322 return;
323
324 mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
325 }
326
clearDrawLooper()327 void GraphicsContext::clearDrawLooper()
328 {
329 if (contextDisabled())
330 return;
331
332 mutableState()->clearDrawLooper();
333 }
334
hasShadow() const335 bool GraphicsContext::hasShadow() const
336 {
337 return !!immutableState()->drawLooper();
338 }
339
getTransformedClipBounds(FloatRect * bounds) const340 bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const
341 {
342 if (contextDisabled())
343 return false;
344 SkIRect skIBounds;
345 if (!m_canvas->getClipDeviceBounds(&skIBounds))
346 return false;
347 SkRect skBounds = SkRect::Make(skIBounds);
348 *bounds = FloatRect(skBounds);
349 return true;
350 }
351
getTotalMatrix() const352 SkMatrix GraphicsContext::getTotalMatrix() const
353 {
354 if (contextDisabled())
355 return SkMatrix::I();
356
357 if (!isRecording())
358 return m_canvas->getTotalMatrix();
359
360 const RecordingState& recordingState = m_recordingStateStack.last();
361 SkMatrix totalMatrix = recordingState.m_savedMatrix;
362 totalMatrix.preConcat(m_canvas->getTotalMatrix());
363
364 return totalMatrix;
365 }
366
adjustTextRenderMode(SkPaint * paint)367 void GraphicsContext::adjustTextRenderMode(SkPaint* paint)
368 {
369 if (contextDisabled())
370 return;
371
372 if (!paint->isLCDRenderText())
373 return;
374
375 paint->setLCDRenderText(couldUseLCDRenderedText());
376 }
377
couldUseLCDRenderedText()378 bool GraphicsContext::couldUseLCDRenderedText()
379 {
380 // Our layers only have a single alpha channel. This means that subpixel
381 // rendered text cannot be composited correctly when the layer is
382 // collapsed. Therefore, subpixel text is contextDisabled when we are drawing
383 // onto a layer.
384 if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque())
385 return false;
386
387 return shouldSmoothFonts();
388 }
389
setCompositeOperation(CompositeOperator compositeOperation,WebBlendMode blendMode)390 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
391 {
392 if (contextDisabled())
393 return;
394 mutableState()->setCompositeOperation(compositeOperation, blendMode);
395 }
396
colorFilter()397 SkColorFilter* GraphicsContext::colorFilter()
398 {
399 return immutableState()->colorFilter();
400 }
401
setColorFilter(ColorFilter colorFilter)402 void GraphicsContext::setColorFilter(ColorFilter colorFilter)
403 {
404 GraphicsContextState* stateToSet = mutableState();
405
406 // We only support one active color filter at the moment. If (when) this becomes a problem,
407 // we should switch to using color filter chains (Skia work in progress).
408 ASSERT(!stateToSet->colorFilter());
409 stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter));
410 }
411
readPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,int x,int y)412 bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y)
413 {
414 if (contextDisabled())
415 return false;
416
417 return m_canvas->readPixels(info, pixels, rowBytes, x, y);
418 }
419
setMatrix(const SkMatrix & matrix)420 void GraphicsContext::setMatrix(const SkMatrix& matrix)
421 {
422 if (contextDisabled())
423 return;
424
425 realizeCanvasSave();
426
427 m_canvas->setMatrix(matrix);
428 }
429
concat(const SkMatrix & matrix)430 void GraphicsContext::concat(const SkMatrix& matrix)
431 {
432 if (contextDisabled())
433 return;
434
435 if (matrix.isIdentity())
436 return;
437
438 realizeCanvasSave();
439
440 m_canvas->concat(matrix);
441 }
442
beginTransparencyLayer(float opacity,const FloatRect * bounds)443 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
444 {
445 beginLayer(opacity, immutableState()->compositeOperator(), bounds);
446 }
447
beginLayer(float opacity,CompositeOperator op,const FloatRect * bounds,ColorFilter colorFilter,ImageFilter * imageFilter)448 void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter)
449 {
450 if (contextDisabled())
451 return;
452
453 SkPaint layerPaint;
454 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
455 layerPaint.setXfermode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()).get());
456 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get());
457 layerPaint.setImageFilter(imageFilter);
458
459 if (bounds) {
460 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds);
461 saveLayer(&skBounds, &layerPaint);
462 } else {
463 saveLayer(0, &layerPaint);
464 }
465
466 #if ASSERT_ENABLED
467 ++m_layerCount;
468 #endif
469 }
470
endLayer()471 void GraphicsContext::endLayer()
472 {
473 if (contextDisabled())
474 return;
475
476 restoreLayer();
477
478 ASSERT(m_layerCount > 0);
479 #if ASSERT_ENABLED
480 --m_layerCount;
481 #endif
482 }
483
beginRecording(const FloatRect & bounds)484 void GraphicsContext::beginRecording(const FloatRect& bounds)
485 {
486 RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds));
487
488 SkCanvas* savedCanvas = m_canvas;
489 SkMatrix savedMatrix = getTotalMatrix();
490
491 if (!contextDisabled()) {
492 IntRect recordingRect = enclosingIntRect(bounds);
493 m_canvas = displayList->beginRecording(recordingRect.size());
494
495 // We want the bounds offset mapped to (0, 0), such that the display list content
496 // is fully contained within the SkPictureRecord's bounds.
497 if (!toFloatSize(bounds.location()).isZero()) {
498 m_canvas->translate(-bounds.x(), -bounds.y());
499 // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here.
500 savedMatrix.preTranslate(bounds.x(), bounds.y());
501 }
502 }
503
504 m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displayList));
505 }
506
endRecording()507 PassRefPtr<DisplayList> GraphicsContext::endRecording()
508 {
509 ASSERT(!m_recordingStateStack.isEmpty());
510
511 RecordingState recording = m_recordingStateStack.last();
512 if (!contextDisabled()) {
513 ASSERT(recording.m_displayList->isRecording());
514 recording.m_displayList->endRecording();
515 }
516
517 m_recordingStateStack.removeLast();
518 m_canvas = recording.m_savedCanvas;
519
520 return recording.m_displayList.release();
521 }
522
isRecording() const523 bool GraphicsContext::isRecording() const
524 {
525 return !m_recordingStateStack.isEmpty();
526 }
527
drawDisplayList(DisplayList * displayList)528 void GraphicsContext::drawDisplayList(DisplayList* displayList)
529 {
530 ASSERT(displayList);
531 ASSERT(!displayList->isRecording());
532
533 if (contextDisabled() || displayList->bounds().isEmpty())
534 return;
535
536 realizeCanvasSave();
537
538 const FloatRect& bounds = displayList->bounds();
539 if (bounds.x() || bounds.y())
540 m_canvas->translate(bounds.x(), bounds.y());
541
542 m_canvas->drawPicture(displayList->picture());
543
544 if (bounds.x() || bounds.y())
545 m_canvas->translate(-bounds.x(), -bounds.y());
546 }
547
drawConvexPolygon(size_t numPoints,const FloatPoint * points,bool shouldAntialias)548 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
549 {
550 if (contextDisabled())
551 return;
552
553 if (numPoints <= 1)
554 return;
555
556 SkPath path;
557 setPathFromConvexPoints(&path, numPoints, points);
558
559 SkPaint paint(immutableState()->fillPaint());
560 paint.setAntiAlias(shouldAntialias);
561 drawPath(path, paint);
562
563 if (strokeStyle() != NoStroke)
564 drawPath(path, immutableState()->strokePaint());
565 }
566
drawFocusRing(const Path & focusRingPath,int width,int offset,const Color & color)567 void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color)
568 {
569 // FIXME: Implement support for offset.
570 if (contextDisabled())
571 return;
572
573 SkPaint paint;
574 paint.setAntiAlias(true);
575 paint.setStyle(SkPaint::kStroke_Style);
576 paint.setColor(color.rgb());
577
578 drawOuterPath(focusRingPath.skPath(), paint, width);
579 drawInnerPath(focusRingPath.skPath(), paint, width);
580 }
581
drawFocusRing(const Vector<IntRect> & rects,int width,int offset,const Color & color)582 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
583 {
584 if (contextDisabled())
585 return;
586
587 unsigned rectCount = rects.size();
588 if (!rectCount)
589 return;
590
591 SkRegion focusRingRegion;
592 const int focusRingOutset = getFocusRingOutset(offset);
593 for (unsigned i = 0; i < rectCount; i++) {
594 SkIRect r = rects[i];
595 r.inset(-focusRingOutset, -focusRingOutset);
596 focusRingRegion.op(r, SkRegion::kUnion_Op);
597 }
598
599 SkPath path;
600 SkPaint paint;
601 paint.setAntiAlias(true);
602 paint.setStyle(SkPaint::kStroke_Style);
603
604 paint.setColor(color.rgb());
605 focusRingRegion.getBoundaryPath(&path);
606 drawOuterPath(path, paint, width);
607 drawInnerPath(path, paint, width);
608 }
609
areaCastingShadowInHole(const IntRect & holeRect,int shadowBlur,int shadowSpread,const IntSize & shadowOffset)610 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
611 {
612 IntRect bounds(holeRect);
613
614 bounds.inflate(shadowBlur);
615
616 if (shadowSpread < 0)
617 bounds.inflate(-shadowSpread);
618
619 IntRect offsetBounds = bounds;
620 offsetBounds.move(-shadowOffset);
621 return unionRect(bounds, offsetBounds);
622 }
623
drawInnerShadow(const RoundedRect & rect,const Color & shadowColor,const IntSize shadowOffset,int shadowBlur,int shadowSpread,Edges clippedEdges)624 void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges)
625 {
626 if (contextDisabled())
627 return;
628
629 IntRect holeRect(rect.rect());
630 holeRect.inflate(-shadowSpread);
631
632 if (holeRect.isEmpty()) {
633 if (rect.isRounded())
634 fillRoundedRect(rect, shadowColor);
635 else
636 fillRect(rect.rect(), shadowColor);
637 return;
638 }
639
640 if (clippedEdges & LeftEdge) {
641 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
642 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
643 }
644 if (clippedEdges & TopEdge) {
645 holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur);
646 holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur);
647 }
648 if (clippedEdges & RightEdge)
649 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
650 if (clippedEdges & BottomEdge)
651 holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur);
652
653 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
654
655 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset);
656 RoundedRect roundedHole(holeRect, rect.radii());
657
658 save();
659 if (rect.isRounded()) {
660 Path path;
661 path.addRoundedRect(rect);
662 clipPath(path);
663 roundedHole.shrinkRadii(shadowSpread);
664 } else {
665 clip(rect.rect());
666 }
667
668 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
669 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
670 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
671 setDrawLooper(drawLooperBuilder.release());
672 fillRectWithRoundedHole(outerRect, roundedHole, fillColor);
673 restore();
674 clearDrawLooper();
675 }
676
drawLine(const IntPoint & point1,const IntPoint & point2)677 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
678 {
679 if (contextDisabled())
680 return;
681
682 StrokeStyle penStyle = strokeStyle();
683 if (penStyle == NoStroke)
684 return;
685
686 FloatPoint p1 = point1;
687 FloatPoint p2 = point2;
688 bool isVerticalLine = (p1.x() == p2.x());
689 int width = roundf(strokeThickness());
690
691 // We know these are vertical or horizontal lines, so the length will just
692 // be the sum of the displacement component vectors give or take 1 -
693 // probably worth the speed up of no square root, which also won't be exact.
694 FloatSize disp = p2 - p1;
695 int length = SkScalarRoundToInt(disp.width() + disp.height());
696 SkPaint paint(immutableState()->strokePaint(length));
697
698 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
699 // Do a rect fill of our endpoints. This ensures we always have the
700 // appearance of being a border. We then draw the actual dotted/dashed line.
701 SkRect r1, r2;
702 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
703 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
704
705 if (isVerticalLine) {
706 r1.offset(-width / 2, 0);
707 r2.offset(-width / 2, -width);
708 } else {
709 r1.offset(0, -width / 2);
710 r2.offset(-width, -width / 2);
711 }
712 SkPaint fillPaint;
713 fillPaint.setColor(paint.getColor());
714 drawRect(r1, fillPaint);
715 drawRect(r2, fillPaint);
716 }
717
718 adjustLineToPixelBoundaries(p1, p2, width, penStyle);
719 SkPoint pts[2] = { p1.data(), p2.data() };
720
721 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
722
723 if (m_trackOpaqueRegion)
724 m_opaqueRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint);
725 }
726
drawLineForDocumentMarker(const FloatPoint & pt,float width,DocumentMarkerLineStyle style)727 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
728 {
729 if (contextDisabled())
730 return;
731
732 int deviceScaleFactor = m_useHighResMarker ? 2 : 1;
733
734 // Create the pattern we'll use to draw the underline.
735 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
736 static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
737 static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
738 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
739 if (!misspellBitmap[index]) {
740 #if OS(MACOSX)
741 // Match the artwork used by the Mac.
742 const int rowPixels = 4 * deviceScaleFactor;
743 const int colPixels = 3 * deviceScaleFactor;
744 SkBitmap bitmap;
745 if (!bitmap.allocN32Pixels(rowPixels, colPixels))
746 return;
747
748 bitmap.eraseARGB(0, 0, 0, 0);
749 const uint32_t transparentColor = 0x00000000;
750
751 if (deviceScaleFactor == 1) {
752 const uint32_t colors[2][6] = {
753 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200, 0xe0e02400 },
754 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828, 0xe0515151 }
755 };
756
757 // Pattern: a b a a b a
758 // c d c c d c
759 // e f e e f e
760 for (int x = 0; x < colPixels; ++x) {
761 uint32_t* row = bitmap.getAddr32(0, x);
762 row[0] = colors[index][x * 2];
763 row[1] = colors[index][x * 2 + 1];
764 row[2] = colors[index][x * 2];
765 row[3] = transparentColor;
766 }
767 } else if (deviceScaleFactor == 2) {
768 const uint32_t colors[2][18] = {
769 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
770 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
771 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a,
772 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 }
773 };
774
775 // Pattern: a b c c b a
776 // d e f f e d
777 // g h j j h g
778 // k l m m l k
779 // n o p p o n
780 // q r s s r q
781 for (int x = 0; x < colPixels; ++x) {
782 uint32_t* row = bitmap.getAddr32(0, x);
783 row[0] = colors[index][x * 3];
784 row[1] = colors[index][x * 3 + 1];
785 row[2] = colors[index][x * 3 + 2];
786 row[3] = colors[index][x * 3 + 2];
787 row[4] = colors[index][x * 3 + 1];
788 row[5] = colors[index][x * 3];
789 row[6] = transparentColor;
790 row[7] = transparentColor;
791 }
792 } else
793 ASSERT_NOT_REACHED();
794
795 misspellBitmap[index] = new SkBitmap(bitmap);
796 #else
797 // We use a 2-pixel-high misspelling indicator because that seems to be
798 // what WebKit is designed for, and how much room there is in a typical
799 // page for it.
800 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
801 const int colPixels = 2 * deviceScaleFactor;
802 SkBitmap bitmap;
803 if (!bitmap.allocN32Pixels(rowPixels, colPixels))
804 return;
805
806 bitmap.eraseARGB(0, 0, 0, 0);
807 if (deviceScaleFactor == 1)
808 draw1xMarker(&bitmap, index);
809 else if (deviceScaleFactor == 2)
810 draw2xMarker(&bitmap, index);
811 else
812 ASSERT_NOT_REACHED();
813
814 misspellBitmap[index] = new SkBitmap(bitmap);
815 #endif
816 }
817
818 #if OS(MACOSX)
819 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
820 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
821
822 // Make sure to draw only complete dots.
823 int rowPixels = misspellBitmap[index]->width();
824 float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
825 if (rowPixels - widthMod > deviceScaleFactor)
826 width -= widthMod / deviceScaleFactor;
827 #else
828 SkScalar originX = WebCoreFloatToSkScalar(pt.x());
829
830 // Offset it vertically by 1 so that there's some space under the text.
831 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
832 originX *= deviceScaleFactor;
833 originY *= deviceScaleFactor;
834 #endif
835
836 SkMatrix localMatrix;
837 localMatrix.setTranslate(originX, originY);
838 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(
839 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
840
841 SkPaint paint;
842 paint.setShader(shader.get());
843
844 SkRect rect;
845 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
846
847 if (deviceScaleFactor == 2) {
848 save();
849 scale(0.5, 0.5);
850 }
851 drawRect(rect, paint);
852 if (deviceScaleFactor == 2)
853 restore();
854 }
855
drawLineForText(const FloatPoint & pt,float width,bool printing)856 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
857 {
858 if (contextDisabled())
859 return;
860
861 if (width <= 0)
862 return;
863
864 SkPaint paint;
865 switch (strokeStyle()) {
866 case NoStroke:
867 case SolidStroke:
868 case DoubleStroke:
869 case WavyStroke: {
870 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
871 SkRect r;
872 r.fLeft = WebCoreFloatToSkScalar(pt.x());
873 // Avoid anti-aliasing lines. Currently, these are always horizontal.
874 // Round to nearest pixel to match text and other content.
875 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f));
876 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
877 r.fBottom = r.fTop + SkIntToScalar(thickness);
878 paint = immutableState()->fillPaint();
879 // Text lines are drawn using the stroke color.
880 paint.setColor(effectiveStrokeColor());
881 drawRect(r, paint);
882 return;
883 }
884 case DottedStroke:
885 case DashedStroke: {
886 int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f));
887 drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y));
888 return;
889 }
890 }
891
892 ASSERT_NOT_REACHED();
893 }
894
895 // Draws a filled rectangle with a stroked border.
drawRect(const IntRect & rect)896 void GraphicsContext::drawRect(const IntRect& rect)
897 {
898 if (contextDisabled())
899 return;
900
901 ASSERT(!rect.isEmpty());
902 if (rect.isEmpty())
903 return;
904
905 SkRect skRect = rect;
906 int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000;
907 if (fillcolorNotTransparent)
908 drawRect(skRect, immutableState()->fillPaint());
909
910 if (immutableState()->strokeData().style() != NoStroke
911 && immutableState()->strokeData().color().alpha()) {
912 // Stroke a width: 1 inset border
913 SkPaint paint(immutableState()->fillPaint());
914 paint.setColor(effectiveStrokeColor());
915 paint.setStyle(SkPaint::kStroke_Style);
916 paint.setStrokeWidth(1);
917
918 skRect.inset(0.5f, 0.5f);
919 drawRect(skRect, paint);
920 }
921 }
922
drawText(const Font & font,const TextRunPaintInfo & runInfo,const FloatPoint & point)923 void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point)
924 {
925 if (contextDisabled())
926 return;
927
928 font.drawText(this, runInfo, point);
929 }
930
drawEmphasisMarks(const Font & font,const TextRunPaintInfo & runInfo,const AtomicString & mark,const FloatPoint & point)931 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point)
932 {
933 if (contextDisabled())
934 return;
935
936 font.drawEmphasisMarks(this, runInfo, mark, point);
937 }
938
drawBidiText(const Font & font,const TextRunPaintInfo & runInfo,const FloatPoint & point,Font::CustomFontNotReadyAction customFontNotReadyAction)939 void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
940 {
941 if (contextDisabled())
942 return;
943
944 // sub-run painting is not supported for Bidi text.
945 const TextRun& run = runInfo.run;
946 ASSERT((runInfo.from == 0) && (runInfo.to == run.length()));
947 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
948 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
949 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
950
951 // FIXME: This ownership should be reversed. We should pass BidiRunList
952 // to BidiResolver in createBidiRunsForLine.
953 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
954 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
955 if (!bidiRuns.runCount())
956 return;
957
958 FloatPoint currPoint = point;
959 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
960 while (bidiRun) {
961 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
962 bool isRTL = bidiRun->level() % 2;
963 subrun.setDirection(isRTL ? RTL : LTR);
964 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
965
966 TextRunPaintInfo subrunInfo(subrun);
967 subrunInfo.bounds = runInfo.bounds;
968 font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction);
969
970 bidiRun = bidiRun->next();
971 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
972 if (bidiRun)
973 currPoint.move(font.width(subrun), 0);
974 }
975
976 bidiRuns.deleteRuns();
977 }
978
drawHighlightForText(const Font & font,const TextRun & run,const FloatPoint & point,int h,const Color & backgroundColor,int from,int to)979 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to)
980 {
981 if (contextDisabled())
982 return;
983
984 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
985 }
986
drawImage(Image * image,const IntPoint & p,CompositeOperator op,RespectImageOrientationEnum shouldRespectImageOrientation)987 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
988 {
989 if (!image)
990 return;
991 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
992 }
993
drawImage(Image * image,const IntRect & r,CompositeOperator op,RespectImageOrientationEnum shouldRespectImageOrientation)994 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
995 {
996 if (!image)
997 return;
998 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
999 }
1000
drawImage(Image * image,const FloatRect & dest,const FloatRect & src,CompositeOperator op,RespectImageOrientationEnum shouldRespectImageOrientation)1001 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1002 {
1003 drawImage(image, dest, src, op, blink::WebBlendModeNormal, shouldRespectImageOrientation);
1004 }
1005
drawImage(Image * image,const FloatRect & dest)1006 void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
1007 {
1008 if (!image)
1009 return;
1010 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
1011 }
1012
drawImage(Image * image,const FloatRect & dest,const FloatRect & src,CompositeOperator op,WebBlendMode blendMode,RespectImageOrientationEnum shouldRespectImageOrientation)1013 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
1014 {
1015 if (contextDisabled() || !image)
1016 return;
1017 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation);
1018 }
1019
drawTiledImage(Image * image,const IntRect & destRect,const IntPoint & srcPoint,const IntSize & tileSize,CompositeOperator op,WebBlendMode blendMode,const IntSize & repeatSpacing)1020 void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing)
1021 {
1022 if (contextDisabled() || !image)
1023 return;
1024 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing);
1025 }
1026
drawTiledImage(Image * image,const IntRect & dest,const IntRect & srcRect,const FloatSize & tileScaleFactor,Image::TileRule hRule,Image::TileRule vRule,CompositeOperator op)1027 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect,
1028 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
1029 {
1030 if (contextDisabled() || !image)
1031 return;
1032
1033 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
1034 // Just do a scale.
1035 drawImage(image, dest, srcRect, op);
1036 return;
1037 }
1038
1039 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op);
1040 }
1041
drawImageBuffer(ImageBuffer * image,const FloatRect & dest,const FloatRect * src,CompositeOperator op)1042 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
1043 const FloatRect* src, CompositeOperator op)
1044 {
1045 if (contextDisabled() || !image)
1046 return;
1047
1048 image->draw(this, dest, src, op);
1049 }
1050
writePixels(const SkImageInfo & info,const void * pixels,size_t rowBytes,int x,int y)1051 void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
1052 {
1053 if (contextDisabled())
1054 return;
1055
1056 m_canvas->writePixels(info, pixels, rowBytes, x, y);
1057
1058 if (m_trackOpaqueRegion) {
1059 SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height());
1060 SkPaint paint;
1061
1062 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1063 if (kOpaque_SkAlphaType != info.alphaType())
1064 paint.setAlpha(0x80); // signal to m_opaqueRegion that we are not fully opaque
1065
1066 m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1067 // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly,
1068 // rather than cons-ing up a paint with an xfermode and alpha
1069 }
1070 }
1071
writePixels(const SkBitmap & bitmap,int x,int y)1072 void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y)
1073 {
1074 if (contextDisabled())
1075 return;
1076
1077 if (!bitmap.getTexture()) {
1078 SkAutoLockPixels alp(bitmap);
1079 if (bitmap.getPixels())
1080 writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y);
1081 }
1082 }
1083
drawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint * paint)1084 void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
1085 {
1086 if (contextDisabled())
1087 return;
1088
1089 m_canvas->drawBitmap(bitmap, left, top, paint);
1090
1091 if (m_trackOpaqueRegion) {
1092 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height());
1093 m_opaqueRegion.didDrawRect(this, rect, *paint, &bitmap);
1094 }
1095 }
1096
drawBitmapRect(const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint * paint)1097 void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
1098 const SkRect& dst, const SkPaint* paint)
1099 {
1100 if (contextDisabled())
1101 return;
1102
1103 SkCanvas::DrawBitmapRectFlags flags =
1104 immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag;
1105
1106 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1107
1108 if (m_trackOpaqueRegion)
1109 m_opaqueRegion.didDrawRect(this, dst, *paint, &bitmap);
1110 }
1111
drawOval(const SkRect & oval,const SkPaint & paint)1112 void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint)
1113 {
1114 if (contextDisabled())
1115 return;
1116
1117 m_canvas->drawOval(oval, paint);
1118
1119 if (m_trackOpaqueRegion)
1120 m_opaqueRegion.didDrawBounded(this, oval, paint);
1121 }
1122
drawPath(const SkPath & path,const SkPaint & paint)1123 void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint)
1124 {
1125 if (contextDisabled())
1126 return;
1127
1128 m_canvas->drawPath(path, paint);
1129
1130 if (m_trackOpaqueRegion)
1131 m_opaqueRegion.didDrawPath(this, path, paint);
1132 }
1133
drawRect(const SkRect & rect,const SkPaint & paint)1134 void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint)
1135 {
1136 if (contextDisabled())
1137 return;
1138
1139 m_canvas->drawRect(rect, paint);
1140
1141 if (m_trackOpaqueRegion)
1142 m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1143 }
1144
didDrawRect(const SkRect & rect,const SkPaint & paint,const SkBitmap * bitmap)1145 void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
1146 {
1147 if (contextDisabled())
1148 return;
1149
1150 if (m_trackOpaqueRegion)
1151 m_opaqueRegion.didDrawRect(this, rect, paint, bitmap);
1152 }
1153
drawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkRect & textRect,const SkPaint & paint)1154 void GraphicsContext::drawPosText(const void* text, size_t byteLength,
1155 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint)
1156 {
1157 if (contextDisabled())
1158 return;
1159
1160 m_canvas->drawPosText(text, byteLength, pos, paint);
1161 didDrawTextInRect(textRect);
1162
1163 // FIXME: compute bounds for positioned text.
1164 if (m_trackOpaqueRegion)
1165 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStroke);
1166 }
1167
fillPath(const Path & pathToFill)1168 void GraphicsContext::fillPath(const Path& pathToFill)
1169 {
1170 if (contextDisabled() || pathToFill.isEmpty())
1171 return;
1172
1173 // Use const_cast and temporarily modify the fill type instead of copying the path.
1174 SkPath& path = const_cast<SkPath&>(pathToFill.skPath());
1175 SkPath::FillType previousFillType = path.getFillType();
1176
1177 SkPath::FillType temporaryFillType =
1178 immutableState()->fillRule() == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1179 path.setFillType(temporaryFillType);
1180
1181 drawPath(path, immutableState()->fillPaint());
1182
1183 path.setFillType(previousFillType);
1184 }
1185
fillRect(const FloatRect & rect)1186 void GraphicsContext::fillRect(const FloatRect& rect)
1187 {
1188 if (contextDisabled())
1189 return;
1190
1191 SkRect r = rect;
1192
1193 drawRect(r, immutableState()->fillPaint());
1194 }
1195
fillRect(const FloatRect & rect,const Color & color)1196 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1197 {
1198 if (contextDisabled())
1199 return;
1200
1201 SkRect r = rect;
1202 SkPaint paint = immutableState()->fillPaint();
1203 paint.setColor(color.rgb());
1204 drawRect(r, paint);
1205 }
1206
fillBetweenRoundedRects(const IntRect & outer,const IntSize & outerTopLeft,const IntSize & outerTopRight,const IntSize & outerBottomLeft,const IntSize & outerBottomRight,const IntRect & inner,const IntSize & innerTopLeft,const IntSize & innerTopRight,const IntSize & innerBottomLeft,const IntSize & innerBottomRight,const Color & color)1207 void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight,
1208 const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color) {
1209 if (contextDisabled())
1210 return;
1211
1212 SkVector outerRadii[4];
1213 SkVector innerRadii[4];
1214 setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft);
1215 setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft);
1216
1217 SkRRect rrOuter;
1218 SkRRect rrInner;
1219 rrOuter.setRectRadii(outer, outerRadii);
1220 rrInner.setRectRadii(inner, innerRadii);
1221
1222 SkPaint paint(immutableState()->fillPaint());
1223 paint.setColor(color.rgb());
1224
1225 m_canvas->drawDRRect(rrOuter, rrInner, paint);
1226
1227 if (m_trackOpaqueRegion)
1228 m_opaqueRegion.didDrawBounded(this, rrOuter.getBounds(), paint);
1229 }
1230
fillBetweenRoundedRects(const RoundedRect & outer,const RoundedRect & inner,const Color & color)1231 void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color)
1232 {
1233 fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(),
1234 inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color);
1235 }
1236
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color)1237 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
1238 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
1239 {
1240 if (contextDisabled())
1241 return;
1242
1243 if (topLeft.width() + topRight.width() > rect.width()
1244 || bottomLeft.width() + bottomRight.width() > rect.width()
1245 || topLeft.height() + bottomLeft.height() > rect.height()
1246 || topRight.height() + bottomRight.height() > rect.height()) {
1247 // Not all the radii fit, return a rect. This matches the behavior of
1248 // Path::createRoundedRectangle. Without this we attempt to draw a round
1249 // shadow for a square box.
1250 fillRect(rect, color);
1251 return;
1252 }
1253
1254 SkVector radii[4];
1255 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft);
1256
1257 SkRRect rr;
1258 rr.setRectRadii(rect, radii);
1259
1260 SkPaint paint(immutableState()->fillPaint());
1261 paint.setColor(color.rgb());
1262
1263 m_canvas->drawRRect(rr, paint);
1264
1265 if (m_trackOpaqueRegion)
1266 m_opaqueRegion.didDrawBounded(this, rr.getBounds(), paint);
1267 }
1268
fillEllipse(const FloatRect & ellipse)1269 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1270 {
1271 if (contextDisabled())
1272 return;
1273
1274 SkRect rect = ellipse;
1275 drawOval(rect, immutableState()->fillPaint());
1276 }
1277
strokePath(const Path & pathToStroke)1278 void GraphicsContext::strokePath(const Path& pathToStroke)
1279 {
1280 if (contextDisabled() || pathToStroke.isEmpty())
1281 return;
1282
1283 const SkPath& path = pathToStroke.skPath();
1284 drawPath(path, immutableState()->strokePaint());
1285 }
1286
strokeRect(const FloatRect & rect)1287 void GraphicsContext::strokeRect(const FloatRect& rect)
1288 {
1289 strokeRect(rect, strokeThickness());
1290 }
1291
strokeRect(const FloatRect & rect,float lineWidth)1292 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1293 {
1294 if (contextDisabled())
1295 return;
1296
1297 SkPaint paint(immutableState()->strokePaint());
1298 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1299 // Reset the dash effect to account for the width
1300 immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0);
1301 // strokerect has special rules for CSS when the rect is degenerate:
1302 // if width==0 && height==0, do nothing
1303 // if width==0 || height==0, then just draw line for the other dimension
1304 SkRect r(rect);
1305 bool validW = r.width() > 0;
1306 bool validH = r.height() > 0;
1307 if (validW && validH) {
1308 drawRect(r, paint);
1309 } else if (validW || validH) {
1310 // we are expected to respect the lineJoin, so we can't just call
1311 // drawLine -- we have to create a path that doubles back on itself.
1312 SkPath path;
1313 path.moveTo(r.fLeft, r.fTop);
1314 path.lineTo(r.fRight, r.fBottom);
1315 path.close();
1316 drawPath(path, paint);
1317 }
1318 }
1319
strokeEllipse(const FloatRect & ellipse)1320 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1321 {
1322 if (contextDisabled())
1323 return;
1324
1325 drawOval(ellipse, immutableState()->strokePaint());
1326 }
1327
clipRoundedRect(const RoundedRect & rect,SkRegion::Op regionOp)1328 void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp)
1329 {
1330 if (contextDisabled())
1331 return;
1332
1333 if (!rect.isRounded()) {
1334 clipRect(rect.rect(), NotAntiAliased, regionOp);
1335 return;
1336 }
1337
1338 SkVector radii[4];
1339 RoundedRect::Radii wkRadii = rect.radii();
1340 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft());
1341
1342 SkRRect r;
1343 r.setRectRadii(rect.rect(), radii);
1344
1345 clipRRect(r, AntiAliased, regionOp);
1346 }
1347
clipOut(const Path & pathToClip)1348 void GraphicsContext::clipOut(const Path& pathToClip)
1349 {
1350 if (contextDisabled())
1351 return;
1352
1353 // Use const_cast and temporarily toggle the inverse fill type instead of copying the path.
1354 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1355 path.toggleInverseFillType();
1356 clipPath(path, AntiAliased);
1357 path.toggleInverseFillType();
1358 }
1359
clipPath(const Path & pathToClip,WindRule clipRule)1360 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
1361 {
1362 if (contextDisabled() || pathToClip.isEmpty())
1363 return;
1364
1365 // Use const_cast and temporarily modify the fill type instead of copying the path.
1366 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1367 SkPath::FillType previousFillType = path.getFillType();
1368
1369 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1370 path.setFillType(temporaryFillType);
1371 clipPath(path, AntiAliased);
1372
1373 path.setFillType(previousFillType);
1374 }
1375
clipConvexPolygon(size_t numPoints,const FloatPoint * points,bool antialiased)1376 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
1377 {
1378 if (contextDisabled())
1379 return;
1380
1381 if (numPoints <= 1)
1382 return;
1383
1384 SkPath path;
1385 setPathFromConvexPoints(&path, numPoints, points);
1386 clipPath(path, antialiased ? AntiAliased : NotAntiAliased);
1387 }
1388
clipOutRoundedRect(const RoundedRect & rect)1389 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
1390 {
1391 if (contextDisabled())
1392 return;
1393
1394 clipRoundedRect(rect, SkRegion::kDifference_Op);
1395 }
1396
canvasClip(const Path & pathToClip,WindRule clipRule)1397 void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule)
1398 {
1399 if (contextDisabled())
1400 return;
1401
1402 // Use const_cast and temporarily modify the fill type instead of copying the path.
1403 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1404 SkPath::FillType previousFillType = path.getFillType();
1405
1406 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1407 path.setFillType(temporaryFillType);
1408 clipPath(path);
1409
1410 path.setFillType(previousFillType);
1411 }
1412
clipRect(const SkRect & rect,AntiAliasingMode aa,SkRegion::Op op)1413 void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1414 {
1415 if (contextDisabled())
1416 return;
1417
1418 realizeCanvasSave();
1419
1420 m_canvas->clipRect(rect, op, aa == AntiAliased);
1421 }
1422
clipPath(const SkPath & path,AntiAliasingMode aa,SkRegion::Op op)1423 void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op)
1424 {
1425 if (contextDisabled())
1426 return;
1427
1428 realizeCanvasSave();
1429
1430 m_canvas->clipPath(path, op, aa == AntiAliased);
1431 }
1432
clipRRect(const SkRRect & rect,AntiAliasingMode aa,SkRegion::Op op)1433 void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1434 {
1435 if (contextDisabled())
1436 return;
1437
1438 realizeCanvasSave();
1439
1440 m_canvas->clipRRect(rect, op, aa == AntiAliased);
1441 }
1442
beginCull(const FloatRect & rect)1443 void GraphicsContext::beginCull(const FloatRect& rect)
1444 {
1445 if (contextDisabled())
1446 return;
1447
1448 realizeCanvasSave();
1449 m_canvas->pushCull(rect);
1450 }
1451
endCull()1452 void GraphicsContext::endCull()
1453 {
1454 if (contextDisabled())
1455 return;
1456
1457 realizeCanvasSave();
1458
1459 m_canvas->popCull();
1460 }
1461
rotate(float angleInRadians)1462 void GraphicsContext::rotate(float angleInRadians)
1463 {
1464 if (contextDisabled())
1465 return;
1466
1467 realizeCanvasSave();
1468
1469 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1470 }
1471
translate(float x,float y)1472 void GraphicsContext::translate(float x, float y)
1473 {
1474 if (contextDisabled())
1475 return;
1476
1477 if (!x && !y)
1478 return;
1479
1480 realizeCanvasSave();
1481
1482 m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1483 }
1484
scale(float x,float y)1485 void GraphicsContext::scale(float x, float y)
1486 {
1487 if (contextDisabled())
1488 return;
1489
1490 if (x == 1.0f && y == 1.0f)
1491 return;
1492
1493 realizeCanvasSave();
1494
1495 m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1496 }
1497
setURLForRect(const KURL & link,const IntRect & destRect)1498 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1499 {
1500 if (contextDisabled())
1501 return;
1502
1503 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1504 SkAnnotateRectWithURL(m_canvas, destRect, url.get());
1505 }
1506
setURLFragmentForRect(const String & destName,const IntRect & rect)1507 void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect)
1508 {
1509 if (contextDisabled())
1510 return;
1511
1512 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data()));
1513 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get());
1514 }
1515
addURLTargetAtPoint(const String & name,const IntPoint & pos)1516 void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos)
1517 {
1518 if (contextDisabled())
1519 return;
1520
1521 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data()));
1522 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData);
1523 }
1524
getCTM() const1525 AffineTransform GraphicsContext::getCTM() const
1526 {
1527 if (contextDisabled())
1528 return AffineTransform();
1529
1530 SkMatrix m = getTotalMatrix();
1531 return AffineTransform(SkScalarToDouble(m.getScaleX()),
1532 SkScalarToDouble(m.getSkewY()),
1533 SkScalarToDouble(m.getSkewX()),
1534 SkScalarToDouble(m.getScaleY()),
1535 SkScalarToDouble(m.getTranslateX()),
1536 SkScalarToDouble(m.getTranslateY()));
1537 }
1538
fillRect(const FloatRect & rect,const Color & color,CompositeOperator op)1539 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op)
1540 {
1541 if (contextDisabled())
1542 return;
1543
1544 CompositeOperator previousOperator = compositeOperation();
1545 setCompositeOperation(op);
1546 fillRect(rect, color);
1547 setCompositeOperation(previousOperator);
1548 }
1549
fillRoundedRect(const RoundedRect & rect,const Color & color)1550 void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color)
1551 {
1552 if (contextDisabled())
1553 return;
1554
1555 if (rect.isRounded())
1556 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color);
1557 else
1558 fillRect(rect.rect(), color);
1559 }
1560
fillRectWithRoundedHole(const IntRect & rect,const RoundedRect & roundedHoleRect,const Color & color)1561 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color)
1562 {
1563 if (contextDisabled())
1564 return;
1565
1566 Path path;
1567 path.addRect(rect);
1568
1569 if (!roundedHoleRect.radii().isZero())
1570 path.addRoundedRect(roundedHoleRect);
1571 else
1572 path.addRect(roundedHoleRect.rect());
1573
1574 WindRule oldFillRule = fillRule();
1575 Color oldFillColor = fillColor();
1576
1577 setFillRule(RULE_EVENODD);
1578 setFillColor(color);
1579
1580 fillPath(path);
1581
1582 setFillRule(oldFillRule);
1583 setFillColor(oldFillColor);
1584 }
1585
clearRect(const FloatRect & rect)1586 void GraphicsContext::clearRect(const FloatRect& rect)
1587 {
1588 if (contextDisabled())
1589 return;
1590
1591 SkRect r = rect;
1592 SkPaint paint(immutableState()->fillPaint());
1593 paint.setXfermodeMode(SkXfermode::kClear_Mode);
1594 drawRect(r, paint);
1595 }
1596
adjustLineToPixelBoundaries(FloatPoint & p1,FloatPoint & p2,float strokeWidth,StrokeStyle penStyle)1597 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
1598 {
1599 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
1600 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
1601 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
1602 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
1603 if (penStyle == DottedStroke || penStyle == DashedStroke) {
1604 if (p1.x() == p2.x()) {
1605 p1.setY(p1.y() + strokeWidth);
1606 p2.setY(p2.y() - strokeWidth);
1607 } else {
1608 p1.setX(p1.x() + strokeWidth);
1609 p2.setX(p2.x() - strokeWidth);
1610 }
1611 }
1612
1613 if (static_cast<int>(strokeWidth) % 2) { //odd
1614 if (p1.x() == p2.x()) {
1615 // We're a vertical line. Adjust our x.
1616 p1.setX(p1.x() + 0.5f);
1617 p2.setX(p2.x() + 0.5f);
1618 } else {
1619 // We're a horizontal line. Adjust our y.
1620 p1.setY(p1.y() + 0.5f);
1621 p2.setY(p2.y() + 0.5f);
1622 }
1623 }
1624 }
1625
createCompatibleBuffer(const IntSize & size,OpacityMode opacityMode) const1626 PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, OpacityMode opacityMode) const
1627 {
1628 // Make the buffer larger if the context's transform is scaling it so we need a higher
1629 // resolution than one pixel per unit. Also set up a corresponding scale factor on the
1630 // graphics context.
1631
1632 AffineTransform transform = getCTM();
1633 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
1634
1635 SkAlphaType alphaType = (opacityMode == Opaque) ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1636 SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType);
1637 RefPtr<SkSurface> skSurface = adoptRef(m_canvas->newSurface(info));
1638 if (!skSurface)
1639 return nullptr;
1640 OwnPtr<ImageBufferSurface> surface = adoptPtr(new CompatibleImageBufferSurface(skSurface.release(), scaledSize, opacityMode));
1641 ASSERT(surface->isValid());
1642 OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release()));
1643
1644 buffer->context()->scale(static_cast<float>(scaledSize.width()) / size.width(),
1645 static_cast<float>(scaledSize.height()) / size.height());
1646
1647 return buffer.release();
1648 }
1649
setPathFromConvexPoints(SkPath * path,size_t numPoints,const FloatPoint * points)1650 void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
1651 {
1652 path->incReserve(numPoints);
1653 path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
1654 WebCoreFloatToSkScalar(points[0].y()));
1655 for (size_t i = 1; i < numPoints; ++i) {
1656 path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
1657 WebCoreFloatToSkScalar(points[i].y()));
1658 }
1659
1660 /* The code used to just blindly call this
1661 path->setIsConvex(true);
1662 But webkit can sometimes send us non-convex 4-point values, so we mark the path's
1663 convexity as unknown, so it will get computed by skia at draw time.
1664 See crbug.com 108605
1665 */
1666 SkPath::Convexity convexity = SkPath::kConvex_Convexity;
1667 if (numPoints == 4)
1668 convexity = SkPath::kUnknown_Convexity;
1669 path->setConvexity(convexity);
1670 }
1671
drawOuterPath(const SkPath & path,SkPaint & paint,int width)1672 void GraphicsContext::drawOuterPath(const SkPath& path, SkPaint& paint, int width)
1673 {
1674 #if OS(MACOSX)
1675 paint.setAlpha(64);
1676 paint.setStrokeWidth(width);
1677 paint.setPathEffect(SkCornerPathEffect::Create((width - 1) * 0.5f))->unref();
1678 #else
1679 paint.setStrokeWidth(1);
1680 paint.setPathEffect(SkCornerPathEffect::Create(1))->unref();
1681 #endif
1682 drawPath(path, paint);
1683 }
1684
drawInnerPath(const SkPath & path,SkPaint & paint,int width)1685 void GraphicsContext::drawInnerPath(const SkPath& path, SkPaint& paint, int width)
1686 {
1687 #if OS(MACOSX)
1688 paint.setAlpha(128);
1689 paint.setStrokeWidth(width * 0.5f);
1690 drawPath(path, paint);
1691 #endif
1692 }
1693
setRadii(SkVector * radii,IntSize topLeft,IntSize topRight,IntSize bottomRight,IntSize bottomLeft)1694 void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft)
1695 {
1696 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()),
1697 SkIntToScalar(topLeft.height()));
1698 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()),
1699 SkIntToScalar(topRight.height()));
1700 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()),
1701 SkIntToScalar(bottomRight.height()));
1702 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()),
1703 SkIntToScalar(bottomLeft.height()));
1704 }
1705
WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)1706 PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)
1707 {
1708 switch (colorFilter) {
1709 case ColorFilterLuminanceToAlpha:
1710 return adoptRef(SkLumaColorFilter::Create());
1711 case ColorFilterLinearRGBToSRGB:
1712 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
1713 case ColorFilterSRGBToLinearRGB:
1714 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
1715 case ColorFilterNone:
1716 break;
1717 default:
1718 ASSERT_NOT_REACHED();
1719 break;
1720 }
1721
1722 return nullptr;
1723 }
1724
1725 #if !OS(MACOSX)
draw2xMarker(SkBitmap * bitmap,int index)1726 void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index)
1727 {
1728 const SkPMColor lineColor = lineColors(index);
1729 const SkPMColor antiColor1 = antiColors1(index);
1730 const SkPMColor antiColor2 = antiColors2(index);
1731
1732 uint32_t* row1 = bitmap->getAddr32(0, 0);
1733 uint32_t* row2 = bitmap->getAddr32(0, 1);
1734 uint32_t* row3 = bitmap->getAddr32(0, 2);
1735 uint32_t* row4 = bitmap->getAddr32(0, 3);
1736
1737 // Pattern: X0o o0X0o o0
1738 // XX0o o0XXX0o o0X
1739 // o0XXX0o o0XXX0o
1740 // o0X0o o0X0o
1741 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
1742 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
1743 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
1744 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
1745
1746 for (int x = 0; x < bitmap->width() + 8; x += 8) {
1747 int count = std::min(bitmap->width() - x, 8);
1748 if (count > 0) {
1749 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
1750 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
1751 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
1752 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
1753 }
1754 }
1755 }
1756
draw1xMarker(SkBitmap * bitmap,int index)1757 void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index)
1758 {
1759 const uint32_t lineColor = lineColors(index);
1760 const uint32_t antiColor = antiColors2(index);
1761
1762 // Pattern: X o o X o o X
1763 // o X o o X o
1764 uint32_t* row1 = bitmap->getAddr32(0, 0);
1765 uint32_t* row2 = bitmap->getAddr32(0, 1);
1766 for (int x = 0; x < bitmap->width(); x++) {
1767 switch (x % 4) {
1768 case 0:
1769 row1[x] = lineColor;
1770 break;
1771 case 1:
1772 row1[x] = antiColor;
1773 row2[x] = antiColor;
1774 break;
1775 case 2:
1776 row2[x] = lineColor;
1777 break;
1778 case 3:
1779 row1[x] = antiColor;
1780 row2[x] = antiColor;
1781 break;
1782 }
1783 }
1784 }
1785
lineColors(int index)1786 const SkPMColor GraphicsContext::lineColors(int index)
1787 {
1788 static const SkPMColor colors[] = {
1789 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
1790 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray.
1791 };
1792
1793 return colors[index];
1794 }
1795
antiColors1(int index)1796 const SkPMColor GraphicsContext::antiColors1(int index)
1797 {
1798 static const SkPMColor colors[] = {
1799 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red.
1800 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray.
1801 };
1802
1803 return colors[index];
1804 }
1805
antiColors2(int index)1806 const SkPMColor GraphicsContext::antiColors2(int index)
1807 {
1808 static const SkPMColor colors[] = {
1809 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
1810 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray
1811 };
1812
1813 return colors[index];
1814 }
1815 #endif
1816
didDrawTextInRect(const SkRect & textRect)1817 void GraphicsContext::didDrawTextInRect(const SkRect& textRect)
1818 {
1819 if (m_trackTextRegion) {
1820 TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect");
1821 m_textRegion.join(textRect);
1822 }
1823 }
1824
1825 }
1826