From cb01de3fa26e4f9d22e47162dc4dd8ad090bef28 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 9 Apr 2022 14:41:23 -0500 Subject: [PATCH] feat: Add pixelRatio override for Text rendering Closes #2291 --- CHANGELOG.md | 2 +- sandbox/src/game.ts | 2 +- src/engine/Engine.ts | 14 +++++++++++++- src/engine/Graphics/Font.ts | 8 ++++++++ src/engine/Screen.ts | 4 ++-- src/spec/EngineSpec.ts | 8 ++++++++ src/spec/ScreenSpec.ts | 4 ++-- 7 files changed, 35 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d76afacc2..473c171a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - +- `ex.Engine` now support setting the pixel ratio in the constructor `new ex.Engine({pixelRatio: 2})`, this is useful for smooth `ex.Text` rendering when `antialiasing: false` and rendering pixel art type graphics - `ex.TileMap` now supports per Tile custom colliders! ```typescript const tileMap = new ex.TileMap(...); diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index c0c64f487..074d65bbf 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -46,7 +46,7 @@ var game = new ex.Engine({ height: 600 / 2, viewport: { width: 800, height: 600 }, canvasElementId: 'game', - suppressHiDPIScaling: false, + pixelRatio: 4, suppressPlayButton: true, pointerScope: ex.Input.PointerScope.Canvas, displayMode: ex.DisplayMode.FitScreen, diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index d786863cb..ca1500f1c 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -101,6 +101,17 @@ export interface EngineOptions { */ antialiasing?: boolean; + /** + * Optionally upscale the number of pixels in the canvas. Normally only useful if you need a smoother look to your assets, especially + * [[Text]]. + * + * **WARNING** It is recommended you try using `antialiasing: true` before adjusting pixel ratio. Pixel ratio will consume more memory + * and on mobile may break if the internal size of the canvas exceeds 4k pixels in width or height. + * + * Default is based the display's pixel ratio, for example a HiDPI screen might have the value 2; + */ + pixelRatio?: number; + /** * Optionally configure the native canvas transparent backdrop */ @@ -657,9 +668,10 @@ O|===|* >________________>\n\ resolution: options.resolution, displayMode, position: options.position, - pixelRatio: options.suppressHiDPIScaling ? 1 : null + pixelRatio: options.suppressHiDPIScaling ? 1 : (options.pixelRatio ?? null) }); + // Set default filtering based on antialiasing TextureLoader.filtering = options.antialiasing ? ImageFiltering.Blended : ImageFiltering.Pixel; if (options.backgroundColor) { diff --git a/src/engine/Graphics/Font.ts b/src/engine/Graphics/Font.ts index d0739273f..990cca8e2 100644 --- a/src/engine/Graphics/Font.ts +++ b/src/engine/Graphics/Font.ts @@ -277,14 +277,21 @@ export class Font extends Graphic implements FontRenderer { this.clearCache(); } this.checkAndClearCache(); + // Get bitmap for rastering text, this is cached by raster properties const bitmap = this._getTextBitmap(text, colorOverride); const isNewBitmap = !this._bitmapUsage.get(bitmap); + + // Bounds of the text this._textBounds = this.measureText(text); + // TODO if the text is bigger than 4k we need to split it somehow + if (isNewBitmap) { + // Setting dimension is expensive because it invalidates the bitmap this._setDimension(this._textBounds, bitmap); } + // Apply affine transformations this._preDraw(ex, x, y); const lines = text.split('\n'); @@ -301,6 +308,7 @@ export class Font extends Graphic implements FontRenderer { TextureLoader.load(bitmap.canvas, this.filtering, true); } + // Send draw cal to the ex graphics context ex.drawImage( bitmap.canvas, 0, diff --git a/src/engine/Screen.ts b/src/engine/Screen.ts index 489cad257..47ff511bd 100644 --- a/src/engine/Screen.ts +++ b/src/engine/Screen.ts @@ -384,8 +384,8 @@ export class Screen { if (!supported && !this._alreadyWarned) { this._alreadyWarned = true; // warn once this._logger.warn( - `The currently configured resolution (${this.resolution.width}x${this.resolution.height})` + - ' is too large for the platform WebGL implementation, this may work but cause WebGL rendering to behave oddly.' + + `The currently configured resolution (${this.resolution.width}x${this.resolution.height}) and pixel ratio (${this.pixelRatio})` + + ' are too large for the platform WebGL implementation, this may work but cause WebGL rendering to behave oddly.' + ' Try reducing the resolution or disabling Hi DPI scaling to avoid this' + ' (read more here https://excaliburjs.com/docs/screens#understanding-viewport--resolution).'); } diff --git a/src/spec/EngineSpec.ts b/src/spec/EngineSpec.ts index cb8774919..8b2ea1eb6 100644 --- a/src/spec/EngineSpec.ts +++ b/src/spec/EngineSpec.ts @@ -153,6 +153,14 @@ describe('The engine', () => { expect(engine.graphicsContext.snapToPixel).toBeTrue(); }); + it('can set pixelRatio', () => { + engine = TestUtils.engine({width: 100, height: 100, pixelRatio: 5, suppressHiDPIScaling: false}); + expect(engine.pixelRatio).toBe(5); + expect(engine.screen.pixelRatio).toBe(5); + expect(engine.screen.scaledWidth).toBe(500); + expect(engine.screen.scaledHeight).toBe(500); + }); + it('should emit a preframe event', () => { const fired = jasmine.createSpy('fired'); engine.on('preframe', fired); diff --git a/src/spec/ScreenSpec.ts b/src/spec/ScreenSpec.ts index 2bab5c913..27ff8a2ec 100644 --- a/src/spec/ScreenSpec.ts +++ b/src/spec/ScreenSpec.ts @@ -610,8 +610,8 @@ describe('A Screen', () => { sut.applyResolutionAndViewport(); expect(context.checkIfResolutionSupported).toHaveBeenCalled(); expect(logger.warn).toHaveBeenCalledOnceWith( - `The currently configured resolution (${sut.resolution.width}x${sut.resolution.height})` + - ' is too large for the platform WebGL implementation, this may work but cause WebGL rendering to behave oddly.' + + `The currently configured resolution (${sut.resolution.width}x${sut.resolution.height}) and pixel ratio (${sut.pixelRatio})` + + ' are too large for the platform WebGL implementation, this may work but cause WebGL rendering to behave oddly.' + ' Try reducing the resolution or disabling Hi DPI scaling to avoid this' + ' (read more here https://excaliburjs.com/docs/screens#understanding-viewport--resolution).' );