From a4ea8c30fedfc69f23667d81f61db91c0f1b787a Mon Sep 17 00:00:00 2001 From: Etienne Membrives Date: Fri, 19 Dec 2014 15:45:38 +0100 Subject: [PATCH] Update from https://crrev.com/308996 Add patch for V8 build rules R=qsr@chromium.org Review URL: https://codereview.chromium.org/816543004 --- engine/bindings/scripts/blink_idl_parser.py | 14 +- engine/platform/graphics/GraphicsContext.cpp | 12 - .../graphics/GraphicsContext.cpp.orig | 1856 +++++++++++++++++ .../platform/graphics/GraphicsContext.cpp.rej | 22 + engine/platform/graphics/GraphicsContext.h | 4 +- .../platform/graphics/GraphicsContext.h.orig | 531 +++++ .../platform/graphics/GraphicsContext.h.rej | 15 + .../graphics/GraphicsContextRecorder.cpp | 6 +- 8 files changed, 2441 insertions(+), 19 deletions(-) create mode 100644 engine/platform/graphics/GraphicsContext.cpp.orig create mode 100644 engine/platform/graphics/GraphicsContext.cpp.rej create mode 100644 engine/platform/graphics/GraphicsContext.h.orig create mode 100644 engine/platform/graphics/GraphicsContext.h.rej diff --git a/engine/bindings/scripts/blink_idl_parser.py b/engine/bindings/scripts/blink_idl_parser.py index 1e226051b8a..87b32198dcd 100644 --- a/engine/bindings/scripts/blink_idl_parser.py +++ b/engine/bindings/scripts/blink_idl_parser.py @@ -254,7 +254,13 @@ class BlinkIDLParser(IDLParser): # ExceptionFields, and Attribute removed from this rule. p[0] = p[1] - # [b47.1] + # [b47.1] FIXME: rename to ExceptionAttribute + def p_Attribute(self, p): + """Attribute : ReadOnly ATTRIBUTE Type identifier ';'""" + p[0] = self.BuildNamed('Attribute', p, 4, + ListFromConcat(p[1], p[3])) + + # [b47.2] def p_ExceptionOperation(self, p): """ExceptionOperation : Type identifier '(' ')' ';'""" # Needed to handle one case in DOMException.idl: @@ -275,6 +281,12 @@ class BlinkIDLParser(IDLParser): items = ListFromConcat(p[2], p[3]) p[0] = self.BuildProduction('ExtAttributes', p, 1, items) + # Error handling for ExtendedAttributeList. + # We can't upstream this because we override ExtendedAttributeList. + def p_ExtendedAttributeListError(self, p): + """ExtendedAttributeList : '[' ExtendedAttribute ',' error""" + p[0] = self.BuildError(p, "ExtendedAttributeList") + # [b50] Allow optional trailing comma # Blink-only, marked as WONTFIX in Web IDL spec: # https://www.w3.org/Bugs/Public/show_bug.cgi?id=22156 diff --git a/engine/platform/graphics/GraphicsContext.cpp b/engine/platform/graphics/GraphicsContext.cpp index 2a7fb351330..a0a8870f9ad 100644 --- a/engine/platform/graphics/GraphicsContext.cpp +++ b/engine/platform/graphics/GraphicsContext.cpp @@ -385,18 +385,6 @@ void GraphicsContext::adjustTextRenderMode(SkPaint* paint) paint->setLCDRenderText(couldUseLCDRenderedText()); } -bool GraphicsContext::couldUseLCDRenderedText() -{ - // Our layers only have a single alpha channel. This means that subpixel - // rendered text cannot be composited correctly when the layer is - // collapsed. Therefore, subpixel text is contextDisabled when we are drawing - // onto a layer. - if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque()) - return false; - - return shouldSmoothFonts(); -} - void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) { if (contextDisabled()) diff --git a/engine/platform/graphics/GraphicsContext.cpp.orig b/engine/platform/graphics/GraphicsContext.cpp.orig new file mode 100644 index 00000000000..2a7fb351330 --- /dev/null +++ b/engine/platform/graphics/GraphicsContext.cpp.orig @@ -0,0 +1,1856 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sky/engine/config.h" +#include "sky/engine/platform/graphics/GraphicsContext.h" + +#include "sky/engine/platform/TraceEvent.h" +#include "sky/engine/platform/geometry/IntRect.h" +#include "sky/engine/platform/geometry/RoundedRect.h" +#include "sky/engine/platform/graphics/BitmapImage.h" +#include "sky/engine/platform/graphics/DisplayList.h" +#include "sky/engine/platform/graphics/Gradient.h" +#include "sky/engine/platform/graphics/ImageBuffer.h" +#include "sky/engine/platform/graphics/skia/SkiaUtils.h" +#include "sky/engine/platform/text/BidiResolver.h" +#include "sky/engine/platform/text/TextRunIterator.h" +#include "sky/engine/platform/weborigin/KURL.h" +#include "sky/engine/wtf/Assertions.h" +#include "sky/engine/wtf/MathExtras.h" +#include "third_party/skia/include/core/SkAnnotation.h" +#include "third_party/skia/include/core/SkClipStack.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/effects/SkCornerPathEffect.h" +#include "third_party/skia/include/effects/SkLumaColorFilter.h" +#include "third_party/skia/include/effects/SkMatrixImageFilter.h" +#include "third_party/skia/include/effects/SkPictureImageFilter.h" +#include "third_party/skia/include/gpu/GrRenderTarget.h" +#include "third_party/skia/include/gpu/GrTexture.h" + +namespace blink { + +namespace { + +class CompatibleImageBufferSurface : public ImageBufferSurface { + WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED; +public: + CompatibleImageBufferSurface(PassRefPtr surface, const IntSize& size, OpacityMode opacityMode) + : ImageBufferSurface(size, opacityMode) + , m_surface(surface) + { + } + virtual ~CompatibleImageBufferSurface() { } + + virtual SkCanvas* canvas() const override { return m_surface ? m_surface->getCanvas() : 0; } + virtual bool isValid() const override { return m_surface; } + virtual bool isAccelerated() const override { return isValid() && m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); } + virtual Platform3DObject getBackingTexture() const override + { + ASSERT(isAccelerated()); + GrRenderTarget* renderTarget = m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); + if (renderTarget) { + return renderTarget->asTexture()->getTextureHandle(); + } + return 0; + }; + +private: + RefPtr m_surface; +}; + +} // unnamed namespace + +struct GraphicsContext::CanvasSaveState { + CanvasSaveState(bool pendingSave, int count) + : m_pendingSave(pendingSave), m_restoreCount(count) { } + + bool m_pendingSave; + int m_restoreCount; +}; + +struct GraphicsContext::RecordingState { + RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr displayList) + : m_savedCanvas(currentCanvas) + , m_displayList(displayList) + , m_savedMatrix(currentMatrix) + { + } + + SkCanvas* m_savedCanvas; + RefPtr m_displayList; + const SkMatrix m_savedMatrix; +}; + +GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting) + : m_canvas(canvas) + , m_paintStateStack() + , m_paintStateIndex(0) + , m_pendingCanvasSave(false) + , m_annotationMode(0) +#if ENABLE(ASSERT) + , m_annotationCount(0) + , m_layerCount(0) + , m_disableDestructionChecks(false) +#endif + , m_disabledState(disableContextOrPainting) + , m_deviceScaleFactor(1.0f) + , m_regionTrackingMode(RegionTrackingDisabled) + , m_trackTextRegion(false) + , m_accelerated(false) + , m_isCertainlyOpaque(true) + , m_antialiasHairlineImages(false) + , m_shouldSmoothFonts(true) +{ + ASSERT(canvas); + + // FIXME: Do some tests to determine how many states are typically used, and allocate + // several here. + m_paintStateStack.append(GraphicsContextState::create()); + m_paintState = m_paintStateStack.last().get(); +} + +GraphicsContext::~GraphicsContext() +{ +#if ENABLE(ASSERT) + if (!m_disableDestructionChecks) { + ASSERT(!m_paintStateIndex); + ASSERT(!m_paintState->saveCount()); + ASSERT(!m_annotationCount); + ASSERT(!m_layerCount); + ASSERT(m_recordingStateStack.isEmpty()); + ASSERT(m_canvasStateStack.isEmpty()); + } +#endif +} + +void GraphicsContext::resetCanvas(SkCanvas* canvas) +{ + ASSERT(canvas); + m_canvas = canvas; + m_trackedRegion.reset(); +} + +void GraphicsContext::setRegionTrackingMode(RegionTrackingMode mode) +{ + m_regionTrackingMode = mode; + if (mode == RegionTrackingOpaque) + m_trackedRegion.setTrackedRegionType(RegionTracker::Opaque); + else if (mode == RegionTrackingOverwrite) + m_trackedRegion.setTrackedRegionType(RegionTracker::Overwrite); +} + +void GraphicsContext::save() +{ + if (contextDisabled()) + return; + + m_paintState->incrementSaveCount(); + + m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount())); + m_pendingCanvasSave = true; +} + +void GraphicsContext::restore() +{ + if (contextDisabled()) + return; + + if (!m_paintStateIndex && !m_paintState->saveCount()) { + WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); + return; + } + + if (m_paintState->saveCount()) { + m_paintState->decrementSaveCount(); + } else { + m_paintStateIndex--; + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + } + + CanvasSaveState savedState = m_canvasStateStack.last(); + m_canvasStateStack.removeLast(); + m_pendingCanvasSave = savedState.m_pendingSave; + m_canvas->restoreToCount(savedState.m_restoreCount); +} + +void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->saveLayer(bounds, paint); + if (regionTrackingEnabled()) + m_trackedRegion.pushCanvasLayer(paint); +} + +void GraphicsContext::restoreLayer() +{ + if (contextDisabled()) + return; + + m_canvas->restore(); + if (regionTrackingEnabled()) + m_trackedRegion.popCanvasLayer(this); +} + +void GraphicsContext::beginAnnotation(const AnnotationList& annotations) +{ + if (contextDisabled()) + return; + + canvas()->beginCommentGroup("GraphicsContextAnnotation"); + + AnnotationList::const_iterator end = annotations.end(); + for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it) + canvas()->addComment(it->first, it->second.ascii().data()); + +#if ENABLE(ASSERT) + ++m_annotationCount; +#endif +} + +void GraphicsContext::endAnnotation() +{ + if (contextDisabled()) + return; + + ASSERT(m_annotationCount > 0); + canvas()->endCommentGroup(); + +#if ENABLE(ASSERT) + --m_annotationCount; +#endif +} + +void GraphicsContext::setStrokePattern(PassRefPtr pattern) +{ + if (contextDisabled()) + return; + + ASSERT(pattern); + if (!pattern) { + setStrokeColor(Color::black); + return; + } + mutableState()->setStrokePattern(pattern); +} + +void GraphicsContext::setStrokeGradient(PassRefPtr gradient) +{ + if (contextDisabled()) + return; + + ASSERT(gradient); + if (!gradient) { + setStrokeColor(Color::black); + return; + } + mutableState()->setStrokeGradient(gradient); +} + +void GraphicsContext::setFillPattern(PassRefPtr pattern) +{ + if (contextDisabled()) + return; + + ASSERT(pattern); + if (!pattern) { + setFillColor(Color::black); + return; + } + + mutableState()->setFillPattern(pattern); +} + +void GraphicsContext::setFillGradient(PassRefPtr gradient) +{ + if (contextDisabled()) + return; + + ASSERT(gradient); + if (!gradient) { + setFillColor(Color::black); + return; + } + + mutableState()->setFillGradient(gradient); +} + +void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, + DrawLooperBuilder::ShadowTransformMode shadowTransformMode, + DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode) +{ + if (contextDisabled()) + return; + + if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) { + clearShadow(); + return; + } + + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode); + drawLooperBuilder->addUnmodifiedContent(); + setDrawLooper(drawLooperBuilder.release()); +} + +void GraphicsContext::setDrawLooper(PassOwnPtr drawLooperBuilder) +{ + if (contextDisabled()) + return; + + mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper()); +} + +void GraphicsContext::clearDrawLooper() +{ + if (contextDisabled()) + return; + + mutableState()->clearDrawLooper(); +} + +bool GraphicsContext::hasShadow() const +{ + return !!immutableState()->drawLooper(); +} + +bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const +{ + if (contextDisabled()) + return false; + SkIRect skIBounds; + if (!m_canvas->getClipDeviceBounds(&skIBounds)) + return false; + SkRect skBounds = SkRect::Make(skIBounds); + *bounds = FloatRect(skBounds); + return true; +} + +SkMatrix GraphicsContext::getTotalMatrix() const +{ + if (contextDisabled()) + return SkMatrix::I(); + + if (!isRecording()) + return m_canvas->getTotalMatrix(); + + const RecordingState& recordingState = m_recordingStateStack.last(); + SkMatrix totalMatrix = recordingState.m_savedMatrix; + totalMatrix.preConcat(m_canvas->getTotalMatrix()); + + return totalMatrix; +} + +void GraphicsContext::adjustTextRenderMode(SkPaint* paint) +{ + if (contextDisabled()) + return; + + if (!paint->isLCDRenderText()) + return; + + paint->setLCDRenderText(couldUseLCDRenderedText()); +} + +bool GraphicsContext::couldUseLCDRenderedText() +{ + // Our layers only have a single alpha channel. This means that subpixel + // rendered text cannot be composited correctly when the layer is + // collapsed. Therefore, subpixel text is contextDisabled when we are drawing + // onto a layer. + if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque()) + return false; + + return shouldSmoothFonts(); +} + +void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) +{ + if (contextDisabled()) + return; + mutableState()->setCompositeOperation(compositeOperation, blendMode); +} + +SkColorFilter* GraphicsContext::colorFilter() const +{ + return immutableState()->colorFilter(); +} + +void GraphicsContext::setColorFilter(ColorFilter colorFilter) +{ + GraphicsContextState* stateToSet = mutableState(); + + // We only support one active color filter at the moment. If (when) this becomes a problem, + // we should switch to using color filter chains (Skia work in progress). + ASSERT(!stateToSet->colorFilter()); + stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter)); +} + +bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y) +{ + if (contextDisabled()) + return false; + + return m_canvas->readPixels(info, pixels, rowBytes, x, y); +} + +void GraphicsContext::setMatrix(const SkMatrix& matrix) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->setMatrix(matrix); +} + +void GraphicsContext::concat(const SkMatrix& matrix) +{ + if (contextDisabled()) + return; + + if (matrix.isIdentity()) + return; + + realizeCanvasSave(); + + m_canvas->concat(matrix); +} + +void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds) +{ + beginLayer(opacity, immutableState()->compositeOperator(), bounds); +} + +void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter) +{ + if (contextDisabled()) + return; + + SkPaint layerPaint; + layerPaint.setAlpha(static_cast(opacity * 255)); + layerPaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode())); + layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get()); + layerPaint.setImageFilter(imageFilter); + + if (bounds) { + SkRect skBounds = WebCoreFloatRectToSKRect(*bounds); + saveLayer(&skBounds, &layerPaint); + } else { + saveLayer(0, &layerPaint); + } + +#if ENABLE(ASSERT) + ++m_layerCount; +#endif +} + +void GraphicsContext::endLayer() +{ + if (contextDisabled()) + return; + + restoreLayer(); + + ASSERT(m_layerCount > 0); +#if ENABLE(ASSERT) + --m_layerCount; +#endif +} + +void GraphicsContext::beginRecording(const FloatRect& bounds) +{ + RefPtr displayList = adoptRef(new DisplayList(bounds)); + + SkCanvas* savedCanvas = m_canvas; + SkMatrix savedMatrix = getTotalMatrix(); + + if (!contextDisabled()) { + IntRect recordingRect = enclosingIntRect(bounds); + m_canvas = displayList->beginRecording(recordingRect.size()); + + // We want the bounds offset mapped to (0, 0), such that the display list content + // is fully contained within the SkPictureRecord's bounds. + if (!toFloatSize(bounds.location()).isZero()) { + m_canvas->translate(-bounds.x(), -bounds.y()); + // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here. + savedMatrix.preTranslate(bounds.x(), bounds.y()); + } + } + + m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displayList)); +} + +PassRefPtr GraphicsContext::endRecording() +{ + ASSERT(!m_recordingStateStack.isEmpty()); + + RecordingState recording = m_recordingStateStack.last(); + if (!contextDisabled()) { + ASSERT(recording.m_displayList->isRecording()); + recording.m_displayList->endRecording(); + } + + m_recordingStateStack.removeLast(); + m_canvas = recording.m_savedCanvas; + + return recording.m_displayList.release(); +} + +bool GraphicsContext::isRecording() const +{ + return !m_recordingStateStack.isEmpty(); +} + +void GraphicsContext::drawDisplayList(DisplayList* displayList) +{ + ASSERT(displayList); + ASSERT(!displayList->isRecording()); + + if (contextDisabled() || displayList->bounds().isEmpty()) + return; + + realizeCanvasSave(); + + const FloatRect& bounds = displayList->bounds(); + if (bounds.x() || bounds.y()) { + SkMatrix m; + m.setTranslate(bounds.x(), bounds.y()); + m_canvas->drawPicture(displayList->picture(), &m, 0); + } else { + m_canvas->drawPicture(displayList->picture()); + } +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) +{ + if (contextDisabled()) + return; + + if (numPoints <= 1) + return; + + SkPath path; + setPathFromConvexPoints(&path, numPoints, points); + + SkPaint paint(immutableState()->fillPaint()); + paint.setAntiAlias(shouldAntialias); + drawPath(path, paint); + + if (strokeStyle() != NoStroke) + drawPath(path, immutableState()->strokePaint()); +} + +float GraphicsContext::prepareFocusRingPaint(SkPaint& paint, const Color& color, int width) const +{ + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(color.rgb()); + paint.setStrokeWidth(focusRingWidth(width)); + return 1; +} + +void GraphicsContext::drawFocusRingPath(const SkPath& path, const Color& color, int width) +{ + SkPaint paint; + float cornerRadius = prepareFocusRingPaint(paint, color, width); + + paint.setPathEffect(SkCornerPathEffect::Create(SkFloatToScalar(cornerRadius)))->unref(); + + // Outer path + drawPath(path, paint); +} + +void GraphicsContext::drawFocusRingRect(const SkRect& rect, const Color& color, int width) +{ + SkPaint paint; + float cornerRadius = prepareFocusRingPaint(paint, color, width); + + SkRRect rrect; + rrect.setRectXY(rect, SkFloatToScalar(cornerRadius), SkFloatToScalar(cornerRadius)); + + // Outer rect + drawRRect(rrect, paint); +} + +void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color) +{ + // FIXME: Implement support for offset. + if (contextDisabled()) + return; + + drawFocusRingPath(focusRingPath.skPath(), color, width); +} + +void GraphicsContext::drawFocusRing(const Vector& rects, int width, int offset, const Color& color) +{ + if (contextDisabled()) + return; + + unsigned rectCount = rects.size(); + if (!rectCount) + return; + + SkRegion focusRingRegion; + const int outset = focusRingOutset(offset); + for (unsigned i = 0; i < rectCount; i++) { + SkIRect r = rects[i]; + r.inset(-outset, -outset); + focusRingRegion.op(r, SkRegion::kUnion_Op); + } + + if (focusRingRegion.isRect()) { + drawFocusRingRect(SkRect::MakeFromIRect(focusRingRegion.getBounds()), color, width); + } else { + SkPath path; + if (focusRingRegion.getBoundaryPath(&path)) + drawFocusRingPath(path, color, width); + } +} + +static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset) +{ + IntRect bounds(holeRect); + + bounds.inflate(shadowBlur); + + if (shadowSpread < 0) + bounds.inflate(-shadowSpread); + + IntRect offsetBounds = bounds; + offsetBounds.move(-shadowOffset); + return unionRect(bounds, offsetBounds); +} + +void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges) +{ + if (contextDisabled()) + return; + + IntRect holeRect(rect.rect()); + holeRect.inflate(-shadowSpread); + + if (holeRect.isEmpty()) { + if (rect.isRounded()) + fillRoundedRect(rect, shadowColor); + else + fillRect(rect.rect(), shadowColor); + return; + } + + if (clippedEdges & LeftEdge) { + holeRect.move(-std::max(shadowOffset.width(), 0) - shadowBlur, 0); + holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowBlur); + } + if (clippedEdges & TopEdge) { + holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowBlur); + holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowBlur); + } + if (clippedEdges & RightEdge) + holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowBlur); + if (clippedEdges & BottomEdge) + holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowBlur); + + Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); + + IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset); + RoundedRect roundedHole(holeRect, rect.radii()); + + save(); + if (rect.isRounded()) { + Path path; + path.addRoundedRect(rect); + clipPath(path); + roundedHole.shrinkRadii(shadowSpread); + } else { + clip(rect.rect()); + } + + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, + DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); + setDrawLooper(drawLooperBuilder.release()); + fillRectWithRoundedHole(outerRect, roundedHole, fillColor); + restore(); + clearDrawLooper(); +} + +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (contextDisabled()) + return; + + StrokeStyle penStyle = strokeStyle(); + if (penStyle == NoStroke) + return; + + FloatPoint p1 = point1; + FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); + int width = roundf(strokeThickness()); + + // We know these are vertical or horizontal lines, so the length will just + // be the sum of the displacement component vectors give or take 1 - + // probably worth the speed up of no square root, which also won't be exact. + FloatSize disp = p2 - p1; + int length = SkScalarRoundToInt(disp.width() + disp.height()); + SkPaint paint(immutableState()->strokePaint(length)); + + if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + SkRect r1, r2; + r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); + r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); + + if (isVerticalLine) { + r1.offset(-width / 2, 0); + r2.offset(-width / 2, -width); + } else { + r1.offset(0, -width / 2); + r2.offset(-width, -width / 2); + } + SkPaint fillPaint; + fillPaint.setColor(paint.getColor()); + drawRect(r1, fillPaint); + drawRect(r2, fillPaint); + } + + adjustLineToPixelBoundaries(p1, p2, width, penStyle); + SkPoint pts[2] = { p1.data(), p2.data() }; + + m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint); +} + +void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style) +{ + if (contextDisabled()) + return; + + // Use 2x resources for a device scale factor of 1.5 or above. + int deviceScaleFactor = m_deviceScaleFactor > 1.5f ? 2 : 1; + + // Create the pattern we'll use to draw the underline. + int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0; + static SkBitmap* misspellBitmap1x[2] = { 0, 0 }; + static SkBitmap* misspellBitmap2x[2] = { 0, 0 }; + SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x; + if (!misspellBitmap[index]) { + // We use a 2-pixel-high misspelling indicator because that seems to be + // what WebKit is designed for, and how much room there is in a typical + // page for it. + const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below. + const int colPixels = 2 * deviceScaleFactor; + SkBitmap bitmap; + bitmap.allocN32Pixels(rowPixels, colPixels); + + bitmap.eraseARGB(0, 0, 0, 0); + if (deviceScaleFactor == 1) + draw1xMarker(&bitmap, index); + else if (deviceScaleFactor == 2) + draw2xMarker(&bitmap, index); + else + ASSERT_NOT_REACHED(); + + misspellBitmap[index] = new SkBitmap(bitmap); + } + + SkScalar originX = WebCoreFloatToSkScalar(pt.x()); + + // Offset it vertically by 1 so that there's some space under the text. + SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; + originX *= deviceScaleFactor; + originY *= deviceScaleFactor; + + SkMatrix localMatrix; + localMatrix.setTranslate(originX, originY); + RefPtr shader = adoptRef(SkShader::CreateBitmapShader( + *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); + + SkPaint paint; + paint.setShader(shader.get()); + + SkRect rect; + rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height())); + + if (deviceScaleFactor == 2) { + save(); + scale(0.5, 0.5); + } + drawRect(rect, paint); + if (deviceScaleFactor == 2) + restore(); +} + +void GraphicsContext::drawLineForText(const FloatPoint& pt, float width) +{ + if (contextDisabled()) + return; + + if (width <= 0) + return; + + SkPaint paint; + switch (strokeStyle()) { + case NoStroke: + case SolidStroke: + case DoubleStroke: + case WavyStroke: { + int thickness = SkMax32(static_cast(strokeThickness()), 1); + SkRect r; + r.fLeft = WebCoreFloatToSkScalar(pt.x()); + // Avoid anti-aliasing lines. Currently, these are always horizontal. + // Round to nearest pixel to match text and other content. + r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f)); + r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); + r.fBottom = r.fTop + SkIntToScalar(thickness); + paint = immutableState()->fillPaint(); + // Text lines are drawn using the stroke color. + paint.setColor(effectiveStrokeColor()); + drawRect(r, paint); + return; + } + case DottedStroke: + case DashedStroke: { + int y = floorf(pt.y() + std::max(strokeThickness() / 2.0f, 0.5f)); + drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y)); + return; + } + } + + ASSERT_NOT_REACHED(); +} + +// Draws a filled rectangle with a stroked border. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (contextDisabled()) + return; + + ASSERT(!rect.isEmpty()); + if (rect.isEmpty()) + return; + + SkRect skRect = rect; + int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000; + if (fillcolorNotTransparent) + drawRect(skRect, immutableState()->fillPaint()); + + if (immutableState()->strokeData().style() != NoStroke + && immutableState()->strokeData().color().alpha()) { + // Stroke a width: 1 inset border + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(effectiveStrokeColor()); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(1); + + skRect.inset(0.5f, 0.5f); + drawRect(skRect, paint); + } +} + +void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point) +{ + if (contextDisabled()) + return; + + font.drawText(this, runInfo, point); +} + +void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) +{ + if (contextDisabled()) + return; + + font.drawEmphasisMarks(this, runInfo, mark, point); +} + +void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) +{ + if (contextDisabled()) + return; + + // sub-run painting is not supported for Bidi text. + const TextRun& run = runInfo.run; + ASSERT((runInfo.from == 0) && (runInfo.to == run.length())); + BidiResolver bidiResolver; + bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); + bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); + + // FIXME: This ownership should be reversed. We should pass BidiRunList + // to BidiResolver in createBidiRunsForLine. + BidiRunList& bidiRuns = bidiResolver.runs(); + bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); + if (!bidiRuns.runCount()) + return; + + FloatPoint currPoint = point; + BidiCharacterRun* bidiRun = bidiRuns.firstRun(); + while (bidiRun) { + TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); + bool isRTL = bidiRun->level() % 2; + subrun.setDirection(isRTL ? RTL : LTR); + subrun.setDirectionalOverride(bidiRun->dirOverride()); + + TextRunPaintInfo subrunInfo(subrun); + subrunInfo.bounds = runInfo.bounds; + font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction); + + bidiRun = bidiRun->next(); + // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. + if (bidiRun) + currPoint.move(font.width(subrun), 0); + } + + bidiRuns.deleteRuns(); +} + +void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to) +{ + if (contextDisabled()) + return; + + fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor); +} + +void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) +{ + if (!image) + return; + drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) +{ + if (!image) + return; + drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) +{ + drawImage(image, dest, src, op, WebBlendModeNormal, shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage(Image* image, const FloatRect& dest) +{ + if (!image) + return; + drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size()))); +} + +void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation) +{ + if (contextDisabled() || !image) + return; + image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation); +} + +void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing) +{ + if (contextDisabled() || !image) + return; + image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing); +} + +void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, + const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) +{ + if (contextDisabled() || !image) + return; + + if (hRule == Image::StretchTile && vRule == Image::StretchTile) { + // Just do a scale. + drawImage(image, dest, srcRect, op); + return; + } + + image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op); +} + +void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest, + const FloatRect* src, CompositeOperator op, WebBlendMode blendMode) +{ + if (contextDisabled() || !image) + return; + + image->draw(this, dest, src, op, blendMode); +} + +void GraphicsContext::drawPicture(PassRefPtr picture, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode) +{ + if (contextDisabled() || !picture) + return; + + SkMatrix ctm = m_canvas->getTotalMatrix(); + SkRect deviceDest; + ctm.mapRect(&deviceDest, dest); + SkRect sourceBounds = WebCoreFloatRectToSKRect(src); + + RefPtr pictureFilter = adoptRef(SkPictureImageFilter::Create(picture.get(), sourceBounds)); + SkMatrix layerScale; + layerScale.setScale(deviceDest.width() / src.width(), deviceDest.height() / src.height()); + RefPtr matrixFilter = adoptRef(SkMatrixImageFilter::Create(layerScale, SkPaint::kLow_FilterLevel, pictureFilter.get())); + SkPaint picturePaint; + picturePaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, blendMode)); + picturePaint.setImageFilter(matrixFilter.get()); + SkRect layerBounds = SkRect::MakeWH(std::max(deviceDest.width(), sourceBounds.width()), std::max(deviceDest.height(), sourceBounds.height())); + m_canvas->save(); + m_canvas->resetMatrix(); + m_canvas->translate(deviceDest.x(), deviceDest.y()); + m_canvas->saveLayer(&layerBounds, &picturePaint); + m_canvas->restore(); + m_canvas->restore(); +} + +void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y) +{ + if (contextDisabled()) + return; + + m_canvas->writePixels(info, pixels, rowBytes, x, y); + + if (regionTrackingEnabled()) { + SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height()); + SkPaint paint; + + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + if (kOpaque_SkAlphaType != info.alphaType()) + paint.setAlpha(0x80); // signal to m_trackedRegion that we are not fully opaque + + m_trackedRegion.didDrawRect(this, rect, paint, 0); + // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly, + // rather than cons-ing up a paint with an xfermode and alpha + } +} + +void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y) +{ + if (contextDisabled()) + return; + + if (!bitmap.getTexture()) { + SkAutoLockPixels alp(bitmap); + if (bitmap.getPixels()) + writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y); + } +} + +void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawBitmap(bitmap, left, top, paint); + + if (regionTrackingEnabled()) { + SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()); + m_trackedRegion.didDrawRect(this, rect, *paint, &bitmap); + } +} + +void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, + const SkRect& dst, const SkPaint* paint) +{ + if (contextDisabled()) + return; + + SkCanvas::DrawBitmapRectFlags flags = + immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag; + + m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, dst, *paint, &bitmap); +} + +void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawOval(oval, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, oval, paint); +} + +void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawPath(path, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawPath(this, path, paint); +} + +void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawRect(rect, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, rect, paint, 0); +} + +void GraphicsContext::drawRRect(const SkRRect& rrect, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawRRect(rrect, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rrect.rect(), paint); +} + +void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap) +{ + if (contextDisabled()) + return; + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, rect, paint, bitmap); +} + +void GraphicsContext::drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkRect& textRect, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawPosText(text, byteLength, pos, paint); + didDrawTextInRect(textRect); + + // FIXME: compute bounds for positioned text. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); +} + +void GraphicsContext::drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint); + didDrawTextInRect(textRect); + + // FIXME: compute bounds for positioned text. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); +} + +void GraphicsContext::drawTextBlob(const SkTextBlob* blob, const SkPoint& origin, const SkPaint& paint) +{ + if (contextDisabled()) + return; + + m_canvas->drawTextBlob(blob, origin.x(), origin.y(), paint); + + SkRect bounds = blob->bounds(); + bounds.offset(origin); + didDrawTextInRect(bounds); + + // FIXME: use bounds here if it helps performance. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); +} + +void GraphicsContext::fillPath(const Path& pathToFill) +{ + if (contextDisabled() || pathToFill.isEmpty()) + return; + + // Use const_cast and temporarily modify the fill type instead of copying the path. + SkPath& path = const_cast(pathToFill.skPath()); + SkPath::FillType previousFillType = path.getFillType(); + + SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(immutableState()->fillRule()); + path.setFillType(temporaryFillType); + + drawPath(path, immutableState()->fillPaint()); + + path.setFillType(previousFillType); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (contextDisabled()) + return; + + SkRect r = rect; + + drawRect(r, immutableState()->fillPaint()); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) +{ + if (contextDisabled()) + return; + + SkRect r = rect; + SkPaint paint = immutableState()->fillPaint(); + paint.setColor(color.rgb()); + drawRect(r, paint); +} + +void GraphicsContext::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) { + if (contextDisabled()) + return; + + SkVector outerRadii[4]; + SkVector innerRadii[4]; + setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft); + setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft); + + SkRRect rrOuter; + SkRRect rrInner; + rrOuter.setRectRadii(outer, outerRadii); + rrInner.setRectRadii(inner, innerRadii); + + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(color.rgb()); + + m_canvas->drawDRRect(rrOuter, rrInner, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rrOuter.getBounds(), paint); +} + +void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color) +{ + fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(), + inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, + const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) +{ + if (contextDisabled()) + return; + + if (topLeft.width() + topRight.width() > rect.width() + || bottomLeft.width() + bottomRight.width() > rect.width() + || topLeft.height() + bottomLeft.height() > rect.height() + || topRight.height() + bottomRight.height() > rect.height()) { + // Not all the radii fit, return a rect. This matches the behavior of + // Path::createRoundedRectangle. Without this we attempt to draw a round + // shadow for a square box. + fillRect(rect, color); + return; + } + + SkVector radii[4]; + setRadii(radii, topLeft, topRight, bottomRight, bottomLeft); + + SkRRect rr; + rr.setRectRadii(rect, radii); + + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(color.rgb()); + + m_canvas->drawRRect(rr, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rr.getBounds(), paint); +} + +void GraphicsContext::fillEllipse(const FloatRect& ellipse) +{ + if (contextDisabled()) + return; + + SkRect rect = ellipse; + drawOval(rect, immutableState()->fillPaint()); +} + +void GraphicsContext::strokePath(const Path& pathToStroke) +{ + if (contextDisabled() || pathToStroke.isEmpty()) + return; + + const SkPath& path = pathToStroke.skPath(); + drawPath(path, immutableState()->strokePaint()); +} + +void GraphicsContext::strokeRect(const FloatRect& rect) +{ + strokeRect(rect, strokeThickness()); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (contextDisabled()) + return; + + SkPaint paint(immutableState()->strokePaint()); + paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); + // Reset the dash effect to account for the width + immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0); + // strokerect has special rules for CSS when the rect is degenerate: + // if width==0 && height==0, do nothing + // if width==0 || height==0, then just draw line for the other dimension + SkRect r(rect); + bool validW = r.width() > 0; + bool validH = r.height() > 0; + if (validW && validH) { + drawRect(r, paint); + } else if (validW || validH) { + // we are expected to respect the lineJoin, so we can't just call + // drawLine -- we have to create a path that doubles back on itself. + SkPath path; + path.moveTo(r.fLeft, r.fTop); + path.lineTo(r.fRight, r.fBottom); + path.close(); + drawPath(path, paint); + } +} + +void GraphicsContext::strokeEllipse(const FloatRect& ellipse) +{ + if (contextDisabled()) + return; + + drawOval(ellipse, immutableState()->strokePaint()); +} + +void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp) +{ + if (contextDisabled()) + return; + + if (!rect.isRounded()) { + clipRect(rect.rect(), NotAntiAliased, regionOp); + return; + } + + SkVector radii[4]; + RoundedRect::Radii wkRadii = rect.radii(); + setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft()); + + SkRRect r; + r.setRectRadii(rect.rect(), radii); + + clipRRect(r, AntiAliased, regionOp); +} + +void GraphicsContext::clipOut(const Path& pathToClip) +{ + if (contextDisabled()) + return; + + // Use const_cast and temporarily toggle the inverse fill type instead of copying the path. + SkPath& path = const_cast(pathToClip.skPath()); + path.toggleInverseFillType(); + clipPath(path, AntiAliased); + path.toggleInverseFillType(); +} + +void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) +{ + if (contextDisabled() || pathToClip.isEmpty()) + return; + + // Use const_cast and temporarily modify the fill type instead of copying the path. + SkPath& path = const_cast(pathToClip.skPath()); + SkPath::FillType previousFillType = path.getFillType(); + + SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); + path.setFillType(temporaryFillType); + clipPath(path, AntiAliased); + + path.setFillType(previousFillType); +} + +void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) +{ + if (contextDisabled()) + return; + + if (numPoints <= 1) + return; + + SkPath path; + setPathFromConvexPoints(&path, numPoints, points); + clipPath(path, antialiased ? AntiAliased : NotAntiAliased); +} + +void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) +{ + if (contextDisabled()) + return; + + clipRoundedRect(rect, SkRegion::kDifference_Op); +} + +void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule) +{ + if (contextDisabled()) + return; + + // Use const_cast and temporarily modify the fill type instead of copying the path. + SkPath& path = const_cast(pathToClip.skPath()); + SkPath::FillType previousFillType = path.getFillType(); + + SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); + path.setFillType(temporaryFillType); + clipPath(path); + + path.setFillType(previousFillType); +} + +void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->clipRect(rect, op, aa == AntiAliased); +} + +void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->clipPath(path, op, aa == AntiAliased); +} + +void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->clipRRect(rect, op, aa == AntiAliased); +} + +void GraphicsContext::rotate(float angleInRadians) +{ + if (contextDisabled()) + return; + + realizeCanvasSave(); + + m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f))); +} + +void GraphicsContext::translate(float x, float y) +{ + if (contextDisabled()) + return; + + if (!x && !y) + return; + + realizeCanvasSave(); + + m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); +} + +void GraphicsContext::scale(float x, float y) +{ + if (contextDisabled()) + return; + + if (x == 1.0f && y == 1.0f) + return; + + realizeCanvasSave(); + + m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); +} + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ + if (contextDisabled()) + return; + + SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data())); + SkAnnotateRectWithURL(m_canvas, destRect, url.get()); +} + +void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect) +{ + if (contextDisabled()) + return; + + SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data())); + SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get()); +} + +void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos) +{ + if (contextDisabled()) + return; + + SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data())); + SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData); +} + +AffineTransform GraphicsContext::getCTM() const +{ + if (contextDisabled()) + return AffineTransform(); + + SkMatrix m = getTotalMatrix(); + return AffineTransform(SkScalarToDouble(m.getScaleX()), + SkScalarToDouble(m.getSkewY()), + SkScalarToDouble(m.getSkewX()), + SkScalarToDouble(m.getScaleY()), + SkScalarToDouble(m.getTranslateX()), + SkScalarToDouble(m.getTranslateY())); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op) +{ + if (contextDisabled()) + return; + + CompositeOperator previousOperator = compositeOperation(); + setCompositeOperation(op); + fillRect(rect, color); + setCompositeOperation(previousOperator); +} + +void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color) +{ + if (contextDisabled()) + return; + + if (rect.isRounded()) + fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color); + else + fillRect(rect.rect(), color); +} + +void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color) +{ + if (contextDisabled()) + return; + + Path path; + path.addRect(rect); + + if (!roundedHoleRect.radii().isZero()) + path.addRoundedRect(roundedHoleRect); + else + path.addRect(roundedHoleRect.rect()); + + WindRule oldFillRule = fillRule(); + Color oldFillColor = fillColor(); + + setFillRule(RULE_EVENODD); + setFillColor(color); + + fillPath(path); + + setFillRule(oldFillRule); + setFillColor(oldFillColor); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (contextDisabled()) + return; + + SkRect r = rect; + SkPaint paint(immutableState()->fillPaint()); + paint.setXfermodeMode(SkXfermode::kClear_Mode); + drawRect(r, paint); +} + +void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle) +{ + // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic + // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g., + // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave + // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. + if (penStyle == DottedStroke || penStyle == DashedStroke) { + if (p1.x() == p2.x()) { + p1.setY(p1.y() + strokeWidth); + p2.setY(p2.y() - strokeWidth); + } else { + p1.setX(p1.x() + strokeWidth); + p2.setX(p2.x() - strokeWidth); + } + } + + if (static_cast(strokeWidth) % 2) { //odd + if (p1.x() == p2.x()) { + // We're a vertical line. Adjust our x. + p1.setX(p1.x() + 0.5f); + p2.setX(p2.x() + 0.5f); + } else { + // We're a horizontal line. Adjust our y. + p1.setY(p1.y() + 0.5f); + p2.setY(p2.y() + 0.5f); + } + } +} + +PassOwnPtr GraphicsContext::createRasterBuffer(const IntSize& size, OpacityMode opacityMode) const +{ + // Make the buffer larger if the context's transform is scaling it so we need a higher + // resolution than one pixel per unit. Also set up a corresponding scale factor on the + // graphics context. + + AffineTransform transform = getCTM(); + IntSize scaledSize(static_cast(ceil(size.width() * transform.xScale())), static_cast(ceil(size.height() * transform.yScale()))); + + SkAlphaType alphaType = (opacityMode == Opaque) ? kOpaque_SkAlphaType : kPremul_SkAlphaType; + SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType); + RefPtr skSurface = adoptRef(SkSurface::NewRaster(info)); + if (!skSurface) + return nullptr; + OwnPtr surface = adoptPtr(new CompatibleImageBufferSurface(skSurface.release(), scaledSize, opacityMode)); + ASSERT(surface->isValid()); + OwnPtr buffer = adoptPtr(new ImageBuffer(surface.release())); + + buffer->context()->scale(static_cast(scaledSize.width()) / size.width(), + static_cast(scaledSize.height()) / size.height()); + + return buffer.release(); +} + +void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points) +{ + path->incReserve(numPoints); + path->moveTo(WebCoreFloatToSkScalar(points[0].x()), + WebCoreFloatToSkScalar(points[0].y())); + for (size_t i = 1; i < numPoints; ++i) { + path->lineTo(WebCoreFloatToSkScalar(points[i].x()), + WebCoreFloatToSkScalar(points[i].y())); + } + + /* The code used to just blindly call this + path->setIsConvex(true); + But webkit can sometimes send us non-convex 4-point values, so we mark the path's + convexity as unknown, so it will get computed by skia at draw time. + See crbug.com 108605 + */ + SkPath::Convexity convexity = SkPath::kConvex_Convexity; + if (numPoints == 4) + convexity = SkPath::kUnknown_Convexity; + path->setConvexity(convexity); +} + +void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft) +{ + radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()), + SkIntToScalar(topLeft.height())); + radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()), + SkIntToScalar(topRight.height())); + radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()), + SkIntToScalar(bottomRight.height())); + radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()), + SkIntToScalar(bottomLeft.height())); +} + +PassRefPtr GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter) +{ + switch (colorFilter) { + case ColorFilterLuminanceToAlpha: + return adoptRef(SkLumaColorFilter::Create()); + case ColorFilterLinearRGBToSRGB: + return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB); + case ColorFilterSRGBToLinearRGB: + return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); + case ColorFilterNone: + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + return nullptr; +} + +void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index) +{ + const SkPMColor lineColor = lineColors(index); + const SkPMColor antiColor1 = antiColors1(index); + const SkPMColor antiColor2 = antiColors2(index); + + uint32_t* row1 = bitmap->getAddr32(0, 0); + uint32_t* row2 = bitmap->getAddr32(0, 1); + uint32_t* row3 = bitmap->getAddr32(0, 2); + uint32_t* row4 = bitmap->getAddr32(0, 3); + + // Pattern: X0o o0X0o o0 + // XX0o o0XXX0o o0X + // o0XXX0o o0XXX0o + // o0X0o o0X0o + const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 }; + const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor }; + const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 }; + const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 }; + + for (int x = 0; x < bitmap->width() + 8; x += 8) { + int count = std::min(bitmap->width() - x, 8); + if (count > 0) { + memcpy(row1 + x, row1Color, count * sizeof(SkPMColor)); + memcpy(row2 + x, row2Color, count * sizeof(SkPMColor)); + memcpy(row3 + x, row3Color, count * sizeof(SkPMColor)); + memcpy(row4 + x, row4Color, count * sizeof(SkPMColor)); + } + } +} + +void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index) +{ + const uint32_t lineColor = lineColors(index); + const uint32_t antiColor = antiColors2(index); + + // Pattern: X o o X o o X + // o X o o X o + uint32_t* row1 = bitmap->getAddr32(0, 0); + uint32_t* row2 = bitmap->getAddr32(0, 1); + for (int x = 0; x < bitmap->width(); x++) { + switch (x % 4) { + case 0: + row1[x] = lineColor; + break; + case 1: + row1[x] = antiColor; + row2[x] = antiColor; + break; + case 2: + row2[x] = lineColor; + break; + case 3: + row1[x] = antiColor; + row2[x] = antiColor; + break; + } + } +} + +SkPMColor GraphicsContext::lineColors(int index) +{ + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red. + SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray. + }; + + return colors[index]; +} + +SkPMColor GraphicsContext::antiColors1(int index) +{ + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red. + SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray. + }; + + return colors[index]; +} + +SkPMColor GraphicsContext::antiColors2(int index) +{ + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red + SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray + }; + + return colors[index]; +} + +void GraphicsContext::didDrawTextInRect(const SkRect& textRect) +{ + if (m_trackTextRegion) { + TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect"); + m_textRegion.join(textRect); + } +} + +void GraphicsContext::preparePaintForDrawRectToRect( + SkPaint* paint, + const SkRect& srcRect, + const SkRect& destRect, + CompositeOperator compositeOp, + WebBlendMode blendMode, + bool isLazyDecoded, + bool isDataComplete) const +{ + paint->setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); + paint->setColorFilter(this->colorFilter()); + paint->setAlpha(this->getNormalizedAlpha()); + paint->setLooper(this->drawLooper()); + paint->setAntiAlias(shouldDrawAntiAliased(this, destRect)); + + InterpolationQuality resampling; + if (this->isAccelerated()) { + resampling = InterpolationLow; + } else if (isLazyDecoded) { + resampling = InterpolationHigh; + } else { + // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). + SkRect destRectTarget = destRect; + SkMatrix totalMatrix = this->getTotalMatrix(); + if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) + totalMatrix.mapRect(&destRectTarget, destRect); + + resampling = computeInterpolationQuality(totalMatrix, + SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), + SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()), + isDataComplete); + } + + if (resampling == InterpolationNone) { + // FIXME: This is to not break tests (it results in the filter bitmap flag + // being set to true). We need to decide if we respect InterpolationNone + // being returned from computeInterpolationQuality. + resampling = InterpolationLow; + } + resampling = limitInterpolationQuality(this, resampling); + paint->setFilterLevel(static_cast(resampling)); +} + +} // namespace blink diff --git a/engine/platform/graphics/GraphicsContext.cpp.rej b/engine/platform/graphics/GraphicsContext.cpp.rej new file mode 100644 index 00000000000..8abf473393e --- /dev/null +++ b/engine/platform/graphics/GraphicsContext.cpp.rej @@ -0,0 +1,22 @@ +--- platform/graphics/GraphicsContext.cpp ++++ platform/graphics/GraphicsContext.cpp +@@ -419,19 +419,6 @@ + paint->setLCDRenderText(couldUseLCDRenderedText()); + } + +-bool GraphicsContext::couldUseLCDRenderedText() const +-{ +- ASSERT(m_canvas); +- // Our layers only have a single alpha channel. This means that subpixel +- // rendered text cannot be composited correctly when the layer is +- // collapsed. Therefore, subpixel text is contextDisabled when we are drawing +- // onto a layer. +- if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque()) +- return false; +- +- return shouldSmoothFonts(); +-} +- + void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) + { + if (contextDisabled()) diff --git a/engine/platform/graphics/GraphicsContext.h b/engine/platform/graphics/GraphicsContext.h index 782fa23e84d..09a9f1ffca1 100644 --- a/engine/platform/graphics/GraphicsContext.h +++ b/engine/platform/graphics/GraphicsContext.h @@ -165,11 +165,10 @@ public: // FIXME: the setter is only used once, at construction time; convert to a constructor param, // and possibly consolidate with other flags (paintDisabled, isPrinting, ...) void setShouldSmoothFonts(bool smoothFonts) { m_shouldSmoothFonts = smoothFonts; } - bool shouldSmoothFonts() const { return m_shouldSmoothFonts; } // Turn off LCD text for the paint if not supported on this context. void adjustTextRenderMode(SkPaint*); - bool couldUseLCDRenderedText(); + bool couldUseLCDRenderedText() const { return m_isCertainlyOpaque && m_shouldSmoothFonts; } void setTextDrawingMode(TextDrawingModeFlags mode) { mutableState()->setTextDrawingMode(mode); } TextDrawingModeFlags textDrawingMode() const { return immutableState()->textDrawingMode(); } @@ -198,7 +197,6 @@ public: // the canvas may have transparency (as is the case when rendering // to a canvas object). void setCertainlyOpaque(bool isOpaque) { m_isCertainlyOpaque = isOpaque; } - bool isCertainlyOpaque() const { return m_isCertainlyOpaque; } bool isAccelerated() const { return m_accelerated; } void setAccelerated(bool accelerated) { m_accelerated = accelerated; } diff --git a/engine/platform/graphics/GraphicsContext.h.orig b/engine/platform/graphics/GraphicsContext.h.orig new file mode 100644 index 00000000000..782fa23e84d --- /dev/null +++ b/engine/platform/graphics/GraphicsContext.h.orig @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXT_H_ +#define SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXT_H_ + +#include "sky/engine/platform/PlatformExport.h" +#include "sky/engine/platform/fonts/Font.h" +#include "sky/engine/platform/geometry/FloatRect.h" +#include "sky/engine/platform/graphics/DashArray.h" +#include "sky/engine/platform/graphics/DrawLooperBuilder.h" +#include "sky/engine/platform/graphics/GraphicsContextAnnotation.h" +#include "sky/engine/platform/graphics/GraphicsContextState.h" +#include "sky/engine/platform/graphics/ImageBufferSurface.h" +#include "sky/engine/platform/graphics/ImageFilter.h" +#include "sky/engine/platform/graphics/ImageOrientation.h" +#include "sky/engine/platform/graphics/RegionTracker.h" +#include "sky/engine/platform/graphics/skia/SkiaUtils.h" +#include "sky/engine/wtf/FastAllocBase.h" +#include "sky/engine/wtf/Forward.h" +#include "sky/engine/wtf/Noncopyable.h" +#include "sky/engine/wtf/PassOwnPtr.h" + +class SkBitmap; +class SkPaint; +class SkPath; +class SkRRect; +class SkTextBlob; +struct SkRect; + +namespace blink { + +class DisplayList; +class ImageBuffer; +class KURL; + +class PLATFORM_EXPORT GraphicsContext { + WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED; +public: + enum AntiAliasingMode { + NotAntiAliased, + AntiAliased + }; + enum AccessMode { + ReadOnly, + ReadWrite + }; + + enum DisabledMode { + NothingDisabled = 0, // Run as normal. + FullyDisabled = 1 // Do absolutely minimal work to remove the cost of the context from performance tests. + }; + + explicit GraphicsContext(SkCanvas*, DisabledMode = NothingDisabled); + ~GraphicsContext(); + + // Returns the canvas used for painting. Must not be called if painting is disabled. + // Accessing the backing canvas this way flushes all queued save ops, + // so it should be avoided. Use the corresponding draw/matrix/clip methods instead. + SkCanvas* canvas() + { + // Flush any pending saves. + realizeCanvasSave(); + + return m_canvas; + } + const SkCanvas* canvas() const + { + return m_canvas; + } + + void resetCanvas(SkCanvas*); + + bool contextDisabled() const { return m_disabledState; } + + // ---------- State management methods ----------------- + void save(); + void restore(); + unsigned saveCount() { return m_canvasStateStack.size(); } +#if ENABLE(ASSERT) + void disableDestructionChecks() { m_disableDestructionChecks = true; } +#endif + + void saveLayer(const SkRect* bounds, const SkPaint*); + void restoreLayer(); + + bool hasStroke() const { return strokeStyle() != NoStroke && strokeThickness() > 0; } + + float strokeThickness() const { return immutableState()->strokeData().thickness(); } + void setStrokeThickness(float thickness) { mutableState()->setStrokeThickness(thickness); } + + StrokeStyle strokeStyle() const { return immutableState()->strokeData().style(); } + void setStrokeStyle(StrokeStyle style) { mutableState()->setStrokeStyle(style); } + + Color strokeColor() const { return immutableState()->strokeData().color(); } + void setStrokeColor(const Color& color) { mutableState()->setStrokeColor(color); } + SkColor effectiveStrokeColor() const { return immutableState()->effectiveStrokeColor(); } + + Pattern* strokePattern() const { return immutableState()->strokeData().pattern(); } + void setStrokePattern(PassRefPtr); + + Gradient* strokeGradient() const { return immutableState()->strokeData().gradient(); } + void setStrokeGradient(PassRefPtr); + + void setLineCap(LineCap cap) { mutableState()->setLineCap(cap); } + void setLineDash(const DashArray& dashes, float dashOffset) { mutableState()->setLineDash(dashes, dashOffset); } + void setLineJoin(LineJoin join) { mutableState()->setLineJoin(join); } + void setMiterLimit(float limit) { mutableState()->setMiterLimit(limit); } + + WindRule fillRule() const { return immutableState()->fillRule(); } + void setFillRule(WindRule fillRule) { mutableState()->setFillRule(fillRule); } + + Color fillColor() const { return immutableState()->fillColor(); } + void setFillColor(const Color& color) { mutableState()->setFillColor(color); } + SkColor effectiveFillColor() const { return immutableState()->effectiveFillColor(); } + + void setFillPattern(PassRefPtr); + Pattern* fillPattern() const { return immutableState()->fillPattern(); } + + void setFillGradient(PassRefPtr); + Gradient* fillGradient() const { return immutableState()->fillGradient(); } + + SkDrawLooper* drawLooper() const { return immutableState()->drawLooper(); } + + bool getTransformedClipBounds(FloatRect* bounds) const; + SkMatrix getTotalMatrix() const; + + void setShouldAntialias(bool antialias) { mutableState()->setShouldAntialias(antialias); } + bool shouldAntialias() const { return immutableState()->shouldAntialias(); } + + // Disable the anti-aliasing optimization for scales/multiple-of-90-degrees + // rotations of thin ("hairline") images. + // Note: This will only be reliable when the device pixel scale/ratio is + // fixed (e.g. when drawing to context backed by an ImageBuffer). + void disableAntialiasingOptimizationForHairlineImages() { ASSERT(!isRecording()); m_antialiasHairlineImages = true; } + bool shouldAntialiasHairlineImages() const { return m_antialiasHairlineImages; } + + void setShouldClampToSourceRect(bool clampToSourceRect) { mutableState()->setShouldClampToSourceRect(clampToSourceRect); } + bool shouldClampToSourceRect() const { return immutableState()->shouldClampToSourceRect(); } + + // FIXME: the setter is only used once, at construction time; convert to a constructor param, + // and possibly consolidate with other flags (paintDisabled, isPrinting, ...) + void setShouldSmoothFonts(bool smoothFonts) { m_shouldSmoothFonts = smoothFonts; } + bool shouldSmoothFonts() const { return m_shouldSmoothFonts; } + + // Turn off LCD text for the paint if not supported on this context. + void adjustTextRenderMode(SkPaint*); + bool couldUseLCDRenderedText(); + + void setTextDrawingMode(TextDrawingModeFlags mode) { mutableState()->setTextDrawingMode(mode); } + TextDrawingModeFlags textDrawingMode() const { return immutableState()->textDrawingMode(); } + + void setAlphaAsFloat(float alpha) { mutableState()->setAlphaAsFloat(alpha);} + int getNormalizedAlpha() const + { + int alpha = immutableState()->alpha(); + return alpha > 255 ? 255 : alpha; + } + + void setImageInterpolationQuality(InterpolationQuality quality) { mutableState()->setInterpolationQuality(quality); } + InterpolationQuality imageInterpolationQuality() const { return immutableState()->interpolationQuality(); } + + void setCompositeOperation(CompositeOperator, WebBlendMode = WebBlendModeNormal); + CompositeOperator compositeOperation() const { return immutableState()->compositeOperator(); } + WebBlendMode blendModeOperation() const { return immutableState()->blendMode(); } + + // Speicy the device scale factor which may change the way document markers + // and fonts are rendered. + void setDeviceScaleFactor(float factor) { m_deviceScaleFactor = factor; } + float deviceScaleFactor() const { return m_deviceScaleFactor; } + + // If true we are (most likely) rendering to a web page and the + // canvas has been prepared with an opaque background. If false, + // the canvas may have transparency (as is the case when rendering + // to a canvas object). + void setCertainlyOpaque(bool isOpaque) { m_isCertainlyOpaque = isOpaque; } + bool isCertainlyOpaque() const { return m_isCertainlyOpaque; } + + bool isAccelerated() const { return m_accelerated; } + void setAccelerated(bool accelerated) { m_accelerated = accelerated; } + + // The opaque region is empty until tracking is turned on. + // It is never clerared by the context. + enum RegionTrackingMode { + RegionTrackingDisabled = 0, + RegionTrackingOpaque, + RegionTrackingOverwrite + }; + void setRegionTrackingMode(RegionTrackingMode); + bool regionTrackingEnabled() { return m_regionTrackingMode != RegionTrackingDisabled; } + const RegionTracker& opaqueRegion() const { return m_trackedRegion; } + + // The text region is empty until tracking is turned on. + // It is never clerared by the context. + void setTrackTextRegion(bool track) { m_trackTextRegion = track; } + const SkRect& textRegion() const { return m_textRegion; } + + AnnotationModeFlags annotationMode() const { return m_annotationMode; } + void setAnnotationMode(const AnnotationModeFlags mode) { m_annotationMode = mode; } + + SkColorFilter* colorFilter() const; + void setColorFilter(ColorFilter); + // ---------- End state management methods ----------------- + + // Get the contents of the image buffer + bool readPixels(const SkImageInfo&, void* pixels, size_t rowBytes, int x, int y); + + // Get the current fill style. + const SkPaint& fillPaint() const { return immutableState()->fillPaint(); } + + // Get the current stroke style. + const SkPaint& strokePaint() const { return immutableState()->strokePaint(); } + + // These draw methods will do both stroking and filling. + // FIXME: ...except drawRect(), which fills properly but always strokes + // using a 1-pixel stroke inset from the rect borders (of the correct + // stroke color). + void drawRect(const IntRect&); + void drawLine(const IntPoint&, const IntPoint&); + void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false); + + void fillPath(const Path&); + void strokePath(const Path&); + + void fillEllipse(const FloatRect&); + void strokeEllipse(const FloatRect&); + + void fillRect(const FloatRect&); + void fillRect(const FloatRect&, const Color&); + void fillRect(const FloatRect&, const Color&, CompositeOperator); + void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&); + void fillRoundedRect(const RoundedRect&, const Color&); + + void clearRect(const FloatRect&); + + void strokeRect(const FloatRect&); + void strokeRect(const FloatRect&, float lineWidth); + + void fillBetweenRoundedRects(const IntRect&, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight, + const IntRect&, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color&); + void fillBetweenRoundedRects(const RoundedRect&, const RoundedRect&, const Color&); + + void drawDisplayList(DisplayList*); + + void drawImage(Image*, const IntPoint&, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, const IntRect&, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, const FloatRect& destRect); + void drawImage(Image*, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode, RespectImageOrientationEnum = DoNotRespectImageOrientation); + + void drawTiledImage(Image*, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, + CompositeOperator = CompositeSourceOver, WebBlendMode = WebBlendModeNormal, const IntSize& repeatSpacing = IntSize()); + void drawTiledImage(Image*, const IntRect& destRect, const IntRect& srcRect, + const FloatSize& tileScaleFactor, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, + CompositeOperator = CompositeSourceOver); + + void drawImageBuffer(ImageBuffer*, const FloatRect& destRect, const FloatRect* srcRect = 0, CompositeOperator = CompositeSourceOver, WebBlendMode = WebBlendModeNormal); + + void drawPicture(PassRefPtr, const FloatRect& dest, const FloatRect& src, CompositeOperator, WebBlendMode); + + // These methods write to the canvas and modify the opaque region, if tracked. + // Also drawLine(const IntPoint& point1, const IntPoint& point2) and fillRoundedRect + void writePixels(const SkImageInfo&, const void* pixels, size_t rowBytes, int x, int y); + void writePixels(const SkBitmap&, int x, int y); + void drawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint* = 0); + void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint* = 0); + void drawOval(const SkRect&, const SkPaint&); + void drawPath(const SkPath&, const SkPaint&); + // After drawing directly to the context's canvas, use this function to notify the context so + // it can track the opaque region. + // FIXME: this is still needed only because ImageSkia::paintSkBitmap() may need to notify for a + // smaller rect than the one drawn to, due to its clipping logic. + void didDrawRect(const SkRect&, const SkPaint&, const SkBitmap* = 0); + void drawRect(const SkRect&, const SkPaint&); + void drawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkRect& textRect, const SkPaint&); + void drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint&); + void drawTextBlob(const SkTextBlob*, const SkPoint& origin, const SkPaint&); + + void clip(const IntRect& rect) { clipRect(rect); } + void clip(const FloatRect& rect) { clipRect(rect); } + void clipRoundedRect(const RoundedRect&, SkRegion::Op = SkRegion::kIntersect_Op); + void clipOut(const IntRect& rect) { clipRect(rect, NotAntiAliased, SkRegion::kDifference_Op); } + void clipOutRoundedRect(const RoundedRect&); + void clipPath(const Path&, WindRule = RULE_EVENODD); + void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true); + void clipRect(const SkRect&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op); + + void drawText(const Font&, const TextRunPaintInfo&, const FloatPoint&); + void drawEmphasisMarks(const Font&, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&); + void drawBidiText(const Font&, const TextRunPaintInfo&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady); + void drawHighlightForText(const Font&, const TextRun&, const FloatPoint&, int h, const Color& backgroundColor, int from = 0, int to = -1); + + void drawLineForText(const FloatPoint&, float width); + enum DocumentMarkerLineStyle { + DocumentMarkerSpellingLineStyle, + DocumentMarkerGrammarLineStyle + }; + void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle); + + void beginTransparencyLayer(float opacity, const FloatRect* = 0); + void beginLayer(float opacity, CompositeOperator, const FloatRect* = 0, ColorFilter = ColorFilterNone, ImageFilter* = 0); + void endLayer(); + + // Instead of being dispatched to the active canvas, draw commands following beginRecording() + // are stored in a display list that can be replayed at a later time. + void beginRecording(const FloatRect& bounds); + PassRefPtr endRecording(); + + bool hasShadow() const; + void setShadow(const FloatSize& offset, float blur, const Color&, + DrawLooperBuilder::ShadowTransformMode = DrawLooperBuilder::ShadowRespectsTransforms, + DrawLooperBuilder::ShadowAlphaMode = DrawLooperBuilder::ShadowRespectsAlpha); + void clearShadow() { clearDrawLooper(); } + + // It is assumed that this draw looper is used only for shadows + // (i.e. a draw looper is set if and only if there is a shadow). + // The builder passed into this method will be destroyed. + void setDrawLooper(PassOwnPtr); + void clearDrawLooper(); + + void drawFocusRing(const Vector&, int width, int offset, const Color&); + void drawFocusRing(const Path&, int width, int offset, const Color&); + + enum Edge { + NoEdge = 0, + TopEdge = 1 << 1, + RightEdge = 1 << 2, + BottomEdge = 1 << 3, + LeftEdge = 1 << 4 + }; + typedef unsigned Edges; + void drawInnerShadow(const RoundedRect&, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges = NoEdge); + + // This clip function is used only by code. It allows + // implementations to handle clipping on the canvas differently since + // the discipline is different. + void canvasClip(const Path&, WindRule = RULE_EVENODD); + void clipOut(const Path&); + + // ---------- Transformation methods ----------------- + // Note that the getCTM method returns only the current transform from Blink's perspective, + // which is not the final transform used to place content on screen. It cannot be relied upon + // for testing where a point will appear on screen or how large it will be. + AffineTransform getCTM() const; + void concatCTM(const AffineTransform& affine) { concat(affineTransformToSkMatrix(affine)); } + void setCTM(const AffineTransform& affine) { setMatrix(affineTransformToSkMatrix(affine)); } + void setMatrix(const SkMatrix&); + + void scale(float x, float y); + void rotate(float angleInRadians); + void translate(float x, float y); + + // This function applies the device scale factor to the context, making the context capable of + // acting as a base-level context for a HiDPI environment. + void applyDeviceScaleFactor(float deviceScaleFactor) { scale(deviceScaleFactor, deviceScaleFactor); } + // ---------- End transformation methods ----------------- + + // URL drawing + void setURLForRect(const KURL&, const IntRect&); + void setURLFragmentForRect(const String& name, const IntRect&); + void addURLTargetAtPoint(const String& name, const IntPoint&); + + // Create an image buffer compatible with this context, with suitable resolution + // for drawing into the buffer and then into this context. + PassOwnPtr createRasterBuffer(const IntSize&, OpacityMode = NonOpaque) const; + + static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle); + + void beginAnnotation(const AnnotationList&); + void endAnnotation(); + + void preparePaintForDrawRectToRect( + SkPaint*, + const SkRect& srcRect, + const SkRect& destRect, + CompositeOperator, + WebBlendMode, + bool isLazyDecoded = false, + bool isDataComplete = true) const; + + static int focusRingOutsetExtent(int offset, int width) + { + return focusRingOutset(offset) + (focusRingWidth(width) + 1) / 2; + } + +private: + const GraphicsContextState* immutableState() const { return m_paintState; } + + GraphicsContextState* mutableState() + { + realizePaintSave(); + return m_paintState; + } + + static void setPathFromConvexPoints(SkPath*, size_t, const FloatPoint*); + static void setRadii(SkVector*, IntSize, IntSize, IntSize, IntSize); + + static PassRefPtr WebCoreColorFilterToSkiaColorFilter(ColorFilter); + + static inline int focusRingOutset(int offset) { return 0; } + static inline int focusRingWidth(int width) { return 1; } + static SkPMColor lineColors(int); + static SkPMColor antiColors1(int); + static SkPMColor antiColors2(int); + static void draw1xMarker(SkBitmap*, int); + static void draw2xMarker(SkBitmap*, int); + + // Helpers for drawing a focus ring (drawFocusRing) + float prepareFocusRingPaint(SkPaint&, const Color&, int width) const; + void drawFocusRingPath(const SkPath&, const Color&, int width); + void drawFocusRingRect(const SkRect&, const Color&, int width); + + + // SkCanvas wrappers. + void clipPath(const SkPath&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op); + void clipRRect(const SkRRect&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op); + void concat(const SkMatrix&); + void drawRRect(const SkRRect&, const SkPaint&); + + // Apply deferred paint state saves + void realizePaintSave() + { + if (contextDisabled()) + return; + + if (m_paintState->saveCount()) { + m_paintState->decrementSaveCount(); + ++m_paintStateIndex; + if (m_paintStateStack.size() == m_paintStateIndex) { + m_paintStateStack.append(GraphicsContextState::createAndCopy(*m_paintState)); + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + } else { + GraphicsContextState* priorPaintState = m_paintState; + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + m_paintState->copy(*priorPaintState); + } + } + } + + // Apply deferred canvas state saves + void realizeCanvasSave() + { + if (!m_pendingCanvasSave || contextDisabled()) + return; + + m_canvas->save(); + m_pendingCanvasSave = false; + } + + void didDrawTextInRect(const SkRect& textRect); + + void fillRectWithRoundedHole(const IntRect&, const RoundedRect& roundedHoleRect, const Color&); + + bool isRecording() const; + + // null indicates painting is contextDisabled. Never delete this object. + SkCanvas* m_canvas; + + // Paint states stack. Enables local drawing state change with save()/restore() calls. + // This state controls the appearance of drawn content. + // We do not delete from this stack to avoid memory churn. + Vector > m_paintStateStack; + // Current index on the stack. May not be the last thing on the stack. + unsigned m_paintStateIndex; + // Raw pointer to the current state. + GraphicsContextState* m_paintState; + + // Currently pending save flags for Skia Canvas state. + // Canvas state includes the canavs, it's matrix and clips. Think of it as _where_ + // the draw operations will happen. + struct CanvasSaveState; + Vector m_canvasStateStack; + bool m_pendingCanvasSave; + + AnnotationModeFlags m_annotationMode; + + struct RecordingState; + Vector m_recordingStateStack; + +#if ENABLE(ASSERT) + unsigned m_annotationCount; + unsigned m_layerCount; + bool m_disableDestructionChecks; +#endif + // Tracks the region painted opaque via the GraphicsContext. + RegionTracker m_trackedRegion; + + // Tracks the region where text is painted via the GraphicsContext. + SkRect m_textRegion; + + unsigned m_disabledState; + + float m_deviceScaleFactor; + + // Activation for the above region tracking features + unsigned m_regionTrackingMode : 2; + bool m_trackTextRegion : 1; + + bool m_accelerated : 1; + bool m_isCertainlyOpaque : 1; + bool m_antialiasHairlineImages : 1; + bool m_shouldSmoothFonts : 1; +}; + +} // namespace blink + +#endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXT_H_ diff --git a/engine/platform/graphics/GraphicsContext.h.rej b/engine/platform/graphics/GraphicsContext.h.rej new file mode 100644 index 00000000000..291091426b9 --- /dev/null +++ b/engine/platform/graphics/GraphicsContext.h.rej @@ -0,0 +1,15 @@ +--- platform/graphics/GraphicsContext.h ++++ platform/graphics/GraphicsContext.h +@@ -155,11 +155,10 @@ + // FIXME: the setter is only used once, at construction time; convert to a constructor param, + // and possibly consolidate with other flags (paintDisabled, isPrinting, ...) + void setShouldSmoothFonts(bool smoothFonts) { m_shouldSmoothFonts = smoothFonts; } +- bool shouldSmoothFonts() const { return m_shouldSmoothFonts; } + + // Turn off LCD text for the paint if not supported on this context. + void adjustTextRenderMode(SkPaint*) const; +- bool couldUseLCDRenderedText() const; ++ bool couldUseLCDRenderedText() const { return m_isCertainlyOpaque && m_shouldSmoothFonts; } + + void setTextDrawingMode(TextDrawingModeFlags mode) { mutableState()->setTextDrawingMode(mode); } + TextDrawingModeFlags textDrawingMode() const { return immutableState()->textDrawingMode(); } diff --git a/engine/platform/graphics/GraphicsContextRecorder.cpp b/engine/platform/graphics/GraphicsContextRecorder.cpp index 05d82f6f6b5..b093f3f8c2c 100644 --- a/engine/platform/graphics/GraphicsContextRecorder.cpp +++ b/engine/platform/graphics/GraphicsContextRecorder.cpp @@ -107,7 +107,7 @@ PassOwnPtr > GraphicsContextSnapshot::replay(unsigned fromStep, uns ReplayingCanvas canvas(bitmap, fromStep, toStep); canvas.scale(scale, scale); canvas.resetStepCount(); - m_picture->draw(&canvas, &canvas); + m_picture->playback(&canvas, &canvas); } OwnPtr > base64Data = adoptPtr(new Vector()); Vector encodedImage; @@ -136,7 +136,7 @@ PassOwnPtr GraphicsContextSnapshot::profile(un if (step) canvas = adoptPtr(new ProfilingCanvas(bitmap)); canvas->setTimings(currentTimings); - m_picture->draw(canvas.get()); + m_picture->playback(canvas.get()); now = WTF::monotonicallyIncreasingTime(); } return timings.release(); @@ -146,7 +146,7 @@ PassRefPtr GraphicsContextSnapshot::snapshotCommandLog() const { const SkIRect bounds = m_picture->cullRect().roundOut(); LoggingCanvas canvas(bounds.width(), bounds.height()); - m_picture->draw(&canvas); + m_picture->playback(&canvas); return canvas.log(); }