@@ -243,6 +243,68 @@ describe("nodetool.game.Tileset", () => {
243243} ) ;
244244
245245describe ( "nodetool.game.SeamlessImage" , ( ) => {
246+ it . each ( [ [ true , false ] , [ false , true ] , [ true , true ] ] ) (
247+ "repairs requested axes x=%s y=%s and preserves the center" ,
248+ async ( checkX , checkY ) => {
249+ const image = await makeImage ( 64 , 64 , ( x , y ) => [ x * 4 , y * 4 , 80 ] ) ;
250+ const result = await runNode ( ".SeamlessImage" , {
251+ image, slot_id : "bg.space" , check_x : checkX , check_y : checkY ,
252+ repair : true , threshold : 0
253+ } ) ;
254+ const output = result . output as { data : Uint8Array ; uri : string ; asset_id : string | null } ;
255+ expect ( output . uri ) . toBe ( "" ) ;
256+ expect ( output . asset_id ) . toBeNull ( ) ;
257+ const { data, info } = await sharp ( output . data ) . ensureAlpha ( ) . raw ( )
258+ . toBuffer ( { resolveWithObject : true } ) ;
259+ expect ( [ info . width , info . height ] ) . toEqual ( [ 64 , 64 ] ) ;
260+ const pixel = ( x : number , y : number ) => [ ...data . subarray ( ( y * 64 + x ) * 4 , ( y * 64 + x ) * 4 + 4 ) ] ;
261+ expect ( pixel ( 32 , 32 ) ) . toEqual ( [ 128 , 128 , 80 , 255 ] ) ;
262+ for ( let i = 0 ; i < 64 ; i ++ ) {
263+ if ( checkX ) expect ( pixel ( 0 , i ) ) . toEqual ( pixel ( 63 , i ) ) ;
264+ if ( checkY ) expect ( pixel ( i , 0 ) ) . toEqual ( pixel ( i , 63 ) ) ;
265+ }
266+ expect ( result . fill ) . toMatchObject ( { seamless_x : checkX , seamless_y : checkY } ) ;
267+ expect ( stamped ( result ) ) . toEqual ( result . fill ) ;
268+ }
269+ ) ;
270+
271+ it . each ( [ [ 1 , 1 ] , [ 1 , 7 ] , [ 7 , 1 ] , [ 2 , 2 ] , [ 9 , 11 ] , [ 1920 , 1080 ] ] ) (
272+ "repairs images sized %s by %s" ,
273+ async ( width , height ) => {
274+ const image = await makeImage ( width , height , ( x , y ) => [ x * 20 , y * 20 , 50 ] ) ;
275+ const result = await runNode ( ".SeamlessImage" , {
276+ image, slot_id : "bg.space" , check_x : true , check_y : true ,
277+ repair : true , threshold : 0
278+ } ) ;
279+ expect ( result . fill ) . toMatchObject ( { size : [ width , height ] , seamless_x : true , seamless_y : true } ) ;
280+ }
281+ ) ;
282+
283+ it ( "does not alter artwork when neither axis requires tiling" , async ( ) => {
284+ const image = await makeImage ( 16 , 16 , ( x , y ) => [ x * 10 , y * 10 , 50 ] ) ;
285+ const result = await runNode ( ".SeamlessImage" , {
286+ image, slot_id : "title" , check_x : false , check_y : false , repair : true
287+ } ) ;
288+ const output = result . output as { data : Uint8Array } ;
289+ expect ( Buffer . from ( output . data ) ) . toEqual ( Buffer . from ( image . data as string , "base64" ) ) ;
290+ } ) ;
291+
292+ it ( "blends transparency without leaking invisible colors into the edge" , async ( ) => {
293+ const pixels = Buffer . alloc ( 16 * 16 * 4 ) ;
294+ for ( let i = 0 ; i < 16 * 16 ; i ++ ) {
295+ pixels . set ( i < 128 ? [ 255 , 0 , 0 , 0 ] : [ 0 , 0 , 255 , 255 ] , i * 4 ) ;
296+ }
297+ const data = await sharp ( pixels , { raw : { width : 16 , height : 16 , channels : 4 } } ) . png ( ) . toBuffer ( ) ;
298+ const result = await runNode ( ".SeamlessImage" , {
299+ image : { type : "image" , data } , slot_id : "bg.space" ,
300+ check_x : false , check_y : true , repair : true , threshold : 0
301+ } ) ;
302+ const output = result . output as { data : Uint8Array } ;
303+ const rgba = await sharp ( output . data ) . ensureAlpha ( ) . raw ( ) . toBuffer ( ) ;
304+ expect ( [ ...rgba . subarray ( 0 , 4 ) ] ) . toEqual ( [ 0 , 0 , 255 , 128 ] ) ;
305+ expect ( [ ...rgba . subarray ( 15 * 16 * 4 , 15 * 16 * 4 + 4 ) ] ) . toEqual ( [ 0 , 0 , 255 , 128 ] ) ;
306+ } ) ;
307+
246308 const bgFar = slot ( "bg.far" ) ;
247309 if ( bgFar . kind !== "image" ) throw new Error ( "bg.far is an image" ) ;
248310 const [ w , h ] = bgFar . size ;
0 commit comments