UNPKG

@luma.gl/gltools

Version:

WebGL2 API Polyfills for WebGL1 WebGLRenderingContext

95 lines (82 loc) 3.14 kB
/** @typedef {import('./device-pixels')} types */ /** * Returns multiplier need to convert CSS size to Device size * @type {types['cssToDeviceRatio']} */ export function cssToDeviceRatio(gl) { // @ts-ignore const {luma} = gl; if (gl.canvas && luma) { // For headless gl we might have used custom width and height // hence prioritize cached clientWidth const cachedSize = luma.canvasSizeInfo; const clientWidth = 'clientWidth' in cachedSize ? cachedSize.clientWidth : gl.canvas.clientWidth; return clientWidth ? gl.drawingBufferWidth / clientWidth : 1; } // use default device pixel ratio return 1; } /** * Maps CSS pixel position to device pixel position * @type {types['cssToDevicePixels']} */ export function cssToDevicePixels(gl, cssPixel, yInvert = true) { const ratio = cssToDeviceRatio(gl); const width = gl.drawingBufferWidth; const height = gl.drawingBufferHeight; return scalePixels(cssPixel, ratio, width, height, yInvert); } // HELPER METHOD /** * Calulates device pixel ratio, used during context creation * @type {types['getDevicePixelRatio']} */ export function getDevicePixelRatio(useDevicePixels) { const windowRatio = typeof window === 'undefined' ? 1 : window.devicePixelRatio || 1; if (Number.isFinite(useDevicePixels)) { // @ts-ignore Can no longer be boolean after previous line return useDevicePixels <= 0 ? 1 : useDevicePixels; } return useDevicePixels ? windowRatio : 1; } // PRIVATE function scalePixels(pixel, ratio, width, height, yInvert) { const x = scaleX(pixel[0], ratio, width); let y = scaleY(pixel[1], ratio, height, yInvert); // Find boundaries of next pixel to provide valid range of device pixel locaitons let t = scaleX(pixel[0] + 1, ratio, width); // If next pixel's position is clamped to boundary, use it as is, otherwise subtract 1 for current pixel boundary const xHigh = t === width - 1 ? t : t - 1; t = scaleY(pixel[1] + 1, ratio, height, yInvert); let yHigh; if (yInvert) { // If next pixel's position is clamped to boundary, use it as is, otherwise clamp it to valid range t = t === 0 ? t : t + 1; // swap y and yHigh yHigh = y; y = t; } else { // If next pixel's position is clamped to boundary, use it as is, otherwise clamp it to valid range yHigh = t === height - 1 ? t : t - 1; // y remains same } return { x, y, // when ratio < 1, current css pixel and next css pixel may point to same device pixel, set width/height to 1 in those cases. width: Math.max(xHigh - x + 1, 1), height: Math.max(yHigh - y + 1, 1) }; } function scaleX(x, ratio, width) { // since we are rounding to nearest, when ratio > 1, edge pixels may point to out of bounds value, clamp to the limit const r = Math.min(Math.round(x * ratio), width - 1); return r; } function scaleY(y, ratio, height, yInvert) { // since we are rounding to nearest, when ratio > 1, edge pixels may point to out of bounds value, clamp to the limit return yInvert ? Math.max(0, height - 1 - Math.round(y * ratio)) : Math.min(Math.round(y * ratio), height - 1); }