diff --git a/package-lock.json b/package-lock.json index 55a2b6e8..058ac623 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "chs-js-lib", - "version": "0.2.19", + "version": "0.2.20", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.2.19", + "version": "0.2.20", "license": "ISC", "dependencies": { "tone": "^14.7.77", diff --git a/package.json b/package.json index 2a1dd77f..de062a36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chs-js-lib", - "version": "0.2.19", + "version": "0.2.20", "description": "JavaScript graphics library used in CodeHS's platform.", "main": "dist/chs.cjs", "module": "dist/chs.mjs", diff --git a/site/examples/graphics/webimage/setimage/setimage.js b/site/examples/graphics/webimage/setimage/setimage.js new file mode 100644 index 00000000..f0fc37d8 --- /dev/null +++ b/site/examples/graphics/webimage/setimage/setimage.js @@ -0,0 +1,6 @@ +var img = new WebImage('https://codehs.com/uploads/056e56f7b32cdc9ed21c681c31166108'); +img.setAnchor({ vertical: 0.5, horizontal: 0.5 }); +img.setPosition(getWidth() / 2, getHeight() / 2); +add(img); + +img.setImage('https://codehs.com/uploads/1e38851d76e7691d923a3d3f3f7eae1d'); diff --git a/site/examples/graphics/webimage/setimage/setimage.md b/site/examples/graphics/webimage/setimage/setimage.md new file mode 100644 index 00000000..94fcdfef --- /dev/null +++ b/site/examples/graphics/webimage/setimage/setimage.md @@ -0,0 +1,9 @@ +--- +title: WebImage - setImage +layout: example +code: setimage.js +width: 500 +height: 500 +--- + +You can replace the image content of a `WebImage` using `setImage()` diff --git a/site/examples/index.html b/site/examples/index.html index 78298af4..02eb2be0 100644 --- a/site/examples/index.html +++ b/site/examples/index.html @@ -175,6 +175,9 @@

WebImage

  • Manipulating ImageData
  • +
  • + setImage +
  • Rotation
  • diff --git a/src/graphics/group.js b/src/graphics/group.js index 398eb196..f99a540f 100644 --- a/src/graphics/group.js +++ b/src/graphics/group.js @@ -23,7 +23,7 @@ class Group extends Thing { * @private * @type {number} */ - devicePixelRatio = window.devicePixelRatio ?? 1; + devicePixelRatio = Math.ceil(window.devicePixelRatio) ?? 1; /** * Constructs a new Group. diff --git a/src/graphics/index.js b/src/graphics/index.js index a6fe04ef..2476e108 100644 --- a/src/graphics/index.js +++ b/src/graphics/index.js @@ -34,7 +34,7 @@ class GraphicsManager extends Manager { * @private * @type {number} */ - devicePixelRatio = window.devicePixelRatio ?? 1; + devicePixelRatio = Math.ceil(window.devicePixelRatio) ?? 1; /** * Set up an instance of the graphics library. diff --git a/src/graphics/webimage.js b/src/graphics/webimage.js index deaa3508..2b4918a1 100644 --- a/src/graphics/webimage.js +++ b/src/graphics/webimage.js @@ -72,9 +72,14 @@ class WebImage extends Thing { this._hiddenCanvas = document.createElement('canvas'); this._hiddenCanvas.width = 1; this._hiddenCanvas.height = 1; - + if (this.image) { + // if this WebImage had an existing image, it may have an unresolved onload callback. + // dont allow original callback to resolve, since it might attempt to load pixel data + // from a potentially empty canvas. + this.image.onload = null; + } this.image = new Image(); - this.image.crossOrigin = true; + this.image.crossOrigin = 'anonymous'; this.image.src = filename; this.filename = filename; this.width = null; diff --git a/test/graphics.test.js b/test/graphics.test.js index 356ed6c1..76be801d 100644 --- a/test/graphics.test.js +++ b/test/graphics.test.js @@ -20,6 +20,14 @@ describe('Graphics', () => { expect(canvas.width).toEqual(20 * window.devicePixelRatio); expect(canvas.height).toEqual(20 * window.devicePixelRatio); }); + it('Rounds devicePixelRatio to prevent floating point sizes', () => { + window.devicePixelRatio = 0.89999; + const g = new Graphics({ shouldUpdate: false }); + g.setSize(20, 20); + const canvas = document.querySelector('canvas'); + expect(canvas.width).toEqual(20); + expect(canvas.height).toEqual(20); + }); }); describe('setFullscreen', () => { it("Updates the canvas' size to the parent's size less padding less border", () => { diff --git a/test/setup.js b/test/setup.js index 91c8b027..3da8dee2 100644 --- a/test/setup.js +++ b/test/setup.js @@ -14,6 +14,7 @@ beforeEach(() => { afterEach(() => { document.body.innerHTML = ''; + window.devicePixelRatio = 1; Object.entries(GraphicsInstances).forEach(([id, instance]) => { instance.cleanup(); }); diff --git a/test/webimage.test.js b/test/webimage.test.js index 8fd521e8..35b0404a 100644 --- a/test/webimage.test.js +++ b/test/webimage.test.js @@ -182,6 +182,32 @@ describe('WebImage', () => { expect(topLeftPixel).toEqual([0, 0, 255, 255]); }); }); + describe('setImage', () => { + it('Allows replacing the image of the WebImage', () => { + const g = new Graphics({ shouldUpdate: false }); + const img = new WebImage('www.codehs.com/doesnt-matter.gif'); + img.setImage(RGBURL); + g.add(img); + img.loaded(() => { + g.redraw(); + expect(g.getPixel(0, 0)).toEqual([255, 0, 0, 255]); + }); + }); + it('Cancels the original onload of the image', () => { + const g = new Graphics({ shouldUpdate: false }); + const img = new WebImage('www.codehs.com/doesnt-matter.gif'); + const firstLoadedSpy = jasmine.createSpy(); + // we have to inspect here to get the actual onload event + img.image.onload = firstLoadedSpy; + img.setImage(RGBURL); + g.add(img); + img.loaded(() => { + g.redraw(); + expect(g.getPixel(0, 0)).toEqual([255, 0, 0, 255]); + expect(firstLoadedSpy).not.toHaveBeenCalled(); + }); + }); + }); describe('setRed/Blue/Green/Alpha', () => { it('Updates the underlying data', () => { const img = new WebImage(RGBURL);