diff --git a/CHANGELOG.md b/CHANGELOG.md index b976769b..44f44db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added support for different webgl texture blending modes as `ex.ImageFiltering` : + * `ex.ImageFiltering.Blended` - Blended is useful when you have high resolution artwork and would like it blended and smoothed + * `ex.ImageFiltering.Pixel` - Pixel is useful when you do not want smoothing aka antialiasing applied to your graphics. +- Excalibur will set a "default" blend mode based on the `ex.EngineOption` antialiasing property, but can be overridden per graphic + - `antialiasing: true`, then the blend mode defaults to `ex.ImageFiltering.Blended` + - `antialiasing: false`, then the blend mode defaults to `ex.ImageFiltering.Pixel` +- `ex.Text/ex.Font` defaults to blended which improves the default look of text rendering dramatically! +- `ex.Circle` and `ex.Polygon` also default to blended which improves the default look dramatically! +- `ex.ImageSource` can now specify a blend mode before the Image is loaded, otherwise - Added new `measureText` method to the `ex.SpriteFont` and `ex.Font` to return the bounds of any particular text - Added new `Clock` api to manage the core main loop. Clocks hide the implementation detail of how the mainloop runs, users just knows that it ticks somehow. Clocks additionally encapsulate any related browser timing, like `performance.now()` 1. `StandardClock` encapsulates the existing `requestAnimationFrame` api logic diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index 94e46bb7..d2c69605 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -147,10 +147,10 @@ bootstrap(game); var heartTex = new ex.ImageSource('../images/heart.png'); var heartImageSource = new ex.ImageSource('../images/heart.png'); -var imageRun = new ex.ImageSource('../images/PlayerRun.png'); +var imageRun = new ex.ImageSource('../images/PlayerRun.png', false, ex.ImageFiltering.Blended); var imageJump = new ex.ImageSource('../images/PlayerJump.png'); var imageRun2 = new ex.ImageSource('../images/PlayerRun.png'); -var imageBlocks = new ex.ImageSource('../images/BlockA0.png'); +var imageBlocks = new ex.ImageSource('../images/BlockA0.png', false, ex.ImageFiltering.Blended); var imageBlocksLegacy = new ex.LegacyDrawing.Texture('../images/BlockA0.png'); var spriteFontImage = new ex.ImageSource('../images/SpriteFont.png'); var jump = new ex.Sound('../sounds/jump.wav', '../sounds/jump.mp3'); @@ -189,7 +189,8 @@ newSprite.scale = ex.vec(2, 2); var circle = new ex.Circle({ radius: 10, - color: ex.Color.Red + color: ex.Color.Red, + filtering: ex.ImageFiltering.Blended }); var rect = new ex.Rectangle({ @@ -384,6 +385,8 @@ var blockSprite = new ex.Sprite({ height: 49 } }); +otherPointer.get(ex.TransformComponent).z = 100; +otherPointer.graphics.use(blockSprite); // Create spritesheet //var spriteSheetRun = new ex.SpriteSheet(imageRun, 21, 1, 96, 96); var spriteSheetRun = ex.SpriteSheet.fromImageSource({ @@ -477,7 +480,7 @@ for (var i = 0; i < 36; i++) { //var block = new ex.Actor(currentX, 350 + Math.random() * 100, tileBlockWidth, tileBlockHeight, color); //block.collisionType = ex.CollisionType.Fixed; block.body.group = blockGroup; - block.graphics.add(blockAnimation); + block.graphics.add(blockSprite); game.add(block); } diff --git a/sandbox/tests/gif/animatedGif.ts b/sandbox/tests/gif/animatedGif.ts index e8639be2..a01335d3 100644 --- a/sandbox/tests/gif/animatedGif.ts +++ b/sandbox/tests/gif/animatedGif.ts @@ -1,11 +1,11 @@ /// -// ex.Flags.enable(ex.Legacy.LegacyDrawing) var game = new ex.Engine({ canvasElementId: 'game', width: 600, height: 400, - displayMode: ex.DisplayMode.FitScreen + displayMode: ex.DisplayMode.FitScreen, + antialiasing: false }); game.currentScene.camera.zoom = 2; @@ -13,7 +13,7 @@ game.currentScene.camera.zoom = 2; var gif: ex.Gif = new ex.Gif('./sword.gif', ex.Color.Black); var loader = new ex.Loader([gif]); game.start(loader).then(() => { - var actor = new ex.Actor({x: game.drawWidth / 2, y: game.drawHeight / 2, width: gif.width, height: gif.height}); + var actor = new ex.Actor({x: game.currentScene.camera.x, y: game.currentScene.camera.y, width: gif.width, height: gif.height}); actor.graphics.add(gif.toAnimation(500)); game.add(actor); }); diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index b8856895..54f61fd7 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -39,10 +39,11 @@ import * as Input from './Input/Index'; import * as Events from './Events'; import { BrowserEvents } from './Util/Browser'; import { obsolete } from './Util/Decorators'; -import { ExcaliburGraphicsContext, ExcaliburGraphicsContext2DCanvas, ExcaliburGraphicsContextWebGL } from './Graphics'; +import { ExcaliburGraphicsContext, ExcaliburGraphicsContext2DCanvas, ExcaliburGraphicsContextWebGL, TextureLoader } from './Graphics'; import { PointerEventReceiver } from './Input/PointerEventReceiver'; import { FpsSampler } from './Util/Fps'; import { Clock, StandardClock } from './Util/Clock'; +import { ImageFiltering } from './Graphics/Filtering'; /** * Enum representing the different mousewheel event bubble prevention @@ -90,6 +91,12 @@ export interface EngineOptions { /** * Optionally specify antialiasing (smoothing), by default true (smooth pixels) + * + * * `true` - useful for high resolution art work you would like smoothed, this also hints excalibur to load images + * with [[ImageFiltering.Blended]] + * + * * `false` - useful for pixel art style art work you would like sharp, this also hints excalibur to load images + * with [[ImageFiltering.Pixel]] */ antialiasing?: boolean; @@ -652,6 +659,8 @@ O|===|* >________________>\n\ pixelRatio: options.suppressHiDPIScaling ? 1 : null }); + TextureLoader.filtering = options.antialiasing ? ImageFiltering.Blended : ImageFiltering.Pixel; + if (options.backgroundColor) { this.backgroundColor = options.backgroundColor.clone(); } diff --git a/src/engine/Graphics/Circle.ts b/src/engine/Graphics/Circle.ts index 677d3fe5..2b9e5d6e 100644 --- a/src/engine/Graphics/Circle.ts +++ b/src/engine/Graphics/Circle.ts @@ -1,3 +1,4 @@ +import { ImageFiltering } from '.'; import { Raster, RasterOptions } from './Raster'; export interface CircleOptions { @@ -6,6 +7,8 @@ export interface CircleOptions { /** * A circle [[Graphic]] for drawing circles to the [[ExcaliburGraphicsContext]] + * + * Circles default to [[ImageFiltering.Blended]] */ export class Circle extends Raster { private _radius: number = 0; @@ -22,6 +25,7 @@ export class Circle extends Raster { super(options); this.padding = options.padding ?? 2; // default 2 padding for circles looks nice this.radius = options.radius; + this.filtering = options.filtering ?? ImageFiltering.Blended; this.rasterize(); } diff --git a/src/engine/Graphics/Context/texture-loader.ts b/src/engine/Graphics/Context/texture-loader.ts index 2b0ee9f5..e58de8e2 100644 --- a/src/engine/Graphics/Context/texture-loader.ts +++ b/src/engine/Graphics/Context/texture-loader.ts @@ -1,3 +1,4 @@ +import { ImageFiltering } from '../Filtering'; import { HTMLImageSource } from './ExcaliburGraphicsContext'; import { ensurePowerOfTwo, isPowerOfTwo } from './webgl-util'; @@ -5,6 +6,10 @@ import { ensurePowerOfTwo, isPowerOfTwo } from './webgl-util'; * Manages loading image sources into webgl textures, a unique id is associated with all sources */ export class TextureLoader { + /** + * Sets the default filtering for the Excalibur texture loader, default [[ImageFiltering.Blended]] + */ + public static filtering: ImageFiltering = ImageFiltering.Blended; private static _POT_CANVAS = document.createElement('canvas'); private static _POT_CTX = TextureLoader._POT_CANVAS.getContext('2d'); @@ -35,9 +40,10 @@ export class TextureLoader { /** * Loads a graphic into webgl and returns it's texture info, a webgl context must be previously registered * @param image Source graphic + * @param filtering {ImageFiltering} The ImageFiltering mode to apply to the loaded texture * @param forceUpdate Optionally force a texture to be reloaded, useful if the source graphic has changed */ - public static load(image: HTMLImageSource, forceUpdate = false): WebGLTexture { + public static load(image: HTMLImageSource, filtering?: ImageFiltering, forceUpdate = false): WebGLTexture { // Ignore loading if webgl is not registered const gl = TextureLoader._GL; if (!gl) { @@ -70,10 +76,10 @@ export class TextureLoader { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - // TODO support different sampler filter? - // NEAREST for pixel art - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // NEAREST for pixel art, LINEAR for hi-res + const filterMode = filtering ?? TextureLoader.filtering; + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode === ImageFiltering.Pixel ? gl.NEAREST : gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode === ImageFiltering.Pixel ? gl.NEAREST : gl.LINEAR); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source); diff --git a/src/engine/Graphics/Filtering.ts b/src/engine/Graphics/Filtering.ts new file mode 100644 index 00000000..67333df1 --- /dev/null +++ b/src/engine/Graphics/Filtering.ts @@ -0,0 +1,18 @@ + +/** + * Describes the different image filtering modes + */ +export enum ImageFiltering { + + /** + * Pixel is useful when you do not want smoothing aka antialiasing applied to your graphics. + * + * Useful for Pixel art aesthetics. + */ + Pixel = 'Pixel', + + /** + * Blended is useful when you have high resolution artwork and would like it blended and smoothed + */ + Blended = 'Blended' +} \ No newline at end of file diff --git a/src/engine/Graphics/Font.ts b/src/engine/Graphics/Font.ts index 5a3f9798..1074d8bf 100644 --- a/src/engine/Graphics/Font.ts +++ b/src/engine/Graphics/Font.ts @@ -6,6 +6,8 @@ import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext'; import { BaseAlign, Direction, FontOptions, FontStyle, FontUnit, TextAlign, FontRenderer } from './FontCommon'; import { Graphic, GraphicOptions } from './Graphic'; import { RasterOptions } from './Raster'; +import { TextureLoader } from '.'; +import { ImageFiltering } from './Filtering'; /** * Represents a system or web font in Excalibur @@ -15,6 +17,12 @@ import { RasterOptions } from './Raster'; * If loading a custom web font be sure to have the font loaded before you use it https://erikonarheim.com/posts/dont-test-fonts/ */ export class Font extends Graphic implements FontRenderer { + /** + * Set the font filtering mode, by default set to [[ImageFiltering.Blended]] regardles of the engine default smoothing + * + * If you have a pixel style font that may be a reason to switch this to [[ImageFiltering.Pixel]] + */ + public filtering: ImageFiltering = ImageFiltering.Blended; constructor(options: FontOptions & GraphicOptions & RasterOptions = {}) { super(options); // <- Graphics properties @@ -25,6 +33,7 @@ export class Font extends Graphic implements FontRenderer { this.strokeColor = options?.strokeColor ?? this.strokeColor; this.lineDash = options?.lineDash ?? this.lineDash; this.lineWidth = options?.lineWidth ?? this.lineWidth; + this.filtering = options?.filtering ?? this.filtering; // Font specific properts this.family = options?.family ?? this.family; @@ -76,7 +85,7 @@ export class Font extends Graphic implements FontRenderer { public quality = 2; // Raster properties for fonts - public padding = 0; + public padding = 2; public smoothing = false; public lineWidth = 1; public lineDash: number[] = []; @@ -292,6 +301,7 @@ export class Font extends Graphic implements FontRenderer { const rasterHeight = bitmap.canvas.height; // draws the bitmap to excalibur graphics context + TextureLoader.load(bitmap.canvas, this.filtering, true); ex.drawImage( bitmap.canvas, 0, diff --git a/src/engine/Graphics/Graphic.ts b/src/engine/Graphics/Graphic.ts index 6bcfbdd0..933401bd 100644 --- a/src/engine/Graphics/Graphic.ts +++ b/src/engine/Graphics/Graphic.ts @@ -49,6 +49,7 @@ export abstract class Graphic { private static _ID: number = 0; readonly id = Graphic._ID++; + /** * Gets or sets wether to show debug information about the graphic */ diff --git a/src/engine/Graphics/ImageSource.ts b/src/engine/Graphics/ImageSource.ts index bca8714d..f38b58f9 100644 --- a/src/engine/Graphics/ImageSource.ts +++ b/src/engine/Graphics/ImageSource.ts @@ -3,10 +3,13 @@ import { Texture } from '../Drawing/Texture'; import { Sprite } from './Sprite'; import { Loadable } from '../Interfaces/Index'; import { Logger } from '../Util/Log'; +import { TextureLoader } from '.'; +import { ImageFiltering } from './Filtering'; export class ImageSource implements Loadable { private _logger = Logger.getInstance(); private _resource: Resource; + private _filtering: ImageFiltering; /** * The original size of the source image in pixels @@ -46,10 +49,13 @@ export class ImageSource implements Loadable { /** * The path to the image, can also be a data url like 'data:image/' - * @param path + * @param path {string} Path to the image resource relative from the HTML document hosting the game, or absolute + * @param bustCache {boolean} Should excalibur add a cache busting querystring? + * @param filtering {ImageFiltering} Optionally override the image filtering set by [[EngineOptions.antialiasing]] */ - constructor(public readonly path: string, bustCache: boolean = false) { + constructor(public readonly path: string, bustCache: boolean = false, filtering?: ImageFiltering) { this._resource = new Resource(path, 'blob', bustCache); + this._filtering = filtering; if (path.endsWith('.svg') || path.endsWith('.gif')) { this._logger.warn(`Image type is not fully supported, you may have mixed results ${path}. Fully supported: jpg, bmp, and png`); } @@ -85,6 +91,7 @@ export class ImageSource implements Loadable { } catch (error) { throw `Error loading ImageSource from path '${this.path}' with error [${error.message}]`; } + TextureLoader.load(this.data, this._filtering); // todo emit complete this._loadedResolve(this.data); return this.data; diff --git a/src/engine/Graphics/Polygon.ts b/src/engine/Graphics/Polygon.ts index 2e41a033..e9c065fd 100644 --- a/src/engine/Graphics/Polygon.ts +++ b/src/engine/Graphics/Polygon.ts @@ -1,3 +1,4 @@ +import { ImageFiltering } from '.'; import { Vector, vec } from '../Math/vector'; import { Raster, RasterOptions } from './Raster'; @@ -7,6 +8,8 @@ export interface PolygonOptions { /** * A polygon [[Graphic]] for drawing arbitrary polygons to the [[ExcaliburGraphicsContext]] + * + * Polygons default to [[ImageFiltering.Blended]] */ export class Polygon extends Raster { private _points: Vector[]; @@ -30,6 +33,7 @@ export class Polygon extends Raster { constructor(options: RasterOptions & PolygonOptions) { super(options); this.points = options.points; + this.filtering = ImageFiltering.Blended; this.rasterize(); } diff --git a/src/engine/Graphics/Raster.ts b/src/engine/Graphics/Raster.ts index e1c0804d..a486f6e9 100644 --- a/src/engine/Graphics/Raster.ts +++ b/src/engine/Graphics/Raster.ts @@ -6,14 +6,47 @@ import { Vector } from '../Math/vector'; import { BoundingBox } from '../Collision/BoundingBox'; import { watch } from '../Util/Watch'; import { TextureLoader } from './Context/texture-loader'; +import { ImageFiltering } from './Filtering'; + export interface RasterOptions { + /** + * Optionally specify "smoothing" if you want antialiasing to apply to the raster's bitmap context, by default `false` + */ smoothing?: boolean; + + /** + * Optionally specify the color of the raster's bitmap context, by default [[Color.Black]] + */ color?: Color; + + /** + * Optionally specify the stroke color of the raster's bitmap context, by default undefined + */ strokeColor?: Color; + + /** + * Optionally specify the line width of the raster's bitmap, by default 1 pixel + */ lineWidth?: number; + + /** + * Optionally specify the line dash of the raster's bitmap, by default `[]` which means none + */ lineDash?: number[]; + + /** + * Optionally specify the padding to apply to the bitmap + */ padding?: number; + + /** + * Optionally specify what image filtering mode should be used, [[ImageFiltering.Pixel]] for pixel art, + * [[ImageFiltering.Blended]] for hi-res art + * + * By default unset, rasters defer to the engine antialiasing setting + */ + filtering?: ImageFiltering; } /** @@ -23,6 +56,7 @@ export interface RasterOptions { * Implementors must implemenet the [[Raster.execute]] method to rasterize their drawing. */ export abstract class Raster extends Graphic { + public filtering: ImageFiltering = null; public _bitmap: HTMLCanvasElement; protected _ctx: CanvasRenderingContext2D; private _dirty: boolean = true; @@ -36,8 +70,8 @@ export abstract class Raster extends Graphic { this.lineWidth = options.lineWidth ?? this.lineWidth; this.lineDash = options.lineDash ?? this.lineDash; this.padding = options.padding ?? this.padding; + this.filtering = options.filtering ?? this.filtering; } - this._bitmap = document.createElement('canvas'); // get the default canvas width/height as a fallback const bitmapWidth = options?.width ?? this._bitmap.width; @@ -216,7 +250,7 @@ export abstract class Raster extends Graphic { this.execute(this._ctx); this._ctx.restore(); // The webgl texture needs to be updated if it exists after a raster cycle - TextureLoader.load(this._bitmap, true); + TextureLoader.load(this._bitmap, this.filtering, true); } protected _applyRasterProperites(ctx: CanvasRenderingContext2D) { diff --git a/src/engine/Graphics/index.ts b/src/engine/Graphics/index.ts index 1ed1873d..f660985b 100644 --- a/src/engine/Graphics/index.ts +++ b/src/engine/Graphics/index.ts @@ -31,3 +31,6 @@ export * from './Context/debug-text'; export * from './PostProcessor/PostProcessor'; export * from './PostProcessor/ColorBlindnessMode'; export * from './PostProcessor/ColorBlindnessPostProcessor'; + +export * from './Context/texture-loader'; +export * from './Filtering'; \ No newline at end of file diff --git a/src/engine/Label.ts b/src/engine/Label.ts index 1d9bdb2b..680ec19b 100644 --- a/src/engine/Label.ts +++ b/src/engine/Label.ts @@ -41,7 +41,7 @@ export interface LabelOptions { */ export class Label extends Actor { public font: Font = new Font(); - private _text: Text = new Text({ text: '', font: this.font }); + private _text: Text = new Text({ text: '', font: this.font}); /** * The text to draw. diff --git a/src/engine/Loader.ts b/src/engine/Loader.ts index 0fd8510b..0b19469d 100644 --- a/src/engine/Loader.ts +++ b/src/engine/Loader.ts @@ -10,6 +10,7 @@ import loaderCss from './Loader.css'; import { Canvas } from './Graphics/Canvas'; import { Vector } from './Math/vector'; import { clamp, delay } from './Util/Util'; +import { ImageFiltering } from './Graphics/Filtering'; /** * Pre-loading assets @@ -78,6 +79,7 @@ import { clamp, delay } from './Util/Util'; */ export class Loader extends Class implements Loadable[]> { public canvas: Canvas = new Canvas({ + filtering: ImageFiltering.Blended, smoothing: true, draw: this.draw.bind(this) }); diff --git a/src/spec/EngineSpec.ts b/src/spec/EngineSpec.ts index 571b6388..86923085 100644 --- a/src/spec/EngineSpec.ts +++ b/src/spec/EngineSpec.ts @@ -80,6 +80,20 @@ describe('The engine', () => { expect(engine.screen.resolution.height).toBe(600); }); + it('should hint the texture loader image filter to Blended when aa=true', () => { + engine = TestUtils.engine({ + antialiasing: true + }); + expect(ex.TextureLoader.filtering).toBe(ex.ImageFiltering.Blended); + }); + + it('should hint the texture loader image filter to Pixel when aa=false', () => { + engine = TestUtils.engine({ + antialiasing: false + }); + expect(ex.TextureLoader.filtering).toBe(ex.ImageFiltering.Pixel); + }); + it('should not show the play button when suppressPlayButton is turned on', (done) => { reset(); engine = TestUtils.engine({ diff --git a/src/spec/GraphicsCircleSpec.ts b/src/spec/GraphicsCircleSpec.ts index 2f362740..fd193dfa 100644 --- a/src/spec/GraphicsCircleSpec.ts +++ b/src/spec/GraphicsCircleSpec.ts @@ -20,6 +20,13 @@ describe('A Circle Graphic', () => { expect(sut.height).toBe(24); }); + it('has default filtering', () => { + const sut = new ex.Circle({ + radius: 10 + }); + expect(sut.filtering).toBe(ex.ImageFiltering.Blended); + }); + it('can have a radius', () => { const sut = new ex.Circle({ padding: 0, diff --git a/src/spec/GraphicsImageSourceSpec.ts b/src/spec/GraphicsImageSourceSpec.ts index 96323c15..7a602422 100644 --- a/src/spec/GraphicsImageSourceSpec.ts +++ b/src/spec/GraphicsImageSourceSpec.ts @@ -29,6 +29,30 @@ describe('A ImageSource', () => { expect(whenLoaded).toHaveBeenCalledTimes(1); }); + it('can load images with an image filtering Blended', async () => { + spyOn(ex.TextureLoader, 'load').and.callThrough(); + const spriteFontImage = new ex.ImageSource('src/spec/images/GraphicsTextSpec/spritefont.png', false, ex.ImageFiltering.Blended); + const whenLoaded = jasmine.createSpy('whenLoaded'); + const image = await spriteFontImage.load(); + await spriteFontImage.ready.then(whenLoaded); + + expect(image.src).not.toBeNull(); + expect(whenLoaded).toHaveBeenCalledTimes(1); + expect(ex.TextureLoader.load).toHaveBeenCalledWith(image, ex.ImageFiltering.Blended); + }); + + it('can load images with an image filtering Pixel', async () => { + spyOn(ex.TextureLoader, 'load').and.callThrough(); + const spriteFontImage = new ex.ImageSource('src/spec/images/GraphicsTextSpec/spritefont.png', false, ex.ImageFiltering.Pixel); + const whenLoaded = jasmine.createSpy('whenLoaded'); + const image = await spriteFontImage.load(); + await spriteFontImage.ready.then(whenLoaded); + + expect(image.src).not.toBeNull(); + expect(whenLoaded).toHaveBeenCalledTimes(1); + expect(ex.TextureLoader.load).toHaveBeenCalledWith(image, ex.ImageFiltering.Pixel); + }); + it('can convert to a Sprite', async () => { const spriteFontImage = new ex.ImageSource('src/spec/images/GraphicsTextSpec/spritefont.png'); const sprite = spriteFontImage.toSprite(); diff --git a/src/spec/GraphicsPolygonSpec.ts b/src/spec/GraphicsPolygonSpec.ts index a57869e9..919f3aca 100644 --- a/src/spec/GraphicsPolygonSpec.ts +++ b/src/spec/GraphicsPolygonSpec.ts @@ -26,6 +26,14 @@ describe('A Polygon Graphic', () => { expect(poly).toBeDefined(); }); + it('has default filtering', () => { + const sut = new ex.Polygon({ + points: [ex.vec(10 * 5, 0), ex.vec(0, 20 * 5), ex.vec(20 * 5, 20 * 5)], + color: ex.Color.Green + }); + expect(sut.filtering).toBe(ex.ImageFiltering.Blended); + }); + it('can be cloned', () => { const poly = new ex.Polygon({ points: [ex.vec(10 * 5, 0), ex.vec(0, 20 * 5), ex.vec(20 * 5, 20 * 5)], diff --git a/src/spec/GraphicsTextSpec.ts b/src/spec/GraphicsTextSpec.ts index 54dc1974..c9318d43 100644 --- a/src/spec/GraphicsTextSpec.ts +++ b/src/spec/GraphicsTextSpec.ts @@ -94,6 +94,14 @@ describe('A Text Graphic', () => { expect(sut).toBeDefined(); }); + it('defaults to blended', () => { + const sut = new ex.Text({ + text: 'yo' + }); + const font = sut.font as ex.Font; + expect(font.filtering).toBe(ex.ImageFiltering.Blended); + }); + it('can be cloned', () => { const sut = new ex.Text({ text: 'some text', @@ -115,7 +123,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -143,7 +152,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -174,7 +184,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -188,7 +199,8 @@ describe('A Text Graphic', () => { const sut = new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }); const bounds = sut.measureText('some extra long text that we want to measure'); @@ -203,7 +215,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -233,7 +246,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -263,7 +277,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -293,7 +308,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -323,7 +339,8 @@ describe('A Text Graphic', () => { font: new ex.Font({ family: 'Open Sans', size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -354,7 +371,8 @@ describe('A Text Graphic', () => { family: 'Open Sans', bold: true, size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -383,7 +401,8 @@ describe('A Text Graphic', () => { family: 'Open Sans', style: ex.FontStyle.Italic, size: 18, - quality: 1 + quality: 1, + padding: 0 }) }); @@ -412,6 +431,7 @@ describe('A Text Graphic', () => { family: 'Open Sans', size: 18, quality: 1, + padding: 0, shadow: { blur: 5, offset: ex.vec(4, 4),