/* Phaneron - Clustered, accelerated and cloud-fit video server, pre-assembled and in kit form. Copyright (C) 2020 Streampunk Media Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . https://www.streampunk.media/ mailto:furnace@streampunk.media 14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K. */ import { ProcessImpl } from './imageProcess' import { clContext as nodenCLContext, OpenCLBuffer, KernelParams } from 'nodencl' const resizeKernel = ` __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 resize( __read_only image2d_t input, __private float scale, __private float offsetX, __private float offsetY, __global float* restrict flip, __write_only image2d_t output) { int w = get_image_width(output); int h = get_image_height(output); int outX = get_global_id(0); int outY = get_global_id(1); int2 posOut = {outX, outY}; float2 inPos = (float2)(outX / (float) w, outY / (float) h); float centreOffX = (-0.5f - offsetX) / scale + 0.5f; float centreOffY = (-0.5f - offsetY) / scale + 0.5f; float2 off = (float2)(fma(centreOffX, flip[1], flip[0]), fma(centreOffY, flip[3], flip[2])); float2 mul = (float2)(flip[1] / scale, flip[3] / scale); float2 posIn = fma(inPos, mul, off); float4 in = read_imagef(input, samplerIn, posIn); write_imagef(output, posOut, in); } ` export default class Resize extends ProcessImpl { private readonly clContext: nodenCLContext private flipH: boolean private flipV: boolean private flipArr: Float32Array private readonly flipArrBytes: number private flipVals: OpenCLBuffer | null = null constructor(clContext: nodenCLContext, width: number, height: number) { super('resize', width, height, resizeKernel, 'resize') this.clContext = clContext this.flipH = false this.flipV = false this.flipArr = Float32Array.from([0.0, 1.0, 0.0, 1.0]) this.flipArrBytes = this.flipArr.length * this.flipArr.BYTES_PER_ELEMENT } private async updateFlip(flipH: boolean, flipV: boolean, clQueue: number): Promise { if (this.flipVals === null) throw new Error('Resize.updateFlip failed with no program available') this.flipH = flipH this.flipV = flipV this.flipArr = Float32Array.from([ this.flipH ? 1.0 : 0.0, this.flipH ? -1.0 : 1.0, this.flipV ? 1.0 : 0.0, this.flipV ? -1.0 : 1.0 ]) await this.flipVals.hostAccess('writeonly', clQueue, Buffer.from(this.flipArr.buffer)) return this.flipVals.hostAccess('none', clQueue) } async init(): Promise { this.flipVals = await this.clContext.createBuffer( this.flipArrBytes, 'readonly', 'coarse', undefined, 'flipVals' ) return this.updateFlip(false, false, this.clContext.queue.load) } async getKernelParams(params: KernelParams): Promise { const flipH = params.flipH as boolean const flipV = params.flipV as boolean const scale = params.scale as number const offsetX = params.offsetX as number const offsetY = params.offsetY as number if (!(this.flipH === flipH && this.flipV === flipV)) await this.updateFlip(flipH, flipV, this.clContext.queue.load) if (scale && !(scale > 0.0)) throw 'resize scale factor must be greater than zero' if (offsetX && !(offsetX >= -1.0 && offsetX <= 1.0)) throw 'resize offsetX must be between -1.0 and +1.0' if (offsetY && !(offsetY >= -1.0 && offsetY <= 1.0)) throw 'resize offsetX must be between -1.0 and +1.0' this.flipVals?.addRef() return Promise.resolve({ input: params.input, scale: params.scale || 1.0, offsetX: params.offsetX || 0.0, offsetY: params.offsetY || 0.0, flip: this.flipVals, output: params.output }) } releaseRefs(): void { this.flipVals?.release() } }