From c980349c712247d288755b9413bc886e2b8488b4 Mon Sep 17 00:00:00 2001 From: Justin Young <62815737+jyoung4242@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:04:13 -0400 Subject: [PATCH] feat: Nine-slice Graphic (#3241) - Added a new `ex.NineSlice` `Graphic` for creating arbitrarily resizable rectangular regions, useful for creating UI, backgrounds, and other resizable elements. ```typescript var nineSlice = new ex.NineSlice({ width: 300, height: 100, source: inputTile, sourceConfig: { width: 64, height: 64, topMargin: 5, leftMargin: 7, bottomMargin: 5, rightMargin: 7 }, destinationConfig: { drawCenter: true, horizontalStretch: ex.NineSliceStretch.Stretch, verticalStretch: ex.NineSliceStretch.Stretch } }); actor.graphics.add(nineSlice); ``` --- CHANGELOG.md | 23 + karma.conf.js | 106 ++-- sandbox/tests/9-slice/InputTile.png | Bin 0 -> 407 bytes sandbox/tests/9-slice/index.html | 12 + sandbox/tests/9-slice/index.ts | 35 + site/docs/04-graphics/04.6-nineslice.mdx | 147 +++++ site/docs/04-graphics/9slice.png | Bin 0 -> 41085 bytes site/docs/04-graphics/InputTile.png | Bin 0 -> 407 bytes site/docs/04-graphics/noCenter.png | Bin 0 -> 1259 bytes site/docs/04-graphics/sliced.png | Bin 0 -> 25931 bytes site/docs/04-graphics/stretched.png | Bin 0 -> 1261 bytes site/docs/04-graphics/tiled.png | Bin 0 -> 1310 bytes site/docs/04-graphics/tiledfit.png | Bin 0 -> 1322 bytes src/engine/Graphics/NineSlice.ts | 600 ++++++++++++++++++ src/engine/Graphics/index.ts | 1 + src/spec/NineSliceSpec.ts | 213 +++++++ .../GraphicsNineSliceSpec/InputTile.png | Bin 0 -> 407 bytes ...Image_128_128_v_stretch_h_stretch.aseprite | Bin 0 -> 887 bytes ...esultImage_128_128_v_stretch_h_stretch.png | Bin 0 -> 838 bytes ...resultImage_128_128_v_tile_h_tile.aseprite | Bin 0 -> 1075 bytes .../resultImage_128_128_v_tile_h_tile.png | Bin 0 -> 707 bytes ...Image_128_128_v_tilefit_h_tilefit.aseprite | Bin 0 -> 1200 bytes ...esultImage_128_128_v_tilefit_h_tilefit.png | Bin 0 -> 1045 bytes .../resultImage_64_64_noCenter.png | Bin 0 -> 386 bytes 24 files changed, 1087 insertions(+), 50 deletions(-) create mode 100644 sandbox/tests/9-slice/InputTile.png create mode 100644 sandbox/tests/9-slice/index.html create mode 100644 sandbox/tests/9-slice/index.ts create mode 100644 site/docs/04-graphics/04.6-nineslice.mdx create mode 100644 site/docs/04-graphics/9slice.png create mode 100644 site/docs/04-graphics/InputTile.png create mode 100644 site/docs/04-graphics/noCenter.png create mode 100644 site/docs/04-graphics/sliced.png create mode 100644 site/docs/04-graphics/stretched.png create mode 100644 site/docs/04-graphics/tiled.png create mode 100644 site/docs/04-graphics/tiledfit.png create mode 100644 src/engine/Graphics/NineSlice.ts create mode 100644 src/spec/NineSliceSpec.ts create mode 100644 src/spec/images/GraphicsNineSliceSpec/InputTile.png create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_stretch_h_stretch.aseprite create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_stretch_h_stretch.png create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tile_h_tile.aseprite create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tile_h_tile.png create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.aseprite create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.png create mode 100644 src/spec/images/GraphicsNineSliceSpec/resultImage_64_64_noCenter.png diff --git a/CHANGELOG.md b/CHANGELOG.md index d19d4bdac..d4a45c911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,29 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added a new `ex.NineSlice` `Graphic` for creating arbitrarily resizable rectangular regions, useful for creating UI, backgrounds, and other resizable elements. + ```typescript + var nineSlice = new ex.NineSlice({ + width: 300, + height: 100, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 7, + bottomMargin: 5, + rightMargin: 7 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } + }); + + actor.graphics.add(nineSlice); + ``` - Added a method to force graphics on screen `ex.GraphicsComponent.forceOnScreen` - Added new `ex.Slide` scene transition, which can slide a screen shot of the current screen: `up`, `down`, `left`, or `right`. Optionally you can add an `ex.EasingFunction`, by default `ex.EasingFunctions.Linear` ```typescript diff --git a/karma.conf.js b/karma.conf.js index b11f56e8e..0b53e2105 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -8,40 +8,39 @@ process.env.CHROME_BIN = require('puppeteer').executablePath(); console.log('Chromium', process.env.CHROMIUM_BIN); const isAppveyor = process.env.APPVEYOR_BUILD_NUMBER ? true : false; -const KarmaJasmineSeedReporter = function(baseReporterDecorator) { +const KarmaJasmineSeedReporter = function (baseReporterDecorator) { baseReporterDecorator(this); - this.onBrowserComplete = function(browser, result) { + this.onBrowserComplete = function (browser, result) { if (result.order && result.order.random && result.order.seed) { this.write('\n%s: Randomized with seed %s\n', browser.name, result.order.seed); } }; - this.onRunComplete = function() { - } + this.onRunComplete = function () {}; }; -const seedReporter = { - 'reporter:jasmine-seed': ['type', KarmaJasmineSeedReporter], // 1. 'jasmine-seed' is a name that can be referenced in karma.conf.js +const seedReporter = { + 'reporter:jasmine-seed': ['type', KarmaJasmineSeedReporter] // 1. 'jasmine-seed' is a name that can be referenced in karma.conf.js }; -const SlowSpecsReporter = function(baseReporterDecorator) { +const SlowSpecsReporter = function (baseReporterDecorator) { baseReporterDecorator(this); let slowSpecs = []; this.specSuccess = this.specFailure = function (browser, result) { - const seconds = (result.time) / 1000; + const seconds = result.time / 1000; slowSpecs.push({ time: result.time, name: result.fullName, - message:`Spec ${result.fullName} took ${seconds} seconds\n` + message: `Spec ${result.fullName} took ${seconds} seconds\n` }); }; - this.onBrowserComplete = function(browser, result) { - this.write('\n') + this.onBrowserComplete = function (browser, result) { + this.write('\n'); slowSpecs.sort((a, b) => { return b.time - a.time; - }) + }); for (const spec of slowSpecs.slice(0, 20)) { let color = '\u001b[32m'; // green let timeSeconds = spec.time/1000; @@ -57,8 +56,8 @@ const SlowSpecsReporter = function(baseReporterDecorator) { }; }; const timingReporter = { - 'reporter:jasmine-slow': ['type', SlowSpecsReporter], // 1. -} + 'reporter:jasmine-slow': ['type', SlowSpecsReporter] // 1. +}; const TimeoutSpecsReporter = function(baseReporterDecorator, logger, emitter) { baseReporterDecorator(this); @@ -78,6 +77,10 @@ const timeoutReporter = { module.exports = (config) => { config.set({ + browserConsoleLogOptions: { + terminal: true, + level: '' + }, singleRun: true, frameworks: ['jasmine', 'webpack'], plugins: [ @@ -101,20 +104,20 @@ module.exports = (config) => { }, proxies: { // smooths over loading files because karma prepends '/base/' to everything - '/src/' : '/base/src/' + '/src/': '/base/src/' }, - files: [ - 'src/spec/_boot.ts', - { pattern: 'src/spec/images/**/*.mp3', included: false, served: true }, - { pattern: 'src/spec/images/**/*.ogg', included: false, served: true }, - { pattern: 'src/spec/images/**/*.svg', included: false, served: true }, - { pattern: 'src/spec/images/**/*.png', included: false, served: true }, - { pattern: 'src/spec/images/**/*.gif', included: false, served: true }, - { pattern: 'src/spec/images/**/*.txt', included: false, served: true }, - { pattern: 'src/spec/images/**/*.css', included: false, served: true }, - { pattern: 'src/spec/images/**/*.woff2', included: false, served: true }, - { pattern: 'src/spec/fonts/**/*.ttf', included: false, served: true }, - ], + files: [ + 'src/spec/_boot.ts', + { pattern: 'src/spec/images/**/*.mp3', included: false, served: true }, + { pattern: 'src/spec/images/**/*.ogg', included: false, served: true }, + { pattern: 'src/spec/images/**/*.svg', included: false, served: true }, + { pattern: 'src/spec/images/**/*.png', included: false, served: true }, + { pattern: 'src/spec/images/**/*.gif', included: false, served: true }, + { pattern: 'src/spec/images/**/*.txt', included: false, served: true }, + { pattern: 'src/spec/images/**/*.css', included: false, served: true }, + { pattern: 'src/spec/images/**/*.woff2', included: false, served: true }, + { pattern: 'src/spec/fonts/**/*.ttf', included: false, served: true } + ], mime: { 'text/x-typescript': ['ts', 'tsx'] }, preprocessors: { './src/spec/_boot.ts': ['webpack'] @@ -125,14 +128,14 @@ module.exports = (config) => { resolve: { extensions: ['.ts', '.js'], alias: { - "@excalibur": path.resolve(__dirname, './src/engine/') + '@excalibur': path.resolve(__dirname, './src/engine/') } }, plugins: [ new webpack.DefinePlugin({ - 'process.env.__EX_VERSION': '\'test-runner\'', + 'process.env.__EX_VERSION': "'test-runner'", 'process.env.NODE_ENV': JSON.stringify('test') - }), + }) ], module: { rules: [ @@ -176,16 +179,13 @@ module.exports = (config) => { } }, webpackMiddleware: { - // webpack-dev-middleware configuration - // i. e. - stats: 'normal' + // webpack-dev-middleware configuration + // i. e. + stats: 'normal' }, reporters: ['jasmine-order', 'progress', /*'spec'*/, 'coverage-istanbul','jasmine-seed', 'jasmine-slow', 'jasmine-timeout'], coverageReporter: { - reporters: [ - { type: 'html', dir: 'coverage/' }, - { type: 'lcovonly', dir: 'coverage/', file: 'lcov.info' }, - { type: 'text-summary' }] + reporters: [{ type: 'html', dir: 'coverage/' }, { type: 'lcovonly', dir: 'coverage/', file: 'lcov.info' }, { type: 'text-summary' }] }, coverageIstanbulReporter: { // reports can be any that are listed here: https://github.com/istanbuljs/istanbuljs/tree/aae256fb8b9a3d19414dcf069c592e88712c32c6/packages/istanbul-reports/lib @@ -195,24 +195,24 @@ module.exports = (config) => { dir: path.join(__dirname, 'coverage') }, browsers: ['ChromiumHeadless_with_audio'], - browserDisconnectTolerance : 1, + browserDisconnectTolerance: 1, browserDisconnectTimeout: 60000, // appveyor is slow :( browserNoActivityTimeout: 60000, // appveyor is slow :( customLaunchers: { ChromeHeadless_with_audio: { - base: 'ChromeHeadless', - flags: ['--autoplay-policy=no-user-gesture-required', '--mute-audio', '--disable-gpu', '--no-sandbox'] + base: 'ChromeHeadless', + flags: ['--autoplay-policy=no-user-gesture-required', '--mute-audio', '--disable-gpu', '--no-sandbox'] }, ChromiumHeadless_with_audio: { - base: 'ChromiumHeadless', - flags: [ - '--autoplay-policy=no-user-gesture-required', - '--mute-audio', - '--disable-gpu', - '--no-sandbox', - '--enable-precise-memory-info', - '--js-flags="--max_old_space_size=8192"' - ] + base: 'ChromiumHeadless', + flags: [ + '--autoplay-policy=no-user-gesture-required', + '--mute-audio', + '--disable-gpu', + '--no-sandbox', + '--enable-precise-memory-info', + '--js-flags="--max_old_space_size=8192"' + ] }, ChromiumHeadless_with_debug: { base: 'ChromiumHeadless', @@ -220,7 +220,13 @@ module.exports = (config) => { }, Chromium_with_debug: { base: 'Chromium', - flags: ['--remote-debugging-address=0.0.0.0', '--remote-debugging-port=9222', '--disable-web-security', '--mute-audio', '--no-sandbox'] + flags: [ + '--remote-debugging-address=0.0.0.0', + '--remote-debugging-port=9222', + '--disable-web-security', + '--mute-audio', + '--no-sandbox' + ] } } }); diff --git a/sandbox/tests/9-slice/InputTile.png b/sandbox/tests/9-slice/InputTile.png new file mode 100644 index 0000000000000000000000000000000000000000..9df9c61ccde68434cb0b1200bf7261bf9e0428c2 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z13g_FLn`LH zovrBY94OIt|0M6>dPTR>r`VWhD|DQkzah=QPjGX`x?KlvO|o}n*_^N;BO*BGP}f0) zCdbJqzTCNduJX&h@16Ib9y)y3a(Qj<;)`#b8Mn=Q=k&P#^vhdu1#w5}X2rcxpLp`@ zsbhsNx3~7l)la&`-VxV$ + + + + + 9-Slice + + + + + + diff --git a/sandbox/tests/9-slice/index.ts b/sandbox/tests/9-slice/index.ts new file mode 100644 index 000000000..7c66a62d4 --- /dev/null +++ b/sandbox/tests/9-slice/index.ts @@ -0,0 +1,35 @@ +var game = new ex.Engine({ + width: 600, + height: 400, + displayMode: ex.DisplayMode.FitScreenAndFill, + pixelArt: true +}); + +var inputTile = new ex.ImageSource('./InputTile.png'); + +var actor = new ex.Actor({ + pos: ex.vec(200, 200) +}); +game.add(actor); +var nineSlice = new ex.NineSlice({ + width: 300, + height: 100, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 7, + bottomMargin: 5, + rightMargin: 7 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } +}); + +actor.graphics.add(nineSlice); +var loader = new ex.Loader([inputTile]); +game.start(loader).then(() => {}); diff --git a/site/docs/04-graphics/04.6-nineslice.mdx b/site/docs/04-graphics/04.6-nineslice.mdx new file mode 100644 index 000000000..490b0ba5d --- /dev/null +++ b/site/docs/04-graphics/04.6-nineslice.mdx @@ -0,0 +1,147 @@ +--- +title: Nine-Slice +slug: /nineslice +section: Graphics +--- + +## Overview + +The NineSlice is a Graphic which uses a 2D technique allowing for reuse of an image at various sizes without needing to prepare multiple assets. The process includes taking an input sprite texture, and splitting it into 9 sections, which then can be repainted in a new sprite texture without scaling or distorting the texture. This is very useful in game development for UI panels or buttons or textured platforms. + +For example: let's say this is your source texture sprite. + +![Input Tile Image](InputTile.png) + +The nine slice Graphic will break that texture up into 9 separate parts. + +![sliced texture](9slice.png) + +Then based on your output parameters specified it will 'redraw' this texture to whatever specified output graphic you want. + +You can set parameters under `sourceConfig` properties to control the margins. This lets you dial in where the module 'slices' your texture up. For example, with the example 64 pixel x 64 pixel sprite, we can set the top/bottom margins to 5 pixels, and the left/right margins to 6 pixels to get this slicing. + +![sliced ](sliced.png) + +## Configuration + +```typescript +export type NineSliceConfig = GraphicOptions & { + /* + overwrites the GraphicOptions width/height param, as it is required for this module + */ + width: number; + height: number; + + /* + source is the loaded ImageSource, which you can load manually or through a loader + */ + source: ImageSource; + + /* + sourceConfig has all the parameters necessary to cut up the input texture properly, width and height are the overall pixel dimensions of the texture and the margins define the overall cutting boundaries in pixels + */ + + sourceConfig: { + width: number; + height: number; + topMargin: number; + leftMargin: number; + bottomMargin: number; + rightMargin: number; + }; + + /* + destination configuration specifies if you want center piece drawn, the '5' frame, and how you want the algorithm to manipulate the frame textures horizontally and vertically + */ + destinationConfig: { + drawCenter: boolean; + horizontalStretch: NineSliceStretch; //exported enum + verticalStretch: NineSliceStretch; //exported enum + }; +}; +``` + +## Usage + +This is a Graphic object so it can be used on either an Actor or ScreenElement entity as a primary graphic, or as a part of a graphic group. + +```ts +export class Player extends Actor { + private myNineSlice: NineSlice; + constructor() { + super({ name: 'player', width: 300, height: 128, pos: new Vector(100, 100) }); + const myNineSliceConfig: NineSliceConfig = { + width: 300, + height: 128, + source: Resources.myImage, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: NineSliceStretch.TileFit, + verticalStretch: NineSliceStretch.TileFit + } + }; + + this.myNineSlice = new NineSlice(myNineSliceConfig); + this.graphics.use(this.myNineSlice); + } +} +``` + +## Stretch Options + +There are two parameters in the configuration that need to be set to dictate the behavior of the drawing destination graphic, `horizontalStretch` and `verticalStretch`. They are of type `NineSliceStretch`. The `horizontalStretch` parameter will dictate the behavior of tile slices 2,5, and 8, as these frames can stretch horizontally. The `verticalStretch` parameter dictates how tile slices 4,5,6 behave, as they can stretch vertically. + +The class includes some exported enumerated options: + +```ts +export enum NineSliceStretch { + Stretch, + Tile, + TileFit +} +``` + +### Stretch + +![Stretched Texture](stretched.png) + +The stretch setting takes input slice and paints in across the entirety of the output dimensions, causing a stretching of the texture as its redrawn, but this is guaranteed to fit the designated area, just with some potential image distortion. + +### Tile + +![Tiled Texture](tiled.png) + +The tile setting takes input slice and paints in across the entirety of the output dimensions in a repeating texture fashion that will not distort the source image at all, but may not fit properly in the destination space, leaving a potentially partial drawing of the texture remaining. + +### TileFit + +![Tile Fitted Texture](tiledfit.png) + +The tile fit setting takes the input slice and paints it across the entirety of the output dimensions in a repeating texture, but it 'resizes' the output tiling to create a 'best fit' of the texture, which is calculated to fit properly, but may have some squished and distorted texture. + +## Draw Center + +```ts +destinationConfig: { + drawCenter: boolean; //<------------ this property + ... + }; +``` + +The boolean flag `drawCenter` under `destinationConfig` tells the module if the section 5, middle section should be drawn or not, if this is set to `false` then it gets skipped in its draw routine, and remains 'transparent' so that the NineSlice is more of a border or frame Graphic. + +![No Center](noCenter.png) + +Look, Ma! No Center! + +## Changing the Graphic + +At this point, if you modify the graphic details, it is recommended to recreate a new instance of the graphic and update your Actor or Screen Element if it needs changed. The existing instance will not dynamically update itself. diff --git a/site/docs/04-graphics/9slice.png b/site/docs/04-graphics/9slice.png new file mode 100644 index 0000000000000000000000000000000000000000..a397c5c7a63d3bef35a74bf21c613ea7cc0b25eb GIT binary patch literal 41085 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 literal 0 HcmV?d00001 diff --git a/site/docs/04-graphics/InputTile.png b/site/docs/04-graphics/InputTile.png new file mode 100644 index 0000000000000000000000000000000000000000..9df9c61ccde68434cb0b1200bf7261bf9e0428c2 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z13g_FLn`LH zovrBY94OIt|0M6>dPTR>r`VWhD|DQkzah=QPjGX`x?KlvO|o}n*_^N;BO*BGP}f0) zCdbJqzTCNduJX&h@16Ib9y)y3a(Qj<;)`#b8Mn=Q=k&P#^vhdu1#w5}X2rcxpLp`@ zsbhsNx3~7l)la&`-VxV$cK(*n z*}gr!ZG7fTpIr3)NahY1DPaXgD+jNVhAA%?G%qp+71vjNefanGM#lfU=l_+sJ^%A1 z#vtzfC3*HgOwOk6{P6MWjfTp**5$c5r$2spb2_u?C4;s(qP-~77Ku-9+S ze&cPic~%U~5HlAsFL7b_=;WIqB&(o`Y+0Q@`#;Bh)vN{`V7aXye4n4~eVeIw`q{aY z8yP>}n)UWx%(SOJ*7j!ZLfEsv?DEbpHTCB6j;ELXcmH$LmtAc8_hp}-9Q~JPBkuqI z-}e{Gm}Mkz+BqtL5e=h3LX#l5^5(_HuX8v4pUD6IzTaomguPehc9G2ft%hgk|DEy7 zW)D33ao;c5S8r*0Hnp<+`!irxi~U(s3e0L>F0Mw-0>G?h`RD5=U{>2FyVIV`tj2!8 wmp!5q68C3oFEifY*GJ2quyi3175$sBb5Dc4?%|mRzygZF)78&qol`;+07XFFKL7v# literal 0 HcmV?d00001 diff --git a/site/docs/04-graphics/sliced.png b/site/docs/04-graphics/sliced.png new file mode 100644 index 0000000000000000000000000000000000000000..17b5961ab7a5088cde25e4708f3fec1c67ac1f0e GIT binary patch literal 25931 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 literal 0 HcmV?d00001 diff --git a/site/docs/04-graphics/tiled.png b/site/docs/04-graphics/tiled.png new file mode 100644 index 0000000000000000000000000000000000000000..2213c2c77b69b3f68917db47d4e845dbd598107a GIT binary patch literal 1310 zcmeAS@N?(olHy`uVBq!ia0y~yV6+3W`#IQvWb)1JAwY_;ILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1K(i~W;~w1A_b^sv!{z=NJZS++XuU^q{;~?{p`H|1p!fEAYdxO>=+xzqN_oZK+Ke=qM_S&BoX*^HHG4Ad z+<4I&_Ss}|<*Zx_|LhAbWxLr5j#cfgi?l6Eym0fQ9`F3ajbFd+nw_)i!`&Bo+RO7| z*YEb7{!L!XeVxwK>g@7YC$DWQK7J4GD$BY@8J62imd!hJ?bFZh)Q1P(Z7TYmXM3}x z=>EK-g02r|kI$EGP*;Ei28X*rO%q@S*tmQM$T+TUM8P%+|<@VYtqv*YH``5_r4*1nylKDf1Z_WDa zcT+xpjApN33f}u@_r<$mes_1pmCx3DYah}V6RGw6+Pf>0*XBO&+q?Lx&DoTBY0~G4 z{~Wnkl=srdcIMirpT$ofYQCHL^xNI4O)sD9vwgZCyoTYw`bY}A;MVtU<9zPtVHHz;HB+-HBA+2nS?ymR*P)1Uv_73QDz`u%h9 zQ@i)qN0vWc#yNlcE^z+eas0dd_X)Gk+MG3O|NevFStnE+-C2oVKgHQuysE5^iI)P) OGzL#sKbLh*2~7ZgpKXojvyWjcs&+^-U-oDEJ`TvifC#zm< zoAThN2RF#rHuF7k$^Xh`?vE}0T))A5&q=@Kdn*2}nkSWQ7aLg?E3GSSzIMI&-lrcI zFaHd+>+qlQZ}WBa@9$Y_-g{Mh+Vr=^#$UH=aBVif7#YfUUwZxWV@q?DQ}4emPW|;T zWbaE|=h?IWN1eUT_j&tj`Dgq0Rd0#e`G3JZ6TZB=L1j9M75Cs?#N&IKNU^c?_nf`< z*ILbLZqd(^CAOw~xe{8{d2{r{_2#Vs`uWF3V}zf#f19Tje16{b*~dQY+M4?NjE}8u z&cz45=UCs*f3v4*(K`mab-UTmU60*fFT1Dn+^V7<_bO~n^>U>(f9K8DzRbV;m2bY} zEgPHtv-ZnA%MZ6dQ(yZ%V`tHS$Gw>{cXlm(6XEn>&qJJ%PZRH>B(&s&4<~Nq+})qN z>GI0mdDiRat*~zBfBmlUbLsV5pl9!bJ$r`r{in$OqC{#)D7W%F#;oA*8}&Hms0V69gB``+upk1uVTxas}3oko8PR^9tD zZQVcdiwOYnKP@R zZY( { + this._initialize(); + }); + } + + /** + * Sets the target width of the 9 slice (pixels), and recalculates the 9 slice if desired (auto) + * @param newWidth + * @param auto + */ + setTargetWidth(newWidth: number, auto: boolean = false) { + this._config.width = newWidth; + if (auto) { + this._initialize(); + } + } + + /** + * Sets the target height of the 9 slice (pixels), and recalculates the 9 slice if desired (auto) + * @param newHeight + * @param auto + */ + setTargetHeight(newHeight: number, auto: boolean = false) { + this._config.height = newHeight; + if (auto) { + this._initialize(); + } + } + + /** + * Sets the 9 slice margins (pixels), and recalculates the 9 slice if desired (auto) + */ + setMargins(left: number, top: number, right: number, bottom: number, auto: boolean = false) { + this._config.sourceConfig.leftMargin = left; + this._config.sourceConfig.topMargin = top; + this._config.sourceConfig.rightMargin = right; + this._config.sourceConfig.bottomMargin = bottom; + if (auto) { + this._initialize(); + } + } + + /** + * Sets the stretching strategy for the 9 slice, and recalculates the 9 slice if desired (auto) + * + */ + setStretch(type: 'horizontal' | 'vertical' | 'both', stretch: NineSliceStretch, auto: boolean = false) { + if (type === 'horizontal') { + this._config.destinationConfig.horizontalStretch = stretch; + } else if (type === 'vertical') { + this._config.destinationConfig.verticalStretch = stretch; + } else { + this._config.destinationConfig.horizontalStretch = stretch; + this._config.destinationConfig.verticalStretch = stretch; + } + if (auto) { + this._initialize(); + } + } + + /** + * Returns the config of the 9 slice + */ + getConfig(): NineSliceConfig { + return this._config; + } + + /** + * Draws 1 of the 9 tiles based on parameters passed in + * context is the ExcaliburGraphicsContext from the _drawImage function + * destinationSize is the size of the destination image as a vector (width,height) + * targetCanvas is the canvas to draw to + * horizontalStretch and verticalStretch are the horizontal and vertical stretching strategies + * marginW and marginH are optional margins for the 9 slice for positioning + * @param context + * @param targetCanvas + * @param destinationSize + * @param horizontalStretch + * @param verticalStretch + * @param marginWidth + * @param marginHeight + */ + protected _drawTile( + context: ExcaliburGraphicsContext, + targetCanvas: HTMLCanvasElement, + destinationSize: Vector, + horizontalStretch: NineSliceStretch, + verticalStretch: NineSliceStretch, + marginWidth?: number, + marginHeight?: number + ) { + const tempMarginW = marginWidth || 0; + const tempMarginH = marginHeight || 0; + let tempSizeX: number, tempPositionX: number, tempSizeY: number, tempPositionY: number; + const numTilesX = this._getNumberOfTiles(targetCanvas.width, destinationSize.x, horizontalStretch); + const numTilesY = this._getNumberOfTiles(targetCanvas.height, destinationSize.y, verticalStretch); + + for (let i = 0; i < numTilesX; i++) { + for (let j = 0; j < numTilesY; j++) { + let { tempSize, tempPosition } = this._calculateParams( + i, + numTilesX, + targetCanvas.width, + destinationSize.x, + this._config.destinationConfig.horizontalStretch + ); + tempSizeX = tempSize; + tempPositionX = tempPosition; + + ({ tempSize, tempPosition } = this._calculateParams( + j, + numTilesY, + targetCanvas.height, + destinationSize.y, + this._config.destinationConfig.verticalStretch + )); + tempSizeY = tempSize; + tempPositionY = tempPosition; + + context.drawImage( + targetCanvas, + 0, + 0, + targetCanvas.width, + targetCanvas.height, + tempMarginW + tempPositionX, + tempMarginH + tempPositionY, + tempSizeX, + tempSizeY + ); + } + } + } + + /** + * Draws the 9 slices to the canvas + */ + protected _drawImage(ex: ExcaliburGraphicsContext, x: number, y: number): void { + if (this._imgSource.isLoaded()) { + // Top left, no stretching + + this._drawTile( + ex, + this._canvasA, + + new Vector(this._config.sourceConfig.leftMargin, this._config.sourceConfig.topMargin), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch + ); + + // Top, middle, horizontal stretching + this._drawTile( + ex, + this._canvasB, + + new Vector( + this._config.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin, + this._config.sourceConfig.topMargin + ), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + this._config.sourceConfig.leftMargin, + 0 + ); + + // Top right, no stretching + + this._drawTile( + ex, + this._canvasC, + + new Vector(this._config.sourceConfig.rightMargin, this._config.sourceConfig.topMargin), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + + this._config.width - this._config.sourceConfig.rightMargin, + 0 + ); + + // middle, left, vertical stretching + + this._drawTile( + ex, + this._canvasD, + new Vector( + this._config.sourceConfig.leftMargin, + + this._config.height - this._config.sourceConfig.bottomMargin - this._config.sourceConfig.topMargin + ), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + 0, + this._config.sourceConfig.topMargin + ); + + // center, both stretching + if (this._config.destinationConfig.drawCenter) { + this._drawTile( + ex, + this._canvasE, + new Vector( + this._config.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin, + + this._config.height - this._config.sourceConfig.bottomMargin - this._config.sourceConfig.topMargin + ), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + this._config.sourceConfig.leftMargin, + this._config.sourceConfig.topMargin + ); + } + // middle, right, vertical stretching + this._drawTile( + ex, + this._canvasF, + + new Vector( + this._config.sourceConfig.rightMargin, + + this._config.height - this._config.sourceConfig.bottomMargin - this._config.sourceConfig.topMargin + ), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + + this._config.width - this._config.sourceConfig.rightMargin, + this._config.sourceConfig.topMargin + ); + + // bottom left, no stretching + this._drawTile( + ex, + this._canvasG, + new Vector(this._config.sourceConfig.leftMargin, this._config.sourceConfig.bottomMargin), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + 0, + + this._config.height - this._config.sourceConfig.bottomMargin + ); + + // bottom middle, horizontal stretching + this._drawTile( + ex, + this._canvasH, + + new Vector( + this._config.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin, + this._config.sourceConfig.bottomMargin + ), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + this._config.sourceConfig.leftMargin, + + this._config.height - this._config.sourceConfig.bottomMargin + ); + + // bottom right, no stretching + this._drawTile( + ex, + this._canvasI, + new Vector(this._config.sourceConfig.rightMargin, this._config.sourceConfig.bottomMargin), + this._config.destinationConfig.horizontalStretch, + this._config.destinationConfig.verticalStretch, + + this._config.width - this._config.sourceConfig.rightMargin, + + this._config.height - this._config.sourceConfig.bottomMargin + ); + } else { + this._logger.warnOnce( + `NineSlice ImageSource ${this._imgSource.path}` + + ` is not yet loaded and won't be drawn. Please call .load() or include in a Loader.\n\n` + + `Read https://excaliburjs.com/docs/imagesource for more information.` + ); + } + } + + /** + * Slices the source sprite into the 9 slice canvases internally + */ + protected _initialize() { + this._sourceSprite = this._imgSource.image; + + // top left slice + this._canvasA.width = this._config.sourceConfig.leftMargin; + this._canvasA.height = this._config.sourceConfig.topMargin; + const aCtx = this._canvasA.getContext('2d'); + + aCtx?.drawImage(this._sourceSprite, 0, 0, this._canvasA.width, this._canvasA.height, 0, 0, this._canvasA.width, this._canvasA.height); + + // top slice + + this._canvasB.width = this._config.sourceConfig.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin; + this._canvasB.height = this._config.sourceConfig.topMargin; + + const bCtx = this._canvasB.getContext('2d'); + bCtx?.drawImage( + this._sourceSprite, + this._config.sourceConfig.leftMargin, + 0, + this._canvasB.width, + this._canvasB.height, + 0, + 0, + this._canvasB.width, + this._canvasB.height + ); + + // top right slice + this._canvasC.width = this._config.sourceConfig.rightMargin; + this._canvasC.height = this._config.sourceConfig.topMargin; + const cCtx = this._canvasC.getContext('2d'); + cCtx?.drawImage( + this._sourceSprite, + this._sourceSprite.width - this._config.sourceConfig.rightMargin, + 0, + this._canvasC.width, + this._canvasC.height, + 0, + 0, + this._canvasC.width, + this._canvasC.height + ); + + // middle left slice + this._canvasD.width = this._config.sourceConfig.leftMargin; + this._canvasD.height = this._config.sourceConfig.height - this._config.sourceConfig.topMargin - this._config.sourceConfig.bottomMargin; + const dCtx = this._canvasD.getContext('2d'); + dCtx?.drawImage( + this._sourceSprite, + 0, + this._config.sourceConfig.topMargin, + this._canvasD.width, + this._canvasD.height, + 0, + 0, + this._canvasD.width, + this._canvasD.height + ); + + // middle slice + this._canvasE.width = this._config.sourceConfig.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin; + this._canvasE.height = this._config.sourceConfig.height - this._config.sourceConfig.topMargin - this._config.sourceConfig.bottomMargin; + const eCtx = this._canvasE.getContext('2d'); + eCtx?.drawImage( + this._sourceSprite, + this._config.sourceConfig.leftMargin, + this._config.sourceConfig.topMargin, + this._canvasE.width, + this._canvasE.height, + 0, + 0, + this._canvasE.width, + this._canvasE.height + ); + + // middle right slice + this._canvasF.width = this._config.sourceConfig.rightMargin; + this._canvasF.height = this._config.sourceConfig.height - this._config.sourceConfig.topMargin - this._config.sourceConfig.bottomMargin; + const fCtx = this._canvasF.getContext('2d'); + fCtx?.drawImage( + this._sourceSprite, + + this._config.sourceConfig.width - this._config.sourceConfig.rightMargin, + this._config.sourceConfig.topMargin, + this._canvasF.width, + this._canvasF.height, + 0, + 0, + this._canvasF.width, + this._canvasF.height + ); + + // bottom left slice + this._canvasG.width = this._config.sourceConfig.leftMargin; + this._canvasG.height = this._config.sourceConfig.bottomMargin; + const gCtx = this._canvasG.getContext('2d'); + gCtx?.drawImage( + this._sourceSprite, + 0, + this._config.sourceConfig.height - this._config.sourceConfig.bottomMargin, + this._canvasG.width, + this._canvasG.height, + 0, + 0, + this._canvasG.width, + this._canvasG.height + ); + + // bottom slice + this._canvasH.width = this._config.sourceConfig.width - this._config.sourceConfig.leftMargin - this._config.sourceConfig.rightMargin; + this._canvasH.height = this._config.sourceConfig.bottomMargin; + const hCtx = this._canvasH.getContext('2d'); + hCtx?.drawImage( + this._sourceSprite, + this._config.sourceConfig.leftMargin, + this._config.sourceConfig.height - this._config.sourceConfig.bottomMargin, + this._canvasH.width, + this._canvasH.height, + 0, + 0, + this._canvasH.width, + this._canvasH.height + ); + + // bottom right slice + this._canvasI.width = this._config.sourceConfig.rightMargin; + this._canvasI.height = this._config.sourceConfig.bottomMargin; + const iCtx = this._canvasI.getContext('2d'); + iCtx?.drawImage( + this._sourceSprite, + this._sourceSprite.width - this._config.sourceConfig.rightMargin, + this._config.sourceConfig.height - this._config.sourceConfig.bottomMargin, + this._canvasI.width, + this._canvasI.height, + 0, + 0, + this._canvasI.width, + this._canvasI.height + ); + } + + /** + * Clones the 9 slice + */ + + clone(): NineSlice { + return new NineSlice(this._config); + } + + /** + * Returns the number of tiles + */ + protected _getNumberOfTiles(tileSize: number, destinationSize: number, strategy: NineSliceStretch): number { + switch (strategy) { + case NineSliceStretch.Stretch: + return 1; + case NineSliceStretch.Tile: + return Math.ceil(destinationSize / tileSize); + case NineSliceStretch.TileFit: + return Math.ceil(destinationSize / tileSize); + } + } + + /** + * Returns the position and size of the tile + */ + protected _calculateParams( + tileNum: number, + numTiles: number, + tileSize: number, + destinationSize: number, + strategy: NineSliceStretch + ): { tempPosition: number; tempSize: number } { + switch (strategy) { + case NineSliceStretch.Stretch: + return { + tempPosition: 0, + tempSize: destinationSize + }; + case NineSliceStretch.Tile: + // if last tile, adjust size + if (tileNum === numTiles - 1) { + //last tile + return { + tempPosition: tileNum * tileSize, + tempSize: tileSize - (numTiles * tileSize - destinationSize) + }; + } else { + return { + tempPosition: tileNum * tileSize, + tempSize: tileSize + }; + } + + case NineSliceStretch.TileFit: + const reducedTileSize = destinationSize / numTiles; + const position = tileNum * reducedTileSize; + return { + tempPosition: position, + tempSize: reducedTileSize + }; + } + } +} diff --git a/src/engine/Graphics/index.ts b/src/engine/Graphics/index.ts index 6ab82bae7..8c4055e54 100644 --- a/src/engine/Graphics/index.ts +++ b/src/engine/Graphics/index.ts @@ -25,6 +25,7 @@ export * from './Font'; export * from './FontCache'; export * from './SpriteFont'; export * from './Canvas'; +export * from './NineSlice'; export * from './Context/ExcaliburGraphicsContext'; export * from './Context/ExcaliburGraphicsContext2DCanvas'; diff --git a/src/spec/NineSliceSpec.ts b/src/spec/NineSliceSpec.ts new file mode 100644 index 000000000..13053b7dc --- /dev/null +++ b/src/spec/NineSliceSpec.ts @@ -0,0 +1,213 @@ +import * as ex from '@excalibur'; +import { ExcaliburAsyncMatchers, ExcaliburMatchers } from 'excalibur-jasmine'; + +const inputTile = new ex.ImageSource('src/spec/images/GraphicsNineSliceSpec/InputTile.png'); + +describe('A NineSlice', () => { + let canvasElement: HTMLCanvasElement; + + let ctx: ex.ExcaliburGraphicsContext; + + let testGraphicConfigSmall: ex.NineSliceConfig; + let testGraphicConfigStretch: ex.NineSliceConfig; + let testGraphicConfigTile: ex.NineSliceConfig; + let testGraphicConfigTileFit: ex.NineSliceConfig; + let testGraphicConfigNoCenter: ex.NineSliceConfig; + + beforeAll(async () => { + await inputTile.load(); + testGraphicConfigSmall = { + width: 64, + height: 64, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } + }; + testGraphicConfigStretch = { + width: 128, + height: 128, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } + }; + testGraphicConfigTile = { + width: 128, + height: 128, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Tile, + verticalStretch: ex.NineSliceStretch.Tile + } + }; + testGraphicConfigTileFit = { + width: 128, + height: 128, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.TileFit, + verticalStretch: ex.NineSliceStretch.TileFit + } + }; + testGraphicConfigNoCenter = { + width: 64, + height: 64, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 5, + rightMargin: 6 + }, + destinationConfig: { + drawCenter: false, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } + }; + }); + beforeEach(() => { + jasmine.addMatchers(ExcaliburMatchers); + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); + canvasElement = document.createElement('canvas'); + canvasElement.width = 64; + canvasElement.height = 64; + ctx = new ex.ExcaliburGraphicsContext2DCanvas({ canvasElement }); + }); + + it('can exist', () => { + expect(ex.NineSlice).toBeDefined(); + }); + + it('can have its config modified', () => { + const dummyConfig = { + width: 64, + height: 64, + source: inputTile, + sourceConfig: { + width: 64, + height: 64, + topMargin: 5, + leftMargin: 6, + bottomMargin: 6, + rightMargin: 5 + }, + destinationConfig: { + drawCenter: true, + horizontalStretch: ex.NineSliceStretch.Stretch, + verticalStretch: ex.NineSliceStretch.Stretch + } + }; + + const sut = new ex.NineSlice(dummyConfig); + + sut.setMargins(1, 2, 3, 4); + expect(sut.getConfig().sourceConfig.leftMargin).toBe(1); + expect(sut.getConfig().sourceConfig.topMargin).toBe(2); + expect(sut.getConfig().sourceConfig.rightMargin).toBe(3); + expect(sut.getConfig().sourceConfig.bottomMargin).toBe(4); + + sut.setTargetWidth(256); + expect(sut.getConfig().width).toBe(256); + sut.setTargetHeight(256); + expect(sut.getConfig().height).toBe(256); + + sut.setStretch('horizontal', ex.NineSliceStretch.Tile); + sut.setStretch('vertical', ex.NineSliceStretch.TileFit); + expect(sut.getConfig().destinationConfig.horizontalStretch).toBe(ex.NineSliceStretch.Tile); + expect(sut.getConfig().destinationConfig.verticalStretch).toBe(ex.NineSliceStretch.TileFit); + }); + + it('can clone the Graphic', () => { + const sut = new ex.NineSlice(testGraphicConfigSmall); + const newSut = sut.clone(); + expect(newSut).not.toBe(sut); + expect(newSut).toBeInstanceOf(ex.NineSlice); + }); + + it('can draw a copy of input tile', async () => { + canvasElement.width = 64; + canvasElement.height = 64; + ctx.clear(); + const sut = new ex.NineSlice(testGraphicConfigSmall); + sut.draw(ctx, 0, 0); + await expectAsync(canvasElement).toEqualImage('src/spec/images/GraphicsNineSliceSpec/InputTile.png'); + }); + + it('can draw a tiled copy of input tile', async () => { + canvasElement.width = 128; + canvasElement.height = 128; + ctx.clear(); + const sut = new ex.NineSlice(testGraphicConfigTile); + sut.draw(ctx, 0, 0); + await expectAsync(canvasElement).toEqualImage('src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tile_h_tile.png'); + }); + + it('can draw a fitted tile copy of input tile', async () => { + canvasElement.width = 128; + canvasElement.height = 128; + ctx.clear(); + const sut = new ex.NineSlice(testGraphicConfigTileFit); + sut.draw(ctx, 0, 0); + await expectAsync(canvasElement).toEqualImage('src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.png'); + }); + + it('can draw a stretched copy of input tile', async () => { + canvasElement.width = 128; + canvasElement.height = 128; + ctx.clear(); + const sut = new ex.NineSlice(testGraphicConfigStretch); + sut.draw(ctx, 0, 0); + await expectAsync(canvasElement).toEqualImage('src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_stretch_h_stretch.png'); + }); + + it('can draw the frame of a tile with no center', async () => { + canvasElement.width = 64; + canvasElement.height = 64; + ctx.clear(); + const sut = new ex.NineSlice(testGraphicConfigNoCenter); + sut.draw(ctx, 0, 0); + await expectAsync(canvasElement).toEqualImage('src/spec/images/GraphicsNineSliceSpec/resultImage_64_64_noCenter.png'); + }); +}); diff --git a/src/spec/images/GraphicsNineSliceSpec/InputTile.png b/src/spec/images/GraphicsNineSliceSpec/InputTile.png new file mode 100644 index 0000000000000000000000000000000000000000..9df9c61ccde68434cb0b1200bf7261bf9e0428c2 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z13g_FLn`LH zovrBY94OIt|0M6>dPTR>r`VWhD|DQkzah=QPjGX`x?KlvO|o}n*_^N;BO*BGP}f0) zCdbJqzTCNduJX&h@16Ib9y)y3a(Qj<;)`#b8Mn=Q=k&P#^vhdu1#w5}X2rcxpLp`@ zsbhsNx3~7l)la&`-VxV$g{Q5@f9s{1!S9h;G6?*gkFMA#NC6`}jPXBl)p#I&L z^n8i>|NsA(zxdz3dsT1$*&Twn;?MOTEq!%u_tK-0dsd#+T^6?au~D2v+tcEU-r>EI zFFkrw_EzWY`TQwj=^vK`#`{kzPn4MU{q5&1J2!+!&3S2m>*aaB`m$e}FVCO*@6DI= z<@VNpZ;ntQ-cR*9>w>r6)73rq$Tv67aO*5tV6YqL% rzFJ1_od0`n_m^F@Pr$)_=~T+of1m4b-|~O|mJ=hk{<9r?9GCU}o@iaSW-L z^Y*%gI};==eG3? zCCkE@4<}1W$a!bUsp#BzoBaOPyS+GP zoBFel0_{<2mwo*jpJn3Y8MJlZ%}w_$_&LU38R9D0O3~2T=L%I$uqaT>snh~ zeSLh?EQPT>)$* zl66@?HVepg3P6UEf{Ck!O`4@)f0*_CLbaO<(%v1O_4||A%%=fa`wYydKvcw)tvS#iv89t#=KM$K6O|t9#0? ze}?Z(_xl$w=NE2Cx-YK&_{+bINtcg4b3T9PXG!YQu&wid@A_Y}=hofOoLx6+Ud;Tp zyXIB=|L!;WyY+MJX7Bp%FaG}c?%n#+&Gx^I*=Lq_chA}zB^!TUFupG)f9!d6{xq}w zwKX5A-n_Mqx%J2Fe1yesUY$9|pPoK=`{RpWlb4>&|9ao<)%g|me{=1o0FBo8TK+TO z)%#BiuiSs?ymJ5P_MrOd{7e7o$$R~c9U=-!|5pFc{Jrhu_xZ2(*S`L%f9z&{!b2Oq zyZ?uhCy0uQz5kznT9{t-|KwBWm;Z^0ugL5F@84go{r&OBlRqkdIBk5H^m$==-kbMp u?(F)vpKpHeefH@hv-Qm6tCs!KoB!+jf2H5I-+9kIzwF)r{~R;_hXDWvx&4v= literal 0 HcmV?d00001 diff --git a/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tile_h_tile.png b/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tile_h_tile.png new file mode 100644 index 0000000000000000000000000000000000000000..fd315ecbedf2b1a3d01f699a45d12fd365153acc GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSjKx9jP7LeL$-HD>V5;?WaSW-L z^Y*S`c5t9f+sE%Jl3V6G%gmW0vM`zT;L6#_>Fnx(EBcl$SnL`T`-jOdDScsIq>jh) zrtaoumL)r7Z!LWqVv#%L&TpIjss(zU@9Zl*&Xt||c3VDH>l*)Z&+xrh?3d25;me$N zEVAs{yzuMl;OSjF}s>-!WEYOssDB zdN}jh^SGQ4FS)uKXDV!Zl#KN#&o^?`K-eSARE_ z>;Lrk!Rx8{Ws-OH6q+SdQy`~6!v+4jJMXPNU-!|pT7FmrETB_X$2IvbcO89ZJ6T-G@y GGywplc>aI@ literal 0 HcmV?d00001 diff --git a/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.aseprite b/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..b5a7fbce39d8e3fdfd03a71148f22de2c0b98f10 GIT binary patch literal 1200 zcmdnM!ocugDI-Gz5GpV*GB7Zt05Jjp#Xy1#0zgO$U;woL*GCqxtw1J&7!b27fUQKb zE(^$J0l7{A$WT%+an-O%vo!1vv%X)bc5^}6yTh}7e=?i-G$3oAf%%j??@aw5ZxtgO zOMCkggM_B&yobrN|Hq$wa{Bh4|NsBbUc0@eu01m=D>5vo%&BXQkQ;JL6_p}?8D3N<=+3i z_(xOqe*5^{&xPYomsj1leUSJ2oc)iP&y0Uxit)D&m%o}9pTEy*zS?||{fn#1Cf^PF z)pzI2r%$tmZ$5pK`(E#!mp?mOmig~K^Ybwpb?)EasO?_+MRw;*)qg)8_y2eN`}6<% z{{R2iK68tbiCuMO9xs3IlASkOZ@qmnaq>#Tds;|3Qg`p18FqY{_U82U>utT8o38^| z#@pX7wMx7H@!L1Qm&RqO<~MUo&*KllCCEx)*M=BYQ4k583k)&GgveY1SeryrFct#_pZ z9lZ4YiL*NPK6`!2&);8vX5LNf*F~GpANn`{?b9>w>+`MF?EgRTz5s@t^_qWs{;##) zHr@Dr{hRmS?LYm#Kb!CMd;5QTIq4Rd*ds9C9y9rz-S_xd#p)ljcQ?;|v+Yv(leB4X zpQS~~Mz1?F@9)1-t=)dvx~0qHujPF^x63bk=?~f6-@Y%a-#&Gpa`o*ftMZo>Wz|3H pcN@;F^O0G7_l@;GyV7mQW&u;K{Xh1v&;ReY|M&kt!(N9gGXbSPOfLWc literal 0 HcmV?d00001 diff --git a/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.png b/src/spec/images/GraphicsNineSliceSpec/resultImage_128_128_v_tilefit_h_tilefit.png new file mode 100644 index 0000000000000000000000000000000000000000..a658d3ccb5455aa9f5c50eaa8cc826e810d263a4 GIT binary patch literal 1045 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSjKx9jP7LeL$-HD>U_S5Z;uumf z=j|PTkI97$$3CW;<>+QdicD?{@RXg>vq&dv`GFVn1#T*;9C>kJAfN{ zB(S==By3!;pT#;-N><3r;BZuHqt|2;<7-C0izo2uzZcWK@q6D#oB5C5w|_9$_n`XM z&c1oI|AjSk-o5uJ)%m~ja`qw3^LJnVOVBaw3Fq%i&%Ll;E3TY}`EI$vmER}cX|fqJ zc?(8t5k3F@`c2!TDfd?0+EQ*Q_D+C3h~Y`hFSZ90zDXw(*Q?v9?3dt6uUxiZ(Ne*> z|1#R!{!ZPukMT#%<%!eHuL4c!bYVJtSeb)`>&?6*p?>~HYJLeOmT4CApPiihgCplf zhH;F^dcF4h_U!z(^P{&LIe&hxcX-~dxhrmNJPXuqu2e3-XKMM${PvE0Tnqx0w{k7k zNA8Tc&+tZs!IIU%k8wgU!xNe?ip1HZKTmPiWqKjdxzo#r&uKsLCZfvYA!-E~M z3vAb?C zF?8_qNuFzVZT-bYdtXU2NQtdCh&}SS`NocYr#?B(+)?n4E6?TKE!)En9-rV~u=r$N z{dt*q1XGAkRK~92+TUAjo?16#)~&Y=vlebLj0fg(22WQ%mvv4FO#s=CwhRCO literal 0 HcmV?d00001 diff --git a/src/spec/images/GraphicsNineSliceSpec/resultImage_64_64_noCenter.png b/src/spec/images/GraphicsNineSliceSpec/resultImage_64_64_noCenter.png new file mode 100644 index 0000000000000000000000000000000000000000..99cd1e1f4f15880e21d6138cfad13a3e565bbd27 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zZ9H8ZLn`LH zovql-*YE0&`*m#Oj4%HSd|zH}D7J6A_U#|JlGCEERlfQ4vi{aLCE;1;v;M!_-rpdn z@A-_`VtvEw7fV_*?#_&qKe*mo=+;r~`iqyW`g;wR+0DPpb9`x6y>i3JM`liIXT0DI zV2EmPW6WTbVqL(RA<`hWAdpr9YG!JCt)0GK>FV!JxcD<*Ltbw#TkLx$#h*9L*A_p& ym+%M%6es;g%40}Nw