diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/skia')
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 |