| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2008, Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 #include "config.h" | |
| 32 #include "core/platform/graphics/skia/NativeImageSkia.h" | |
| 33 | |
| 34 #include "core/platform/graphics/GraphicsContext.h" | |
| 35 #include "core/platform/graphics/Image.h" | |
| 36 #include "core/platform/graphics/DeferredImageDecoder.h" | |
| 37 #include "core/platform/graphics/skia/SkiaUtils.h" | |
| 38 #include "platform/PlatformInstrumentation.h" | |
| 39 #include "platform/TraceEvent.h" | |
| 40 #include "platform/geometry/FloatPoint.h" | |
| 41 #include "platform/geometry/FloatRect.h" | |
| 42 #include "platform/geometry/FloatSize.h" | |
| 43 #include "skia/ext/image_operations.h" | |
| 44 #include "third_party/skia/include/core/SkMatrix.h" | |
| 45 #include "third_party/skia/include/core/SkPaint.h" | |
| 46 #include "third_party/skia/include/core/SkScalar.h" | |
| 47 #include "third_party/skia/include/core/SkShader.h" | |
| 48 | |
| 49 #include <math.h> | |
| 50 #include <limits> | |
| 51 | |
| 52 namespace WebCore { | |
| 53 | |
| 54 static bool nearlyIntegral(float value) | |
| 55 { | |
| 56 return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon(); | |
| 57 } | |
| 58 | |
| 59 ResamplingMode NativeImageSkia::computeResamplingMode(const SkMatrix& matrix, fl
oat srcWidth, float srcHeight, float destWidth, float destHeight) const | |
| 60 { | |
| 61 // The percent change below which we will not resample. This usually means | |
| 62 // an off-by-one error on the web page, and just doing nearest neighbor | |
| 63 // sampling is usually good enough. | |
| 64 const float kFractionalChangeThreshold = 0.025f; | |
| 65 | |
| 66 // Images smaller than this in either direction are considered "small" and | |
| 67 // are not resampled ever (see below). | |
| 68 const int kSmallImageSizeThreshold = 8; | |
| 69 | |
| 70 // The amount an image can be stretched in a single direction before we | |
| 71 // say that it is being stretched so much that it must be a line or | |
| 72 // background that doesn't need resampling. | |
| 73 const float kLargeStretch = 3.0f; | |
| 74 | |
| 75 // Figure out if we should resample this image. We try to prune out some | |
| 76 // common cases where resampling won't give us anything, since it is much | |
| 77 // slower than drawing stretched. | |
| 78 float diffWidth = fabs(destWidth - srcWidth); | |
| 79 float diffHeight = fabs(destHeight - srcHeight); | |
| 80 bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon(); | |
| 81 bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon(); | |
| 82 // We don't need to resample if the source and destination are the same. | |
| 83 if (widthNearlyEqual && heightNearlyEqual) | |
| 84 return NoResampling; | |
| 85 | |
| 86 if (srcWidth <= kSmallImageSizeThreshold | |
| 87 || srcHeight <= kSmallImageSizeThreshold | |
| 88 || destWidth <= kSmallImageSizeThreshold | |
| 89 || destHeight <= kSmallImageSizeThreshold) { | |
| 90 // Small image detected. | |
| 91 | |
| 92 // Resample in the case where the new size would be non-integral. | |
| 93 // This can cause noticeable breaks in repeating patterns, except | |
| 94 // when the source image is only one pixel wide in that dimension. | |
| 95 if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<fl
oat>::epsilon()) | |
| 96 || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limi
ts<float>::epsilon())) | |
| 97 return LinearResampling; | |
| 98 | |
| 99 // Otherwise, don't resample small images. These are often used for | |
| 100 // borders and rules (think 1x1 images used to make lines). | |
| 101 return NoResampling; | |
| 102 } | |
| 103 | |
| 104 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= d
estWidth) { | |
| 105 // Large image detected. | |
| 106 | |
| 107 // Don't resample if it is being stretched a lot in only one direction. | |
| 108 // This is trying to catch cases where somebody has created a border | |
| 109 // (which might be large) and then is stretching it to fill some part | |
| 110 // of the page. | |
| 111 if (widthNearlyEqual || heightNearlyEqual) | |
| 112 return NoResampling; | |
| 113 | |
| 114 // The image is growing a lot and in more than one direction. Resampling | |
| 115 // is slow and doesn't give us very much when growing a lot. | |
| 116 return LinearResampling; | |
| 117 } | |
| 118 | |
| 119 if ((diffWidth / srcWidth < kFractionalChangeThreshold) | |
| 120 && (diffHeight / srcHeight < kFractionalChangeThreshold)) { | |
| 121 // It is disappointingly common on the web for image sizes to be off by | |
| 122 // one or two pixels. We don't bother resampling if the size difference | |
| 123 // is a small fraction of the original size. | |
| 124 return NoResampling; | |
| 125 } | |
| 126 | |
| 127 // When the image is not yet done loading, use linear. We don't cache the | |
| 128 // partially resampled images, and as they come in incrementally, it causes | |
| 129 // us to have to resample the whole thing every time. | |
| 130 if (!isDataComplete()) | |
| 131 return LinearResampling; | |
| 132 | |
| 133 // Everything else gets resampled. | |
| 134 // High quality interpolation only enabled for scaling and translation. | |
| 135 if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Ma
sk))) | |
| 136 return AwesomeResampling; | |
| 137 | |
| 138 return LinearResampling; | |
| 139 } | |
| 140 | |
| 141 static ResamplingMode limitResamplingMode(GraphicsContext* context, ResamplingMo
de resampling) | |
| 142 { | |
| 143 switch (context->imageInterpolationQuality()) { | |
| 144 case InterpolationNone: | |
| 145 return NoResampling; | |
| 146 case InterpolationMedium: | |
| 147 // For now we treat InterpolationMedium and InterpolationLow the same. | |
| 148 case InterpolationLow: | |
| 149 if (resampling == AwesomeResampling) | |
| 150 return LinearResampling; | |
| 151 break; | |
| 152 case InterpolationHigh: | |
| 153 case InterpolationDefault: | |
| 154 break; | |
| 155 } | |
| 156 | |
| 157 return resampling; | |
| 158 } | |
| 159 | |
| 160 // This function is used to scale an image and extract a scaled fragment. | |
| 161 // | |
| 162 // ALGORITHM | |
| 163 // | |
| 164 // Because the scaled image size has to be integers, we approximate the real | |
| 165 // scale with the following formula (only X direction is shown): | |
| 166 // | |
| 167 // scaledImageWidth = round(scaleX * imageRect.width()) | |
| 168 // approximateScaleX = scaledImageWidth / imageRect.width() | |
| 169 // | |
| 170 // With this method we maintain a constant scale factor among fragments in | |
| 171 // the scaled image. This allows fragments to stitch together to form the | |
| 172 // full scaled image. The downside is there will be a small difference | |
| 173 // between |scaleX| and |approximateScaleX|. | |
| 174 // | |
| 175 // A scaled image fragment is identified by: | |
| 176 // | |
| 177 // - Scaled image size | |
| 178 // - Scaled image fragment rectangle (IntRect) | |
| 179 // | |
| 180 // Scaled image size has been determined and the next step is to compute the | |
| 181 // rectangle for the scaled image fragment which needs to be an IntRect. | |
| 182 // | |
| 183 // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY) | |
| 184 // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect) | |
| 185 // | |
| 186 // Finally we extract the scaled image fragment using | |
| 187 // (scaledImageSize, enclosingScaledSrcRect). | |
| 188 // | |
| 189 SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, floa
t scaleX, float scaleY, SkRect* scaledSrcRect) const | |
| 190 { | |
| 191 SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height()); | |
| 192 SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.widt
h() * scaleX)), | |
| 193 clampToInteger(roundf(imageSize.height() * scaleY))); | |
| 194 | |
| 195 SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height()); | |
| 196 SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImage
Size.height()); | |
| 197 | |
| 198 SkMatrix scaleTransform; | |
| 199 scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_Sca
leToFit); | |
| 200 scaleTransform.mapRect(scaledSrcRect, srcRect); | |
| 201 | |
| 202 scaledSrcRect->intersect(scaledImageRect); | |
| 203 SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); | |
| 204 | |
| 205 // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because | |
| 206 // of float inaccuracy so clip to get inside. | |
| 207 enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize)); | |
| 208 | |
| 209 // scaledSrcRect is relative to the pixel snapped fragment we're extracting. | |
| 210 scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y
()); | |
| 211 | |
| 212 return resizedBitmap(scaledImageSize, enclosingScaledSrcRect); | |
| 213 } | |
| 214 | |
| 215 // This does a lot of computation to resample only the portion of the bitmap | |
| 216 // that will only be drawn. This is critical for performance since when we are | |
| 217 // scrolling, for example, we are only drawing a small strip of the image. | |
| 218 // Resampling the whole image every time is very slow, so this speeds up things | |
| 219 // dramatically. | |
| 220 // | |
| 221 // Note: this code is only used when the canvas transformation is limited to | |
| 222 // scaling or translation. | |
| 223 void NativeImageSkia::drawResampledBitmap(GraphicsContext* context, SkPaint& pai
nt, const SkRect& srcRect, const SkRect& destRect) const | |
| 224 { | |
| 225 TRACE_EVENT0("skia", "drawResampledBitmap"); | |
| 226 // We want to scale |destRect| with transformation in the canvas to obtain | |
| 227 // the final scale. The final scale is a combination of scale transform | |
| 228 // in canvas and explicit scaling (srcRect and destRect). | |
| 229 SkRect screenRect; | |
| 230 context->getTotalMatrix().mapRect(&screenRect, destRect); | |
| 231 float realScaleX = screenRect.width() / srcRect.width(); | |
| 232 float realScaleY = screenRect.height() / srcRect.height(); | |
| 233 | |
| 234 // This part of code limits scaling only to visible portion in the | |
| 235 SkRect destRectVisibleSubset; | |
| 236 ClipRectToCanvas(context, destRect, &destRectVisibleSubset); | |
| 237 | |
| 238 // ClipRectToCanvas often overshoots, resulting in a larger region than our | |
| 239 // original destRect. Intersecting gets us back inside. | |
| 240 if (!destRectVisibleSubset.intersect(destRect)) | |
| 241 return; // Nothing visible in destRect. | |
| 242 | |
| 243 // Find the corresponding rect in the source image. | |
| 244 SkMatrix destToSrcTransform; | |
| 245 SkRect srcRectVisibleSubset; | |
| 246 destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToF
it); | |
| 247 destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset); | |
| 248 | |
| 249 SkRect scaledSrcRect; | |
| 250 SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubs
et, realScaleX, realScaleY, &scaledSrcRect); | |
| 251 | |
| 252 context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisible
Subset, &paint); | |
| 253 } | |
| 254 | |
| 255 NativeImageSkia::NativeImageSkia() | |
| 256 : m_resolutionScale(1) | |
| 257 , m_resizeRequests(0) | |
| 258 { | |
| 259 } | |
| 260 | |
| 261 NativeImageSkia::NativeImageSkia(const SkBitmap& other, float resolutionScale) | |
| 262 : m_image(other) | |
| 263 , m_resolutionScale(resolutionScale) | |
| 264 , m_resizeRequests(0) | |
| 265 { | |
| 266 } | |
| 267 | |
| 268 NativeImageSkia::NativeImageSkia(const SkBitmap& image, float resolutionScale, c
onst SkBitmap& resizedImage, const ImageResourceInfo& cachedImageInfo, int resiz
eRequests) | |
| 269 : m_image(image) | |
| 270 , m_resolutionScale(resolutionScale) | |
| 271 , m_resizedImage(resizedImage) | |
| 272 , m_cachedImageInfo(cachedImageInfo) | |
| 273 , m_resizeRequests(resizeRequests) | |
| 274 { | |
| 275 } | |
| 276 | |
| 277 NativeImageSkia::~NativeImageSkia() | |
| 278 { | |
| 279 } | |
| 280 | |
| 281 int NativeImageSkia::decodedSize() const | |
| 282 { | |
| 283 return m_image.getSize() + m_resizedImage.getSize(); | |
| 284 } | |
| 285 | |
| 286 bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkI
Rect& scaledImageSubset) const | |
| 287 { | |
| 288 bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize; | |
| 289 bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contai
ns(scaledImageSubset); | |
| 290 return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empt
y(); | |
| 291 } | |
| 292 | |
| 293 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const Sk
IRect& scaledImageSubset) const | |
| 294 { | |
| 295 ASSERT(!DeferredImageDecoder::isLazyDecoded(m_image)); | |
| 296 | |
| 297 if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) { | |
| 298 bool shouldCache = isDataComplete() | |
| 299 && shouldCacheResampling(scaledImageSize, scaledImageSubset); | |
| 300 | |
| 301 PlatformInstrumentation::willResizeImage(shouldCache); | |
| 302 SkBitmap resizedImage = skia::ImageOperations::Resize(m_image, skia::Ima
geOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height()
, scaledImageSubset); | |
| 303 resizedImage.setImmutable(); | |
| 304 PlatformInstrumentation::didResizeImage(); | |
| 305 | |
| 306 if (!shouldCache) | |
| 307 return resizedImage; | |
| 308 | |
| 309 m_resizedImage = resizedImage; | |
| 310 } | |
| 311 | |
| 312 SkBitmap resizedSubset; | |
| 313 SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset
); | |
| 314 m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect); | |
| 315 return resizedSubset; | |
| 316 } | |
| 317 | |
| 318 static bool hasNon90rotation(GraphicsContext* context) | |
| 319 { | |
| 320 return !context->getTotalMatrix().rectStaysRect(); | |
| 321 } | |
| 322 | |
| 323 void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, cons
t SkRect& destRect, PassRefPtr<SkXfermode> compOp) const | |
| 324 { | |
| 325 TRACE_EVENT0("skia", "NativeImageSkia::draw"); | |
| 326 SkPaint paint; | |
| 327 paint.setXfermode(compOp.get()); | |
| 328 paint.setColorFilter(context->colorFilter()); | |
| 329 paint.setAlpha(context->getNormalizedAlpha()); | |
| 330 paint.setLooper(context->drawLooper()); | |
| 331 // only antialias if we're rotated or skewed | |
| 332 paint.setAntiAlias(hasNon90rotation(context)); | |
| 333 | |
| 334 ResamplingMode resampling; | |
| 335 if (context->isAccelerated()) { | |
| 336 resampling = LinearResampling; | |
| 337 } else if (context->printing()) { | |
| 338 resampling = NoResampling; | |
| 339 } else { | |
| 340 // Take into account scale applied to the canvas when computing sampling
mode (e.g. CSS scale or page scale). | |
| 341 SkRect destRectTarget = destRect; | |
| 342 SkMatrix totalMatrix = context->getTotalMatrix(); | |
| 343 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPersp
ective_Mask))) | |
| 344 totalMatrix.mapRect(&destRectTarget, destRect); | |
| 345 | |
| 346 resampling = computeResamplingMode(totalMatrix, | |
| 347 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), | |
| 348 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTar
get.height())); | |
| 349 } | |
| 350 | |
| 351 if (resampling == NoResampling) { | |
| 352 // FIXME: This is to not break tests (it results in the filter bitmap fl
ag | |
| 353 // being set to true). We need to decide if we respect NoResampling | |
| 354 // being returned from computeResamplingMode. | |
| 355 resampling = LinearResampling; | |
| 356 } | |
| 357 resampling = limitResamplingMode(context, resampling); | |
| 358 paint.setFilterBitmap(resampling == LinearResampling); | |
| 359 | |
| 360 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); | |
| 361 // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images | |
| 362 // as an experiment. Once this filtering code path becomes stable we should | |
| 363 // turn this on for all cases, including non-defer-decoded images. | |
| 364 bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; | |
| 365 | |
| 366 if (useBicubicFilter) | |
| 367 paint.setFilterLevel(SkPaint::kHigh_FilterLevel); | |
| 368 | |
| 369 if (resampling == AwesomeResampling && !useBicubicFilter) { | |
| 370 // Resample the image and then draw the result to canvas with bilinear | |
| 371 // filtering. | |
| 372 drawResampledBitmap(context, paint, srcRect, destRect); | |
| 373 } else { | |
| 374 // We want to filter it if we decided to do interpolation above, or if | |
| 375 // there is something interesting going on with the matrix (like a rotat
ion). | |
| 376 // Note: for serialization, we will want to subset the bitmap first so w
e | |
| 377 // don't send extra pixels. | |
| 378 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint); | |
| 379 } | |
| 380 if (isLazyDecoded) | |
| 381 PlatformInstrumentation::didDrawLazyPixelRef(reinterpret_cast<unsigned l
ong long>(bitmap().pixelRef())); | |
| 382 context->didDrawRect(destRect, paint, &bitmap()); | |
| 383 } | |
| 384 | |
| 385 static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, in
t spaceHeight) | |
| 386 { | |
| 387 SkBitmap result; | |
| 388 result.setConfig(bitmap.config(), | |
| 389 bitmap.width() + spaceWidth, | |
| 390 bitmap.height() + spaceHeight); | |
| 391 result.allocPixels(); | |
| 392 | |
| 393 result.eraseColor(SK_ColorTRANSPARENT); | |
| 394 bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.r
owBytes() * result.height(), result.rowBytes()); | |
| 395 | |
| 396 return result; | |
| 397 } | |
| 398 | |
| 399 void NativeImageSkia::drawPattern( | |
| 400 GraphicsContext* context, | |
| 401 const FloatRect& floatSrcRect, | |
| 402 const FloatSize& scale, | |
| 403 const FloatPoint& phase, | |
| 404 CompositeOperator compositeOp, | |
| 405 const FloatRect& destRect, | |
| 406 blink::WebBlendMode blendMode, | |
| 407 const IntSize& repeatSpacing) const | |
| 408 { | |
| 409 FloatRect normSrcRect = floatSrcRect; | |
| 410 normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height())); | |
| 411 if (destRect.isEmpty() || normSrcRect.isEmpty()) | |
| 412 return; // nothing to draw | |
| 413 | |
| 414 SkMatrix totalMatrix = context->getTotalMatrix(); | |
| 415 SkScalar ctmScaleX = totalMatrix.getScaleX(); | |
| 416 SkScalar ctmScaleY = totalMatrix.getScaleY(); | |
| 417 totalMatrix.preScale(scale.width(), scale.height()); | |
| 418 | |
| 419 // Figure out what size the bitmap will be in the destination. The | |
| 420 // destination rect is the bounds of the pattern, we need to use the | |
| 421 // matrix to see how big it will be. | |
| 422 SkRect destRectTarget; | |
| 423 totalMatrix.mapRect(&destRectTarget, normSrcRect); | |
| 424 | |
| 425 float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); | |
| 426 float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); | |
| 427 | |
| 428 // Compute the resampling mode. | |
| 429 ResamplingMode resampling; | |
| 430 if (context->isAccelerated() || context->printing()) | |
| 431 resampling = LinearResampling; | |
| 432 else | |
| 433 resampling = computeResamplingMode(totalMatrix, normSrcRect.width(), nor
mSrcRect.height(), destBitmapWidth, destBitmapHeight); | |
| 434 resampling = limitResamplingMode(context, resampling); | |
| 435 | |
| 436 SkMatrix shaderTransform; | |
| 437 RefPtr<SkShader> shader; | |
| 438 | |
| 439 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); | |
| 440 // Bicubic filter is only applied to defer-decoded images, see | |
| 441 // NativeImageSkia::draw for details. | |
| 442 bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; | |
| 443 | |
| 444 if (resampling == AwesomeResampling && !useBicubicFilter) { | |
| 445 // Do nice resampling. | |
| 446 float scaleX = destBitmapWidth / normSrcRect.width(); | |
| 447 float scaleY = destBitmapHeight / normSrcRect.height(); | |
| 448 SkRect scaledSrcRect; | |
| 449 | |
| 450 // The image fragment generated here is not exactly what is | |
| 451 // requested. The scale factor used is approximated and image | |
| 452 // fragment is slightly larger to align to integer | |
| 453 // boundaries. | |
| 454 SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, sca
leY, &scaledSrcRect); | |
| 455 if (repeatSpacing.isZero()) { | |
| 456 shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::
kRepeat_TileMode, SkShader::kRepeat_TileMode)); | |
| 457 } else { | |
| 458 shader = adoptRef(SkShader::CreateBitmapShader( | |
| 459 createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScal
eX, repeatSpacing.height() * ctmScaleY), | |
| 460 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); | |
| 461 } | |
| 462 | |
| 463 // Since we just resized the bitmap, we need to remove the scale | |
| 464 // applied to the pixels in the bitmap shader. This means we need | |
| 465 // CTM * shaderTransform to have identity scale. Since we | |
| 466 // can't modify CTM (or the rectangle will be drawn in the wrong | |
| 467 // place), we must set shaderTransform's scale to the inverse of | |
| 468 // CTM scale. | |
| 469 shaderTransform.setScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 /
ctmScaleY : 1); | |
| 470 } else { | |
| 471 // No need to resample before drawing. | |
| 472 SkBitmap srcSubset; | |
| 473 bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); | |
| 474 if (repeatSpacing.isZero()) { | |
| 475 shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::
kRepeat_TileMode, SkShader::kRepeat_TileMode)); | |
| 476 } else { | |
| 477 shader = adoptRef(SkShader::CreateBitmapShader( | |
| 478 createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScal
eX, repeatSpacing.height() * ctmScaleY), | |
| 479 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); | |
| 480 } | |
| 481 | |
| 482 // Because no resizing occurred, the shader transform should be | |
| 483 // set to the pattern's transform, which just includes scale. | |
| 484 shaderTransform.setScale(scale.width(), scale.height()); | |
| 485 } | |
| 486 | |
| 487 // We also need to translate it such that the origin of the pattern is the | |
| 488 // origin of the destination rect, which is what WebKit expects. Skia uses | |
| 489 // the coordinate system origin as the base for the pattern. If WebKit wants | |
| 490 // a shifted image, it will shift it from there using the shaderTransform. | |
| 491 float adjustedX = phase.x() + normSrcRect.x() * scale.width(); | |
| 492 float adjustedY = phase.y() + normSrcRect.y() * scale.height(); | |
| 493 shaderTransform.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(ad
justedY)); | |
| 494 shader->setLocalMatrix(shaderTransform); | |
| 495 | |
| 496 SkPaint paint; | |
| 497 paint.setShader(shader.get()); | |
| 498 paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).ge
t()); | |
| 499 paint.setColorFilter(context->colorFilter()); | |
| 500 | |
| 501 paint.setFilterBitmap(resampling == LinearResampling); | |
| 502 if (useBicubicFilter) | |
| 503 paint.setFilterLevel(SkPaint::kHigh_FilterLevel); | |
| 504 if (isLazyDecoded) | |
| 505 PlatformInstrumentation::didDrawLazyPixelRef(reinterpret_cast<unsigned l
ong long>(bitmap().pixelRef())); | |
| 506 | |
| 507 context->drawRect(destRect, paint); | |
| 508 } | |
| 509 | |
| 510 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, cons
t SkIRect& scaledImageSubset) const | |
| 511 { | |
| 512 // Check whether the requested dimensions match previous request. | |
| 513 bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, sca
ledImageSubset); | |
| 514 if (matchesPreviousRequest) | |
| 515 ++m_resizeRequests; | |
| 516 else { | |
| 517 m_cachedImageInfo.set(scaledImageSize, scaledImageSubset); | |
| 518 m_resizeRequests = 0; | |
| 519 // Reset m_resizedImage now, because we don't distinguish | |
| 520 // between the last requested resize info and m_resizedImage's | |
| 521 // resize info. | |
| 522 m_resizedImage.reset(); | |
| 523 } | |
| 524 | |
| 525 // We can not cache incomplete frames. This might be a good optimization in | |
| 526 // the future, were we know how much of the frame has been decoded, so when | |
| 527 // we incrementally draw more of the image, we only have to resample the | |
| 528 // parts that are changed. | |
| 529 if (!isDataComplete()) | |
| 530 return false; | |
| 531 | |
| 532 // If the destination bitmap is excessively large, we'll never allow caching
. | |
| 533 static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL; | |
| 534 unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSiz
e.width()) * static_cast<unsigned long long>(scaledImageSize.height()); | |
| 535 unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImag
eSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height()); | |
| 536 | |
| 537 if (fragmentSize > kLargeBitmapSize) | |
| 538 return false; | |
| 539 | |
| 540 // If the destination bitmap is small, we'll always allow caching, since | |
| 541 // there is not very much penalty for computing it and it may come in handy. | |
| 542 static const unsigned kSmallBitmapSize = 4096; | |
| 543 if (fragmentSize <= kSmallBitmapSize) | |
| 544 return true; | |
| 545 | |
| 546 // If "too many" requests have been made for this bitmap, we assume that | |
| 547 // many more will be made as well, and we'll go ahead and cache it. | |
| 548 static const int kManyRequestThreshold = 4; | |
| 549 if (m_resizeRequests >= kManyRequestThreshold) | |
| 550 return true; | |
| 551 | |
| 552 // If more than 1/4 of the resized image is requested, it's worth caching. | |
| 553 return fragmentSize > fullSize / 4; | |
| 554 } | |
| 555 | |
| 556 NativeImageSkia::ImageResourceInfo::ImageResourceInfo() | |
| 557 { | |
| 558 scaledImageSize.setEmpty(); | |
| 559 scaledImageSubset.setEmpty(); | |
| 560 } | |
| 561 | |
| 562 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImage
Size, const SkIRect& otherScaledImageSubset) const | |
| 563 { | |
| 564 return scaledImageSize == otherScaledImageSize && scaledImageSubset == other
ScaledImageSubset; | |
| 565 } | |
| 566 | |
| 567 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize
, const SkIRect& otherScaledImageSubset) | |
| 568 { | |
| 569 scaledImageSize = otherScaledImageSize; | |
| 570 scaledImageSubset = otherScaledImageSubset; | |
| 571 } | |
| 572 | |
| 573 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca
ledImageSubset) | |
| 574 { | |
| 575 if (!scaledImageSubset.contains(otherScaledImageSubset)) | |
| 576 return SkIRect::MakeEmpty(); | |
| 577 SkIRect subsetRect = otherScaledImageSubset; | |
| 578 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y()); | |
| 579 return subsetRect; | |
| 580 } | |
| 581 | |
| 582 } // namespace WebCore | |
| OLD | NEW |