import { timed } from "@thi.ng/bench/timed" ; import { abgr32 } from "@thi.ng/color/int/int" ; import { luminanceAbgr32 } from "@thi.ng/color/luminance-rgb" ; import { sortMapped } from "@thi.ng/color/sort" ; import { div } from "@thi.ng/hiccup-html/blocks" ; import { checkbox , inputFile , inputNumber , inputRange , label , type , InputNumericAttribs } from "@thi.ng/hiccup-html/forms" ; import { h1 } from "@thi.ng/hiccup-html/sections" ; import { closedOpen , intersection } from "@thi.ng/intervals" ; import { ABGR8888 } from "@thi.ng/pixel/format/abgr8888" ; import { IntBuffer , intBufferFromImage } from "@thi.ng/pixel/int" ; import { SYSTEM } from "@thi.ng/random/system" ; import type { NumOrElement } from "@thi.ng/rdom" ; import { $compile } from "@thi.ng/rdom/compile" ; import { Component } from "@thi.ng/rdom/component" ; import { $replace } from "@thi.ng/rdom/replace" ; import { CloseMode } from "@thi.ng/rstream/api" ; import { reactive , Stream , stream } from "@thi.ng/rstream/stream" ; import { sync } from "@thi.ng/rstream/sync" ; import { map } from "@thi.ng/transducers/map" ; interface ProcessParams { iter : number ; horizontal : boolean ; reverse : boolean ; min : number ; max : number ; } param buf param param1 const pixelSortBuffer = ( buf , { iter , horizontal , reverse , min , max } ) => { const { pixels , width , height } = buf ; const row = closedOpen ( 0 , width ) ; const column = closedOpen ( 0 , height ) ; for ( let i = iter ; i -- > 0 ; ) { const num = SYSTEM . minmax ( min , max ) | 0 ; const n2 = num >> 1 ; const x = SYSTEM . minmax ( horizontal ? - n2 : 0 , width ) | 0 ; const y = SYSTEM . minmax ( horizontal ? 0 : - n2 , height ) | 0 ; const ix = intersection ( closedOpen ( x , x + num ) , row ) ! ; const iy = intersection ( closedOpen ( y , y + num ) , column ) ! ; if ( ! ( ix && iy && ix . size && iy . size ) ) continue ; const strip = abgr32 . mapBuffer ( pixels , ( horizontal ? ix : iy ) . size , iy . l * width + ix . l , 1 , horizontal ? 1 : width ) ; sortMapped ( strip , luminanceAbgr32 , reverse ) ; } return buf ; } ; const processImage = ( img , opts ) => timed ( ( ) => pixelSortBuffer ( intBufferFromImage ( img , ABGR8888 ) , opts ) ) ; const file = stream < File > ( ) ; const image = stream < HTMLImageElement > ( ) ; const iter = reactive ( 1000 ) ; const min = reactive ( 50 ) ; const max = reactive ( 200 ) ; const horizontal = reactive ( true ) ; const reverse = reactive ( false ) ; const result = sync ( { src : { image , iter , horizontal , reverse , min , max , } , closeOut : CloseMode . NEVER , xform : map ( ( params ) => processImage ( params . image , params ) ) , } ) ; file . subscribe ( { next ( file ) { const url = URL . createObjectURL ( file ) ; const img = new Image ( ) ; img . onload = ( ) => { image . next ( img ) ; URL . revokeObjectURL ( url ) ; } ; img . src = url ; } , } ) ; class PixelCanvas extends Component { constructor ( protected buffer ) { super ( ) ; } async mount ( parent , index ? ) { const buf = this . buffer ; this . el = this . $el ( "canvas" , { width : buf . width , height : buf . height } , null , parent , index ) ; buf . blitCanvas ( < HTMLCanvasElement > this . el ) ; return this . el ; } } const labeledRange = ( stream , id , labelBody , opts ) => { const onchange = ( e ) => stream . next ( parseInt ( ( < HTMLInputElement > e . target ) . value ) ) ; return div ( ".mb2" , { } , label ( ".dib.w4" , { for : id } , labelBody ) , inputRange ( { ... opts , id , value : stream , onchange , } ) , inputNumber ( ".ml3.w4.tr" , { ... opts , value : stream , onchange , } ) ) ; } ; const labeledCheckbox = ( stream , id , labelBody ) => div ( ".mb2" , { } , label ( ".dib.w4" , { for : id } , labelBody ) , checkbox ( { id , checked : stream , onchange : ( e ) => stream . next ( ! ! ( < HTMLInputElement > e . target ) . checked ) , } ) ) ; $compile ( div ( { } , h1 ( { } , "Glitch my pic!" ) , div ( ".mb2" , { } , label ( ".dib.w4" , { for : "file" } , "Image" ) , inputFile ( { id : "file" , accept : [ "image/jpg" , "image/png" , "image/gif" ] , multiple : false , onchange : ( e ) => file . next ( ( < HTMLInputElement > e . target ) . files ! [ 0 ] ) , } ) ) , labeledRange ( iter , "iter" , "Iterations" , { min : 0 , max : 50000 , step : 1000 , } ) , labeledRange ( min , "min" , "Min. scatter" , { min : 0 , max : 200 , step : 5 , } ) , labeledRange ( max , "max" , "Max. scatter" , { min : 0 , max : 200 , step : 5 , } ) , labeledCheckbox ( horizontal , "horizontal" , "Horizontal" ) , labeledCheckbox ( reverse , "order" , "Reverse order" ) , div ( { } , $replace ( result . map ( ( buf ) => new PixelCanvas ( buf ) ) ) ) ) ) . mount ( document . getElementById ( "app" ) ! ) ;