| /* |
| * Copyright (c) 2008, 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: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| * OWNER 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. |
| */ |
| |
| #include "config.h" |
| |
| #include "PlatformContextSkia.h" |
| |
| #include "Extensions3D.h" |
| #include "GraphicsContext.h" |
| #include "GraphicsContext3D.h" |
| #include "ImageBuffer.h" |
| #include "NativeImageSkia.h" |
| #include "SkiaUtils.h" |
| #include "Texture.h" |
| #include "TilingData.h" |
| |
| #include "skia/ext/image_operations.h" |
| #include "skia/ext/platform_canvas.h" |
| |
| #include "SkBitmap.h" |
| #include "SkColorPriv.h" |
| #include "SkDashPathEffect.h" |
| #include "SkShader.h" |
| |
| #include <wtf/MathExtras.h> |
| #include <wtf/Vector.h> |
| |
| #if PLATFORM(CHROMIUM) |
| #include "TraceEvent.h" |
| #endif |
| |
| namespace WebCore { |
| |
| // State ----------------------------------------------------------------------- |
| |
| // Encapsulates the additional painting state information we store for each |
| // pushed graphics state. |
| struct PlatformContextSkia::State { |
| State(); |
| State(const State&); |
| ~State(); |
| |
| // Common shader state. |
| float m_alpha; |
| SkXfermode::Mode m_xferMode; |
| bool m_useAntialiasing; |
| SkDrawLooper* m_looper; |
| |
| // Fill. |
| SkColor m_fillColor; |
| |
| // Stroke. |
| StrokeStyle m_strokeStyle; |
| SkColor m_strokeColor; |
| float m_strokeThickness; |
| int m_dashRatio; // Ratio of the length of a dash to its width. |
| float m_miterLimit; |
| SkPaint::Cap m_lineCap; |
| SkPaint::Join m_lineJoin; |
| SkDashPathEffect* m_dash; |
| |
| // Text. (See TextModeFill & friends in GraphicsContext.h.) |
| TextDrawingModeFlags m_textDrawingMode; |
| |
| // Helper function for applying the state's alpha value to the given input |
| // color to produce a new output color. |
| SkColor applyAlpha(SkColor) const; |
| |
| // If non-empty, the current State is clipped to this image. |
| SkBitmap m_imageBufferClip; |
| // If m_imageBufferClip is non-empty, this is the region the image is clipped to. |
| SkRect m_clip; |
| |
| InterpolationQuality m_interpolationQuality; |
| |
| PlatformContextSkia::State cloneInheritedProperties(); |
| private: |
| // Not supported. |
| void operator=(const State&); |
| }; |
| |
| // Note: Keep theses default values in sync with GraphicsContextState. |
| PlatformContextSkia::State::State() |
| : m_alpha(1) |
| , m_xferMode(SkXfermode::kSrcOver_Mode) |
| , m_useAntialiasing(true) |
| , m_looper(0) |
| , m_fillColor(0xFF000000) |
| , m_strokeStyle(SolidStroke) |
| , m_strokeColor(Color::black) |
| , m_strokeThickness(0) |
| , m_dashRatio(3) |
| , m_miterLimit(4) |
| , m_lineCap(SkPaint::kDefault_Cap) |
| , m_lineJoin(SkPaint::kDefault_Join) |
| , m_dash(0) |
| , m_textDrawingMode(TextModeFill) |
| #if USE(LOW_QUALITY_IMAGE_INTERPOLATION) |
| , m_interpolationQuality(InterpolationLow) |
| #else |
| , m_interpolationQuality(InterpolationHigh) |
| #endif |
| { |
| } |
| |
| PlatformContextSkia::State::State(const State& other) |
| : m_alpha(other.m_alpha) |
| , m_xferMode(other.m_xferMode) |
| , m_useAntialiasing(other.m_useAntialiasing) |
| , m_looper(other.m_looper) |
| , m_fillColor(other.m_fillColor) |
| , m_strokeStyle(other.m_strokeStyle) |
| , m_strokeColor(other.m_strokeColor) |
| , m_strokeThickness(other.m_strokeThickness) |
| , m_dashRatio(other.m_dashRatio) |
| , m_miterLimit(other.m_miterLimit) |
| , m_lineCap(other.m_lineCap) |
| , m_lineJoin(other.m_lineJoin) |
| , m_dash(other.m_dash) |
| , m_textDrawingMode(other.m_textDrawingMode) |
| , m_imageBufferClip(other.m_imageBufferClip) |
| , m_clip(other.m_clip) |
| , m_interpolationQuality(other.m_interpolationQuality) |
| { |
| // Up the ref count of these. SkSafeRef does nothing if its argument is 0. |
| SkSafeRef(m_looper); |
| SkSafeRef(m_dash); |
| } |
| |
| PlatformContextSkia::State::~State() |
| { |
| SkSafeUnref(m_looper); |
| SkSafeUnref(m_dash); |
| } |
| |
| // Returns a new State with all of this object's inherited properties copied. |
| PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties() |
| { |
| return PlatformContextSkia::State(*this); |
| } |
| |
| SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const |
| { |
| int s = roundf(m_alpha * 256); |
| if (s >= 256) |
| return c; |
| if (s < 0) |
| return 0; |
| |
| int a = SkAlphaMul(SkColorGetA(c), s); |
| return (c & 0x00FFFFFF) | (a << 24); |
| } |
| |
| // PlatformContextSkia --------------------------------------------------------- |
| |
| // Danger: canvas can be NULL. |
| PlatformContextSkia::PlatformContextSkia(SkCanvas* canvas) |
| : m_canvas(canvas) |
| , m_trackOpaqueRegion(false) |
| , m_printing(false) |
| , m_accelerated(false) |
| , m_deferred(false) |
| , m_drawingToImageBuffer(false) |
| , m_deviceScaleFactor(1) |
| #if defined(SK_SUPPORT_HINTING_SCALE_FACTOR) |
| , m_hintingScaleFactor(SK_Scalar1) |
| #endif |
| { |
| m_stateStack.append(State()); |
| m_state = &m_stateStack.last(); |
| |
| // will be assigned in setGraphicsContext() |
| m_gc = 0; |
| } |
| |
| PlatformContextSkia::~PlatformContextSkia() |
| { |
| } |
| |
| void PlatformContextSkia::setCanvas(SkCanvas* canvas) |
| { |
| m_canvas = canvas; |
| } |
| |
| void PlatformContextSkia::setDrawingToImageBuffer(bool value) |
| { |
| m_drawingToImageBuffer = value; |
| } |
| |
| bool PlatformContextSkia::isDrawingToImageBuffer() const |
| { |
| return m_drawingToImageBuffer; |
| } |
| |
| void PlatformContextSkia::save() |
| { |
| m_stateStack.append(m_state->cloneInheritedProperties()); |
| m_state = &m_stateStack.last(); |
| |
| // The clip image only needs to be applied once. Reset the image so that we |
| // don't attempt to clip multiple times. |
| m_state->m_imageBufferClip.reset(); |
| |
| // Save our native canvas. |
| canvas()->save(); |
| } |
| |
| void PlatformContextSkia::saveLayer(const SkRect* bounds, const SkPaint* paint) |
| { |
| m_canvas->saveLayer(bounds, paint); |
| if (bounds) |
| m_canvas->clipRect(*bounds); |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.pushCanvasLayer(paint); |
| } |
| |
| void PlatformContextSkia::saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags saveFlags) |
| { |
| m_canvas->saveLayer(bounds, paint, saveFlags); |
| if (bounds) |
| m_canvas->clipRect(*bounds); |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.pushCanvasLayer(paint); |
| } |
| |
| void PlatformContextSkia::restoreLayer() |
| { |
| m_canvas->restore(); |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.popCanvasLayer(this); |
| } |
| |
| void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, |
| const ImageBuffer* imageBuffer) |
| { |
| SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), |
| SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) }; |
| |
| if (imageBuffer->internalSize().isEmpty()) { |
| m_canvas->clipRect(bounds); |
| return; |
| } |
| |
| // Skia doesn't support clipping to an image, so we create a layer. The next |
| // time restore is invoked the layer and |imageBuffer| are combined to |
| // create the resulting image. |
| |
| m_state->m_clip = bounds; |
| // Get the absolute coordinates of the stored clipping rectangle to make it |
| // independent of any transform changes. |
| canvas()->getTotalMatrix().mapRect(&m_state->m_clip); |
| |
| SkCanvas::SaveFlags saveFlags = static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag); |
| saveLayer(&bounds, 0, saveFlags); |
| |
| const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); |
| |
| if (m_trackOpaqueRegion) { |
| SkRect opaqueRect = bitmap->isOpaque() ? m_state->m_clip : SkRect::MakeEmpty(); |
| m_opaqueRegion.setImageMask(opaqueRect); |
| } |
| |
| // Copy off the image as |imageBuffer| may be deleted before restore is invoked. |
| if (bitmap->isImmutable()) |
| m_state->m_imageBufferClip = *bitmap; |
| else { |
| // We need to make a deep-copy of the pixels themselves, so they don't |
| // change on us between now and when we want to apply them in restore() |
| bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); |
| } |
| } |
| |
| void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) |
| { |
| canvas()->clipPath(clipPath, SkRegion::kIntersect_Op, true); |
| } |
| |
| void PlatformContextSkia::restore() |
| { |
| if (!m_state->m_imageBufferClip.empty()) { |
| applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); |
| canvas()->restore(); |
| } |
| |
| m_stateStack.removeLast(); |
| m_state = &m_stateStack.last(); |
| |
| // Restore our native canvas. |
| canvas()->restore(); |
| } |
| |
| void PlatformContextSkia::drawRect(SkRect rect) |
| { |
| SkPaint paint; |
| int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000; |
| if (fillcolorNotTransparent) { |
| setupPaintForFilling(&paint); |
| canvas()->drawRect(rect, paint); |
| didDrawRect(rect, paint); |
| } |
| |
| if (m_state->m_strokeStyle != NoStroke |
| && (m_state->m_strokeColor & 0xFF000000)) { |
| // We do a fill of four rects to simulate the stroke of a border. |
| paint.reset(); |
| setupPaintForFilling(&paint); |
| // need to jam in the strokeColor |
| paint.setColor(this->effectiveStrokeColor()); |
| |
| SkRect topBorder = { rect.fLeft, rect.fTop, rect.fRight, rect.fTop + 1 }; |
| canvas()->drawRect(topBorder, paint); |
| didDrawRect(topBorder, paint); |
| SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.fRight, rect.fBottom }; |
| canvas()->drawRect(bottomBorder, paint); |
| didDrawRect(bottomBorder, paint); |
| SkRect leftBorder = { rect.fLeft, rect.fTop + 1, rect.fLeft + 1, rect.fBottom - 1 }; |
| canvas()->drawRect(leftBorder, paint); |
| didDrawRect(leftBorder, paint); |
| SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, rect.fRight, rect.fBottom - 1 }; |
| canvas()->drawRect(rightBorder, paint); |
| didDrawRect(rightBorder, paint); |
| } |
| } |
| |
| void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const |
| { |
| #if defined(SK_DEBUG) |
| { |
| SkPaint defaultPaint; |
| SkASSERT(*paint == defaultPaint); |
| } |
| #endif |
| |
| paint->setAntiAlias(m_state->m_useAntialiasing); |
| paint->setXfermodeMode(m_state->m_xferMode); |
| paint->setLooper(m_state->m_looper); |
| #if defined(SK_SUPPORT_HINTING_SCALE_FACTOR) |
| paint->setHintingScaleFactor(m_hintingScaleFactor); |
| #endif |
| } |
| |
| void PlatformContextSkia::setupShader(SkPaint* paint, Gradient* grad, Pattern* pat, SkColor color) const |
| { |
| SkShader* shader = 0; |
| |
| if (grad) { |
| shader = grad->platformGradient(); |
| color = SK_ColorBLACK; |
| } else if (pat) { |
| shader = pat->platformPattern(m_gc->getCTM()); |
| color = SK_ColorBLACK; |
| paint->setFilterBitmap(interpolationQuality() != InterpolationNone); |
| } |
| |
| paint->setColor(m_state->applyAlpha(color)); |
| paint->setShader(shader); |
| } |
| |
| void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const |
| { |
| setupPaintCommon(paint); |
| |
| const GraphicsContextState& state = m_gc->state(); |
| setupShader(paint, state.fillGradient.get(), state.fillPattern.get(), m_state->m_fillColor); |
| } |
| |
| float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const |
| { |
| setupPaintCommon(paint); |
| |
| const GraphicsContextState& state = m_gc->state(); |
| setupShader(paint, state.strokeGradient.get(), state.strokePattern.get(), m_state->m_strokeColor); |
| |
| float width = m_state->m_strokeThickness; |
| |
| paint->setStyle(SkPaint::kStroke_Style); |
| paint->setStrokeWidth(SkFloatToScalar(width)); |
| paint->setStrokeCap(m_state->m_lineCap); |
| paint->setStrokeJoin(m_state->m_lineJoin); |
| paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); |
| |
| if (m_state->m_dash) |
| paint->setPathEffect(m_state->m_dash); |
| else { |
| switch (m_state->m_strokeStyle) { |
| case NoStroke: |
| case SolidStroke: |
| #if ENABLE(CSS3_TEXT) |
| case DoubleStroke: |
| case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=93509 - Needs platform support. |
| #endif // CSS3_TEXT |
| break; |
| case DashedStroke: |
| width = m_state->m_dashRatio * width; |
| // Fall through. |
| case DottedStroke: |
| // Truncate the width, since we don't want fuzzy dots or dashes. |
| int dashLength = static_cast<int>(width); |
| // Subtract off the endcaps, since they're rendered separately. |
| int distance = length - 2 * static_cast<int>(m_state->m_strokeThickness); |
| int phase = 1; |
| if (dashLength > 1) { |
| // Determine how many dashes or dots we should have. |
| int numDashes = distance / dashLength; |
| int remainder = distance % dashLength; |
| // Adjust the phase to center the dashes within the line. |
| if (numDashes % 2 == 0) { |
| // Even: shift right half a dash, minus half the remainder |
| phase = (dashLength - remainder) / 2; |
| } else { |
| // Odd: shift right a full dash, minus half the remainder |
| phase = dashLength - remainder / 2; |
| } |
| } |
| SkScalar dashLengthSk = SkIntToScalar(dashLength); |
| SkScalar intervals[2] = { dashLengthSk, dashLengthSk }; |
| paint->setPathEffect(new SkDashPathEffect(intervals, 2, SkIntToScalar(phase)))->unref(); |
| } |
| } |
| |
| return width; |
| } |
| |
| void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl) |
| { |
| SkRefCnt_SafeAssign(m_state->m_looper, dl); |
| } |
| |
| void PlatformContextSkia::setMiterLimit(float ml) |
| { |
| m_state->m_miterLimit = ml; |
| } |
| |
| void PlatformContextSkia::setAlpha(float alpha) |
| { |
| m_state->m_alpha = alpha; |
| } |
| |
| void PlatformContextSkia::setLineCap(SkPaint::Cap lc) |
| { |
| m_state->m_lineCap = lc; |
| } |
| |
| void PlatformContextSkia::setLineJoin(SkPaint::Join lj) |
| { |
| m_state->m_lineJoin = lj; |
| } |
| |
| void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm) |
| { |
| m_state->m_xferMode = pdm; |
| } |
| |
| void PlatformContextSkia::setFillColor(SkColor color) |
| { |
| m_state->m_fillColor = color; |
| } |
| |
| SkDrawLooper* PlatformContextSkia::getDrawLooper() const |
| { |
| return m_state->m_looper; |
| } |
| |
| StrokeStyle PlatformContextSkia::getStrokeStyle() const |
| { |
| return m_state->m_strokeStyle; |
| } |
| |
| void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle) |
| { |
| m_state->m_strokeStyle = strokeStyle; |
| } |
| |
| void PlatformContextSkia::setStrokeColor(SkColor strokeColor) |
| { |
| m_state->m_strokeColor = strokeColor; |
| } |
| |
| float PlatformContextSkia::getStrokeThickness() const |
| { |
| return m_state->m_strokeThickness; |
| } |
| |
| void PlatformContextSkia::setStrokeThickness(float thickness) |
| { |
| m_state->m_strokeThickness = thickness; |
| } |
| |
| TextDrawingModeFlags PlatformContextSkia::getTextDrawingMode() const |
| { |
| return m_state->m_textDrawingMode; |
| } |
| |
| float PlatformContextSkia::getAlpha() const |
| { |
| return m_state->m_alpha; |
| } |
| |
| int PlatformContextSkia::getNormalizedAlpha() const |
| { |
| int alpha = roundf(m_state->m_alpha * 256); |
| if (alpha > 255) |
| alpha = 255; |
| else if (alpha < 0) |
| alpha = 0; |
| return alpha; |
| } |
| |
| SkXfermode::Mode PlatformContextSkia::getXfermodeMode() const |
| { |
| return m_state->m_xferMode; |
| } |
| |
| void PlatformContextSkia::setTextDrawingMode(TextDrawingModeFlags mode) |
| { |
| m_state->m_textDrawingMode = mode; |
| } |
| |
| void PlatformContextSkia::setUseAntialiasing(bool enable) |
| { |
| m_state->m_useAntialiasing = enable; |
| } |
| |
| SkColor PlatformContextSkia::effectiveFillColor() const |
| { |
| return m_state->applyAlpha(m_state->m_fillColor); |
| } |
| |
| SkColor PlatformContextSkia::effectiveStrokeColor() const |
| { |
| return m_state->applyAlpha(m_state->m_strokeColor); |
| } |
| |
| void PlatformContextSkia::canvasClipPath(const SkPath& path) |
| { |
| m_canvas->clipPath(path); |
| } |
| |
| InterpolationQuality PlatformContextSkia::interpolationQuality() const |
| { |
| return m_state->m_interpolationQuality; |
| } |
| |
| void PlatformContextSkia::setInterpolationQuality(InterpolationQuality interpolationQuality) |
| { |
| m_state->m_interpolationQuality = interpolationQuality; |
| } |
| |
| void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash) |
| { |
| if (dash != m_state->m_dash) { |
| SkSafeUnref(m_state->m_dash); |
| m_state->m_dash = dash; |
| } |
| } |
| |
| void PlatformContextSkia::paintSkPaint(const SkRect& rect, |
| const SkPaint& paint) |
| { |
| m_canvas->drawRect(rect, paint); |
| didDrawRect(rect, paint); |
| } |
| |
| const SkBitmap* PlatformContextSkia::bitmap() const |
| { |
| #if PLATFORM(CHROMIUM) |
| TRACE_EVENT0("skia", "PlatformContextSkia::bitmap"); |
| #endif |
| return &m_canvas->getDevice()->accessBitmap(false); |
| } |
| |
| bool PlatformContextSkia::isNativeFontRenderingAllowed() |
| { |
| #if USE(SKIA_TEXT) |
| return false; |
| #else |
| if (isAccelerated()) |
| return false; |
| return skia::SupportsPlatformPaint(m_canvas); |
| #endif |
| } |
| |
| void PlatformContextSkia::applyClipFromImage(const SkRect& rect, const SkBitmap& imageBuffer) |
| { |
| // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we |
| // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. |
| SkPaint paint; |
| paint.setXfermodeMode(SkXfermode::kDstIn_Mode); |
| m_canvas->save(SkCanvas::kMatrix_SaveFlag); |
| m_canvas->resetMatrix(); |
| m_canvas->drawBitmapRect(imageBuffer, 0, rect, &paint); |
| m_canvas->restore(); |
| } |
| |
| void PlatformContextSkia::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap) |
| { |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.didDrawRect(this, rect, paint, bitmap); |
| } |
| |
| void PlatformContextSkia::didDrawPath(const SkPath& path, const SkPaint& paint) |
| { |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.didDrawPath(this, path, paint); |
| } |
| |
| void PlatformContextSkia::didDrawPoints(SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint) |
| { |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.didDrawPoints(this, mode, numPoints, points, paint); |
| } |
| |
| void PlatformContextSkia::didDrawBounded(const SkRect& rect, const SkPaint& paint) |
| { |
| if (m_trackOpaqueRegion) |
| m_opaqueRegion.didDrawBounded(this, rect, paint); |
| } |
| |
| void PlatformContextSkia::adjustTextRenderMode(SkPaint* paint) |
| { |
| if (!paint->isLCDRenderText()) |
| return; |
| |
| paint->setLCDRenderText(couldUseLCDRenderedText()); |
| } |
| |
| bool PlatformContextSkia::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 disabled when we are drawing |
| // onto a layer. |
| if (canvas()->isDrawingToLayer()) |
| return false; |
| |
| // If this text is not in an image buffer and so won't be externally |
| // composited, then subpixel antialiasing is fine. |
| return !isDrawingToImageBuffer(); |
| } |
| |
| } // namespace WebCore |