@@ -6,27 +6,29 @@ import * as gif from './utils/gif.js';
66 */
77export class Image {
88 /**
9- * This is for internal use.
10- * If you intend on creating a new image, use { @link Image.new} or { @link Image.decode} instead.
11- * @param {SharedArrayBuffer } data
12- * @property {number } width The images width
13- * @property { number } height The images height
9+ * Creates a new image with the given dimensions
10+ * @param { number } width
11+ * @param {number } height
12+ * @param {number } [fillColor = 0] (0..0xffffffff)
13+ * @returns { Image }
1414 */
15- constructor ( data ) {
16- if ( ! ( data instanceof SharedArrayBuffer ) )
17- throw new TypeError ( ` ${ this . constructor . name } is not a constructor. Use \`Image.new\` instead` ) ;
15+ constructor ( width , height , fillColor ) {
16+ width = ~ ~ width ;
17+ height = ~ ~ height ;
1818
19- /** @private */
20- this . __array__ = new Uint8ClampedArray ( data ) ;
19+ if ( width < 1 )
20+ throw new RangeError ( 'Image has to be at least 1 pixel wide' ) ;
21+ if ( height < 1 )
22+ throw new RangeError ( 'Image has to be at least 1 pixel high' ) ;
23+
24+ this . __width__ = width ;
25+ this . __height__ = height ;
2126
2227 /**
2328 * The images RGBA pixel data
2429 * @type {Uint8ClampedArray }
2530 */
26- this . bitmap = this . __array__ . subarray ( 4 , this . __array__ . length ) ;
27-
28- if ( this . bitmap . length !== ( this . width * this . height * 4 ) )
29- throw new Error ( 'Invalid image data' ) ;
31+ this . bitmap = new Uint8ClampedArray ( width * height * 4 ) ;
3032
3133 /** @private */
3234 this . refs = new Array ( this . width ) ;
@@ -37,7 +39,7 @@ export class Image {
3739 const row_length = 4 * this . width ;
3840
3941 while ( offset < this . bitmap . length ) {
40- this . refs [ row ++ ] = new Uint32Array ( this . __array__ . buffer , 4 + offset , this . width ) ;
42+ this . refs [ row ++ ] = new Uint32Array ( this . bitmap . buffer , offset , this . width ) ;
4143
4244 offset += row_length ;
4345 }
@@ -60,57 +62,23 @@ export class Image {
6062 * @returns {Image }
6163 */
6264 static new ( width , height , fillColor = 0 ) {
63- width = ~ ~ width ;
64- height = ~ ~ height ;
65-
66- if ( width > 0xffff )
67- throw new RangeError ( 'Image width cannot exceed 65535 pixels' ) ;
68- if ( width < 1 )
69- throw new RangeError ( 'Image has to be at least 1 pixel wide' ) ;
70- if ( height > 0xffff )
71- throw new RangeError ( 'Image height cannot exceed 65535 pixels' ) ;
72- if ( height < 1 )
73- throw new RangeError ( 'Image has to be at least 1 pixel high' ) ;
74- if ( width * height > 2048 ** 2 )
75- throw new RangeError ( 'Invalid image size (<= 2^22px)' ) ;
76-
77- return Image . __new__ ( width , height , fillColor ) ;
78- }
79-
80- /**
81- * @param {number } width
82- * @param {number } height
83- * @param {number } fillColor
84- * @returns {Image }
85- * @private
86- */
87- static __new__ ( width , height , fillColor = 0 ) {
88- const data = new SharedArrayBuffer ( width * height * 4 + 4 ) ;
89- const dataUint8Clamped = new Uint8ClampedArray ( data ) ;
90- dataUint8Clamped . set ( [ width >> 8 & 0xff , width & 0xff , height >> 8 & 0xff , height & 0xff ] ) ;
91-
92- const image = new Image ( data ) ;
93-
94- if ( fillColor )
95- image . __fast_fill__ ( Image . colorToRGBA ( fillColor ) ) ;
96-
97- return image ;
65+ return new Image ( width , height , fillColor ) ;
9866 }
9967
10068 /**
10169 * The images width
10270 * @returns {number }
10371 */
10472 get width ( ) {
105- return ( this . __array__ [ 0 ] << 8 ) | this . __array__ [ 1 ] ;
73+ return this . __width__ ;
10674 }
10775
10876 /**
10977 * The images height
11078 * @returns {number }
11179 */
11280 get height ( ) {
113- return ( this . __array__ [ 2 ] << 8 ) | this . __array__ [ 3 ] ;
81+ return this . __height__ ;
11482 }
11583
11684 /**
@@ -291,6 +259,8 @@ export class Image {
291259 * @param {number } pixelColor
292260 */
293261 setPixelAt ( x , y , pixelColor ) {
262+ x = ~ ~ x ;
263+ y = ~ ~ y ;
294264 this . __check_boundaries__ ( x , y ) ;
295265 this . __set_pixel__ ( x , y , pixelColor ) ;
296266 return this ;
@@ -303,7 +273,7 @@ export class Image {
303273 * @param {number } pixelColor
304274 */
305275 __set_pixel__ ( x , y , pixelColor ) {
306- const i = ( ~ ~ y - 1 ) * this . width * 4 + ( ~ ~ x - 1 ) * 4 ;
276+ const i = ( ( y - 1 ) * this . width + ( x - 1 ) ) * 4 ;
307277 this . bitmap . set ( Image . colorToRGBA ( pixelColor ) , i ) ;
308278 }
309279
@@ -372,15 +342,9 @@ export class Image {
372342 * @returns {Image }
373343 */
374344 clone ( ) {
375- return new Image ( this . __toSharedBuffer__ ( ) . slice ( ) ) ;
376- }
377-
378- /**
379- * @private
380- * @returns {ArrayBufferLike<SharedArrayBuffer> }
381- */
382- __toSharedBuffer__ ( ) {
383- return this . __array__ . buffer ;
345+ const image = Image . new ( this . width , this . height ) ;
346+ image . bitmap . set ( this . bitmap ) ;
347+ return image ;
384348 }
385349
386350 /**
@@ -425,17 +389,10 @@ export class Image {
425389
426390 width = Math . floor ( width ) ;
427391 height = Math . floor ( height ) ;
428-
429- if ( width > 0xffff )
430- throw new RangeError ( 'Image width cannot exceed 65535 pixels' ) ;
431392 if ( width < 1 )
432393 throw new RangeError ( 'Image has to be at least 1 pixel wide' ) ;
433- if ( height > 0xffff )
434- throw new RangeError ( 'Image height cannot exceed 65535 pixels' ) ;
435394 if ( height < 1 )
436395 throw new RangeError ( 'Image has to be at least 1 pixel high' ) ;
437- if ( width * height > 2048 ** 2 )
438- throw new RangeError ( 'Invalid image size (<= 2^22px)' ) ;
439396
440397 if ( mode === Image . RESIZE_NEAREST_NEIGHBOR )
441398 return this . __resize_nearest_neighbor__ ( width , height ) ;
@@ -448,9 +405,7 @@ export class Image {
448405 * @param {number } height The new height
449406 */
450407 __resize_nearest_neighbor__ ( width , height ) {
451- const sharedArrayBuffer = new SharedArrayBuffer ( width * height * 4 + 4 ) ;
452- const destinationBuffer = new Uint8ClampedArray ( sharedArrayBuffer ) ;
453- destinationBuffer . set ( [ width >> 8 & 0xff , width & 0xff , height >> 8 & 0xff , height & 0xff ] ) ;
408+ const image = new Image ( width , height ) ;
454409
455410 for ( let y = 0 ; y < height ; y ++ ) {
456411 for ( let x = 0 ; x < width ; x ++ ) {
@@ -460,11 +415,13 @@ export class Image {
460415 const destPos = ( y * width + x ) * 4 ;
461416 const srcPos = ( ySrc * this . width + xSrc ) * 4 ;
462417
463- destinationBuffer . set ( this . bitmap . subarray ( srcPos , srcPos + 4 ) , destPos + 4 ) ;
418+ image . bitmap . set ( this . bitmap . subarray ( srcPos , srcPos + 4 ) , destPos ) ;
464419 }
465420 }
466421
467- return this . __apply__ ( new Image ( sharedArrayBuffer ) ) ;
422+ this . __apply__ ( image ) ;
423+
424+ return this ;
468425 }
469426
470427 /**
@@ -491,17 +448,15 @@ export class Image {
491448 x = ~ ~ x ;
492449 y = ~ ~ y ;
493450
494- const sharedArrayBuffer = new SharedArrayBuffer ( width * height * 4 + 4 ) ;
495- const destinationBuffer = new Uint8ClampedArray ( sharedArrayBuffer ) ;
496- destinationBuffer . set ( [ width >> 8 & 0xff , width & 0xff , height >> 8 & 0xff , height & 0xff ] ) ;
451+ const image = new Image ( width , height ) ;
497452
498453 for ( let tY = 0 ; tY < height ; tY ++ ) {
499454 const idx = ( ( tY + y ) * this . width + x ) * 4 ;
500455 const tIdx = tY * width * 4 ;
501- destinationBuffer . set ( this . bitmap . subarray ( idx , idx + width * 4 ) , tIdx + 4 ) ;
456+ image . bitmap . set ( this . bitmap . subarray ( idx , idx + width * 4 ) , tIdx ) ;
502457 }
503458
504- return new Image ( sharedArrayBuffer ) ;
459+ return image ;
505460 }
506461
507462 /**
@@ -726,7 +681,7 @@ export class Image {
726681 const bgPixel = this . getPixelAt ( tX , tY ) ;
727682 const fgPixel = source . getPixelAt ( sX , sY ) ;
728683 if ( ( fgPixel & 0xff ) === 0xff ) this . __set_pixel__ ( tX , tY , fgPixel ) ;
729- if ( ( fgPixel & 0xff ) === 0x00 ) this . __set_pixel__ ( tX , tY , bgPixel ) ;
684+ else if ( ( fgPixel & 0xff ) === 0x00 ) this . __set_pixel__ ( tX , tY , bgPixel ) ;
730685 else this . __set_pixel__ ( tX , tY , Image . __alpha_blend__ ( fgPixel , bgPixel ) ) ;
731686 }
732687
@@ -950,8 +905,10 @@ export class Image {
950905 * @returns {Image }
951906 */
952907 __apply__ ( image ) {
953- this . __array__ = image . __array__ ;
908+ this . __width__ = image . __width__ ;
909+ this . __height__ = image . __height__ ;
954910 this . bitmap = image . bitmap ;
911+ this . refs = image . refs ;
955912
956913 return this ;
957914 }
@@ -971,7 +928,11 @@ export class Image {
971928 * @return {Promise<Image> } The decoded image
972929 */
973930 static async decode ( buffer ) {
974- return new Image ( await png . decode ( new Uint8Array ( buffer ) ) ) ;
931+ const { width, height, pixels} = await png . decode ( new Uint8Array ( buffer ) ) ;
932+ const image = new Image ( width , height ) ;
933+ image . bitmap . set ( pixels ) ;
934+
935+ return image ;
975936 }
976937}
977938
@@ -981,15 +942,18 @@ export class Image {
981942 */
982943export class Frame extends Image {
983944 /**
984- * This is for internal use.
985- * If you intend on creating a frame from an Image, use {@link Frame.from} instead.
986- * @param {SharedArrayBuffer } data
987- * @param {number } [duration = 100]
945+ * Creates a new, blank frame
946+ * @param {number } width
947+ * @param {number } height
948+ * @param {number } [duration = 100] The frames duration (in ms)
949+ * @param {number } [fillColor = 0] (0..0xffffffff)
950+ * @return {Frame }
988951 */
989- constructor ( data , duration = 100 ) {
990- super ( data ) ;
991- if ( isNaN ( duration ) || duration < 0 || duration > 0xffff )
952+ constructor ( width , height , duration = 100 , fillColor ) {
953+ if ( isNaN ( duration ) || duration < 0 )
992954 throw new RangeError ( 'Invalid frame duration' ) ;
955+
956+ super ( width , height , fillColor ) ;
993957 this . duration = duration ;
994958 }
995959
@@ -1005,9 +969,8 @@ export class Frame extends Image {
1005969 * @param {number } [fillColor = 0] (0..0xffffffff)
1006970 * @return {Frame }
1007971 */
1008- static new ( width , height , duration , fillColor ) {
1009- const image = super . new ( width , height , fillColor ) ;
1010- return Frame . from ( image , duration ) ;
972+ static new ( width , height , duration = 100 , fillColor ) {
973+ return new Frame ( width , height , duration , fillColor ) ;
1011974 }
1012975
1013976 /**
@@ -1019,18 +982,10 @@ export class Frame extends Image {
1019982 static from ( image , duration ) {
1020983 if ( ! ( image instanceof Image ) )
1021984 throw new TypeError ( 'Invalid image passed' ) ;
1022- return new Frame ( image . __toSharedBuffer__ ( ) , duration ) ;
1023- }
985+ const frame = new Frame ( image . width , image . height , duration ) ;
986+ frame . bitmap . set ( image . bitmap ) ;
1024987
1025- /**
1026- * @private
1027- */
1028- __toSharedTimedBuffer__ ( ) {
1029- const imageBuffer = this . __array__ ;
1030- const data = new Uint8Array ( imageBuffer . byteLength + 2 ) ;
1031- data . set ( [ ( this . duration >> 8 ) & 0xff , this . duration & 0xff ] , 0 ) ;
1032- data . set ( imageBuffer , 2 ) ;
1033- return data ;
988+ return frame ;
1034989 }
1035990}
1036991
@@ -1055,21 +1010,11 @@ export class GIF extends Array {
10551010 throw new TypeError ( `Frame ${ frames . indexOf ( frame ) } is not an instance of Frame` ) ;
10561011 }
10571012
1058- if ( loopCount < 0 || loopCount > 0xffff )
1013+ if ( loopCount < 0 )
10591014 throw new RangeError ( 'Invalid loop count' ) ;
10601015
10611016 super ( ...frames ) ;
10621017 this . loopCount = loopCount ;
1063-
1064- const sizeSqrt = Math . sqrt ( this . width * this . height ) ;
1065- const maxFrames = Math . floor ( 30 * 512 / sizeSqrt ) ;
1066-
1067- if ( Math . max ( this . width , this . height ) > 0xffff )
1068- throw new RangeError ( 'Invalid GIF dimensions (<= 65535px)' ) ;
1069- if ( this . width * this . height > 2048 ** 2 )
1070- throw new RangeError ( 'Invalid GIF size (<= 2^22px)' ) ;
1071- if ( frames . length > maxFrames )
1072- throw new RangeError ( `GIF cannot exceed ${ maxFrames } frames at this resolution` ) ;
10731018 }
10741019
10751020 toString ( ) {
@@ -1100,13 +1045,6 @@ export class GIF extends Array {
11001045 return Math . max ( ...[ ...this ] . map ( frame => frame . height ) ) ;
11011046 }
11021047
1103- /**
1104- * @private
1105- */
1106- __toArray__ ( ) {
1107- return [ this . loopCount , ...[ ...this ] . map ( frame => frame . __toSharedTimedBuffer__ ( ) ) ] ;
1108- }
1109-
11101048 /**
11111049 * Encodes the image into a GIF
11121050 * @return {Uint8Array } The encoded data
0 commit comments