summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/skia
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/skia')
-rw-r--r--Source/WebCore/platform/graphics/skia/FontCacheSkia.cpp12
-rw-r--r--Source/WebCore/platform/graphics/skia/FontSkia.cpp15
-rw-r--r--Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp247
-rw-r--r--Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp11
-rw-r--r--Source/WebCore/platform/graphics/skia/ImageSkia.cpp289
-rw-r--r--Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.cpp59
-rw-r--r--Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.h44
-rw-r--r--Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp114
-rw-r--r--Source/WebCore/platform/graphics/skia/NativeImageSkia.h58
-rw-r--r--Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp1
-rw-r--r--Source/WebCore/platform/graphics/skia/PlatformContextSkia.h4
-rw-r--r--Source/WebCore/platform/graphics/skia/SimpleFontDataSkia.cpp57
12 files changed, 668 insertions, 243 deletions
diff --git a/Source/WebCore/platform/graphics/skia/FontCacheSkia.cpp b/Source/WebCore/platform/graphics/skia/FontCacheSkia.cpp
index 271c3d7e9..336abc430 100644
--- a/Source/WebCore/platform/graphics/skia/FontCacheSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/FontCacheSkia.cpp
@@ -30,20 +30,16 @@
#include "config.h"
#include "FontCache.h"
-
#include "Font.h"
#include "FontDescription.h"
#include "FontFamily.h"
#include "FontPlatformData.h"
#include "Logging.h"
#include "NotImplemented.h"
-#include "PlatformSupport.h"
#include "SimpleFontData.h"
-
#include "SkPaint.h"
#include "SkTypeface.h"
#include "SkUtils.h"
-
#include <unicode/locid.h>
#include <wtf/Assertions.h>
#include <wtf/text/AtomicString.h>
@@ -55,9 +51,7 @@ void FontCache::platformInit()
{
}
-const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font,
- const UChar* characters,
- int length)
+PassRefPtr<SimpleFontData> FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
{
icu::Locale locale = icu::Locale::getDefault();
FontCache::SimpleFontFamily family;
@@ -94,12 +88,12 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font,
return getCachedFontData(&platformData, DoNotRetain);
}
-SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
+PassRefPtr<SimpleFontData> FontCache::getSimilarFontPlatformData(const Font& font)
{
return 0;
}
-SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& description, ShouldRetain shouldRetain)
+PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& description, ShouldRetain shouldRetain)
{
DEFINE_STATIC_LOCAL(const AtomicString, sansStr, ("Sans"));
DEFINE_STATIC_LOCAL(const AtomicString, serifStr, ("Serif"));
diff --git a/Source/WebCore/platform/graphics/skia/FontSkia.cpp b/Source/WebCore/platform/graphics/skia/FontSkia.cpp
index 03f1417fb..c8097e349 100644
--- a/Source/WebCore/platform/graphics/skia/FontSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/FontSkia.cpp
@@ -31,6 +31,7 @@
#include "config.h"
#include "Font.h"
+#include "FontSmoothingMode.h"
#include "GlyphBuffer.h"
#include "GraphicsContext.h"
#include "LayoutTestSupport.h"
@@ -71,6 +72,16 @@ static void setupPaint(SkPaint* paint, const SimpleFontData* fontData, const Fon
paint->setAutohinted(false); // freetype specific
paint->setLCDRenderText(shouldSmoothFonts);
paint->setSubpixelText(true);
+
+#if OS(DARWIN)
+ // When using CoreGraphics, disable hinting when webkit-font-smoothing:antialiased is used.
+ // See crbug.com/152304
+ if (font->fontDescription().fontSmoothing() == Antialiased)
+ paint->setHinting(SkPaint::kNo_Hinting);
+#endif
+
+ if (font->fontDescription().textRenderingMode() == GeometricPrecision)
+ paint->setHinting(SkPaint::kNo_Hinting);
}
// TODO: This needs to be split into helper functions to better scope the
@@ -120,8 +131,8 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
for (int i = 0; i < numGlyphs; i++) {
pos[i].set(x, y);
- x += SkFloatToScalar(adv[i].width);
- y += SkFloatToScalar(adv[i].height);
+ x += SkFloatToScalar(adv[i].width());
+ y += SkFloatToScalar(adv[i].height());
}
SkCanvas* canvas = gc->platformContext()->canvas();
diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
index 35231a9da..fc9714671 100644
--- a/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
@@ -83,11 +83,6 @@ inline int fastMod(int value, int max)
return value;
}
-inline float square(float n)
-{
- return n * n;
-}
-
} // namespace
// Local helper functions ------------------------------------------------------
@@ -121,6 +116,89 @@ void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int sta
path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
}
+void draw2xMarker(SkBitmap* bitmap, int index)
+{
+
+ static const SkPMColor lineColors[2] = {
+ SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
+ SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0), // Opaque gray.
+ };
+ static const SkPMColor antiColors1[2] = {
+ SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red
+ SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0), // Semitransparent gray
+ };
+ static const SkPMColor antiColors2[2] = {
+ SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
+ SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0), // More transparent gray
+ };
+
+ const SkPMColor lineColor = lineColors[index];
+ const SkPMColor antiColor1 = antiColors1[index];
+ const SkPMColor antiColor2 = antiColors2[index];
+
+ uint32_t* row1 = bitmap->getAddr32(0, 0);
+ uint32_t* row2 = bitmap->getAddr32(0, 1);
+ uint32_t* row3 = bitmap->getAddr32(0, 2);
+ uint32_t* row4 = bitmap->getAddr32(0, 3);
+
+ // Pattern: X0o o0X0o o0
+ // XX0o o0XXX0o o0X
+ // o0XXX0o o0XXX0o
+ // o0X0o o0X0o
+ const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
+ const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
+ const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
+ const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
+
+ for (int x = 0; x < bitmap->width() + 8; x += 8) {
+ int count = min(bitmap->width() - x, 8);
+ if (count > 0) {
+ memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
+ memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
+ memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
+ memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
+ }
+ }
+}
+
+void draw1xMarker(SkBitmap* bitmap, int index)
+{
+ static const uint32_t lineColors[2] = {
+ 0xFF << SK_A32_SHIFT | 0xFF << SK_R32_SHIFT, // Opaque red.
+ 0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Opaque gray.
+ };
+ static const uint32_t antiColors[2] = {
+ 0x60 << SK_A32_SHIFT | 0x60 << SK_R32_SHIFT, // Semitransparent red
+ 0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Semitransparent gray
+ };
+
+ const uint32_t lineColor = lineColors[index];
+ const uint32_t antiColor = antiColors[index];
+
+ // Pattern: X o o X o o X
+ // o X o o X o
+ uint32_t* row1 = bitmap->getAddr32(0, 0);
+ uint32_t* row2 = bitmap->getAddr32(0, 1);
+ for (int x = 0; x < bitmap->width(); x++) {
+ switch (x % 4) {
+ case 0:
+ row1[x] = lineColor;
+ break;
+ case 1:
+ row1[x] = antiColor;
+ row2[x] = antiColor;
+ break;
+ case 2:
+ row2[x] = lineColor;
+ break;
+ case 3:
+ row1[x] = antiColor;
+ row2[x] = antiColor;
+ break;
+ }
+ }
+}
+
// -----------------------------------------------------------------------------
// This may be called with a NULL pointer to create a graphics context that has
@@ -522,92 +600,106 @@ void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float widt
if (paintingDisabled())
return;
+ int deviceScaleFactor = SkScalarRoundToInt(WebCoreFloatToSkScalar(platformContext()->deviceScaleFactor()));
+ ASSERT(deviceScaleFactor == 1 || deviceScaleFactor == 2);
+
// Create the pattern we'll use to draw the underline.
int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
- static SkBitmap* misspellBitmap[2] = { 0, 0 };
+ static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
+ static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
+ SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
if (!misspellBitmap[index]) {
#if PLATFORM(CHROMIUM) && OS(DARWIN)
// Match the artwork used by the Mac.
- const int rowPixels = 4;
- const int colPixels = 3;
-#else
- // We use a 2-pixel-high misspelling indicator because that seems to be
- // what WebKit is designed for, and how much room there is in a typical
- // page for it.
- const int rowPixels = 32; // Must be multiple of 4 for pattern below.
- const int colPixels = 2;
-#endif
+ const int rowPixels = 4 * deviceScaleFactor;
+ const int colPixels = 3 * deviceScaleFactor;
misspellBitmap[index] = new SkBitmap;
misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config,
rowPixels, colPixels);
misspellBitmap[index]->allocPixels();
misspellBitmap[index]->eraseARGB(0, 0, 0, 0);
-#if PLATFORM(CHROMIUM) && OS(DARWIN)
- const uint32_t colors[2][6] = {
- { 0x2A2A0600, 0x57571000, 0xA8A81B00, 0xBFBF1F00, 0x70701200, 0xE0E02400 },
- { 0x2A001503, 0x57002A08, 0xA800540D, 0xBF005F0F, 0x70003809, 0xE0007012 }
- };
const uint32_t transparentColor = 0x00000000;
- // Pattern: a b a a b a
- // c d c c d c
- // e f e e f e
- for (int x = 0; x < colPixels; ++x) {
- uint32_t* row = misspellBitmap[index]->getAddr32(0, x);
- row[0] = colors[index][x * 2];
- row[1] = colors[index][x * 2 + 1];
- row[2] = colors[index][x * 2];
- row[3] = transparentColor;
- }
-#else
- static const uint32_t lineColors[2] = {
- 0xFF << SK_A32_SHIFT | 0xFF << SK_R32_SHIFT, // Opaque red.
- 0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Opaque gray.
- };
- static const uint32_t antiColors[2] = {
- 0x60 << SK_A32_SHIFT | 0x60 << SK_R32_SHIFT, // Semitransparent red
- 0xFF << SK_A32_SHIFT | 0xC0 << SK_R32_SHIFT | 0xC0 << SK_G32_SHIFT | 0xC0 << SK_B32_SHIFT, // Semitransparent gray
- };
- const uint32_t lineColor = lineColors[index];
- const uint32_t antiColor = antiColors[index];
-
- // Pattern: X o o X o o X
- // o X o o X o
- uint32_t* row1 = misspellBitmap[index]->getAddr32(0, 0);
- uint32_t* row2 = misspellBitmap[index]->getAddr32(0, 1);
- for (int x = 0; x < rowPixels; x++) {
- switch (x % 4) {
- case 0:
- row1[x] = lineColor;
- break;
- case 1:
- row1[x] = antiColor;
- row2[x] = antiColor;
- break;
- case 2:
- row2[x] = lineColor;
- break;
- case 3:
- row1[x] = antiColor;
- row2[x] = antiColor;
- break;
+ if (deviceScaleFactor == 1) {
+ const uint32_t colors[2][6] = {
+ { 0x2A2A0600, 0x57571000, 0xA8A81B00, 0xBFBF1F00, 0x70701200, 0xE0E02400 },
+ { 0x2A001503, 0x57002A08, 0xA800540D, 0xBF005F0F, 0x70003809, 0xE0007012 }
+ };
+
+ // Pattern: a b a a b a
+ // c d c c d c
+ // e f e e f e
+ for (int x = 0; x < colPixels; ++x) {
+ uint32_t* row = misspellBitmap[index]->getAddr32(0, x);
+ row[0] = colors[index][x * 2];
+ row[1] = colors[index][x * 2 + 1];
+ row[2] = colors[index][x * 2];
+ row[3] = transparentColor;
}
- }
+ } else if (deviceScaleFactor == 2) {
+ const uint32_t colors[2][18] = {
+ { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
+ 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
+ { 0x0a000400, 0x33031b06, 0x55062f0b, 0x37041e06, 0x6e083d0d, 0x6e083d0d, 0x7608410e, 0x8d094e11, 0x8d094e11,
+ 0x960a5313, 0xb30d6417, 0xb30d6417, 0x6d073c0d, 0xd90f781c, 0xd90f781c, 0x19010d03, 0x7c094510, 0xce0f731a }
+ };
+
+ // Pattern: a b c c b a
+ // d e f f e d
+ // g h j j h g
+ // k l m m l k
+ // n o p p o n
+ // q r s s r q
+ for (int x = 0; x < colPixels; ++x) {
+ uint32_t* row = misspellBitmap[index]->getAddr32(0, x);
+ row[0] = colors[index][x * 3];
+ row[1] = colors[index][x * 3 + 1];
+ row[2] = colors[index][x * 3 + 2];
+ row[3] = colors[index][x * 3 + 2];
+ row[4] = colors[index][x * 3 + 1];
+ row[5] = colors[index][x * 3];
+ row[6] = transparentColor;
+ row[7] = transparentColor;
+ }
+ } else
+ ASSERT_NOT_REACHED();
+#else
+ // We use a 2-pixel-high misspelling indicator because that seems to be
+ // what WebKit is designed for, and how much room there is in a typical
+ // page for it.
+ const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
+ const int colPixels = 2 * deviceScaleFactor;
+ misspellBitmap[index] = new SkBitmap;
+ misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config, rowPixels, colPixels);
+ misspellBitmap[index]->allocPixels();
+
+ misspellBitmap[index]->eraseARGB(0, 0, 0, 0);
+ if (deviceScaleFactor == 1)
+ draw1xMarker(misspellBitmap[index], index);
+ else if (deviceScaleFactor == 2)
+ draw2xMarker(misspellBitmap[index], index);
+ else
+ ASSERT_NOT_REACHED();
#endif
}
- SkScalar originX = WebCoreFloatToSkScalar(pt.x());
#if PLATFORM(CHROMIUM) && OS(DARWIN)
- SkScalar originY = WebCoreFloatToSkScalar(pt.y());
+ SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
+ SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
+
// Make sure to draw only complete dots.
int rowPixels = misspellBitmap[index]->width();
- float widthMod = fmodf(width, rowPixels);
- if (rowPixels - widthMod > 1)
- width -= widthMod;
+ float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
+ if (rowPixels - widthMod > deviceScaleFactor)
+ width -= widthMod / deviceScaleFactor;
#else
+ SkScalar originX = WebCoreFloatToSkScalar(pt.x());
+
// Offset it vertically by 1 so that there's some space under the text.
SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
+ originX *= deviceScaleFactor;
+ originY *= deviceScaleFactor;
#endif
// Make a shader for the bitmap with an origin of the box we'll draw. This
@@ -616,23 +708,26 @@ void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float widt
*misspellBitmap[index], SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
SkMatrix matrix;
- matrix.reset();
- matrix.postTranslate(originX, originY);
+ matrix.setTranslate(originX, originY);
shader->setLocalMatrix(matrix);
// Assign the shader to the paint & release our reference. The paint will
// now own the shader and the shader will be destroyed when the paint goes
// out of scope.
SkPaint paint;
- paint.setShader(shader);
- shader->unref();
+ paint.setShader(shader)->unref();
SkRect rect;
- rect.set(originX,
- originY,
- originX + WebCoreFloatToSkScalar(width),
- originY + SkIntToScalar(misspellBitmap[index]->height()));
+ rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
+
+ if (deviceScaleFactor == 2) {
+ platformContext()->canvas()->save();
+ platformContext()->canvas()->scale(SK_ScalarHalf, SK_ScalarHalf);
+ }
platformContext()->canvas()->drawRect(rect, paint);
+ if (deviceScaleFactor == 2)
+ platformContext()->canvas()->restore();
+
platformContext()->didDrawRect(rect, paint);
}
diff --git a/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
index 905480d10..82c367d29 100644
--- a/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
@@ -42,6 +42,7 @@
#include "ImageData.h"
#include "JPEGImageEncoder.h"
#include "MIMETypeRegistry.h"
+#include "MemoryInstrumentationSkia.h"
#include "PNGImageEncoder.h"
#include "PlatformContextSkia.h"
#include "SharedGraphicsContext3D.h"
@@ -373,6 +374,16 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality, Coo
return "data:" + mimeType + ";base64," + base64Data;
}
+void ImageBufferData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
+{
+ MemoryClassInfo info(memoryObjectInfo, this);
+ info.addMember(m_canvas);
+ info.addMember(m_platformContext);
+#if USE(ACCELERATED_COMPOSITING)
+ info.addMember(m_layerBridge);
+#endif
+}
+
String ImageDataToDataURL(const ImageData& imageData, const String& mimeType, const double* quality)
{
ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
diff --git a/Source/WebCore/platform/graphics/skia/ImageSkia.cpp b/Source/WebCore/platform/graphics/skia/ImageSkia.cpp
index 8f883d819..e5a52411d 100644
--- a/Source/WebCore/platform/graphics/skia/ImageSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/ImageSkia.cpp
@@ -40,6 +40,7 @@
#include "Logging.h"
#include "NativeImageSkia.h"
#include "PlatformContextSkia.h"
+#include "SkBitmap.h"
#include "SkPixelRef.h"
#include "SkRect.h"
#include "SkShader.h"
@@ -50,6 +51,9 @@
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
+#include <limits>
+#include <math.h>
+
#if PLATFORM(CHROMIUM)
#include "TraceEvent.h"
#endif
@@ -70,11 +74,8 @@ enum ResamplingMode {
RESAMPLE_AWESOME,
};
-static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
+static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const NativeImageSkia& bitmap, float srcWidth, float srcHeight, float destWidth, float destHeight)
{
- int destIWidth = static_cast<int>(destWidth);
- int destIHeight = static_cast<int>(destHeight);
-
// The percent change below which we will not resample. This usually means
// an off-by-one error on the web page, and just doing nearest neighbor
// sampling is usually good enough.
@@ -92,10 +93,13 @@ static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const Native
// Figure out if we should resample this image. We try to prune out some
// common cases where resampling won't give us anything, since it is much
// slower than drawing stretched.
- if (srcWidth == destIWidth && srcHeight == destIHeight) {
- // We don't need to resample if the source and destination are the same.
+ float diffWidth = fabs(destWidth - srcWidth);
+ float diffHeight = fabs(destHeight - srcHeight);
+ bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
+ bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
+ // We don't need to resample if the source and destination are the same.
+ if (widthNearlyEqual && heightNearlyEqual)
return RESAMPLE_NONE;
- }
if (srcWidth <= kSmallImageSizeThreshold
|| srcHeight <= kSmallImageSizeThreshold
@@ -113,7 +117,7 @@ static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const Native
// This is trying to catch cases where somebody has created a border
// (which might be large) and then is stretching it to fill some part
// of the page.
- if (srcWidth == destWidth || srcHeight == destHeight)
+ if (widthNearlyEqual || heightNearlyEqual)
return RESAMPLE_NONE;
// The image is growing a lot and in more than one direction. Resampling
@@ -121,8 +125,8 @@ static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const Native
return RESAMPLE_LINEAR;
}
- if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold)
- && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) {
+ if ((diffWidth / srcWidth < kFractionalChangeThreshold)
+ && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
// It is disappointingly common on the web for image sizes to be off by
// one or two pixels. We don't bother resampling if the size difference
// is a small fraction of the original size.
@@ -162,11 +166,127 @@ static ResamplingMode limitResamplingMode(PlatformContextSkia* platformContext,
return resampling;
}
-// Draws the given bitmap to the given canvas. The subset of the source bitmap
-// identified by src_rect is drawn to the given destination rect. The bitmap
-// will be resampled to resample_width * resample_height (this is the size of
-// the whole image, not the subset). See shouldResampleBitmap for more.
+// Return true if the rectangle is aligned to integer boundaries.
+// See comments for computeBitmapDrawRects() for how this is used.
+static bool areBoundariesIntegerAligned(const SkRect& rect)
+{
+ // Value is 1.19209e-007. This is the tolerance threshold.
+ const float epsilon = std::numeric_limits<float>::epsilon();
+ SkIRect roundedRect = roundedIntRect(rect);
+
+ return fabs(rect.x() - roundedRect.x()) < epsilon
+ && fabs(rect.y() - roundedRect.y()) < epsilon
+ && fabs(rect.right() - roundedRect.right()) < epsilon
+ && fabs(rect.bottom() - roundedRect.bottom()) < epsilon;
+}
+
+// FIXME: Remove this code when SkCanvas accepts SkRect as source rectangle.
+// See crbug.com/117597 for background.
+//
+// WebKit wants to draw a sub-rectangle (FloatRect) in a bitmap and scale it to
+// another FloatRect. However Skia only allows bitmap to be addressed by a
+// IntRect. This function computes the appropriate IntRect that encloses the
+// source rectangle and the corresponding enclosing destination rectangle,
+// while maintaining the scale factor.
+//
+// |srcRect| is the source rectangle in the bitmap. Return true if fancy
+// alignment is required. User of this function needs to clip to |dstRect|.
+// Return false if clipping is not needed.
+//
+// |dstRect| is the input rectangle that |srcRect| is scaled to.
+//
+// |outSrcRect| and |outDstRect| are the corresponding output rectangles.
+//
+// ALGORITHM
+//
+// The objective is to (a) find an enclosing IntRect for the source rectangle
+// and (b) the corresponding FloatRect in destination space.
+//
+// These are the steps performed:
+//
+// 1. IntRect enclosingSrcRect = enclosingIntRect(srcRect)
+//
+// Compute the enclosing IntRect for |srcRect|. This ensures the bitmap
+// image is addressed with integer boundaries.
+//
+// 2. FloatRect enclosingDestRect = mapSrcToDest(enclosingSrcRect)
+//
+// Map the enclosing source rectangle to destination coordinate space.
+//
+// The output will be enclosingSrcRect and enclosingDestRect from the
+// algorithm above.
+static bool computeBitmapDrawRects(const SkISize& bitmapSize, const SkRect& srcRect, const SkRect& dstRect, SkIRect* outSrcRect, SkRect* outDstRect)
+{
+ if (areBoundariesIntegerAligned(srcRect)) {
+ *outSrcRect = roundedIntRect(srcRect);
+ *outDstRect = dstRect;
+ return false;
+ }
+
+ SkIRect bitmapRect = SkIRect::MakeSize(bitmapSize);
+ SkIRect enclosingSrcRect = enclosingIntRect(srcRect);
+ enclosingSrcRect.intersect(bitmapRect); // Clip to bitmap rectangle.
+ SkRect enclosingDstRect;
+ enclosingDstRect.set(enclosingSrcRect);
+ SkMatrix transform;
+ transform.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit);
+ transform.mapRect(&enclosingDstRect);
+ *outSrcRect = enclosingSrcRect;
+ *outDstRect = enclosingDstRect;
+ return true;
+}
+
+// This function is used to scale an image and extract a scaled fragment.
//
+// ALGORITHM
+//
+// Because the scaled image size has to be integers, we approximate the real
+// scale with the following formula (only X direction is shown):
+//
+// scaledImageWidth = round(scaleX * imageRect.width())
+// approximateScaleX = scaledImageWidth / imageRect.width()
+//
+// With this method we maintain a constant scale factor among fragments in
+// the scaled image. This allows fragments to stitch together to form the
+// full scaled image. The downside is there will be a small difference
+// between |scaleX| and |approximateScaleX|.
+//
+// A scaled image fragment is identified by:
+//
+// - Scaled image size
+// - Scaled image fragment rectangle (IntRect)
+//
+// Scaled image size has been determined and the next step is to compute the
+// rectangle for the scaled image fragment which needs to be an IntRect.
+//
+// scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
+// enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
+//
+// Finally we extract the scaled image fragment using
+// (scaledImageSize, enclosingScaledSrcRect).
+//
+static SkBitmap extractScaledImageFragment(const NativeImageSkia& bitmap, const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect, SkIRect* enclosingScaledSrcRect)
+{
+ SkISize imageSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height());
+ SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
+ clampToInteger(roundf(imageSize.height() * scaleY)));
+
+ SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
+ SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());
+
+ SkMatrix scaleTransform;
+ scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
+ scaleTransform.mapRect(scaledSrcRect, srcRect);
+
+ scaledSrcRect->intersect(scaledImageRect);
+ *enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);
+
+ // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
+ // of float inaccuracy so clip to get inside.
+ enclosingScaledSrcRect->intersect(SkIRect::MakeSize(scaledImageSize));
+ return bitmap.resizedBitmap(scaledImageSize, *enclosingScaledSrcRect);
+}
+
// This does a lot of computation to resample only the portion of the bitmap
// that will only be drawn. This is critical for performance since when we are
// scrolling, for example, we are only drawing a small strip of the image.
@@ -175,48 +295,62 @@ static ResamplingMode limitResamplingMode(PlatformContextSkia* platformContext,
//
// Note: this code is only used when the canvas transformation is limited to
// scaling or translation.
-static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect)
+static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect)
{
#if PLATFORM(CHROMIUM)
TRACE_EVENT0("skia", "drawResampledBitmap");
#endif
- // Apply forward transform to destRect to estimate required size of
- // re-sampled bitmap, and use only in calls required to resize, or that
- // check for the required size.
- SkRect destRectTransformed;
- canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect);
- SkIRect destRectTransformedRounded;
- destRectTransformed.round(&destRectTransformedRounded);
-
- // Compute the visible portion of our rect.
+ // We want to scale |destRect| with transformation in the canvas to obtain
+ // the final scale. The final scale is a combination of scale transform
+ // in canvas and explicit scaling (srcRect and destRect).
+ SkRect screenRect;
+ canvas.getTotalMatrix().mapRect(&screenRect, destRect);
+ float realScaleX = screenRect.width() / srcRect.width();
+ float realScaleY = screenRect.height() / srcRect.height();
+
+ // This part of code limits scaling only to visible portion in the
SkRect destRectVisibleSubset;
ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset);
+
// ClipRectToCanvas often overshoots, resulting in a larger region than our
// original destRect. Intersecting gets us back inside.
if (!destRectVisibleSubset.intersect(destRect))
return; // Nothing visible in destRect.
- // Compute the transformed (screen space) portion of the visible portion for
- // use below.
- SkRect destRectVisibleSubsetTransformed;
- canvas.getTotalMatrix().mapRect(&destRectVisibleSubsetTransformed, destRectVisibleSubset);
- SkRect destBitmapSubsetTransformed = destRectVisibleSubsetTransformed;
- destBitmapSubsetTransformed.offset(-destRectTransformed.fLeft,
- -destRectTransformed.fTop);
- SkIRect destBitmapSubsetTransformedRounded;
- destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded);
-
- // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded
- // to go outside the image, so need to clip to avoid problems.
- if (!destBitmapSubsetTransformedRounded.intersect(
- 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height()))
- return; // Image is not visible.
-
- SkBitmap resampled = bitmap.resizedBitmap(srcIRect,
- destRectTransformedRounded.width(),
- destRectTransformedRounded.height(),
- destBitmapSubsetTransformedRounded);
- canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint);
+ // Find the corresponding rect in the source image.
+ SkMatrix destToSrcTransform;
+ SkRect srcRectVisibleSubset;
+ destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit);
+ destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset);
+
+ SkRect scaledSrcRect;
+ SkIRect enclosingScaledSrcRect;
+ SkBitmap scaledImageFragment = extractScaledImageFragment(bitmap, srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect, &enclosingScaledSrcRect);
+
+ // Expand the destination rectangle because the source rectangle was
+ // expanded to fit to integer boundaries.
+ SkMatrix scaledSrcToDestTransform;
+ scaledSrcToDestTransform.setRectToRect(scaledSrcRect, destRectVisibleSubset, SkMatrix::kFill_ScaleToFit);
+ SkRect enclosingDestRect;
+ enclosingDestRect.set(enclosingScaledSrcRect);
+ scaledSrcToDestTransform.mapRect(&enclosingDestRect);
+
+ // The reason we do clipping is because Skia doesn't support SkRect as
+ // source rect. See http://crbug.com/145540.
+ // When Skia supports then use this as the source rect to replace 0.
+ //
+ // scaledSrcRect.offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());
+ canvas.save();
+ canvas.clipRect(destRectVisibleSubset);
+
+ // Because the image fragment is generated with an approxmiated scaling
+ // factor. This draw will perform a close to 1 scaling.
+ //
+ // NOTE: For future optimization. If the difference in scale is so small
+ // that Skia doesn't produce a difference then we can just blit it directly
+ // to enhance performance.
+ canvas.drawBitmapRect(scaledImageFragment, 0, enclosingDestRect, &paint);
+ canvas.restore();
}
static bool hasNon90rotation(PlatformContextSkia* context)
@@ -224,7 +358,7 @@ static bool hasNon90rotation(PlatformContextSkia* context)
return !context->canvas()->getTotalMatrix().rectStaysRect();
}
-static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
+static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
#if PLATFORM(CHROMIUM)
TRACE_EVENT0("skia", "paintSkBitmap");
@@ -249,8 +383,9 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag
if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
canvas->getTotalMatrix().mapRect(&destRectTarget, destRect);
- resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap, srcRect.width(), srcRect.height(),
- SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
+ resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap,
+ SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
+ SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
}
if (resampling == RESAMPLE_NONE) {
@@ -269,7 +404,27 @@ 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.
- canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint);
+ SkIRect enclosingSrcRect;
+ SkRect enclosingDestRect;
+ SkISize bitmapSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height());
+ bool needsClipping = computeBitmapDrawRects(bitmapSize, srcRect, destRect, &enclosingSrcRect, &enclosingDestRect);
+
+ if (enclosingSrcRect.isEmpty() || enclosingDestRect.isEmpty())
+ return;
+
+ // If destination is enlarged because source rectangle didn't align to
+ // integer boundaries then we draw a slightly larger rectangle and clip
+ // to the original destination rectangle.
+ // See http://crbug.com/145540.
+ if (needsClipping) {
+ platformContext->save();
+ platformContext->canvas()->clipRect(destRect);
+ }
+
+ canvas->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint);
+
+ if (needsClipping)
+ platformContext->restore();
}
platformContext->didDrawRect(destRect, paint, &bitmap.bitmap());
}
@@ -323,7 +478,6 @@ void Image::drawPattern(GraphicsContext* context,
if (!bitmap)
return;
- SkIRect srcRect = enclosingIntRect(normSrcRect);
SkMatrix ctm = context->platformContext()->canvas()->getTotalMatrix();
SkMatrix totalMatrix;
totalMatrix.setConcat(ctm, patternTransform);
@@ -342,7 +496,7 @@ void Image::drawPattern(GraphicsContext* context,
if (context->platformContext()->isAccelerated() || context->platformContext()->printing())
resampling = RESAMPLE_LINEAR;
else
- resampling = computeResamplingMode(totalMatrix, *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight);
+ resampling = computeResamplingMode(totalMatrix, *bitmap, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight);
resampling = limitResamplingMode(context->platformContext(), resampling);
// Load the transform WebKit requested.
@@ -351,12 +505,19 @@ void Image::drawPattern(GraphicsContext* context,
SkShader* shader;
if (resampling == RESAMPLE_AWESOME) {
// Do nice resampling.
- int width = static_cast<int>(destBitmapWidth);
- int height = static_cast<int>(destBitmapHeight);
- SkBitmap resampled = bitmap->resizedBitmap(srcRect, width, height);
+ float scaleX = destBitmapWidth / normSrcRect.width();
+ float scaleY = destBitmapHeight / normSrcRect.height();
+ SkRect scaledSrcRect;
+ SkIRect enclosingScaledSrcRect;
+
+ // The image fragment generated here is not exactly what is
+ // requested. The scale factor used is approximated and image
+ // fragment is slightly larger to align to integer
+ // boundaries.
+ SkBitmap resampled = extractScaledImageFragment(*bitmap, normSrcRect, scaleX, scaleY, &scaledSrcRect, &enclosingScaledSrcRect);
shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
- // Since we just resized the bitmap, we need to remove the scale
+ // Since we just resized the bitmap, we need to remove the scale
// applied to the pixels in the bitmap shader. This means we need
// CTM * patternTransform to have identity scale. Since we
// can't modify CTM (or the rectangle will be drawn in the wrong
@@ -367,7 +528,7 @@ void Image::drawPattern(GraphicsContext* context,
} else {
// No need to do nice resampling.
SkBitmap srcSubset;
- bitmap->bitmap().extractSubset(&srcSubset, srcRect);
+ bitmap->bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
}
@@ -443,10 +604,10 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
return; // Nothing to draw.
paintSkBitmap(ctxt->platformContext(),
- *bm,
- enclosingIntRect(normSrcRect),
- normDstRect,
- WebCoreCompositeToSkiaComposite(compositeOp));
+ *bm,
+ normSrcRect,
+ normDstRect,
+ WebCoreCompositeToSkiaComposite(compositeOp));
if (ImageObserver* observer = imageObserver())
observer->didDraw(this);
@@ -467,10 +628,10 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
return; // Nothing to draw.
paintSkBitmap(ctxt->platformContext(),
- m_nativeImage,
- enclosingIntRect(normSrcRect),
- normDstRect,
- WebCoreCompositeToSkiaComposite(compositeOp));
+ m_nativeImage,
+ normSrcRect,
+ normDstRect,
+ WebCoreCompositeToSkiaComposite(compositeOp));
if (ImageObserver* observer = imageObserver())
observer->didDraw(this);
diff --git a/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.cpp b/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.cpp
new file mode 100644
index 000000000..0793c3569
--- /dev/null
+++ b/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 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 "MemoryInstrumentationSkia.h"
+
+#include "PlatformMemoryInstrumentation.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPixelRef.h"
+
+void reportMemoryUsage(const SkBitmap* const& image, WTF::MemoryObjectInfo* memoryObjectInfo)
+{
+ WTF::MemoryClassInfo info(memoryObjectInfo, image);
+ SkPixelRef* pixelRef = image->pixelRef();
+ info.addMember(pixelRef);
+ if (pixelRef)
+ info.addRawBuffer(pixelRef->pixels(), image->getSize());
+}
+
+void reportMemoryUsage(const SkDevice* const& device, WTF::MemoryObjectInfo* memoryObjectInfo)
+{
+ WTF::MemoryClassInfo info(memoryObjectInfo, device);
+ info.addMember(const_cast<SkDevice*>(device)->accessBitmap(false));
+}
+
+void reportMemoryUsage(const SkCanvas* const& canvas, WTF::MemoryObjectInfo* memoryObjectInfo)
+{
+ WTF::MemoryClassInfo info(memoryObjectInfo, canvas);
+ info.addMember(canvas->getDevice());
+}
diff --git a/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.h b/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.h
new file mode 100644
index 000000000..e6f81b0d2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/skia/MemoryInstrumentationSkia.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef MemoryInstrumentationSkia_h
+#define MemoryInstrumentationSkia_h
+
+#include "PlatformMemoryInstrumentation.h"
+
+class SkBitmap;
+class SkDevice;
+class SkCanvas;
+
+void reportMemoryUsage(const SkBitmap* const&, WTF::MemoryObjectInfo*);
+void reportMemoryUsage(const SkDevice* const&, WTF::MemoryObjectInfo*);
+void reportMemoryUsage(const SkCanvas* const&, WTF::MemoryObjectInfo*);
+
+#endif // !defined(MemoryInstrumentationSkia_h)
diff --git a/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp b/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp
index 2c4baed75..1862a2afc 100644
--- a/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp
@@ -34,7 +34,10 @@
#include "NativeImageSkia.h"
#include "GraphicsContext3D.h"
+#include "MemoryInstrumentationSkia.h"
#include "PlatformInstrumentation.h"
+#include "PlatformMemoryInstrumentation.h"
+#include "SkPixelRef.h"
#include "SkiaUtils.h"
namespace WebCore {
@@ -61,56 +64,48 @@ int NativeImageSkia::decodedSize() const
return m_image.getSize() + m_resizedImage.getSize();
}
-bool NativeImageSkia::hasResizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight) const
+bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
- return m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight) && !m_resizedImage.empty();
+ bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
+ bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
+ return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
}
-SkBitmap NativeImageSkia::resizedBitmap(const SkIRect& srcSubset,
- int destWidth,
- int destHeight,
- const SkIRect& destVisibleSubset) const
+SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
- if (!hasResizedBitmap(srcSubset, destWidth, destHeight)) {
+ if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
bool shouldCache = isDataComplete()
- && shouldCacheResampling(srcSubset, destWidth, destHeight, destVisibleSubset);
-
- SkBitmap subset;
- m_image.extractSubset(&subset, srcSubset);
- if (!shouldCache) {
- // Just resize the visible subset and return it.
- PlatformInstrumentation::willResizeImage(shouldCache);
- SkBitmap resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight, destVisibleSubset);
- PlatformInstrumentation::didResizeImage();
- resizedImage.setImmutable();
+ && shouldCacheResampling(scaledImageSize, scaledImageSubset);
+
+ PlatformInstrumentation::willResizeImage(shouldCache);
+ SkBitmap resizedImage = skia::ImageOperations::Resize(m_image, skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
+ resizedImage.setImmutable();
+ PlatformInstrumentation::didResizeImage();
+
+ if (!shouldCache)
return resizedImage;
- } else {
- PlatformInstrumentation::willResizeImage(shouldCache);
- m_resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight);
- PlatformInstrumentation::didResizeImage();
- }
- m_resizedImage.setImmutable();
+
+ m_resizedImage = resizedImage;
}
- SkBitmap visibleBitmap;
- m_resizedImage.extractSubset(&visibleBitmap, destVisibleSubset);
- return visibleBitmap;
+ SkBitmap resizedSubset;
+ SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
+ m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
+ return resizedSubset;
}
-bool NativeImageSkia::shouldCacheResampling(const SkIRect& srcSubset,
- int destWidth,
- int destHeight,
- const SkIRect& destVisibleSubset) const
+bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
// Check whether the requested dimensions match previous request.
- bool matchesPreviousRequest = m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight);
+ bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
if (matchesPreviousRequest)
++m_resizeRequests;
else {
- m_cachedImageInfo.set(srcSubset, destWidth, destHeight);
+ m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
m_resizeRequests = 0;
- // Reset m_resizedImage now, because we don't distinguish between the
- // last requested resize info and m_resizedImage's resize info.
+ // Reset m_resizedImage now, because we don't distinguish
+ // between the last requested resize info and m_resizedImage's
+ // resize info.
m_resizedImage.reset();
}
@@ -123,13 +118,16 @@ bool NativeImageSkia::shouldCacheResampling(const SkIRect& srcSubset,
// If the destination bitmap is excessively large, we'll never allow caching.
static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
- if ((static_cast<unsigned long long>(destWidth) * static_cast<unsigned long long>(destHeight)) > kLargeBitmapSize)
+ unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
+ unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());
+
+ if (fragmentSize > kLargeBitmapSize)
return false;
// If the destination bitmap is small, we'll always allow caching, since
// there is not very much penalty for computing it and it may come in handy.
- static const int kSmallBitmapSize = 4096;
- if (destWidth * destHeight <= kSmallBitmapSize)
+ static const unsigned kSmallBitmapSize = 4096;
+ if (fragmentSize <= kSmallBitmapSize)
return true;
// If "too many" requests have been made for this bitmap, we assume that
@@ -138,28 +136,46 @@ bool NativeImageSkia::shouldCacheResampling(const SkIRect& srcSubset,
if (m_resizeRequests >= kManyRequestThreshold)
return true;
- // If more than 1/4 of the resized image is visible, it's worth caching.
- int destVisibleSize = destVisibleSubset.width() * destVisibleSubset.height();
- return (destVisibleSize > (destWidth * destHeight) / 4);
+ // If more than 1/4 of the resized image is requested, it's worth caching.
+ return fragmentSize > fullSize / 4;
}
NativeImageSkia::CachedImageInfo::CachedImageInfo()
{
- srcSubset.setEmpty();
+ scaledImageSize.setEmpty();
+ scaledImageSubset.setEmpty();
+}
+
+bool NativeImageSkia::CachedImageInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
+{
+ return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
+}
+
+void NativeImageSkia::CachedImageInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
+{
+ scaledImageSize = otherScaledImageSize;
+ scaledImageSubset = otherScaledImageSubset;
+}
+
+SkIRect NativeImageSkia::CachedImageInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
+{
+ if (!scaledImageSubset.contains(otherScaledImageSubset))
+ return SkIRect::MakeEmpty();
+ SkIRect subsetRect = otherScaledImageSubset;
+ subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
+ return subsetRect;
}
-bool NativeImageSkia::CachedImageInfo::isEqual(const SkIRect& otherSrcSubset, int width, int height) const
+void NativeImageSkia::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
{
- return srcSubset == otherSrcSubset
- && requestSize.width() == width
- && requestSize.height() == height;
+ MemoryClassInfo info(memoryObjectInfo, this);
+ info.addMember(m_image);
+ info.addMember(m_resizedImage);
}
-void NativeImageSkia::CachedImageInfo::set(const SkIRect& otherSrcSubset, int width, int height)
+void reportMemoryUsage(const NativeImageSkia* const& image, MemoryObjectInfo* memoryObjectInfo)
{
- srcSubset = otherSrcSubset;
- requestSize.setWidth(width);
- requestSize.setHeight(height);
+ image->reportMemoryUsage(memoryObjectInfo);
}
} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/skia/NativeImageSkia.h b/Source/WebCore/platform/graphics/skia/NativeImageSkia.h
index 2a52e68ee..8b795e2a5 100644
--- a/Source/WebCore/platform/graphics/skia/NativeImageSkia.h
+++ b/Source/WebCore/platform/graphics/skia/NativeImageSkia.h
@@ -33,7 +33,8 @@
#include "SkBitmap.h"
#include "SkRect.h"
-#include "IntSize.h"
+#include "SkSize.h"
+#include <wtf/Forward.h>
namespace WebCore {
@@ -70,39 +71,35 @@ public:
float resolutionScale() const { return m_resolutionScale; }
// We can keep a resized version of the bitmap cached on this object.
- // This function will return true if there is a cached version of the
- // given image subset with the given dimensions and subsets.
- bool hasResizedBitmap(const SkIRect& srcSubset, int width, int height) const;
+ // This function will return true if there is a cached version of the given
+ // scale and subset.
+ bool hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const;
// This will return an existing resized image subset, or generate a new one
- // of the specified size and subsets and possibly cache it.
- // srcSubset is the subset of the image to resize in image space.
- SkBitmap resizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight) const
- {
- SkIRect destVisibleSubset = {0, 0, destWidth, destHeight};
- return resizedBitmap(srcSubset, destWidth, destHeight, destVisibleSubset);
- }
-
- // Same as above, but returns a subset of the destination image (ie: the
- // visible subset). destVisibleSubset is the subset of the resized
- // (destWidth x destHeight) image.
- // In other words:
- // - crop image by srcSubset -> imageSubset.
- // - resize imageSubset to destWidth x destHeight -> destImage.
- // - return destImage cropped by destVisibleSubset.
- SkBitmap resizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight, const SkIRect& destVisibleSubset) const;
+ // of the specified size and subset and possibly cache it.
+ //
+ // scaledImageSize
+ // Dimensions of the scaled full image.
+ //
+ // scaledImageSubset
+ // Rectangle of the subset in the scaled image.
+ SkBitmap resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const;
+
+ void reportMemoryUsage(MemoryObjectInfo*) const;
private:
// CachedImageInfo is used to uniquely identify cached or requested image
// resizes.
+ // Image resize is identified by the scaled image size and scaled image subset.
struct CachedImageInfo {
- IntSize requestSize;
- SkIRect srcSubset;
+ SkISize scaledImageSize;
+ SkIRect scaledImageSubset;
CachedImageInfo();
- bool isEqual(const SkIRect& otherSrcSubset, int width, int height) const;
- void set(const SkIRect& otherSrcSubset, int width, int height);
+ bool isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const;
+ void set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset);
+ SkIRect rectInSubset(const SkIRect& otherScaledImageRect);
};
// Returns true if the given resize operation should either resize the whole
@@ -118,16 +115,14 @@ private:
// better if we're going to be using it more than once (like a bitmap
// scrolling on and off the screen. Since we only cache when doing the
// entire thing, it's best to just do it up front.
- bool shouldCacheResampling(const SkIRect& srcSubset,
- int destWidth,
- int destHeight,
- const SkIRect& destSubset) const;
+ bool shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const;
// The original image.
SkBitmap m_image;
float m_resolutionScale;
- // The cached bitmap. This will be empty() if there is no cached image.
+ // The cached bitmap fragment. This is a subset of the scaled version of
+ // |m_image|. empty() returns true if there is no cached image.
mutable SkBitmap m_resizedImage;
// References how many times that the image size has been requested for
@@ -141,12 +136,11 @@ private:
// resized image, we know that we should probably cache it, even if all of
// those requests individually are small and would not otherwise be cached.
//
- // We also track the source and destination subsets for caching partial
- // image resizes.
+ // We also track scaling information and destination subset for the scaled
+ // image. See comments for CachedImageInfo.
mutable CachedImageInfo m_cachedImageInfo;
mutable int m_resizeRequests;
};
}
#endif // NativeImageSkia_h
-
diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
index a508a6e29..5d6bf936c 100644
--- a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp
@@ -188,6 +188,7 @@ PlatformContextSkia::PlatformContextSkia(SkCanvas* canvas)
, m_accelerated(false)
, m_deferred(false)
, m_drawingToImageBuffer(false)
+ , m_deviceScaleFactor(1)
#if defined(SK_SUPPORT_HINTING_SCALE_FACTOR)
, m_hintingScaleFactor(SK_Scalar1)
#endif
diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h
index 8bc5f32e7..2369f3209 100644
--- a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h
+++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h
@@ -187,6 +187,9 @@ public:
bool isDeferred() const { return m_deferred; }
void setDeferred(bool deferred) { m_deferred = deferred; }
+ float deviceScaleFactor() const { return m_deviceScaleFactor; }
+ void setDeviceScaleFactor(float scale) { m_deviceScaleFactor = scale; }
+
void setTrackOpaqueRegion(bool track) { m_trackOpaqueRegion = track; }
// This will be an empty region unless tracking is enabled.
@@ -239,6 +242,7 @@ private:
bool m_accelerated;
bool m_deferred;
bool m_drawingToImageBuffer;
+ float m_deviceScaleFactor;
#if defined(SK_SUPPORT_HINTING_SCALE_FACTOR)
SkScalar m_hintingScaleFactor;
#endif
diff --git a/Source/WebCore/platform/graphics/skia/SimpleFontDataSkia.cpp b/Source/WebCore/platform/graphics/skia/SimpleFontDataSkia.cpp
index 54486c3aa..4d32c74db 100644
--- a/Source/WebCore/platform/graphics/skia/SimpleFontDataSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/SimpleFontDataSkia.cpp
@@ -42,6 +42,8 @@
#include "SkTypeface.h"
#include "SkTypes.h"
#include "VDMXParser.h"
+#include <unicode/normlzr.h>
+#include <wtf/unicode/Unicode.h>
namespace WebCore {
@@ -110,16 +112,18 @@ void SimpleFontData::platformInit()
m_fontMetrics.setDescent(descent);
float xHeight;
- if (metrics.fXHeight)
+ if (metrics.fXHeight) {
xHeight = metrics.fXHeight;
- else {
- // hack taken from the Windows port
- xHeight = ascent * 0.56f;
+ m_fontMetrics.setXHeight(xHeight);
+ } else {
+ xHeight = ascent * 0.56; // Best guess from Windows font metrics.
+ m_fontMetrics.setXHeight(xHeight);
+ m_fontMetrics.setHasXHeight(false);
}
+
float lineGap = SkScalarToFloat(metrics.fLeading);
m_fontMetrics.setLineGap(lineGap);
- m_fontMetrics.setXHeight(xHeight);
m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
if (platformData().orientation() == Vertical && !isTextOrientationFallback()) {
@@ -159,6 +163,9 @@ void SimpleFontData::platformInit()
}
}
}
+
+ if (int unitsPerEm = paint.getTypeface()->getUnitsPerEm())
+ m_fontMetrics.setUnitsPerEm(unitsPerEm);
}
void SimpleFontData::platformCharWidthInit()
@@ -170,30 +177,30 @@ void SimpleFontData::platformDestroy()
{
}
-PassOwnPtr<SimpleFontData> SimpleFontData::createScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
+PassRefPtr<SimpleFontData> SimpleFontData::createScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
{
const float scaledSize = lroundf(fontDescription.computedSize() * scaleFactor);
- return adoptPtr(new SimpleFontData(FontPlatformData(m_platformData, scaledSize), isCustomFont(), false));
+ return SimpleFontData::create(FontPlatformData(m_platformData, scaledSize), isCustomFont(), false);
}
-SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
+PassRefPtr<SimpleFontData> SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
{
if (!m_derivedFontData)
m_derivedFontData = DerivedFontData::create(isCustomFont());
if (!m_derivedFontData->smallCaps)
m_derivedFontData->smallCaps = createScaledFontData(fontDescription, smallCapsFraction);
- return m_derivedFontData->smallCaps.get();
+ return m_derivedFontData->smallCaps;
}
-SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const
+PassRefPtr<SimpleFontData> SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const
{
if (!m_derivedFontData)
m_derivedFontData = DerivedFontData::create(isCustomFont());
if (!m_derivedFontData->emphasisMark)
m_derivedFontData->emphasisMark = createScaledFontData(fontDescription, emphasisMarkFraction);
- return m_derivedFontData->emphasisMark.get();
+ return m_derivedFontData->emphasisMark;
}
bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
@@ -250,4 +257,32 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
return SkScalarToFloat(width);
}
+#if USE(HARFBUZZ_NG)
+bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
+{
+ if (!m_combiningCharacterSequenceSupport)
+ m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);
+
+ WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
+ if (!addResult.isNewEntry)
+ return addResult.iterator->value;
+
+ UErrorCode error = U_ZERO_ERROR;
+ Vector<UChar, 4> normalizedCharacters(length);
+ int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
+ // Can't render if we have an error or no composition occurred.
+ if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
+ return false;
+
+ SkPaint paint;
+ m_platformData.setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ if (paint.textToGlyphs(&normalizedCharacters[0], normalizedLength * 2, 0)) {
+ addResult.iterator->value = true;
+ return true;
+ }
+ return false;
+}
+#endif
+
} // namespace WebCore