diff options
author | Feng Qian <> | 2009-04-10 18:11:29 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-10 18:11:29 -0700 |
commit | 8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch) | |
tree | 181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/platform/graphics/skia | |
parent | 7ed56f225e0ade046e1c2178977f72b2d896f196 (diff) | |
download | external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2 |
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/platform/graphics/skia')
-rw-r--r-- | WebCore/platform/graphics/skia/GradientSkia.cpp | 20 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/GraphicsContextSkia.cpp | 53 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/ImageBufferSkia.cpp | 23 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/ImageSkia.cpp | 8 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/ImageSourceSkia.cpp | 14 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/PathSkia.cpp | 2 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/PlatformContextSkia.cpp | 165 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/PlatformContextSkia.h | 47 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/SkiaFontWin.cpp | 155 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/SkiaFontWin.h | 35 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp | 189 |
11 files changed, 454 insertions, 257 deletions
diff --git a/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp index eff7c66..2d2000c 100644 --- a/WebCore/platform/graphics/skia/GradientSkia.cpp +++ b/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -136,6 +136,19 @@ SkShader* Gradient::platformGradient() fillStops(m_stops.data(), m_stops.size(), pos, colors); + SkShader::TileMode tile = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { + case SpreadMethodReflect: + tile = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + tile = SkShader::kRepeat_TileMode; + break; + case SpreadMethodPad: + tile = SkShader::kClamp_TileMode; + break; + } + if (m_radial) { // FIXME: CSS radial Gradients allow an offset focal point (the // "start circle"), but skia doesn't seem to support that, so this just @@ -145,13 +158,16 @@ SkShader* Gradient::platformGradient() // description of the expected behavior. m_gradient = SkGradientShader::CreateRadial(m_p1, WebCoreFloatToSkScalar(m_r1), colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } else { SkPoint pts[2] = { m_p0, m_p1 }; m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } + SkMatrix matrix = m_gradientSpaceTransformation; + m_gradient->setLocalMatrix(matrix); + return m_gradient; } diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index e6c7783..376fa4b 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -36,6 +36,7 @@ #include "Color.h" #include "FloatRect.h" #include "Gradient.h" +#include "ImageBuffer.h" #include "IntRect.h" #include "NativeImageSkia.h" #include "NotImplemented.h" @@ -274,11 +275,6 @@ void GraphicsContext::endTransparencyLayer() { if (paintingDisabled()) return; - -#if PLATFORM(WIN_OS) - platformContext()->canvas()->getTopPlatformDevice(). - fixupAlphaBeforeCompositing(); -#endif platformContext()->canvas()->restore(); } @@ -406,8 +402,7 @@ void GraphicsContext::clipPath(WindRule clipRule) if (paintingDisabled()) return; - const SkPath* oldPath = platformContext()->currentPath(); - SkPath path(*oldPath); + SkPath path = platformContext()->currentPathInLocalCoordinates(); path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); platformContext()->canvas()->clipPath(path); } @@ -418,8 +413,9 @@ void GraphicsContext::clipToImageBuffer(const FloatRect& rect, if (paintingDisabled()) return; - // FIXME: This is needed for image masking and complex text fills. - notImplemented(); +#if defined(__linux__) || PLATFORM(WIN_OS) + platformContext()->beginLayerClippedToImage(rect, imageBuffer); +#endif } void GraphicsContext::concatCTM(const TransformationMatrix& xform) @@ -645,6 +641,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, if (paintingDisabled()) return; + if (width <= 0) + return; + int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); SkRect r; r.fLeft = SkIntToScalar(pt.x()); @@ -653,7 +652,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, r.fBottom = r.fTop + SkIntToScalar(thickness); SkPaint paint; - paint.setColor(strokeColor().rgb()); + platformContext()->setupPaintForFilling(&paint); + // Text lines are drawn using the stroke color. + paint.setColor(platformContext()->effectiveStrokeColor()); platformContext()->canvas()->drawRect(r, paint); } @@ -664,9 +665,10 @@ void GraphicsContext::drawRect(const IntRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } platformContext()->drawRect(r); } @@ -676,7 +678,7 @@ void GraphicsContext::fillPath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; @@ -686,7 +688,7 @@ void GraphicsContext::fillPath() if (colorSpace == SolidColorSpace && !fillColor().alpha()) return; - platformContext()->setFillRule(state.fillRule == RULE_EVENODD ? + path.setFillType(state.fillRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); SkPaint paint; @@ -708,9 +710,10 @@ void GraphicsContext::fillRect(const FloatRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the other version of fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } const GraphicsContextState& state = m_common->state; ColorSpace colorSpace = state.fillColorSpace; @@ -775,6 +778,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, // See fillRect(). ClipRectToCanvas(*platformContext()->canvas(), r, &r); + 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; + } + SkPath path; addCornerArc(&path, r, topRight, 270); addCornerArc(&path, r, bottomRight, 0); @@ -784,12 +798,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, SkPaint paint; platformContext()->setupPaintForFilling(&paint); platformContext()->canvas()->drawPath(path, paint); - return fillRect(rect, color); } TransformationMatrix GraphicsContext::getCTM() const { - return platformContext()->canvas()->getTotalMatrix(); + const SkMatrix& m = platformContext()->canvas()->getTotalMatrix(); + return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a + SkScalarToDouble(m.getSkewY()), // b + SkScalarToDouble(m.getSkewX()), // c + SkScalarToDouble(m.getScaleY()), // d + SkScalarToDouble(m.getTranslateX()), // e + SkScalarToDouble(m.getTranslateY())); // f } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) @@ -1050,7 +1069,7 @@ void GraphicsContext::strokePath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index fdfcb85..5e90491 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -31,12 +31,14 @@ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" #include "BitmapImage.h" #include "BitmapImageSingleFrameSkia.h" #include "GraphicsContext.h" #include "ImageData.h" #include "NotImplemented.h" #include "PlatformContextSkia.h" +#include "PNGImageEncoder.h" #include "SkiaUtils.h" using namespace std; @@ -63,6 +65,9 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) m_data.m_platformContext.setCanvas(&m_data.m_canvas); m_context.set(new GraphicsContext(&m_data.m_platformContext)); +#if PLATFORM(WIN_OS) + m_context->platformContext()->setDrawingToImageBuffer(true); +#endif // Make the background transparent. It would be nice if this wasn't // required, but the canvas is currently filled with the magic transparency @@ -101,7 +106,7 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const ASSERT(context()); RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data(); + unsigned char* data = result->data()->data()->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || @@ -188,7 +193,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, unsigned srcBytesPerRow = 4 * source->width(); - const unsigned char* srcRow = source->data()->data() + originY * srcBytesPerRow + originX * 4; + const unsigned char* srcRow = source->data()->data()->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destX, destY + y); @@ -203,8 +208,18 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, String ImageBuffer::toDataURL(const String&) const { - notImplemented(); - return String(); + // Encode the image into a vector. + Vector<unsigned char> pngEncodedData; + PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &pngEncodedData); + + // Convert it into base64. + Vector<char> base64EncodedData; + base64Encode(*reinterpret_cast<Vector<char>*>(&pngEncodedData), base64EncodedData); + // Append with a \0 so that it's a valid string. + base64EncodedData.append('\0'); + + // And the resulting string. + return String::format("data:image/png;base64,%s", base64EncodedData.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index 1123fe9..d7f2830 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -225,6 +225,7 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag { SkPaint paint; paint.setPorterDuffXfermode(compOp); + paint.setFilterBitmap(true); skia::PlatformCanvas* canvas = platformContext->canvas(); @@ -233,7 +234,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height())); if (resampling == RESAMPLE_AWESOME) { - paint.setFilterBitmap(false); drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); } else { // No resampling necessary, we can just draw the bitmap. We want to @@ -241,7 +241,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag // is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so // we don't send extra pixels. - paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); } } @@ -401,6 +400,7 @@ void BitmapImage::invalidatePlatformData() void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; } void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, @@ -427,7 +427,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, paintSkBitmap(ctxt->platformContext(), *bm, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } @@ -447,7 +447,7 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, paintSkBitmap(ctxt->platformContext(), m_nativeImage, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp index f77620b..b5f7e1d 100644 --- a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp @@ -100,16 +100,16 @@ ImageSource::~ImageSource() void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { - // TODO(darin): Figure out what to do with the |data| and |allDataReceived| params. - - if (destroyAll) { - delete m_decoder; - m_decoder = 0; + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); return; } - if (m_decoder) - m_decoder->clearFrameBufferCache(clearBeforeFrame); + delete m_decoder; + m_decoder = 0; + if (data) + setData(data, allDataReceived); } bool ImageSource::initialized() const diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index ca99322..2700da8 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -274,7 +274,7 @@ static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) SkPaint paint; context->platformContext()->setupPaintForStroking(&paint, 0, 0); SkPath boundingPath; - paint.getFillPath(context->platformContext()->currentPath(), &boundingPath); + paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); SkRect r; boundingPath.computeBounds(&r, SkPath::kExact_BoundsType); return r; diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 60dbbe0..6c633f2 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "GraphicsContext.h" +#include "ImageBuffer.h" #include "NativeImageSkia.h" #include "PlatformContextSkia.h" #include "SkiaUtils.h" @@ -45,10 +46,6 @@ #include <wtf/MathExtras.h> -#if defined(__linux__) -#include "GdkSkia.h" -#endif - // State ----------------------------------------------------------------------- // Encapsulates the additional painting state information we store for each @@ -86,6 +83,13 @@ struct PlatformContextSkia::State { // color to produce a new output color. SkColor applyAlpha(SkColor) const; +#if defined(__linux__) || PLATFORM(WIN_OS) + // 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. + WebCore::FloatRect m_clip; +#endif + private: // Not supported. void operator=(const State&); @@ -113,9 +117,28 @@ PlatformContextSkia::State::State() } PlatformContextSkia::State::State(const State& other) + : m_alpha(other.m_alpha) + , m_porterDuffMode(other.m_porterDuffMode) + , m_gradient(other.m_gradient) + , m_pattern(other.m_pattern) + , 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) +#if defined(__linux__) || PLATFORM(WIN_OS) + , m_imageBufferClip(other.m_imageBufferClip) + , m_clip(other.m_clip) +#endif { - memcpy(this, &other, sizeof(State)); - + // Up the ref count of these. saveRef does nothing if 'this' is NULL. m_looper->safeRef(); m_dash->safeRef(); m_gradient->safeRef(); @@ -148,22 +171,16 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) , m_stateStack(sizeof(State)) +#if PLATFORM(WIN_OS) + , m_drawingToImageBuffer(false) +#endif { m_stateStack.append(State()); m_state = &m_stateStack.last(); -#if defined(OS_LINUX) - m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0; -#endif } PlatformContextSkia::~PlatformContextSkia() { -#if defined(OS_LINUX) - if (m_gdkskia) { - g_object_unref(m_gdkskia); - m_gdkskia = 0; - } -#endif } void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) @@ -171,17 +188,72 @@ void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) m_canvas = canvas; } +#if PLATFORM(WIN_OS) +void PlatformContextSkia::setDrawingToImageBuffer(bool value) +{ + m_drawingToImageBuffer = value; +} + +bool PlatformContextSkia::isDrawingToImageBuffer() const +{ + return m_drawingToImageBuffer; +} +#endif + void PlatformContextSkia::save() { m_stateStack.append(*m_state); m_state = &m_stateStack.last(); +#if defined(__linux__) || PLATFORM(WIN_OS) + // 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(); +#endif + // Save our native canvas. canvas()->save(); } +#if defined(__linux__) || PLATFORM(WIN_OS) +void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect, + const WebCore::ImageBuffer* imageBuffer) +{ + // 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 = rect; + SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), + SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; + + canvas()->saveLayerAlpha(&bounds, 255, + static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); + // Copy off the image as |imageBuffer| may be deleted before restore is invoked. + const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); + if (!bitmap->pixelRef()) { + // The bitmap owns it's pixels. This happens when we've allocated the + // pixels in some way and assigned them directly to the bitmap (as + // happens when we allocate a DIB). In this case the assignment operator + // does not copy the pixels, rather the copied bitmap ends up + // referencing the same pixels. As the pixels may not live as long as we + // need it to, we copy the image. + bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); + } else { + // If there is a pixel ref, we can safely use the assignment operator. + m_state->m_imageBufferClip = *bitmap; + } +} +#endif + void PlatformContextSkia::restore() { +#if defined(__linux__) || PLATFORM(WIN_OS) + if (!m_state->m_imageBufferClip.empty()) { + applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); + canvas()->restore(); + } +#endif + m_stateStack.removeLast(); m_state = &m_stateStack.last(); @@ -200,12 +272,21 @@ void PlatformContextSkia::drawRect(SkRect rect) if (m_state->m_strokeStyle != WebCore::NoStroke && (m_state->m_strokeColor & 0xFF000000)) { - if (fillcolorNotTransparent) { - // This call is expensive so don't call it unnecessarily. - paint.reset(); - } - setupPaintForStroking(&paint, &rect, 0); - canvas()->drawRect(rect, paint); + // We do a fill of four rects to simulate the stroke of a border. + SkColor oldFillColor = m_state->m_fillColor; + if (oldFillColor != m_state->m_strokeColor) + setFillColor(m_state->m_strokeColor); + setupPaintForFilling(&paint); + SkRect topBorder = { rect.fLeft, rect.fTop, rect.width(), 1 }; + canvas()->drawRect(topBorder, paint); + SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.width(), 1 }; + canvas()->drawRect(bottomBorder, paint); + SkRect leftBorder = { rect.fLeft, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(leftBorder, paint); + SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(rightBorder, paint); + if (oldFillColor != m_state->m_strokeColor) + setFillColor(oldFillColor); } } @@ -239,10 +320,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i setupPaintCommon(paint); float width = m_state->m_strokeThickness; - // This allows dashing and dotting to work properly for hairline strokes. - if (width == 0) - width = 1; - paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); paint->setStyle(SkPaint::kStroke_Style); paint->setStrokeWidth(SkFloatToScalar(width)); @@ -250,9 +327,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i paint->setStrokeJoin(m_state->m_lineJoin); paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); - if (rect != 0 && (static_cast<int>(roundf(width)) & 1)) - rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); - if (m_state->m_dash) paint->setPathEffect(m_state->m_dash); else { @@ -267,7 +341,8 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i SkScalar dashLength; if (length) { // Determine about how many dashes or dots we should have. - int numDashes = length / roundf(width); + float roundedWidth = roundf(width); + int numDashes = roundedWidth ? (length / roundedWidth) : length; if (!(numDashes & 1)) numDashes++; // Make it odd so we end on a dash/dot. // Use the number of dashes to determine the length of a @@ -366,9 +441,14 @@ void PlatformContextSkia::setUseAntialiasing(bool enable) m_state->m_useAntialiasing = enable; } -SkColor PlatformContextSkia::fillColor() const +SkColor PlatformContextSkia::effectiveFillColor() const { - return m_state->m_fillColor; + return m_state->applyAlpha(m_state->m_fillColor); +} + +SkColor PlatformContextSkia::effectiveStrokeColor() const +{ + return m_state->applyAlpha(m_state->m_strokeColor); } void PlatformContextSkia::beginPath() @@ -378,7 +458,17 @@ void PlatformContextSkia::beginPath() void PlatformContextSkia::addPath(const SkPath& path) { - m_path.addPath(path); + m_path.addPath(path, m_canvas->getTotalMatrix()); +} + +SkPath PlatformContextSkia::currentPathInLocalCoordinates() const +{ + SkPath localPath = m_path; + const SkMatrix& matrix = m_canvas->getTotalMatrix(); + SkMatrix inverseMatrix; + matrix.invert(&inverseMatrix); + localPath.transform(inverseMatrix); + return localPath; } void PlatformContextSkia::setFillRule(SkPath::FillType fr) @@ -425,3 +515,14 @@ bool PlatformContextSkia::isPrinting() { return m_canvas->getTopPlatformDevice().IsVectorial(); } + +#if defined(__linux__) || PLATFORM(WIN_OS) +void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& 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.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode); + m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); +} +#endif diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 78e9a19..8850a6a 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -43,8 +43,6 @@ #include <wtf/Vector.h> -typedef struct _GdkDrawable GdkSkia; - // This class holds the platform-specific state for GraphicsContext. We put // most of our Skia wrappers on this class. In theory, a lot of this stuff could // be moved to GraphicsContext directly, except that some code external to this @@ -73,9 +71,28 @@ public: // to the constructor. void setCanvas(skia::PlatformCanvas*); +#if PLATFORM(WIN_OS) + // If false we're rendering to a GraphicsContext for a web page, if false + // we're not (as is the case when rendering to a canvas object). + // If this is true the contents have not been marked up with the magic + // color and all text drawing needs to go to a layer so that the alpha is + // correctly updated. + void setDrawingToImageBuffer(bool); + bool isDrawingToImageBuffer() const; +#endif + void save(); void restore(); + // Begins a layer that is clipped to the image |imageBuffer| at the location + // |rect|. This layer is implicitly restored when the next restore is + // invoked. + // NOTE: |imageBuffer| may be deleted before the |restore| is invoked. +#if defined(__linux__) || PLATFORM(WIN_OS) + void beginLayerClippedToImage(const WebCore::FloatRect&, + const WebCore::ImageBuffer*); +#endif + // Sets up the common flags on a paint for antialiasing, effects, etc. // This is implicitly called by setupPaintFill and setupPaintStroke, but // you may wish to call it directly sometimes if you don't want that other @@ -116,9 +133,15 @@ public: void beginPath(); void addPath(const SkPath&); - const SkPath* currentPath() const { return &m_path; } + SkPath currentPathInLocalCoordinates() const; + + // Returns the fill color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveFillColor() const; - SkColor fillColor() const; + // Returns the stroke color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveStrokeColor() const; skia::PlatformCanvas* canvas() { return m_canvas; } @@ -142,12 +165,13 @@ public: // possible quality. bool isPrinting(); -#if defined(__linux__) - // FIXME: should be camelCase. - GdkSkia* gdk_skia() const { return m_gdkskia; } +private: +#if defined(__linux__) || PLATFORM(WIN_OS) + // Used when restoring and the state has an image clip. Only shows the pixels in + // m_canvas that are also in imageBuffer. + void applyClipFromImage(const WebCore::FloatRect&, const SkBitmap&); #endif -private: // Defines drawing style. struct State; @@ -161,12 +185,11 @@ private: // mStateStack.back(). State* m_state; - // Current path. + // Current path in global coordinates. SkPath m_path; -#if defined(__linux__) - // A pointer to a GDK Drawable wrapping of this Skia canvas - GdkSkia* m_gdkskia; +#if PLATFORM(WIN_OS) + bool m_drawingToImageBuffer; #endif }; diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp index 6e79a7e..d0cd4c5 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.cpp +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -31,8 +31,13 @@ #include "config.h" #include "SkiaFontWin.h" +#include "PlatformContextSkia.h" +#include "Gradient.h" +#include "Pattern.h" #include "SkCanvas.h" #include "SkPaint.h" +#include "SkShader.h" +#include "TransformationMatrix.h" #include <wtf/ListHashSet.h> #include <wtf/Vector.h> @@ -162,10 +167,10 @@ static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path) addPolyCurveToPath(polyCurve, path); curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; } + path->close(); curGlyph += polyHeader->cb; } - path->close(); return true; } @@ -215,4 +220,152 @@ void SkiaWinOutlineCache::removePathsForFont(HFONT hfont) deleteOutline(outlineCache.find(*i)); } +bool windowsCanHandleTextDrawing(GraphicsContext* context) +{ + // Check for non-translation transforms. Sometimes zooms will look better in + // Skia, and sometimes better in Windows. The main problem is that zooming + // in using Skia will show you the hinted outlines for the smaller size, + // which look weird. All else being equal, it's better to use Windows' text + // drawing, so we don't check for zooms. + const TransformationMatrix& matrix = context->getCTM(); + if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. + return false; + + // Check for stroke effects. + if (context->platformContext()->getTextDrawingMode() != cTextFill) + return false; + + // Check for gradients. + if (context->fillGradient() || context->strokeGradient()) + return false; + + // Check for patterns. + if (context->fillPattern() || context->strokePattern()) + return false; + + // Check for shadow effects. + if (context->platformContext()->getDrawLooper()) + return false; + + return true; +} + +// Draws the given text string using skia. Note that gradient or +// pattern may be NULL, in which case a solid colour is used. +static bool skiaDrawText(HFONT hfont, + HDC dc, + SkCanvas* canvas, + const SkPoint& point, + SkPaint* paint, + const TransformationMatrix& transformationMatrix, + Gradient* gradient, + Pattern* pattern, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + int numGlyphs) +{ + SkShader* shader = NULL; + if (gradient) + shader = gradient->platformGradient(); + else if (pattern) + shader = pattern->createPlatformPattern(transformationMatrix); + + paint->setShader(shader); + float x = point.fX, y = point.fY; + + for (int i = 0; i < numGlyphs; i++) { + const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); + if (!path) + return false; + + float offsetX = 0.0f, offsetY = 0.0f; + if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { + offsetX = offsets[i].du; + offsetY = offsets[i].dv; + } + + SkPath newPath; + newPath.addPath(*path, x + offsetX, y + offsetY); + canvas->drawPath(newPath, *paint); + + x += advances[i]; + } + + return true; +} + +bool paintSkiaText(GraphicsContext* context, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + + PlatformContextSkia* platformContext = context->platformContext(); + int textMode = platformContext->getTextDrawingMode(); + + // Filling (if necessary). This is the common case. + SkPaint paint; + platformContext->setupPaintForFilling(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + bool didFill = false; + + if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { + Gradient* fillGradient = 0; + Pattern* fillPattern = 0; + if (context->fillColorSpace() == GradientColorSpace) + fillGradient = context->fillGradient(); + else if (context->fillColorSpace() == PatternColorSpace) + fillPattern = context->fillPattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), fillGradient, fillPattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + didFill = true; + } + + // Stroking on top (if necessary). + if ((textMode & WebCore::cTextStroke) + && platformContext->getStrokeStyle() != NoStroke + && platformContext->getStrokeThickness() > 0) { + + paint.reset(); + platformContext->setupPaintForStroking(&paint, 0, 0); + paint.setFlags(SkPaint::kAntiAlias_Flag); + if (didFill) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + paint.setLooper(0)->safeUnref(); + } + + Gradient* strokeGradient = 0; + Pattern* strokePattern = 0; + if (context->strokeColorSpace() == GradientColorSpace) + strokeGradient = context->strokeGradient(); + else if (context->strokeColorSpace() == PatternColorSpace) + strokePattern = context->strokePattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), strokeGradient, strokePattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return true; +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.h b/WebCore/platform/graphics/skia/SkiaFontWin.h index 2adab39..0e0c953 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.h +++ b/WebCore/platform/graphics/skia/SkiaFontWin.h @@ -32,8 +32,12 @@ #define SkiaWinOutlineCache_h #include <windows.h> +#include <usp10.h> +class GraphicsContext; class SkPath; +class SkPoint; +class PlatformContextSkia; namespace WebCore { @@ -49,6 +53,37 @@ private: SkiaWinOutlineCache(); }; +// The functions below are used for more complex font drawing (effects such as +// stroking and more complex transforms) than Windows supports directly. Since +// Windows drawing is faster you should use windowsCanHandleTextDrawing first to +// check if using Skia is required at all. +// Note that the text will look different (no ClearType) so this should only be +// used when necessary. +// +// When you call a Skia* text drawing function, various glyph outlines will be +// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont +// when the font is destroyed so that the cache does not outlive the font (since +// the HFONTs are recycled). +// +// Remember that Skia's text drawing origin is the baseline, like WebKit, not +// the top, like Windows. + +// Returns true if advanced font rendering is recommended. +bool windowsCanHandleTextDrawing(GraphicsContext* context); + +// Note that the offsets parameter is optional. If not NULL it represents a +// per glyph offset (such as returned by ScriptPlace Windows API function). +// +// Returns true of the text was drawn successfully. False indicates an error +// from Windows. +bool paintSkiaText(GraphicsContext* graphicsContext, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin); + } // namespace WebCore #endif // SkiaWinOutlineCache_h diff --git a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp index 1e2a194..2d0f9f8 100644 --- a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp +++ b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp @@ -30,193 +30,28 @@ #include "config.h" #include "TransformationMatrix.h" -#include "FloatRect.h" -#include "IntRect.h" - #include "SkiaUtils.h" namespace WebCore { -TransformationMatrix::TransformationMatrix() -{ - m_transform.reset(); -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) -{ - setMatrix(a, b, c, d, e, f); -} - -TransformationMatrix::TransformationMatrix(const SkMatrix& matrix) - : m_transform(matrix) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) -{ - m_transform.reset(); - - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); - - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - SkPoint src, dst; - src.set(WebCoreDoubleToSkScalar(x), WebCoreDoubleToSkScalar(y)); - m_transform.mapPoints(&dst, &src, 1); - - *x2 = SkScalarToDouble(dst.fX); - *y2 = SkScalarToDouble(dst.fY); -} - -IntRect TransformationMatrix::mapRect(const IntRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return enclosingIntRect(dst); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return dst; -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform.preScale(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform.preRotate(WebCoreDoubleToSkScalar(d), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform.preTranslate(WebCoreDoubleToSkScalar(tx), WebCoreDoubleToSkScalar(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - m_transform.preSkew(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -double TransformationMatrix::det() const -{ - return SkScalarToDouble(m_transform.getScaleX()) * SkScalarToDouble(m_transform.getScaleY()) - - SkScalarToDouble(m_transform.getSkewY()) * SkScalarToDouble(m_transform.getSkewX()); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - TransformationMatrix inverse; - m_transform.invert(&inverse.m_transform); - return inverse; -} - TransformationMatrix::operator SkMatrix() const { - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix& m2) const -{ - return m_transform == m2.m_transform; -} + SkMatrix result; -TransformationMatrix &TransformationMatrix::operator*=(const TransformationMatrix& m2) -{ - m_transform.setConcat(m2.m_transform, m_transform); - return *this; -} + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); -TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& m2) -{ - TransformationMatrix cat; - cat.m_transform.setConcat(m2.m_transform, m_transform); - return cat; -} + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); -double TransformationMatrix::a() const -{ - return SkScalarToDouble(m_transform.getScaleX()); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); -} - -double TransformationMatrix::b() const -{ - return SkScalarToDouble(m_transform.getSkewY()); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); -} + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); -double TransformationMatrix::c() const -{ - return SkScalarToDouble(m_transform.getSkewX()); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); -} - -double TransformationMatrix::d() const -{ - return SkScalarToDouble(m_transform.getScaleY()); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); -} - -double TransformationMatrix::e() const -{ - return SkScalarToDouble(m_transform.getTranslateX()); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); -} - -double TransformationMatrix::f() const -{ - return SkScalarToDouble(m_transform.getTranslateY()); -} - -void TransformationMatrix::setF(double f) -{ - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); + return result; } } // namespace WebCore |