import type { UIntArray } from "@thi.ng/api" ; import { peek } from "@thi.ng/arrays/peek" ; import { isNumber } from "@thi.ng/checks/is-number" ; import { clamp0 } from "@thi.ng/math/interval" ; import { FMT_ANSI565 } from "@thi.ng/text-format/ansi" ; import { ClipRect , ImageOpts , SHADES_BLOCK } from "./api.js" ; import { canvas , Canvas } from "./canvas.js" ; import { formatCanvas } from "./format.js" ; import { charCode , intersectRect } from "./utils.js" ; export const blit = ( canvas , x , y , src ) => { x |= 0 ; y |= 0 ; const { data : sbuf , width : sw , height : sh } = src ; const { data : dbuf , width : dw } = canvas ; const { x1 , y1 , y2 , w : iw , h : ih , } = intersectRect ( { x1 : x , y1 : y , x2 : x + sw , y2 : y + sh , w : sw , h : sh } , peek ( canvas . clipRects ) ) ; if ( ! iw || ! ih ) return ; const sx = clamp0 ( x1 - x ) ; const sy = clamp0 ( y1 - y ) ; for ( let yy = sy , dy = y1 ; dy < y2 ; yy ++ , dy ++ ) { let sidx = sx + yy * sw ; let didx = x1 + dy * dw ; dbuf . set ( sbuf . subarray ( sidx , sidx + iw ) , didx ) ; } } ; export const resize = ( canvas , newWidth , newHeight ) => { if ( canvas . width === newWidth && canvas . height === newHeight ) return ; const dest = new Canvas ( newWidth , newHeight ) ; dest . data . fill ( charCode ( 0x20 , canvas . format ) ) ; blit ( dest , 0 , 0 , canvas ) ; canvas . data = dest . data ; canvas . size [ 0 ] = newWidth ; canvas . size [ 1 ] = newHeight ; canvas . clipRects = [ { x1 : 0 , y1 : 0 , x2 : newWidth , y2 : newHeight , w : newWidth , h : newHeight , } , ] ; } ; export const extract = ( canvas , x , y , w , h ) => { const dest = new Canvas ( w , h , canvas . format , peek ( canvas . styles ) ) ; blit ( dest , - x , - y , canvas ) ; return dest ; } ; param canvas param dy param clear export const scrollV = ( canvas , dy , clear = 0x20 ) => { const { data , width } = canvas ; const ch = charCode ( clear , canvas . format ) ; dy *= width ; if ( dy < 0 ) { data . copyWithin ( - dy , 0 , dy ) ; data . fill ( ch , 0 , - dy ) ; } else if ( dy > 0 ) { data . copyWithin ( 0 , dy ) ; data . fill ( ch , - dy ) ; } } ; param canvas param x param y param w param h param pixels param opts export const image = ( canvas , x , y , w , h , pixels , opts ? ) => { x |= 0 ; y |= 0 ; w |= 0 ; h |= 0 ; const { data , width } = canvas ; const { x1 , y1 , x2 , y2 , sx , sy , w : iw , h : ih , } = imgRect ( canvas , x , y , w , h ) ; if ( ! iw || ! ih ) return ; const { chars , format , gamma , invert , bits } = { chars : SHADES_BLOCK , format : canvas . format , gamma : 1 , invert : false , bits : 8 , ... opts , } ; const fmt = isNumber ( format ) ? ( ) => format : format ; const max = ( 1 << bits ) - 1 ; const mask = invert ? max : 0 ; const norm = 1 / max ; const num = chars . length - 1 ; for ( let yy = sy , dy = y1 ; dy < y2 ; yy ++ , dy ++ ) { let sidx = sx + yy * w ; let didx = x1 + dy * width ; for ( let xx = sx , dx = x1 ; dx < x2 ; xx ++ , dx ++ ) { const col = Math . pow ( ( pixels [ sidx ++ ] ^ mask ) * norm , gamma ) ; data [ didx ++ ] = charCode ( chars [ ( col * num + 0.5 ) | 0 ] , fmt ( col ) ) ; } } } ; /**
 * Optimized version of  image , which only uses a single char for all
 * pixels and applies pixel values directly as formatting data (for each pixel).
 *
 *  param canvas param x param y param w param h param pixels param char export const imageRaw = ( canvas , x , y , w , h , pixels , char = "█" ) => { x |= 0 ; y |= 0 ; w |= 0 ; h |= 0 ; const { data , width } = canvas ; const { x1 , y1 , x2 , y2 , sx , sy , w : iw , h : ih , } = imgRect ( canvas , x , y , w , h ) ; if ( ! iw || ! ih ) return ; const code = char . charCodeAt ( 0 ) ; for ( let yy = sy , dy = y1 ; dy < y2 ; yy ++ , dy ++ ) { let sidx = sx + yy * w ; let didx = x1 + dy * width ; for ( let xx = sx , dx = x1 ; dx < x2 ; xx ++ , dx ++ ) { data [ didx ++ ] = code | ( ( pixels [ sidx ++ ] & 0xffff ) << 16 ) ; } } } ; /**
 * Similar to  imageRaw , but always thresholds pixels given `thresh` and
 * converts groups of 2x4 pixels into Unicode Braille characters. Each written
 * char will use given `format` ID (optional) or default to canvas' currently
 * active format.
 *
 *  remarks param canvas param x param y param w param h param pixels param thresh param format export const imageBraille = ( canvas , x , y , w , h , pixels , thresh , format ? ) => { x |= 0 ; y |= 0 ; w |= 0 ; h |= 0 ; const { data , width } = canvas ; const fmt = ( format !== undefined ? format : canvas . format ) << 16 ; const { x1 , y1 , x2 , y2 , sx , sy , w : iw , h : ih , } = imgRect ( canvas , x , y , w >> 1 , h >> 2 ) ; if ( ! iw || ! ih ) return ; const w2 = w * 2 ; const w3 = w * 3 ; const braille = ( i ) => ( pixels [ i ] >= thresh ? 1 : 0 ) | ( pixels [ i + w ] >= thresh ? 2 : 0 ) | ( pixels [ i + w2 ] >= thresh ? 4 : 0 ) | ( pixels [ i + w3 ] >= thresh ? 8 : 0 ) | ( pixels [ i + 1 ] >= thresh ? 16 : 0 ) | ( pixels [ i + w + 1 ] >= thresh ? 32 : 0 ) | ( pixels [ i + w2 + 1 ] >= thresh ? 64 : 0 ) | ( pixels [ i + w3 + 1 ] >= thresh ? 128 : 0 ) | 0x2800 ; for ( let yy = sy , dy = y1 ; dy < y2 ; yy += 4 , dy ++ ) { let sidx = sx + yy * w ; let didx = x1 + dy * width ; for ( let xx = sx , dx = x1 ; dx < x2 ; xx += 2 , dx ++ , sidx += 2 ) { data [ didx ++ ] = braille ( sidx ) | fmt ; } } } ; /**
 * Syntax sugar for  imageBraille . Takes a thi.ng/pixel compatible 8bit
 * grayscale pixel buffer and converts it into a new  canvas .
 *
 *  remarks param src param thresh param format export const imageCanvasBraille = ( src , thresh , format = 0 ) => { const dest = canvas ( src . width >> 1 , src . height >> 2 ) ; imageBraille ( dest , 0 , 0 , src . width , src . height , src . data , thresh , format ) ; return dest ; } ; /**
 * Same as  imageCanvasBrailler , but returns resulting canvas as plain
 * string (of Unicode Braille characters).
 *
 *  param src param thresh export const imageStringBraille = ( src , thresh ) => formatCanvas ( imageCanvasBraille ( src , thresh , 0 ) ) ; /**
 * Syntax sugar for  imageRaw . Takes a thi.ng/pixel compatible 16bit
 * pixel buffer in RGB565 format and converts it into a new  canvas . The
 * optional `char` will be used as character for each pixel.
 *
 *  param src param char export const imageCanvas565 = ( src , char ? ) => { const dest = canvas ( src . width , src . height ) ; imageRaw ( dest , 0 , 0 , src . width , src . height , src . data , char ) ; return dest ; } ; /**
 * Same as  imageCanvas565 , but returns resulting canvas as 24bit ANSI
 * string.
 *
 *  param src param char export const imageString565 = ( src , char ? , fmt = FMT_ANSI565 ) => formatCanvas ( imageCanvas565 ( src , char ) , fmt ) ; const imgRect = ( canvas , x , y , w , h ) => { const rect = < any > ( intersectRect ( { x1 : x , y1 : y , x2 : x + w , y2 : y + h , w , h } , peek ( canvas . clipRects ) ) ) ; rect . sx = clamp0 ( rect . x1 - x ) ; rect . sy = clamp0 ( rect . y1 - y ) ; return rect ; } ;