UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

121 lines (103 loc) 3.1 kB
import { BaseFilter } from './BaseFilter'; import { classRegistry } from '../ClassRegistry'; import { fragmentSource } from './shaders/gamma'; import type { T2DPipelineState, TWebGLUniformLocationMap } from './typedefs'; const GAMMA = 'Gamma' as const; export type GammaInput = [number, number, number]; export type GammaOwnProps = { gamma: GammaInput; }; export const gammaDefaultValues: GammaOwnProps = { gamma: [1, 1, 1], }; /** * Gamma filter class * @example * const filter = new Gamma({ * gamma: [1, 0.5, 2.1] * }); * object.filters.push(filter); * object.applyFilters(); */ export class Gamma extends BaseFilter<typeof GAMMA, GammaOwnProps> { /** * Gamma array value, from 0.01 to 2.2. * @param {Array} gamma * @default */ declare gamma: GammaOwnProps['gamma']; declare rgbValues?: { r: Uint8Array; g: Uint8Array; b: Uint8Array; }; static type = GAMMA; static defaults = gammaDefaultValues; static uniformLocations = ['uGamma']; getFragmentSource() { return fragmentSource; } constructor(options: { gamma?: GammaInput } = {}) { super(options); this.gamma = options.gamma || (( this.constructor as typeof Gamma ).defaults.gamma.concat() as GammaInput); } /** * Apply the Gamma operation to a Uint8Array representing the pixels of an image. * * @param {Object} options * @param {ImageData} options.imageData The Uint8Array to be filtered. */ applyTo2d({ imageData: { data } }: T2DPipelineState) { const gamma = this.gamma, rInv = 1 / gamma[0], gInv = 1 / gamma[1], bInv = 1 / gamma[2]; if (!this.rgbValues) { this.rgbValues = { r: new Uint8Array(256), g: new Uint8Array(256), b: new Uint8Array(256), }; } // This is an optimization - pre-compute a look-up table for each color channel // instead of performing these pow calls for each pixel in the image. const rgb = this.rgbValues; for (let i = 0; i < 256; i++) { rgb.r[i] = Math.pow(i / 255, rInv) * 255; rgb.g[i] = Math.pow(i / 255, gInv) * 255; rgb.b[i] = Math.pow(i / 255, bInv) * 255; } for (let i = 0; i < data.length; i += 4) { data[i] = rgb.r[data[i]]; data[i + 1] = rgb.g[data[i + 1]]; data[i + 2] = rgb.b[data[i + 2]]; } } /** * Send data from this filter to its shader program's uniforms. * * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader. * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects */ sendUniformData( gl: WebGLRenderingContext, uniformLocations: TWebGLUniformLocationMap, ) { gl.uniform3fv(uniformLocations.uGamma, this.gamma); } isNeutralState() { const { gamma } = this; return gamma[0] === 1 && gamma[1] === 1 && gamma[2] === 1; } toObject(): { type: typeof GAMMA; gamma: GammaInput } { return { type: GAMMA, gamma: this.gamma.concat() as GammaInput, }; } } classRegistry.setClass(Gamma);