summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/skia
diff options
context:
space:
mode:
authorFeng Qian <>2009-04-10 18:11:29 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-04-10 18:11:29 -0700
commit8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch)
tree181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/platform/graphics/skia
parent7ed56f225e0ade046e1c2178977f72b2d896f196 (diff)
downloadexternal_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.cpp20
-rw-r--r--WebCore/platform/graphics/skia/GraphicsContextSkia.cpp53
-rw-r--r--WebCore/platform/graphics/skia/ImageBufferSkia.cpp23
-rw-r--r--WebCore/platform/graphics/skia/ImageSkia.cpp8
-rw-r--r--WebCore/platform/graphics/skia/ImageSourceSkia.cpp14
-rw-r--r--WebCore/platform/graphics/skia/PathSkia.cpp2
-rw-r--r--WebCore/platform/graphics/skia/PlatformContextSkia.cpp165
-rw-r--r--WebCore/platform/graphics/skia/PlatformContextSkia.h47
-rw-r--r--WebCore/platform/graphics/skia/SkiaFontWin.cpp155
-rw-r--r--WebCore/platform/graphics/skia/SkiaFontWin.h35
-rw-r--r--WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp189
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