@@ -32,7 +32,7 @@ import { encode as utf8 } from './util/utf8.ts';
3232import { resample , BOX , TRIANGLE , CUBIC } from './util/resample.ts' ;
3333import { rotateBilinear } from './util/rotate.ts' ;
3434import { gaussianBlur , boxBlurRGBA , premultiply , unpremultiply } from './util/blur.ts' ;
35- import { isOptions , finite , color as checkColor } from './util/options.ts' ;
35+ import { isOptions , finite , color as checkColor , quality as checkQuality , effort as checkEffort , compressionLevel } from './util/options.ts' ;
3636import { MAX_PIXELS , decoding } from './util/limits.ts' ;
3737
3838/** A color as a 32-bit RGBA integer (`0xRRGGBBAA`) */
@@ -1797,7 +1797,7 @@ export class Image {
17971797 dominantColor ( ignoreBlackOrOptions : boolean | DominantColorOptions = true , ignoreWhiteArgument = true , thresholdArgument = 0xf ) : Color {
17981798 const { ignoreBlack = true , ignoreWhite = true , threshold : bwThreshold = 0xf } = typeof ignoreBlackOrOptions === 'object' && ignoreBlackOrOptions !== null
17991799 ? ignoreBlackOrOptions : { ignoreBlack : ignoreBlackOrOptions , ignoreWhite : ignoreWhiteArgument , threshold : thresholdArgument } ;
1800- const threshold = Math . min ( 64 , Math . max ( 0 , bwThreshold ) ) * 4 ;
1800+ const threshold = Math . min ( 64 , Math . max ( 0 , finite ( bwThreshold , 'threshold' ) ) ) * 4 ;
18011801 const pixels = this . __bytes__ ( ) ;
18021802
18031803 const color = quantize . dominantColor ( pixels , ( r , g , b ) =>
@@ -2137,8 +2137,7 @@ export class Image {
21372137 ( { compression = 4 , metadata = { } } = compressionOrOptions ) ;
21382138 else metadata = compressionOrOptions as Metadata ;
21392139
2140- const level = Math . min ( 9 , Math . max ( 0 , ~ ~ finite ( compression , 'compression level' ) ) ) ;
2141- return png . encodeRGBA ( this . __bytes__ ( ) , this . width , this . height , level , pngText ( resolveMetadata ( metadata ) ) ) ;
2140+ return png . encodeRGBA ( this . __bytes__ ( ) , this . width , this . height , compressionLevel ( compression ) , pngText ( resolveMetadata ( metadata ) ) ) ;
21422141 }
21432142
21442143 /**
@@ -2218,9 +2217,11 @@ export class Image {
22182217 * @param options See {@link AVIFEncodeOptions}
22192218 */
22202219 async encodeAVIF ( { quality = 60 , alphaQuality = 100 , subsampling = '4:4:4' , effort = 1 , threads = defaultThreads ( ) , metadata = { } } : AVIFEncodeOptions = { } ) : Promise < Uint8Array > {
2220+ checkEffort ( effort , 2 ) ;
22212221 checkThreads ( threads ) ;
2222+ if ( subsampling !== '4:4:4' && subsampling !== '4:2:0' ) throw new TypeError ( `subsampling must be '4:4:4' or '4:2:0', not ${ String ( subsampling ) } ` ) ;
22222223 const exif = exifData ( resolveMetadata ( metadata ) ) ;
2223- return avifEnc . encode ( this . __bytes__ ( ) , this . width , this . height , { quality : finite ( quality , 'quality' ) , alphaQuality : finite ( alphaQuality , 'alphaQuality' ) , subsampling, effort, exif, threads } ) ;
2224+ return avifEnc . encode ( this . __bytes__ ( ) , this . width , this . height , { quality : checkQuality ( quality ) , alphaQuality : checkQuality ( alphaQuality , 'alphaQuality' ) , subsampling, effort, exif, threads } ) ;
22242225 }
22252226
22262227 /**
@@ -2234,12 +2235,11 @@ export class Image {
22342235 * @param options See {@link JXLEncodeOptions}
22352236 */
22362237 async encodeJXL ( { quality, effort = 1 , threads = defaultThreads ( ) , metadata = { } } : JXLEncodeOptions = { } ) : Promise < Uint8Array > {
2237- if ( ! Number . isInteger ( effort ) || effort < 0 || effort > 2 ) throw new RangeError ( `effort must be an integer 0-2, not ${ effort } ` ) ;
2238+ checkEffort ( effort , 2 ) ;
22382239 checkThreads ( threads ) ;
22392240 let distance = 0 ;
22402241 if ( quality !== undefined ) {
2241- const q = finite ( quality , 'quality' ) ;
2242- if ( q < 0 || q > 100 ) throw new RangeError ( `quality must be 0-100, not ${ q } ` ) ;
2242+ const q = checkQuality ( quality ) ;
22432243 // (cjxl's distance of a quality)
22442244 distance = q >= 100 ? 0 : q >= 30 ? 0.1 + ( 100 - q ) * 0.09 : 53 / 3000 * q * q - 23 / 20 * q + 25 ;
22452245 }
@@ -2267,6 +2267,7 @@ export class Image {
22672267 static async decode ( input : BinaryData , options : DecodeOptions = { } ) : Promise < Image > {
22682268 const { applyOrientation = true , applyColorProfile = true , maxPixels = MAX_PIXELS , toneMapping = 'reinhard' , threads = defaultThreads ( ) } = options ;
22692269 checkThreads ( threads ) ;
2270+ if ( ! ( Number ( maxPixels ) > 0 ) ) throw new RangeError ( `maxPixels must be a positive number, not ${ maxPixels } ` ) ;
22702271 if ( toneMapping !== 'reinhard' && toneMapping !== 'clamp' ) throw new TypeError ( `toneMapping must be 'reinhard' or 'clamp', not ${ String ( toneMapping ) } ` ) ;
22712272 const data = view ( input ) ;
22722273 const dataView = new DataView ( data . buffer , data . byteOffset , data . byteLength ) ;
@@ -2386,6 +2387,7 @@ export class Image {
23862387 if ( ! [ 1 , 2 , 3 ] . includes ( mode ) )
23872388 throw new Error ( 'Invalid SVG scaling mode' ) ;
23882389
2390+ finite ( size , 'SVG size' ) ;
23892391 if ( mode === Image . SVG_MODE_SCALE && size <= 0 )
23902392 throw new RangeError ( 'SVG scale must be > 0' ) ;
23912393 if ( mode !== Image . SVG_MODE_SCALE && size < 1 )
@@ -2451,6 +2453,7 @@ export class Image {
24512453 const { color = 0xffffffff , layout = new TextLayout ( ) , strokeWidth = 0 , strokeColor = 0x000000ff , hinting = true , features = { } , variations = { } } = options ;
24522454 const axes = textRenderer . axisValues ( variations ) ;
24532455 size = finite ( size , 'font size' ) ;
2456+ if ( size < 0 ) throw new RangeError ( `Invalid font size: ${ size } ` ) ;
24542457 if ( finite ( strokeWidth , 'stroke width' ) < 0 )
24552458 throw new RangeError ( 'Invalid stroke width' ) ;
24562459 const fonts = Array . isArray ( font ) ? font : [ font ] ;
0 commit comments