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(); + }); + }); + }); + }); });