diff --git a/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.js b/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.js
new file mode 100644
index 00000000..6acd2bdd
--- /dev/null
+++ b/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.js
@@ -0,0 +1,20 @@
+const smoothed = new WebImage('https://codehs.com/uploads/72e9b6f60ac412f32a2fd3a955990c3b');
+smoothed.imageSmoothingEnabled = true;
+const unsmoothed = new WebImage('https://codehs.com/uploads/72e9b6f60ac412f32a2fd3a955990c3b');
+unsmoothed.imageSmoothingEnabled = false;
+
+add(smoothed);
+add(unsmoothed);
+
+unsmoothed.setPosition(getWidth() / 2, 0);
+
+const smoothedLoaded = new Promise(resolve => smoothed.loaded(resolve));
+const unsmoothedLoaded = new Promise(resolve => unsmoothed.loaded(resolve));
+
+const allLoaded = Promise.all([smoothedLoaded, unsmoothedLoaded]).then(() => {
+ const imageWidth = smoothed.width;
+ const scale = getWidth() / 2 / imageWidth;
+ smoothed.setSize(scale * smoothed.width, scale * smoothed.height);
+ unsmoothed.setSize(scale * unsmoothed.width, scale * unsmoothed.height);
+ setSize(smoothed.width * 2, smoothed.height);
+});
diff --git a/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.md b/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.md
new file mode 100644
index 00000000..31d1ffa7
--- /dev/null
+++ b/site/examples/graphics/webimage/imagesmoothing/imagesmoothing.md
@@ -0,0 +1,13 @@
+---
+title: WebImage - imageSmoothing
+layout: example
+code: imagesmoothing.js
+width: 500
+height: 500
+---
+
+`imageSmoothingEnabled` controls how an image is drawn when scaled up.
+
+If `imageSmoothingEnabled` is true, pixels will be blurred.
+
+If `imageSmoothingEnabled` is false, clear edges of pixels will be preserved.
diff --git a/site/examples/index.html b/site/examples/index.html
index 7406206a..3abb622a 100644
--- a/site/examples/index.html
+++ b/site/examples/index.html
@@ -172,6 +172,9 @@
WebImage
Flashlight
+
+ Image Smoothing
+
Manipulating ImageData
diff --git a/src/graphics/webimage.js b/src/graphics/webimage.js
index 4270b2b5..1f0f6d99 100644
--- a/src/graphics/webimage.js
+++ b/src/graphics/webimage.js
@@ -44,6 +44,13 @@ class WebImage extends Thing {
* @type {boolean}
*/
this.imageLoaded = false;
+ /**
+ * Whether image smoothing should be applied when the image is scaled up.
+ * For more information, see {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled}
+ * @default false
+ * @type {boolean}
+ */
+ this.imageSmoothingEnabled = false;
}
/**
@@ -119,19 +126,12 @@ class WebImage extends Thing {
this.updateHiddenCanvas();
}
super.draw(context, () => {
+ const existingImageSmoothing = context.imageSmoothingEnabled;
context.beginPath();
- // the __hiddenCanvas contains the ImageData, sized as it originally was.
- // in order to perform scaling, the destination width and height are
- // currentWidth * (currentWidth / originalWidth),
- // meaning the current size times the amount the size has changed
- context.drawImage(
- this._hiddenCanvas,
- 0,
- 0,
- (this.width * this.width) / this.data.width,
- (this.height * this.height) / this.data.height
- );
+ context.imageSmoothingEnabled = this.imageSmoothingEnabled;
+ context.drawImage(this._hiddenCanvas, 0, 0, this.width, this.height);
context.closePath();
+ context.imageSmoothingEnabled = existingImageSmoothing;
});
}
@@ -370,8 +370,6 @@ class WebImage extends Thing {
* This is automatically called after operations that modify ImageData.
*/
updateHiddenCanvas() {
- this._hiddenCanvas.width = Math.max(this._hiddenCanvas.width, this.width);
- this._hiddenCanvas.height = Math.max(this._hiddenCanvas.height, this.height);
const context = this._hiddenCanvas.getContext('2d');
context.putImageData(this.data, 0, 0);
this._hiddenCanvasOutOfSync = false;
diff --git a/test/webimage.test.js b/test/webimage.test.js
index 35b0404a..cc79f4bd 100644
--- a/test/webimage.test.js
+++ b/test/webimage.test.js
@@ -246,4 +246,24 @@ describe('WebImage', () => {
});
});
});
+ describe('Resizing', () => {
+ it('Properly shrinks the WebImage', () => {
+ const img = new WebImage(RGBURL);
+ const g = new Graphics({ shouldUpdate: false });
+ g.add(img);
+ return new Promise(resolve => {
+ img.loaded(() => {
+ g.redraw();
+ // the img is 90x90
+ let bottomRightPixel = g.getPixel(89, 89);
+ expect(bottomRightPixel).toEqual([0, 0, 255, 255]);
+ img.setSize(50, 50);
+ g.redraw();
+ bottomRightPixel = g.getPixel(49, 49);
+ expect(bottomRightPixel).toEqual([0, 0, 255, 255]);
+ resolve();
+ });
+ });
+ });
+ });
});