Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: Source/platform/graphics/skia/NativeImageSkia.cpp

Issue 328453004: Replace ResamplingMode with InterpolationQuality. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: backpedal Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/platform/graphics/skia/NativeImageSkia.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2008, Google Inc. All rights reserved. 2 * Copyright (c) 2008, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 28 matching lines...) Expand all
39 #include "platform/graphics/GraphicsContext.h" 39 #include "platform/graphics/GraphicsContext.h"
40 #include "platform/graphics/Image.h" 40 #include "platform/graphics/Image.h"
41 #include "platform/graphics/DeferredImageDecoder.h" 41 #include "platform/graphics/DeferredImageDecoder.h"
42 #include "platform/graphics/skia/SkiaUtils.h" 42 #include "platform/graphics/skia/SkiaUtils.h"
43 #include "skia/ext/image_operations.h" 43 #include "skia/ext/image_operations.h"
44 #include "third_party/skia/include/core/SkMatrix.h" 44 #include "third_party/skia/include/core/SkMatrix.h"
45 #include "third_party/skia/include/core/SkPaint.h" 45 #include "third_party/skia/include/core/SkPaint.h"
46 #include "third_party/skia/include/core/SkScalar.h" 46 #include "third_party/skia/include/core/SkScalar.h"
47 #include "third_party/skia/include/core/SkShader.h" 47 #include "third_party/skia/include/core/SkShader.h"
48 48
49 #include <algorithm>
49 #include <math.h> 50 #include <math.h>
50 #include <limits> 51 #include <limits>
51 52
52 namespace WebCore { 53 namespace WebCore {
53 54
54 static bool nearlyIntegral(float value) 55 static bool nearlyIntegral(float value)
55 { 56 {
56 return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon(); 57 return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
57 } 58 }
58 59
59 ResamplingMode NativeImageSkia::computeResamplingMode(const SkMatrix& matrix, fl oat srcWidth, float srcHeight, float destWidth, float destHeight) const 60 InterpolationQuality NativeImageSkia::computeInterpolationQuality(const SkMatrix & matrix, float srcWidth, float srcHeight, float destWidth, float destHeight) co nst
60 { 61 {
61 // The percent change below which we will not resample. This usually means 62 // 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 // an off-by-one error on the web page, and just doing nearest neighbor
63 // sampling is usually good enough. 64 // sampling is usually good enough.
64 const float kFractionalChangeThreshold = 0.025f; 65 const float kFractionalChangeThreshold = 0.025f;
65 66
66 // Images smaller than this in either direction are considered "small" and 67 // Images smaller than this in either direction are considered "small" and
67 // are not resampled ever (see below). 68 // are not resampled ever (see below).
68 const int kSmallImageSizeThreshold = 8; 69 const int kSmallImageSizeThreshold = 8;
69 70
70 // The amount an image can be stretched in a single direction before we 71 // 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 // say that it is being stretched so much that it must be a line or
72 // background that doesn't need resampling. 73 // background that doesn't need resampling.
73 const float kLargeStretch = 3.0f; 74 const float kLargeStretch = 3.0f;
74 75
75 // Figure out if we should resample this image. We try to prune out some 76 // 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 // common cases where resampling won't give us anything, since it is much
77 // slower than drawing stretched. 78 // slower than drawing stretched.
78 float diffWidth = fabs(destWidth - srcWidth); 79 float diffWidth = fabs(destWidth - srcWidth);
79 float diffHeight = fabs(destHeight - srcHeight); 80 float diffHeight = fabs(destHeight - srcHeight);
80 bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon(); 81 bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
81 bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon(); 82 bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
82 // We don't need to resample if the source and destination are the same. 83 // We don't need to resample if the source and destination are the same.
83 if (widthNearlyEqual && heightNearlyEqual) 84 if (widthNearlyEqual && heightNearlyEqual)
84 return NoResampling; 85 return InterpolationNone;
85 86
86 if (srcWidth <= kSmallImageSizeThreshold 87 if (srcWidth <= kSmallImageSizeThreshold
87 || srcHeight <= kSmallImageSizeThreshold 88 || srcHeight <= kSmallImageSizeThreshold
88 || destWidth <= kSmallImageSizeThreshold 89 || destWidth <= kSmallImageSizeThreshold
89 || destHeight <= kSmallImageSizeThreshold) { 90 || destHeight <= kSmallImageSizeThreshold) {
90 // Small image detected. 91 // Small image detected.
91 92
92 // Resample in the case where the new size would be non-integral. 93 // Resample in the case where the new size would be non-integral.
93 // This can cause noticeable breaks in repeating patterns, except 94 // This can cause noticeable breaks in repeating patterns, except
94 // when the source image is only one pixel wide in that dimension. 95 // 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 if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<fl oat>::epsilon())
96 || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limi ts<float>::epsilon())) 97 || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limi ts<float>::epsilon()))
97 return LinearResampling; 98 return InterpolationLow;
98 99
99 // Otherwise, don't resample small images. These are often used for 100 // Otherwise, don't resample small images. These are often used for
100 // borders and rules (think 1x1 images used to make lines). 101 // borders and rules (think 1x1 images used to make lines).
101 return NoResampling; 102 return InterpolationNone;
102 } 103 }
103 104
104 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= d estWidth) { 105 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= d estWidth) {
105 // Large image detected. 106 // Large image detected.
106 107
107 // Don't resample if it is being stretched a lot in only one direction. 108 // 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 // 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 // (which might be large) and then is stretching it to fill some part
110 // of the page. 111 // of the page.
111 if (widthNearlyEqual || heightNearlyEqual) 112 if (widthNearlyEqual || heightNearlyEqual)
112 return NoResampling; 113 return InterpolationNone;
113 114
114 // The image is growing a lot and in more than one direction. Resampling 115 // 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 // is slow and doesn't give us very much when growing a lot.
116 return LinearResampling; 117 return InterpolationLow;
117 } 118 }
118 119
119 if ((diffWidth / srcWidth < kFractionalChangeThreshold) 120 if ((diffWidth / srcWidth < kFractionalChangeThreshold)
120 && (diffHeight / srcHeight < kFractionalChangeThreshold)) { 121 && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
121 // It is disappointingly common on the web for image sizes to be off by 122 // 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 // one or two pixels. We don't bother resampling if the size difference
123 // is a small fraction of the original size. 124 // is a small fraction of the original size.
124 return NoResampling; 125 return InterpolationNone;
125 } 126 }
126 127
127 // When the image is not yet done loading, use linear. We don't cache the 128 // 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 // partially resampled images, and as they come in incrementally, it causes
129 // us to have to resample the whole thing every time. 130 // us to have to resample the whole thing every time.
130 if (!isDataComplete()) 131 if (!isDataComplete())
131 return LinearResampling; 132 return InterpolationLow;
132 133
133 // Everything else gets resampled. 134 // Everything else gets resampled.
134 // High quality interpolation only enabled for scaling and translation. 135 // High quality interpolation only enabled for scaling and translation.
135 if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Ma sk))) 136 if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Ma sk)))
136 return AwesomeResampling; 137 return InterpolationHigh;
137 138
138 return LinearResampling; 139 return InterpolationLow;
139 } 140 }
140 141
141 static ResamplingMode limitResamplingMode(GraphicsContext* context, ResamplingMo de resampling) 142 static InterpolationQuality limitInterpolationQuality(GraphicsContext* context, InterpolationQuality resampling)
142 { 143 {
143 switch (context->imageInterpolationQuality()) { 144 return std::min(resampling, context->imageInterpolationQuality());
144 case InterpolationNone:
145 return NoResampling;
146 case InterpolationMedium:
147 if (resampling == AwesomeResampling)
148 return LinearWithMipmapsResampling;
149 break;
150 case InterpolationLow:
151 if (resampling == AwesomeResampling || resampling == LinearWithMipmapsRe sampling)
152 return LinearResampling;
153 break;
154 case InterpolationHigh:
155 break;
156 }
157
158 return resampling;
159 } 145 }
160 146
161 static SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, Resa mplingMode resampling) 147 static SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, Inte rpolationQuality resampling)
162 { 148 {
149 // FIXME: If we get rid of this special case, this function can go away enti rely.
163 if (useBicubicFilter) 150 if (useBicubicFilter)
164 return SkPaint::kHigh_FilterLevel; 151 return SkPaint::kHigh_FilterLevel;
165 152
166 switch (resampling) { 153 // InterpolationHigh if useBicubicFilter is false means that we do
167 case LinearWithMipmapsResampling:
168 return SkPaint::kMedium_FilterLevel;
169 case LinearResampling:
170 return SkPaint::kLow_FilterLevel;
171 // AwesomeResampling if useBicubicFilter is false means that we do
172 // a manual high quality resampling before drawing to Skia. 154 // a manual high quality resampling before drawing to Skia.
173 case AwesomeResampling: 155 if (resampling == InterpolationHigh)
174 default:
175 return SkPaint::kNone_FilterLevel; 156 return SkPaint::kNone_FilterLevel;
176 } 157
158 return static_cast<SkPaint::FilterLevel>(resampling);
177 } 159 }
178 160
179 // This function is used to scale an image and extract a scaled fragment. 161 // This function is used to scale an image and extract a scaled fragment.
180 // 162 //
181 // ALGORITHM 163 // ALGORITHM
182 // 164 //
183 // Because the scaled image size has to be integers, we approximate the real 165 // Because the scaled image size has to be integers, we approximate the real
184 // scale with the following formula (only X direction is shown): 166 // scale with the following formula (only X direction is shown):
185 // 167 //
186 // scaledImageWidth = round(scaleX * imageRect.width()) 168 // scaledImageWidth = round(scaleX * imageRect.width())
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 TRACE_EVENT0("skia", "NativeImageSkia::draw"); 352 TRACE_EVENT0("skia", "NativeImageSkia::draw");
371 SkPaint paint; 353 SkPaint paint;
372 paint.setXfermode(compOp.get()); 354 paint.setXfermode(compOp.get());
373 paint.setColorFilter(context->colorFilter()); 355 paint.setColorFilter(context->colorFilter());
374 paint.setAlpha(context->getNormalizedAlpha()); 356 paint.setAlpha(context->getNormalizedAlpha());
375 paint.setLooper(context->drawLooper()); 357 paint.setLooper(context->drawLooper());
376 paint.setAntiAlias(shouldDrawAntiAliased(context, destRect)); 358 paint.setAntiAlias(shouldDrawAntiAliased(context, destRect));
377 359
378 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); 360 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
379 361
380 ResamplingMode resampling; 362 InterpolationQuality resampling;
381 if (context->isAccelerated()) { 363 if (context->isAccelerated()) {
382 resampling = LinearResampling; 364 resampling = InterpolationLow;
383 } else if (context->printing()) { 365 } else if (context->printing()) {
384 resampling = NoResampling; 366 resampling = InterpolationNone;
385 } else if (isLazyDecoded) { 367 } else if (isLazyDecoded) {
386 resampling = AwesomeResampling; 368 resampling = InterpolationHigh;
387 } else { 369 } else {
388 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). 370 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
389 SkRect destRectTarget = destRect; 371 SkRect destRectTarget = destRect;
390 SkMatrix totalMatrix = context->getTotalMatrix(); 372 SkMatrix totalMatrix = context->getTotalMatrix();
391 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPersp ective_Mask))) 373 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPersp ective_Mask)))
392 totalMatrix.mapRect(&destRectTarget, destRect); 374 totalMatrix.mapRect(&destRectTarget, destRect);
393 375
394 resampling = computeResamplingMode(totalMatrix, 376 resampling = computeInterpolationQuality(totalMatrix,
395 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), 377 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
396 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTar get.height())); 378 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTar get.height()));
397 } 379 }
398 380
399 if (resampling == NoResampling) { 381 if (resampling == InterpolationNone) {
400 // FIXME: This is to not break tests (it results in the filter bitmap fl ag 382 // FIXME: This is to not break tests (it results in the filter bitmap fl ag
401 // being set to true). We need to decide if we respect NoResampling 383 // being set to true). We need to decide if we respect InterpolationNone
402 // being returned from computeResamplingMode. 384 // being returned from computeInterpolationQuality.
403 resampling = LinearResampling; 385 resampling = InterpolationLow;
404 } 386 }
405 resampling = limitResamplingMode(context, resampling); 387 resampling = limitInterpolationQuality(context, resampling);
406 388
407 // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images 389 // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images
408 // as an experiment. Once this filtering code path becomes stable we should 390 // as an experiment. Once this filtering code path becomes stable we should
409 // turn this on for all cases, including non-defer-decoded images. 391 // turn this on for all cases, including non-defer-decoded images.
410 bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; 392 bool useBicubicFilter = resampling == InterpolationHigh && isLazyDecoded;
411 393
412 paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling)) ; 394 paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling)) ;
413 395
414 if (resampling == AwesomeResampling && !useBicubicFilter) { 396 if (resampling == InterpolationHigh && !useBicubicFilter) {
415 // Resample the image and then draw the result to canvas with bilinear 397 // Resample the image and then draw the result to canvas with bilinear
416 // filtering. 398 // filtering.
417 drawResampledBitmap(context, paint, srcRect, destRect); 399 drawResampledBitmap(context, paint, srcRect, destRect);
418 } else { 400 } else {
419 // We want to filter it if we decided to do interpolation above, or if 401 // We want to filter it if we decided to do interpolation above, or if
420 // there is something interesting going on with the matrix (like a rotat ion). 402 // there is something interesting going on with the matrix (like a rotat ion).
421 // Note: for serialization, we will want to subset the bitmap first so w e 403 // Note: for serialization, we will want to subset the bitmap first so w e
422 // don't send extra pixels. 404 // don't send extra pixels.
423 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint); 405 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);
424 } 406 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 // matrix to see how big it will be. 450 // matrix to see how big it will be.
469 SkRect destRectTarget; 451 SkRect destRectTarget;
470 totalMatrix.mapRect(&destRectTarget, normSrcRect); 452 totalMatrix.mapRect(&destRectTarget, normSrcRect);
471 453
472 float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); 454 float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
473 float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); 455 float destBitmapHeight = SkScalarToFloat(destRectTarget.height());
474 456
475 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); 457 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
476 458
477 // Compute the resampling mode. 459 // Compute the resampling mode.
478 ResamplingMode resampling; 460 InterpolationQuality resampling;
479 if (context->isAccelerated() || context->printing()) 461 if (context->isAccelerated() || context->printing())
480 resampling = LinearResampling; 462 resampling = InterpolationLow;
481 else if (isLazyDecoded) 463 else if (isLazyDecoded)
482 resampling = AwesomeResampling; 464 resampling = InterpolationHigh;
483 else 465 else
484 resampling = computeResamplingMode(totalMatrix, normSrcRect.width(), nor mSrcRect.height(), destBitmapWidth, destBitmapHeight); 466 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width( ), normSrcRect.height(), destBitmapWidth, destBitmapHeight);
485 resampling = limitResamplingMode(context, resampling); 467 resampling = limitInterpolationQuality(context, resampling);
486 468
487 SkMatrix localMatrix; 469 SkMatrix localMatrix;
488 // We also need to translate it such that the origin of the pattern is the 470 // We also need to translate it such that the origin of the pattern is the
489 // origin of the destination rect, which is what WebKit expects. Skia uses 471 // origin of the destination rect, which is what WebKit expects. Skia uses
490 // the coordinate system origin as the base for the pattern. If WebKit wants 472 // the coordinate system origin as the base for the pattern. If WebKit wants
491 // a shifted image, it will shift it from there using the localMatrix. 473 // a shifted image, it will shift it from there using the localMatrix.
492 const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); 474 const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
493 const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); 475 const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
494 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY)); 476 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY));
495 477
496 RefPtr<SkShader> shader; 478 RefPtr<SkShader> shader;
497 479
498 // Bicubic filter is only applied to defer-decoded images, see 480 // Bicubic filter is only applied to defer-decoded images, see
499 // NativeImageSkia::draw for details. 481 // NativeImageSkia::draw for details.
500 bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; 482 bool useBicubicFilter = resampling == InterpolationHigh && isLazyDecoded;
501 483
502 if (resampling == AwesomeResampling && !useBicubicFilter) { 484 if (resampling == InterpolationHigh && !useBicubicFilter) {
503 // Do nice resampling. 485 // Do nice resampling.
504 float scaleX = destBitmapWidth / normSrcRect.width(); 486 float scaleX = destBitmapWidth / normSrcRect.width();
505 float scaleY = destBitmapHeight / normSrcRect.height(); 487 float scaleY = destBitmapHeight / normSrcRect.height();
506 SkRect scaledSrcRect; 488 SkRect scaledSrcRect;
507 489
508 // Since we are resizing the bitmap, we need to remove the scale 490 // Since we are resizing the bitmap, we need to remove the scale
509 // applied to the pixels in the bitmap shader. This means we need 491 // applied to the pixels in the bitmap shader. This means we need
510 // CTM * localMatrix to have identity scale. Since we 492 // CTM * localMatrix to have identity scale. Since we
511 // can't modify CTM (or the rectangle will be drawn in the wrong 493 // can't modify CTM (or the rectangle will be drawn in the wrong
512 // place), we must set localMatrix's scale to the inverse of 494 // place), we must set localMatrix's scale to the inverse of
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca ledImageSubset) 602 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca ledImageSubset)
621 { 603 {
622 if (!scaledImageSubset.contains(otherScaledImageSubset)) 604 if (!scaledImageSubset.contains(otherScaledImageSubset))
623 return SkIRect::MakeEmpty(); 605 return SkIRect::MakeEmpty();
624 SkIRect subsetRect = otherScaledImageSubset; 606 SkIRect subsetRect = otherScaledImageSubset;
625 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y()); 607 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
626 return subsetRect; 608 return subsetRect;
627 } 609 }
628 610
629 } // namespace WebCore 611 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/platform/graphics/skia/NativeImageSkia.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698