import { ProcessImpl } from './imageProcess' ; import { clContext as nodenCLContext , OpenCLBuffer , KernelParams } from 'nodencl' ; import { matrixFlatten , matrixMultiply } from './colourMaths' ; const transformKernel = `
  __constant sampler_t samplerIn =
    CLK_NORMALIZED_COORDS_TRUE |
    CLK_ADDRESS_CLAMP |
    CLK_FILTER_LINEAR;

  __constant sampler_t samplerOut =
    CLK_NORMALIZED_COORDS_FALSE |
    CLK_ADDRESS_CLAMP |
    CLK_FILTER_NEAREST;

  __kernel void transform(
    __read_only image2d_t input,
    __global float4* restrict transformMatrix,
    __write_only image2d_t output) {

    int w = get_image_width(output);
    int h = get_image_height(output);

    // Load two rows of the 3x3 transform matrix via two float4s
    float4 tmpMat0 = transformMatrix[0];
    float4 tmpMat1 = transformMatrix[1];
    float3 mat0 = (float3)(tmpMat0.s0, tmpMat0.s1, tmpMat0.s2);
    float3 mat1 = (float3)(tmpMat0.s3, tmpMat1.s0, tmpMat1.s1);

    int outX = get_global_id(0);
    int outY = get_global_id(1);
    int2 posOut = {outX, outY};

		float3 inPos = (float3)(outX / (float) w - 0.5f, outY / (float) h - 0.5f, 1.0f);
		float2 posIn = (float2)(dot(mat0, inPos) + 0.5f, dot(mat1, inPos) + 0.5f);

    float4 in = read_imagef(input, samplerIn, posIn);
    write_imagef(output, posOut, in);
  }
` ; export default class Transform extends ProcessImpl { clContext : nodenCLContext ; transformMatrix : Array < Float32Array > ; transformArray : Float32Array ; matrixBuffer : OpenCLBuffer | null = null ; curParams : KernelParams | null = null ; constructor ( clContext , width , height ) { super ( 'transform' , width , height , transformKernel , 'transform' ) ; this . clContext = clContext ; this . transformMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; this . transformMatrix [ 0 ] = Float32Array . from ( [ 1.0 , 0.0 , 0.0 ] ) ; this . transformMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 , 0.0 ] ) ; this . transformMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; this . transformArray = matrixFlatten ( this . transformMatrix ) ; } private async updateMatrix ( clQueue ) { if ( ! this . matrixBuffer ) throw new Error ( 'Transform needs to be initialised' ) ; this . transformArray = matrixFlatten ( this . transformMatrix ) ; await this . matrixBuffer . hostAccess ( 'writeonly' , clQueue , Buffer . from ( this . transformArray . buffer ) ) ; return this . matrixBuffer . hostAccess ( 'none' , clQueue ) ; } async init ( ) { this . matrixBuffer = await this . clContext . createBuffer ( this . transformArray . byteLength , 'readonly' , 'coarse' , undefined , 'transformMatrix' ) ; await this . updateMatrix ( this . clContext . queue . load ) ; return this . clContext . waitFinish ( this . clContext . queue . load ) ; } private checkParamsChange ( params ) { return ( this . curParams !== null && params . flipH === this . curParams . flipH && params . flipV === this . curParams . flipV && params . anchorX === this . curParams . anchorX && params . anchorY === this . curParams . anchorY && params . scaleX === this . curParams . scaleX && params . scaleY === this . curParams . scaleY && params . offsetX === this . curParams . offsetX && params . offsetY === this . curParams . offsetY && params . rotate === this . curParams . rotate ) ; } async getKernelParams ( params ) { if ( ! this . checkParamsChange ( params ) ) { const aspect = this . width / this . height ; const flipX = ( params . flipH as boolean ) || false ? - 1.0 : 1.0 ; const flipY = ( params . flipV as boolean ) || false ? - 1.0 : 1.0 ; const anchorX = ( params . anchorX as number ) || 0.0 ; const anchorY = ( params . anchorY as number ) || 0.0 ; const scaleX = ( ( params . scaleX as number ) || 1.0 ) * flipX ; const scaleY = ( ( params . scaleY as number ) || 1.0 ) * flipY ; const offsetX = ( params . offsetX as number ) || 0.0 ; const offsetY = ( params . offsetY as number ) || 0.0 ; const rotate = ( ( params . rotate as number ) || 0.0 ) * 2 * Math . PI ; const anchorInMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; anchorInMatrix [ 0 ] = Float32Array . from ( [ 1.0 , 0.0 , anchorX ] ) ; anchorInMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 , anchorY ] ) ; anchorInMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; const scaleMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; scaleMatrix [ 0 ] = Float32Array . from ( [ 1.0 / ( scaleX * aspect ) , 0.0 , 0.0 ] ) ; scaleMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 / scaleY , 0.0 ] ) ; scaleMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; const rotateMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; rotateMatrix [ 0 ] = Float32Array . from ( [ Math . cos ( rotate ) , - Math . sin ( rotate ) , 0.0 ] ) ; rotateMatrix [ 1 ] = Float32Array . from ( [ Math . sin ( rotate ) , Math . cos ( rotate ) , 0.0 ] ) ; rotateMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; const translateMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; translateMatrix [ 0 ] = Float32Array . from ( [ 1.0 , 0.0 , offsetX * aspect ] ) ; translateMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 , offsetY ] ) ; translateMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; const anchorOutMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; anchorOutMatrix [ 0 ] = Float32Array . from ( [ 1.0 , 0.0 , - anchorX * aspect ] ) ; anchorOutMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 , - anchorY ] ) ; anchorOutMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; const projectMatrix = [ ... new Array ( 3 ) ] . map ( ( ) => new Float32Array ( 3 ) ) ; projectMatrix [ 0 ] = Float32Array . from ( [ aspect , 0.0 , 0.0 ] ) ; projectMatrix [ 1 ] = Float32Array . from ( [ 0.0 , 1.0 , 0.0 ] ) ; projectMatrix [ 2 ] = Float32Array . from ( [ 0.0 , 0.0 , 1.0 ] ) ; this . transformMatrix = matrixMultiply ( matrixMultiply ( matrixMultiply ( matrixMultiply ( matrixMultiply ( anchorInMatrix , scaleMatrix ) , rotateMatrix ) , translateMatrix ) , anchorOutMatrix ) , projectMatrix ) ; await this . updateMatrix ( this . clContext . queue . load ) ; await this . clContext . waitFinish ( this . clContext . queue . load ) ; } this . curParams = params ; this . matrixBuffer ?. addRef ( ) ; return Promise . resolve ( { input : params . input , transformMatrix : this . matrixBuffer , output : params . output } ) ; } releaseRefs ( ) { this . matrixBuffer ?. release ( ) ; } }