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