import { Gradient , TextTexture , TextureFont } from "@replay/core/dist/t" ; import { createProgram , hexToRGB } from "./glUtils" ; import { m2d , Matrix2D } from "./matrix" ; const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texcoord;

uniform mat3 u_matrix;

varying vec2 v_texcoord;

const mat3 flipY = mat3(
  1.0, 0.0, 0.0,
  0.0, -1.0, 0.0,
  0.0, 1.0, 1.0
);

void main() {
  gl_Position = vec4(u_matrix * vec3(a_position, 1.0), 1.0);
  v_texcoord = (flipY * vec3(a_texcoord, 1.0)).xy;
}` ; const fragmentShaderSource = `
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;
uniform float u_opacity;

void main() {
  gl_FragColor = texture2D(u_texture, v_texcoord) * u_opacity;
}
` ; export function getDrawCanvas ( gl , glVao ) { const program = createProgram ( gl , vertexShaderSource , fragmentShaderSource ) ; const vao = glVao . createVertexArrayOES ( ) ; glVao . bindVertexArrayOES ( vao ) ; const aPositionLocation = gl . getAttribLocation ( program , "a_position" ) ; const aTexcoordLocation = gl . getAttribLocation ( program , "a_texcoord" ) ; const uMatrixLocation = gl . getUniformLocation ( program , "u_matrix" ) ; const uTextureLocation = gl . getUniformLocation ( program , "u_texture" ) ; const uOpacityLocation = gl . getUniformLocation ( program , "u_opacity" ) ; const positionBuffer = gl . createBuffer ( ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , positionBuffer ) ; gl . enableVertexAttribArray ( aPositionLocation ) ; gl . vertexAttribPointer ( aPositionLocation , 2 , gl . FLOAT , false , 0 , 0 ) ; const positions = [ - 0.5 , - 0.5 , - 0.5 , 0.5 , 0.5 , - 0.5 , 0.5 , - 0.5 , - 0.5 , 0.5 , 0.5 , 0.5 , ] ; gl . bufferData ( gl . ARRAY_BUFFER , new Float32Array ( positions ) , gl . STATIC_DRAW ) ; const texcoordBuffer = gl . createBuffer ( ) ; gl . bindBuffer ( gl . ARRAY_BUFFER , texcoordBuffer ) ; gl . enableVertexAttribArray ( aTexcoordLocation ) ; gl . vertexAttribPointer ( aTexcoordLocation , 2 , gl . FLOAT , false , 0 , 0 ) ; const texcoords = [ 0 , 0 , 0 , 1 , 1 , 0 , 1 , 0 , 0 , 1 , 1 , 1 , ] ; gl . bufferData ( gl . ARRAY_BUFFER , new Float32Array ( texcoords ) , gl . STATIC_DRAW ) ; glVao . bindVertexArrayOES ( null ) ; return function drawCanvas ( offscreenTexture , matrix , width , height , opacity , devicePixelRatio , prevProgram ) : { program : WebGLProgram ; texture : WebGLTexture ; } { gl . bindTexture ( gl . TEXTURE_2D , offscreenTexture ) ; if ( program !== prevProgram ) { gl . useProgram ( program ) ; glVao . bindVertexArrayOES ( vao ) ; } const uMatrixValue = m2d . multiply ( matrix , m2d . getScaleMatrix ( width / devicePixelRatio , height / devicePixelRatio ) ) ; gl . uniformMatrix3fv ( uMatrixLocation , false , m2d . toUniform3fv ( uMatrixValue ) ) ; gl . uniform1i ( uTextureLocation , 0 ) ; gl . uniform1f ( uOpacityLocation , opacity ) ; gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ; return { program , texture : offscreenTexture } ; } ; } export function createCanvasTexture ( gl , canvas ) { const texture = gl . createTexture ( ) ! ; gl . bindTexture ( gl . TEXTURE_2D , texture ) ; gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_S , gl . CLAMP_TO_EDGE ) ; gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_T , gl . CLAMP_TO_EDGE ) ; gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MIN_FILTER , gl . LINEAR ) ; gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , canvas ) ; return texture ; } /**
 * Returns required align
 */ export function handleTextTexture ( texture , offscreenCanvas , offscreenCanvasCtx , defaultFont , devicePixelRatio ) { const fontDetails = { ... defaultFont , ... texture . props . font } ; const { size = 10 , weight = "normal" , style = "normal" , family , align = "center" , baseline = "middle" , } = fontDetails ; const fontString = `${ style } ${ weight } ${ size ? `${ size }px` : "" } ${ family ? `${ family }` : "" }` ; const { text , strokeThickness = 1 , color } = texture . props ; resetCanvas ( offscreenCanvas , offscreenCanvasCtx , devicePixelRatio , getTextWidth ( offscreenCanvasCtx , text , strokeThickness , fontString ) , size * 1.5 * ( baseline !== "middle" ? 2 : 1 ) ) ; drawText ( offscreenCanvasCtx , fontString , text , color , strokeThickness , baseline , "center" , texture . props . gradient , texture . props . strokeColor ) ; return align ; } function drawText ( ctx , font , text , color , strokeThickness , baseline , align , gradient ? , strokeColor ? ) { ctx . font = font ; ctx . textBaseline = baseline ; ctx . textAlign = align ; if ( strokeColor ) { ctx . strokeStyle = strokeColor ; ctx . lineWidth = strokeThickness ; ctx . strokeText ( text , 0 , 0 ) ; } ctx . fillStyle = gradient ? generateGradient ( ctx , gradient ) : color ; ctx . fillText ( text , 0 , 0 ) ; } const generateGradient = ( ctx , gradient ) : CanvasGradient => { const canvasGradient = gradient . type === "linearHoriz" ? ctx . createLinearGradient ( - gradient . width / 2 , 0 , gradient . width / 2 , 0 ) : ctx . createLinearGradient ( 0 , - gradient . height / 2 , 0 , gradient . height / 2 ) ; gradient . colors . forEach ( ( colour , index ) => { if ( gradient . opacities ) { const a = gradient . opacities [ index ] ; if ( a !== undefined ) { const [ r , g , b ] = hexToRGB ( colour ) ; colour = `rgba(${ r * 255 }, ${ g * 255 }, ${ b * 255 }, ${ a }` ; } } const offset = index / ( gradient . colors . length - 1 ) ; canvasGradient . addColorStop ( offset , colour ) ; } ) ; return canvasGradient ; } ; function resetCanvas ( canvas , ctx , devicePixelRatio , width , height ) { const canvasWidth = devicePixelRatio * width ; const canvasHeight = devicePixelRatio * height ; if ( canvasWidth !== canvas . width || canvasHeight !== canvas . height ) { canvas . width = canvasWidth ; canvas . height = canvasHeight ; ctx . translate ( canvasWidth / 2 , canvasHeight / 2 ) ; ctx . scale ( devicePixelRatio , devicePixelRatio ) ; } else { ctx . clearRect ( - canvasWidth / 2 , - canvasHeight / 2 , canvas . width , canvas . height ) ; } } function getTextWidth ( ctx , text , strokeThickness , font ) { ctx . font = font ; return ctx . measureText ( text ) . width + strokeThickness * 2 ; }