@@ -63,6 +63,21 @@ type Layer = {
6363 export : boolean
6464}
6565
66+ interface FileOptions {
67+ history ?: boolean
68+ }
69+
70+ interface File {
71+ width : number
72+ height : number
73+ transparent : boolean
74+ colors : Color [ ]
75+ layers : Layer [ ]
76+ data : number [ ] [ ] [ ]
77+ undo ?: History [ ]
78+ redo ?: History [ ]
79+ }
80+
6681function toColor ( [ r , g , b , a ] : Color ) {
6782 return `rgba(${ r } , ${ g } , ${ b } , ${ a } )` ;
6883}
@@ -158,28 +173,9 @@ export default class PgInputPixelEditor extends HTMLElement {
158173 }
159174
160175 render ( changes ) {
161- if ( changes . width || changes . height || changes . size ) {
176+ if ( changes . width || changes . height || changes . size || changes . transparent ) {
162177 this . #init( ) ;
163178 }
164- if ( changes . transparent ) {
165- const totalSize = this . size + this . gridSize ;
166- const actualWidth = this . width * totalSize - this . gridSize ;
167- const actualHeight = this . height * totalSize - this . gridSize ;
168- if ( this . transparent ) {
169- for ( let y = 0 ; y < this . height ; y ++ ) {
170- for ( let x = 0 ; x < this . width ; x ++ ) {
171- this . #baseLayerContext. fillStyle = WHITE ;
172- this . #baseLayerContext. fillRect ( x * totalSize , y * totalSize , this . size + 1 , this . size + 1 ) ;
173- this . #baseLayerContext. fillStyle = '#DDD' ;
174- this . #baseLayerContext. fillRect ( x * totalSize + 5 , y * totalSize , 5 , 5 ) ;
175- this . #baseLayerContext. fillRect ( x * totalSize , y * totalSize + 5 , 5 , 5 ) ;
176- }
177- }
178- } else {
179- this . #baseLayerContext. clearRect ( 0 , 0 , actualWidth , actualHeight ) ;
180- }
181- this . #updateGrid( ) ;
182- }
183179 }
184180
185181 #reset = true ;
@@ -189,10 +185,24 @@ export default class PgInputPixelEditor extends HTMLElement {
189185 const actualHeight = this . height * totalSize - this . gridSize ;
190186 this . $canvas . width = actualWidth ;
191187 this . $canvas . height = actualHeight ;
188+ this . #context. clearRect ( 0 , 0 , actualWidth , actualHeight ) ;
192189 [ this . #baseLayer, this . #baseLayerContext] = createLayer ( actualWidth , actualHeight ) ;
193190 [ this . #editLayer, this . #editLayerContext] = createLayer ( actualWidth , actualHeight ) ;
194191 [ this . #noEditLayer, this . #noEditLayerContext] = createLayer ( actualWidth , actualHeight ) ;
195192 [ this . #previewLayer, this . #previewLayerContext] = createLayer ( actualWidth , actualHeight ) ;
193+ if ( this . transparent ) {
194+ for ( let y = 0 ; y < this . height ; y ++ ) {
195+ for ( let x = 0 ; x < this . width ; x ++ ) {
196+ this . #baseLayerContext. fillStyle = WHITE ;
197+ this . #baseLayerContext. fillRect ( x * totalSize , y * totalSize , this . size + 1 , this . size + 1 ) ;
198+ this . #baseLayerContext. fillStyle = '#DDD' ;
199+ this . #baseLayerContext. fillRect ( x * totalSize + 5 , y * totalSize , 5 , 5 ) ;
200+ this . #baseLayerContext. fillRect ( x * totalSize , y * totalSize + 5 , 5 , 5 ) ;
201+ }
202+ }
203+ } else {
204+ this . #baseLayerContext. clearRect ( 0 , 0 , actualWidth , actualHeight ) ;
205+ }
196206 if ( this . #reset) {
197207 this . #layer = 0 ;
198208 this . #layers = [ {
@@ -209,6 +219,7 @@ export default class PgInputPixelEditor extends HTMLElement {
209219 } else {
210220 this . #redraw( ) ;
211221 }
222+ this . #updateGrid( ) ;
212223 }
213224
214225 #redraw( ) {
@@ -665,7 +676,7 @@ export default class PgInputPixelEditor extends HTMLElement {
665676 return ;
666677 }
667678 iterateGrid ( this . #data[ this . #layer] , ( x , y ) => {
668- this . #data[ this . #layer] [ y ] [ x ] = this . #data[ this . #layer] [ y ] [ x ] === 0 ? 1 : 0 ;
679+ this . #data[ this . #layer] [ y ] [ x ] = this . #data[ this . #layer] [ y ] [ x ] === 0 ? 1 : 0 ;
669680 } ) ;
670681 this . #setPixelAll( ) ;
671682 }
@@ -766,4 +777,48 @@ export default class PgInputPixelEditor extends HTMLElement {
766777 inputModeEllipseOutline ( ) {
767778 this . #inputMode = InputMode . EllipseOutline ;
768779 }
780+
781+ async save ( options : FileOptions = { } ) : Promise < File > {
782+ const file : File = {
783+ width : this . width ,
784+ height : this . height ,
785+ transparent : this . transparent ,
786+ colors : this . #colors,
787+ layers : this . #layers,
788+ data : this . #data
789+ } ;
790+ if ( options . history === true ) {
791+ file . undo = this . #undoHistory;
792+ file . redo = this . #redoHistory;
793+ }
794+ // Trim data
795+ for ( let l = 0 ; l < file . data . length ; l ++ ) {
796+ for ( let y = file . data [ l ] . length - 1 ; y >= 0 ; y -- ) {
797+ if ( y >= this . height ) {
798+ file . data [ l ] . pop ( ) ;
799+ continue ;
800+ }
801+ for ( let x = file . data [ l ] [ y ] . length - 1 ; x >= 0 ; x -- ) {
802+ if ( x >= this . width ) {
803+ file . data [ l ] [ y ] . pop ( ) ;
804+ }
805+ }
806+ }
807+ }
808+ // Output
809+ return file ;
810+ }
811+
812+ async open ( json : File ) {
813+ const errors : string [ ] = [ ] ;
814+ // Validate 6 properties exist
815+ const keys = Object . keys ( json ) ;
816+ const required = [ 'width' , 'height' , 'transparent' , 'colors' , 'layers' , 'data' ] ;
817+ required . forEach ( ( key ) => {
818+ if ( ! keys . includes ( key ) ) {
819+ errors . push ( `JSON key '${ key } ' required.` ) ;
820+ }
821+ } ) ;
822+ }
823+
769824}
0 commit comments