Commit 0b8e2f2c authored by fs@opera.com's avatar fs@opera.com

Draw thin slices of an image w/ anti-aliasing for 2D <canvas>

Add a GraphicsContext-scoped flag that allows anti-aliasing of
(all) image-geometry to be enabled in a selective manner.
Enable this behavior for CanvasRenderingContext2D.
When this flag is set, image geometry that would drawn as very
thin (< 1 device pixel wide/high) will be anti-aliased.

BUG=352611

Review URL: https://codereview.chromium.org/210043004

git-svn-id: svn://svn.chromium.org/blink/trunk@170671 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 6ac9cab0
PASS verifyCovered(imgData.data, imgData.pixelOffset(10, 5), 1, 10) is true
PASS verifyCovered(imgData.data, imgData.pixelOffset(5, 5), imgData.width, 10) is true
PASS verifyCovered(imgData.data, imgData.pixelOffset(25, 15), 1, 10) is true
PASS verifyCovered(imgData.data, imgData.pixelOffset(20, 15), imgData.width, 10) is true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<title>Canvas.drawImage with narrow destination.</title>
<script src="../resources/js-test.js"></script>
<script>
function verifyCovered(imgData, offset, stride, length) {
while (length) {
if (imgData[offset + 3] === 0)
return false;
offset += stride * 4;
length--;
}
return true;
}
ImageData.prototype.pixelOffset = function(x, y) {
return (x + y * this.width) * 4;
}
var sourceImage = document.createElement("canvas");
sourceImage.height = sourceImage.width = 1;
sourceImage.getContext("2d").fillRect(0, 0, 1, 1);
var c = document.createElement("canvas");
c.width = c.height = 100;
var ctx = c.getContext("2d");
// Thin horizontal image.
ctx.drawImage(sourceImage, 10, 5.5, 10, 0.95);
// Thin vertical image.
ctx.drawImage(sourceImage, 5.5, 5, 0.95, 10);
// Rotated 90 degrees
ctx.save();
ctx.translate(20, 15);
ctx.rotate(Math.PI / 2);
ctx.drawImage(sourceImage, 0, -.5, 10, 0.95);
ctx.restore();
// Rotated -90 degrees
ctx.save();
ctx.translate(25, 15);
ctx.rotate(-Math.PI / 2);
ctx.drawImage(sourceImage, -.5, 0, 0.95, 10);
ctx.restore();
imgData = ctx.getImageData(0, 0, c.width, c.height);
shouldBeTrue("verifyCovered(imgData.data, imgData.pixelOffset(10, 5), 1, 10)");
shouldBeTrue("verifyCovered(imgData.data, imgData.pixelOffset(5, 5), imgData.width, 10)");
shouldBeTrue("verifyCovered(imgData.data, imgData.pixelOffset(25, 15), 1, 10)");
shouldBeTrue("verifyCovered(imgData.data, imgData.pixelOffset(20, 15), imgData.width, 10)");
successfullyParsed = true;
</script>
<!DOCTYPE html>
<html>
<body>
<script src="../resources/runner.js"></script>
<script>
var source = document.createElement("canvas");
source.width = 300;
source.height = 150;
source.getContext("2d").fillStyle = 'green';
source.getContext("2d").fillRect(0, 0, source.width, source.height);
var target = document.createElement("canvas");
target.width = source.width;
target.height = source.height;
var context = target.getContext("2d")
PerfTestRunner.measureRunsPerSecond({run: function() {
context.drawImage(source, 10.5, 10.5, 200, 200);
}});
</script>
</body>
</html>
......@@ -498,6 +498,7 @@ void HTMLCanvasElement::createImageBufferInternal()
m_imageBuffer->setClient(this);
m_imageBuffer->context()->setShouldClampToSourceRect(false);
m_imageBuffer->context()->disableAntialiasingOptimizationForHairlineImages();
m_imageBuffer->context()->setImageInterpolationQuality(CanvasDefaultInterpolationQuality);
// Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the
// rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated
......
......@@ -130,6 +130,7 @@ GraphicsContext::GraphicsContext(SkCanvas* canvas)
, m_accelerated(false)
, m_isCertainlyOpaque(true)
, m_printing(false)
, m_antialiasHairlineImages(false)
{
// FIXME: Do some tests to determine how many states are typically used, and allocate
// several here.
......
......@@ -136,6 +136,13 @@ public:
void setShouldAntialias(bool antialias) { mutableState()->setShouldAntialias(antialias); }
bool shouldAntialias() const { return immutableState()->shouldAntialias(); }
// Disable the anti-aliasing optimization for scales/multiple-of-90-degrees
// rotations of thin ("hairline") images.
// Note: This will only be reliable when the device pixel scale/ratio is
// fixed (e.g. when drawing to context backed by an ImageBuffer).
void disableAntialiasingOptimizationForHairlineImages() { ASSERT(!isRecording()); m_antialiasHairlineImages = true; }
bool shouldAntialiasHairlineImages() const { return m_antialiasHairlineImages; }
void setShouldClampToSourceRect(bool clampToSourceRect) { mutableState()->setShouldClampToSourceRect(clampToSourceRect); }
bool shouldClampToSourceRect() const { return immutableState()->shouldClampToSourceRect(); }
......@@ -504,6 +511,7 @@ private:
bool m_accelerated : 1;
bool m_isCertainlyOpaque : 1;
bool m_printing : 1;
bool m_antialiasHairlineImages : 1;
};
} // namespace WebCore
......
......@@ -332,9 +332,33 @@ SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const Sk
return resizedSubset;
}
static bool hasNon90rotation(GraphicsContext* context)
static bool shouldDrawAntiAliased(GraphicsContext* context, const SkRect& destRect)
{
return !context->getTotalMatrix().rectStaysRect();
if (!context->shouldAntialias())
return false;
const SkMatrix totalMatrix = context->getTotalMatrix();
// Don't disable anti-aliasing if we're rotated or skewed.
if (!totalMatrix.rectStaysRect())
return true;
// Disable anti-aliasing for scales or n*90 degree rotations.
// Allow to opt out of the optimization though for "hairline" geometry
// images - using the shouldAntialiasHairlineImages() GraphicsContext flag.
if (!context->shouldAntialiasHairlineImages())
return false;
// Check if the dimensions of the destination are "small" (less than one
// device pixel). To prevent sudden drop-outs. Since we know that
// kRectStaysRect_Mask is set, the matrix either has scale and no skew or
// vice versa. We can query the kAffine_Mask flag to determine which case
// it is.
// FIXME: This queries the CTM while drawing, which is generally
// discouraged. Always drawing with AA can negatively impact performance
// though - that's why it's not always on.
SkScalar widthExpansion, heightExpansion;
if (totalMatrix.getType() & SkMatrix::kAffine_Mask)
widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX];
else
widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY];
return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1;
}
void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, const SkRect& destRect, PassRefPtr<SkXfermode> compOp) const
......@@ -345,8 +369,7 @@ void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, cons
paint.setColorFilter(context->colorFilter());
paint.setAlpha(context->getNormalizedAlpha());
paint.setLooper(context->drawLooper());
// only antialias if we're rotated or skewed
paint.setAntiAlias(hasNon90rotation(context));
paint.setAntiAlias(shouldDrawAntiAliased(context, destRect));
ResamplingMode resampling;
if (context->isAccelerated()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment