Move parts to libraries (flutter/engine#26054)
This commit is contained in:
@@ -493,6 +493,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/color_filter.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/image_filter.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/offscreen_canvas.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/offset.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/opacity.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/painting.dart
|
||||
@@ -518,6 +519,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/normalized_gradien
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/webgl_context.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart
|
||||
|
||||
@@ -41,6 +41,9 @@ export 'engine/browser_detection.dart';
|
||||
import 'engine/html_image_codec.dart';
|
||||
export 'engine/html_image_codec.dart';
|
||||
|
||||
import 'engine/html/offscreen_canvas.dart';
|
||||
export 'engine/html/offscreen_canvas.dart';
|
||||
|
||||
import 'engine/html/painting.dart';
|
||||
export 'engine/html/painting.dart';
|
||||
|
||||
@@ -83,6 +86,9 @@ export 'engine/html/shaders/shader_builder.dart';
|
||||
import 'engine/html/shaders/vertex_shaders.dart';
|
||||
export 'engine/html/shaders/vertex_shaders.dart';
|
||||
|
||||
import 'engine/html/shaders/webgl_context.dart';
|
||||
export 'engine/html/shaders/webgl_context.dart';
|
||||
|
||||
import 'engine/mouse_cursor.dart';
|
||||
export 'engine/mouse_cursor.dart';
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
/// Polyfill for html.OffscreenCanvas that is not supported on some browsers.
|
||||
class OffScreenCanvas {
|
||||
html.OffscreenCanvas? offScreenCanvas;
|
||||
html.CanvasElement? canvasElement;
|
||||
int width;
|
||||
int height;
|
||||
static bool? _supported;
|
||||
|
||||
OffScreenCanvas(this.width, this.height) {
|
||||
if (OffScreenCanvas.supported) {
|
||||
offScreenCanvas = html.OffscreenCanvas(width, height);
|
||||
} else {
|
||||
canvasElement = html.CanvasElement(
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
canvasElement!.className = 'gl-canvas';
|
||||
final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
canvasElement!.style
|
||||
..position = 'absolute'
|
||||
..width = '${cssWidth}px'
|
||||
..height = '${cssHeight}px';
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
offScreenCanvas = null;
|
||||
canvasElement = null;
|
||||
}
|
||||
|
||||
/// Returns CanvasRenderContext2D or OffscreenCanvasRenderingContext2D to
|
||||
/// paint into.
|
||||
Object? getContext2d() {
|
||||
return (offScreenCanvas != null
|
||||
? offScreenCanvas!.getContext('2d')
|
||||
: canvasElement!.getContext('2d'));
|
||||
}
|
||||
|
||||
/// Feature detection for transferToImageBitmap on OffscreenCanvas.
|
||||
bool get transferToImageBitmapSupported =>
|
||||
js_util.hasProperty(offScreenCanvas!, 'transferToImageBitmap');
|
||||
|
||||
/// Creates an ImageBitmap object from the most recently rendered image
|
||||
/// of the OffscreenCanvas.
|
||||
///
|
||||
/// !Warning API still in experimental status, feature detect before using.
|
||||
Object? transferToImageBitmap() {
|
||||
return js_util.callMethod(offScreenCanvas!, 'transferToImageBitmap',
|
||||
<dynamic>[]);
|
||||
}
|
||||
|
||||
/// Draws canvas contents to a rendering context.
|
||||
void transferImage(Object targetContext) {
|
||||
// Actual size of canvas may be larger than viewport size. Use
|
||||
// source/destination to draw part of the image data.
|
||||
js_util.callMethod(targetContext, 'drawImage',
|
||||
<dynamic>[offScreenCanvas ?? canvasElement!, 0, 0, width, height,
|
||||
0, 0, width, height]);
|
||||
}
|
||||
|
||||
/// Converts canvas contents to an image and returns as data url.
|
||||
Future<String> toDataUrl() {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
if (offScreenCanvas != null) {
|
||||
offScreenCanvas!.convertToBlob().then((html.Blob value) {
|
||||
final fileReader = html.FileReader();
|
||||
fileReader.onLoad.listen((event) {
|
||||
completer.complete(js_util.getProperty(
|
||||
js_util.getProperty(event, 'target')!, 'result')!);
|
||||
});
|
||||
fileReader.readAsDataUrl(value);
|
||||
});
|
||||
return completer.future;
|
||||
} else {
|
||||
return Future.value(canvasElement!.toDataUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws an image to canvas for both offscreen canvas canvas context2d.
|
||||
void drawImage(Object image, int x, int y, int width, int height) {
|
||||
js_util.callMethod(
|
||||
getContext2d()!, 'drawImage', <dynamic>[image, x, y, width, height]);
|
||||
}
|
||||
|
||||
/// Feature detects OffscreenCanvas.
|
||||
static bool get supported => _supported ??=
|
||||
js_util.hasProperty(html.window, 'OffscreenCanvas');
|
||||
}
|
||||
@@ -58,7 +58,7 @@ void initWebGl() {
|
||||
}
|
||||
|
||||
void disposeWebGl() {
|
||||
_GlContextCache.dispose();
|
||||
GlContextCache.dispose();
|
||||
_glRenderer = null;
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ class _WebGlRenderer implements _GlRenderer {
|
||||
: _writeVerticesTextureFragmentShader(isWebGl2,
|
||||
imageShader.tileModeX, imageShader.tileModeY);
|
||||
|
||||
GlContext gl = _GlContextCache.createGlContext(widthInPixels, heightInPixels)!;
|
||||
GlContext gl = GlContextCache.createGlContext(widthInPixels, heightInPixels)!;
|
||||
|
||||
GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader);
|
||||
gl.useProgram(glProgram);
|
||||
@@ -613,512 +613,3 @@ Float32List _convertVertexPositions(ui.VertexMode mode, Float32List positions) {
|
||||
return triangleList;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiled and cached gl program.
|
||||
class GlProgram {
|
||||
final Object program;
|
||||
GlProgram(this.program);
|
||||
}
|
||||
|
||||
/// JS Interop helper for webgl apis.
|
||||
class GlContext {
|
||||
final Object glContext;
|
||||
final bool isOffscreen;
|
||||
dynamic _kCompileStatus;
|
||||
dynamic _kArrayBuffer;
|
||||
dynamic _kElementArrayBuffer;
|
||||
dynamic _kStaticDraw;
|
||||
dynamic _kFloat;
|
||||
dynamic _kColorBufferBit;
|
||||
dynamic _kTexture2D;
|
||||
dynamic _kTextureWrapS;
|
||||
dynamic _kTextureWrapT;
|
||||
dynamic _kRepeat;
|
||||
dynamic _kClampToEdge;
|
||||
dynamic _kMirroredRepeat;
|
||||
dynamic _kTriangles;
|
||||
dynamic _kLinkStatus;
|
||||
dynamic _kUnsignedByte;
|
||||
dynamic _kUnsignedShort;
|
||||
dynamic _kRGBA;
|
||||
dynamic _kLinear;
|
||||
dynamic _kTextureMinFilter;
|
||||
int? _kTexture0;
|
||||
|
||||
Object? _canvas;
|
||||
int? _widthInPixels;
|
||||
int? _heightInPixels;
|
||||
static late Map<String, GlProgram?> _programCache;
|
||||
|
||||
GlContext.fromOffscreenCanvas(html.OffscreenCanvas canvas)
|
||||
: glContext = canvas.getContext('webgl2', <String, dynamic>{'premultipliedAlpha': false})!,
|
||||
isOffscreen = true {
|
||||
_programCache = <String, GlProgram?>{};
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
GlContext.fromCanvas(html.CanvasElement canvas, bool useWebGl1)
|
||||
: glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2',
|
||||
<String, dynamic>{'premultipliedAlpha': false})!,
|
||||
isOffscreen = false {
|
||||
_programCache = <String, GlProgram?>{};
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
void setViewportSize(int width, int height) {
|
||||
_widthInPixels = width;
|
||||
_heightInPixels = height;
|
||||
}
|
||||
|
||||
/// Draws Gl context contents to canvas context.
|
||||
void drawImage(html.CanvasRenderingContext2D context,
|
||||
double left, double top) {
|
||||
// Actual size of canvas may be larger than viewport size. Use
|
||||
// source/destination to draw part of the image data.
|
||||
js_util.callMethod(context, 'drawImage',
|
||||
<dynamic>[_canvas, 0, 0, _widthInPixels, _heightInPixels,
|
||||
left, top, _widthInPixels, _heightInPixels]);
|
||||
}
|
||||
|
||||
GlProgram cacheProgram(
|
||||
String vertexShaderSource, String fragmentShaderSource) {
|
||||
String cacheKey = '$vertexShaderSource||$fragmentShaderSource';
|
||||
GlProgram? cachedProgram = _programCache[cacheKey];
|
||||
if (cachedProgram == null) {
|
||||
// Create and compile shaders.
|
||||
Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource);
|
||||
Object fragmentShader =
|
||||
compileShader('FRAGMENT_SHADER', fragmentShaderSource);
|
||||
// Create a gl program and link shaders.
|
||||
Object program = createProgram();
|
||||
attachShader(program, vertexShader);
|
||||
attachShader(program, fragmentShader);
|
||||
linkProgram(program);
|
||||
cachedProgram = GlProgram(program);
|
||||
_programCache[cacheKey] = cachedProgram;
|
||||
}
|
||||
return cachedProgram;
|
||||
}
|
||||
|
||||
Object compileShader(String shaderType, String source) {
|
||||
Object? shader = _createShader(shaderType);
|
||||
if (shader == null) {
|
||||
throw Exception(error);
|
||||
}
|
||||
js_util.callMethod(glContext, 'shaderSource', <dynamic>[shader, source]);
|
||||
js_util.callMethod(glContext, 'compileShader', <dynamic>[shader]);
|
||||
bool shaderStatus = js_util
|
||||
.callMethod(glContext, 'getShaderParameter', <dynamic>[shader, compileStatus]);
|
||||
if (!shaderStatus) {
|
||||
throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}');
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
Object createProgram() =>
|
||||
js_util.callMethod(glContext, 'createProgram', const <dynamic>[])!;
|
||||
|
||||
void attachShader(Object? program, Object shader) {
|
||||
js_util.callMethod(glContext, 'attachShader', <dynamic>[program, shader]);
|
||||
}
|
||||
|
||||
void linkProgram(Object program) {
|
||||
js_util.callMethod(glContext, 'linkProgram', <dynamic>[program]);
|
||||
if (!js_util
|
||||
.callMethod(glContext, 'getProgramParameter', <dynamic>[program, kLinkStatus])) {
|
||||
throw Exception(getProgramInfoLog(program));
|
||||
}
|
||||
}
|
||||
|
||||
void useProgram(GlProgram program) {
|
||||
js_util.callMethod(glContext, 'useProgram', <dynamic>[program.program]);
|
||||
}
|
||||
|
||||
Object? createBuffer() =>
|
||||
js_util.callMethod(glContext, 'createBuffer', const <dynamic>[]);
|
||||
|
||||
void bindArrayBuffer(Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindBuffer', <dynamic>[kArrayBuffer, buffer]);
|
||||
}
|
||||
|
||||
Object? createVertexArray() =>
|
||||
js_util.callMethod(glContext, 'createVertexArray', const <dynamic>[]);
|
||||
|
||||
void bindVertexArray(Object vertexObjectArray) {
|
||||
js_util.callMethod(glContext, 'bindVertexArray',
|
||||
<dynamic>[vertexObjectArray]);
|
||||
}
|
||||
|
||||
void unbindVertexArray() {
|
||||
js_util.callMethod(glContext, 'bindVertexArray',
|
||||
<dynamic>[null]);
|
||||
}
|
||||
|
||||
void bindElementArrayBuffer(Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindBuffer', <dynamic>[kElementArrayBuffer, buffer]);
|
||||
}
|
||||
|
||||
Object? createTexture() =>
|
||||
js_util.callMethod(glContext, 'createTexture', const <dynamic>[]);
|
||||
|
||||
void generateMipmap(dynamic target) =>
|
||||
js_util.callMethod(glContext, 'generateMipmap', <dynamic>[target]);
|
||||
|
||||
void bindTexture(dynamic target, Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindTexture', <dynamic>[target, buffer]);
|
||||
}
|
||||
|
||||
void activeTexture(int textureUnit) {
|
||||
js_util.callMethod(glContext, 'activeTexture', <dynamic>[textureUnit]);
|
||||
}
|
||||
|
||||
void texImage2D(dynamic target, int level, dynamic internalFormat,
|
||||
dynamic format, dynamic dataType,
|
||||
dynamic pixels, {int? width, int? height, int border = 0}) {
|
||||
if (width == null) {
|
||||
js_util.callMethod(glContext, 'texImage2D', <dynamic>[
|
||||
target, level, internalFormat, format, dataType, pixels]);
|
||||
} else {
|
||||
js_util.callMethod(glContext, 'texImage2D', <dynamic>[
|
||||
target, level, internalFormat, width, height, border, format, dataType,
|
||||
pixels]);
|
||||
}
|
||||
}
|
||||
|
||||
void texParameteri(dynamic target, dynamic parameterName, dynamic value) {
|
||||
js_util.callMethod(glContext, 'texParameteri', <dynamic>[
|
||||
target, parameterName, value]);
|
||||
}
|
||||
|
||||
void deleteBuffer(Object buffer) {
|
||||
js_util.callMethod(glContext, 'deleteBuffer', <dynamic>[buffer]);
|
||||
}
|
||||
|
||||
void bufferData(TypedData? data, dynamic type) {
|
||||
js_util.callMethod(glContext, 'bufferData', <dynamic>[kArrayBuffer, data, type]);
|
||||
}
|
||||
|
||||
void bufferElementData(TypedData? data, dynamic type) {
|
||||
js_util.callMethod(glContext, 'bufferData', <dynamic>[kElementArrayBuffer, data, type]);
|
||||
}
|
||||
|
||||
void enableVertexAttribArray(dynamic index) {
|
||||
js_util.callMethod(glContext, 'enableVertexAttribArray', <dynamic>[index]);
|
||||
}
|
||||
|
||||
/// Clear background.
|
||||
void clear() {
|
||||
js_util.callMethod(glContext, 'clear', <dynamic>[kColorBufferBit]);
|
||||
}
|
||||
|
||||
/// Destroys gl context.
|
||||
void dispose() {
|
||||
js_util.callMethod(_getExtension('WEBGL_lose_context'), 'loseContext', const <dynamic>[]);
|
||||
}
|
||||
|
||||
void deleteProgram(Object program) {
|
||||
js_util.callMethod(glContext, 'deleteProgram', <dynamic>[program]);
|
||||
}
|
||||
|
||||
void deleteShader(Object shader) {
|
||||
js_util.callMethod(glContext, 'deleteShader', <dynamic>[shader]);
|
||||
}
|
||||
|
||||
dynamic _getExtension(String extensionName) =>
|
||||
js_util.callMethod(glContext, 'getExtension', <dynamic>[extensionName]);
|
||||
|
||||
void drawTriangles(int triangleCount, ui.VertexMode vertexMode) {
|
||||
dynamic mode = _triangleTypeFromMode(vertexMode);
|
||||
js_util.callMethod(glContext, 'drawArrays', <dynamic>[mode, 0, triangleCount]);
|
||||
}
|
||||
|
||||
void drawElements(dynamic type, int indexCount, dynamic indexType) {
|
||||
js_util.callMethod(glContext, 'drawElements', <dynamic>[type, indexCount, indexType, 0]);
|
||||
}
|
||||
|
||||
/// Sets affine transformation from normalized device coordinates
|
||||
/// to window coordinates
|
||||
void viewport(double x, double y, double width, double height) {
|
||||
js_util.callMethod(glContext, 'viewport', <dynamic>[x, y, width, height]);
|
||||
}
|
||||
|
||||
dynamic _triangleTypeFromMode(ui.VertexMode mode) {
|
||||
switch (mode) {
|
||||
case ui.VertexMode.triangles:
|
||||
return kTriangles;
|
||||
case ui.VertexMode.triangleFan:
|
||||
return kTriangleFan;
|
||||
case ui.VertexMode.triangleStrip:
|
||||
return kTriangleStrip;
|
||||
}
|
||||
}
|
||||
|
||||
Object? _createShader(String shaderType) => js_util.callMethod(
|
||||
glContext, 'createShader', <dynamic>[js_util.getProperty(glContext, shaderType)]);
|
||||
|
||||
/// Error state of gl context.
|
||||
dynamic get error => js_util.callMethod(glContext, 'getError', const <dynamic>[]);
|
||||
|
||||
/// Shader compiler error, if this returns [kFalse], to get details use
|
||||
/// [getShaderInfoLog].
|
||||
dynamic get compileStatus =>
|
||||
_kCompileStatus ??= js_util.getProperty(glContext, 'COMPILE_STATUS');
|
||||
|
||||
dynamic get kArrayBuffer =>
|
||||
_kArrayBuffer ??= js_util.getProperty(glContext, 'ARRAY_BUFFER');
|
||||
|
||||
dynamic get kElementArrayBuffer =>
|
||||
_kElementArrayBuffer ??= js_util.getProperty(glContext,
|
||||
'ELEMENT_ARRAY_BUFFER');
|
||||
|
||||
dynamic get kLinkStatus =>
|
||||
_kLinkStatus ??= js_util.getProperty(glContext, 'LINK_STATUS');
|
||||
|
||||
dynamic get kFloat => _kFloat ??= js_util.getProperty(glContext, 'FLOAT');
|
||||
|
||||
dynamic get kRGBA => _kRGBA ??= js_util.getProperty(glContext, 'RGBA');
|
||||
|
||||
dynamic get kUnsignedByte =>
|
||||
_kUnsignedByte ??= js_util.getProperty(glContext, 'UNSIGNED_BYTE');
|
||||
|
||||
dynamic get kUnsignedShort =>
|
||||
_kUnsignedShort ??= js_util.getProperty(glContext, 'UNSIGNED_SHORT');
|
||||
|
||||
dynamic get kStaticDraw =>
|
||||
_kStaticDraw ??= js_util.getProperty(glContext, 'STATIC_DRAW');
|
||||
|
||||
dynamic get kTriangles =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLES');
|
||||
|
||||
dynamic get kTriangleFan =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_FAN');
|
||||
|
||||
dynamic get kTriangleStrip =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_STRIP');
|
||||
|
||||
dynamic get kColorBufferBit =>
|
||||
_kColorBufferBit ??= js_util.getProperty(glContext, 'COLOR_BUFFER_BIT');
|
||||
|
||||
dynamic get kTexture2D =>
|
||||
_kTexture2D ??= js_util.getProperty(glContext, 'TEXTURE_2D');
|
||||
|
||||
int get kTexture0 =>
|
||||
_kTexture0 ??= js_util.getProperty(glContext, 'TEXTURE0') as int;
|
||||
|
||||
dynamic get kTextureWrapS =>
|
||||
_kTextureWrapS ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_S');
|
||||
|
||||
dynamic get kTextureWrapT =>
|
||||
_kTextureWrapT ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_T');
|
||||
|
||||
dynamic get kRepeat =>
|
||||
_kRepeat ??= js_util.getProperty(glContext, 'REPEAT');
|
||||
|
||||
dynamic get kClampToEdge =>
|
||||
_kClampToEdge ??= js_util.getProperty(glContext, 'CLAMP_TO_EDGE');
|
||||
|
||||
dynamic get kMirroredRepeat =>
|
||||
_kMirroredRepeat ??= js_util.getProperty(glContext, 'MIRRORED_REPEAT');
|
||||
|
||||
dynamic get kLinear =>
|
||||
_kLinear ??= js_util.getProperty(glContext, 'LINEAR');
|
||||
|
||||
dynamic get kTextureMinFilter =>
|
||||
_kTextureMinFilter ??= js_util.getProperty(glContext,
|
||||
'TEXTURE_MIN_FILTER');
|
||||
|
||||
/// Returns reference to uniform in program.
|
||||
Object getUniformLocation(Object program, String uniformName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getUniformLocation', <dynamic>[program, uniformName]);
|
||||
if (res == null) {
|
||||
throw Exception('$uniformName not found');
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if uniform exists.
|
||||
bool containsUniform(Object program, String uniformName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getUniformLocation', <dynamic>[program, uniformName]);
|
||||
return res != null;
|
||||
}
|
||||
|
||||
/// Returns reference to uniform in program.
|
||||
Object getAttributeLocation(Object program, String attribName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getAttribLocation', <dynamic>[program, attribName]);
|
||||
if (res == null) {
|
||||
throw Exception('$attribName not found');
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets float uniform value.
|
||||
void setUniform1f(Object uniform, double value) {
|
||||
return js_util
|
||||
.callMethod(glContext, 'uniform1f', <dynamic>[uniform, value]);
|
||||
}
|
||||
|
||||
/// Sets vec2 uniform values.
|
||||
void setUniform2f(Object uniform, double value1, double value2) {
|
||||
return js_util
|
||||
.callMethod(glContext, 'uniform2f', <dynamic>[uniform, value1, value2]);
|
||||
}
|
||||
|
||||
/// Sets vec4 uniform values.
|
||||
void setUniform4f(Object uniform, double value1, double value2, double value3,
|
||||
double value4) {
|
||||
return js_util.callMethod(
|
||||
glContext, 'uniform4f', <dynamic>[uniform, value1, value2, value3, value4]);
|
||||
}
|
||||
|
||||
/// Sets mat4 uniform values.
|
||||
void setUniformMatrix4fv(Object uniform, bool transpose, Float32List value) {
|
||||
return js_util.callMethod(
|
||||
glContext, 'uniformMatrix4fv', <dynamic>[uniform, transpose, value]);
|
||||
}
|
||||
|
||||
/// Shader compile error log.
|
||||
dynamic getShaderInfoLog(Object glShader) {
|
||||
return js_util.callMethod(glContext, 'getShaderInfoLog', <dynamic>[glShader]);
|
||||
}
|
||||
|
||||
/// Errors that occurred during failed linking or validation of program
|
||||
/// objects. Typically called after [linkProgram].
|
||||
String? getProgramInfoLog(Object glProgram) {
|
||||
return js_util.callMethod(glContext, 'getProgramInfoLog', <dynamic>[glProgram]);
|
||||
}
|
||||
|
||||
int? get drawingBufferWidth =>
|
||||
js_util.getProperty(glContext, 'drawingBufferWidth');
|
||||
int? get drawingBufferHeight =>
|
||||
js_util.getProperty(glContext, 'drawingBufferWidth');
|
||||
|
||||
/// Reads gl contents as image data.
|
||||
///
|
||||
/// Warning: data is read bottom up (flipped).
|
||||
html.ImageData readImageData() {
|
||||
const int kBytesPerPixel = 4;
|
||||
final int bufferWidth = _widthInPixels!;
|
||||
final int bufferHeight = _heightInPixels!;
|
||||
if (browserEngine == BrowserEngine.webkit ||
|
||||
browserEngine == BrowserEngine.firefox) {
|
||||
final Uint8List pixels =
|
||||
Uint8List(bufferWidth * bufferHeight * kBytesPerPixel);
|
||||
js_util.callMethod(glContext, 'readPixels',
|
||||
<dynamic>[0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]);
|
||||
return html.ImageData(
|
||||
Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight);
|
||||
} else {
|
||||
final Uint8ClampedList pixels =
|
||||
Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel);
|
||||
js_util.callMethod(glContext, 'readPixels',
|
||||
<dynamic>[0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]);
|
||||
return html.ImageData(pixels, bufferWidth, bufferHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns image data in a form that can be used to create Canvas
|
||||
/// context patterns.
|
||||
Object? readPatternData() {
|
||||
// When using OffscreenCanvas and transferToImageBitmap is supported by
|
||||
// browser create ImageBitmap otherwise use more expensive canvas
|
||||
// allocation.
|
||||
if (_canvas != null &&
|
||||
js_util.hasProperty(_canvas!, 'transferToImageBitmap')) {
|
||||
js_util.callMethod(_canvas!, 'getContext', <dynamic>['webgl2']);
|
||||
Object?imageBitmap = js_util.callMethod(_canvas!, 'transferToImageBitmap',
|
||||
<dynamic>[]);
|
||||
return imageBitmap;
|
||||
} else {
|
||||
html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels);
|
||||
final html.CanvasRenderingContext2D ctx = canvas.context2D;
|
||||
drawImage(ctx, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns image data in data url format.
|
||||
String toImageUrl() {
|
||||
html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels);
|
||||
final html.CanvasRenderingContext2D ctx = canvas.context2D;
|
||||
drawImage(ctx, 0, 0);
|
||||
final String dataUrl = canvas.toDataUrl();
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
return dataUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/// Polyfill for html.OffscreenCanvas that is not supported on some browsers.
|
||||
class _OffScreenCanvas {
|
||||
html.OffscreenCanvas? _canvas;
|
||||
html.CanvasElement? _glCanvas;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
_OffScreenCanvas(this.width, this.height) {
|
||||
if (_OffScreenCanvas.supported) {
|
||||
_canvas = html.OffscreenCanvas(width, height);
|
||||
} else {
|
||||
_glCanvas = html.CanvasElement(
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
_glCanvas!.className = 'gl-canvas';
|
||||
final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
_glCanvas!.style
|
||||
..position = 'absolute'
|
||||
..width = '${cssWidth}px'
|
||||
..height = '${cssHeight}px';
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_canvas = null;
|
||||
_glCanvas = null;
|
||||
}
|
||||
|
||||
/// Feature detects OffscreenCanvas.
|
||||
static bool get supported =>
|
||||
js_util.hasProperty(html.window, 'OffscreenCanvas');
|
||||
}
|
||||
|
||||
/// Creates gl context from cached OffscreenCanvas for webgl rendering to image.
|
||||
class _GlContextCache {
|
||||
static int _maxPixelWidth = 0;
|
||||
static int _maxPixelHeight = 0;
|
||||
static GlContext? _cachedContext;
|
||||
static _OffScreenCanvas? _offScreenCanvas;
|
||||
|
||||
static void dispose() {
|
||||
_maxPixelWidth = 0;
|
||||
_maxPixelHeight = 0;
|
||||
_cachedContext = null;
|
||||
_offScreenCanvas?.dispose();
|
||||
}
|
||||
|
||||
static GlContext? createGlContext(int widthInPixels, int heightInPixels) {
|
||||
if (widthInPixels > _maxPixelWidth || heightInPixels > _maxPixelHeight) {
|
||||
_cachedContext?.dispose();
|
||||
_cachedContext = null;
|
||||
_offScreenCanvas = null;
|
||||
_maxPixelWidth = math.max(_maxPixelWidth, widthInPixels);
|
||||
_maxPixelHeight = math.max(_maxPixelHeight, widthInPixels);
|
||||
}
|
||||
_offScreenCanvas ??= _OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
if (_OffScreenCanvas.supported) {
|
||||
_cachedContext ??=
|
||||
GlContext.fromOffscreenCanvas(_offScreenCanvas!._canvas!);
|
||||
} else {
|
||||
_cachedContext ??= GlContext.fromCanvas(_offScreenCanvas!._glCanvas!,
|
||||
webGLVersion == WebGLVersion.webgl1);
|
||||
}
|
||||
_cachedContext!.setViewportSize(widthInPixels, heightInPixels);
|
||||
return _cachedContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,9 @@ class GradientSweep extends EngineGradient {
|
||||
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
_OffScreenCanvas offScreenCanvas =
|
||||
_OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = _OffScreenCanvas.supported
|
||||
? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!)
|
||||
: GlContext.fromCanvas(
|
||||
offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1);
|
||||
OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = GlContext(offScreenCanvas);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
NormalizedGradient normalizedGradient =
|
||||
@@ -222,12 +219,9 @@ class GradientLinear extends EngineGradient {
|
||||
assert(widthInPixels > 0 && heightInPixels > 0);
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
_OffScreenCanvas offScreenCanvas =
|
||||
_OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = _OffScreenCanvas.supported
|
||||
? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!)
|
||||
: GlContext.fromCanvas(
|
||||
offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1);
|
||||
OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = GlContext(offScreenCanvas);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
NormalizedGradient normalizedGradient =
|
||||
@@ -493,12 +487,9 @@ class GradientRadial extends EngineGradient {
|
||||
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
_OffScreenCanvas offScreenCanvas =
|
||||
_OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = _OffScreenCanvas.supported
|
||||
? GlContext.fromOffscreenCanvas(offScreenCanvas._canvas!)
|
||||
: GlContext.fromCanvas(
|
||||
offScreenCanvas._glCanvas!, webGLVersion == WebGLVersion.webgl1);
|
||||
OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
GlContext gl = GlContext(offScreenCanvas);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
NormalizedGradient normalizedGradient =
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:html' as html;
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../offscreen_canvas.dart';
|
||||
import '../../browser_detection.dart';
|
||||
|
||||
/// Compiled and cached gl program.
|
||||
class GlProgram {
|
||||
final Object program;
|
||||
GlProgram(this.program);
|
||||
}
|
||||
|
||||
/// JS Interop helper for webgl apis.
|
||||
class GlContext {
|
||||
final Object glContext;
|
||||
final bool isOffscreen;
|
||||
dynamic _kCompileStatus;
|
||||
dynamic _kArrayBuffer;
|
||||
dynamic _kElementArrayBuffer;
|
||||
dynamic _kStaticDraw;
|
||||
dynamic _kFloat;
|
||||
dynamic _kColorBufferBit;
|
||||
dynamic _kTexture2D;
|
||||
dynamic _kTextureWrapS;
|
||||
dynamic _kTextureWrapT;
|
||||
dynamic _kRepeat;
|
||||
dynamic _kClampToEdge;
|
||||
dynamic _kMirroredRepeat;
|
||||
dynamic _kTriangles;
|
||||
dynamic _kLinkStatus;
|
||||
dynamic _kUnsignedByte;
|
||||
dynamic _kUnsignedShort;
|
||||
dynamic _kRGBA;
|
||||
dynamic _kLinear;
|
||||
dynamic _kTextureMinFilter;
|
||||
int? _kTexture0;
|
||||
|
||||
Object? _canvas;
|
||||
int? _widthInPixels;
|
||||
int? _heightInPixels;
|
||||
static late Map<String, GlProgram?> _programCache;
|
||||
|
||||
factory GlContext(OffScreenCanvas offScreenCanvas) {
|
||||
return OffScreenCanvas.supported
|
||||
? GlContext._fromOffscreenCanvas(offScreenCanvas.offScreenCanvas!)
|
||||
: GlContext._fromCanvasElement(
|
||||
offScreenCanvas.canvasElement!, webGLVersion == WebGLVersion.webgl1);
|
||||
}
|
||||
|
||||
GlContext._fromOffscreenCanvas(html.OffscreenCanvas canvas)
|
||||
: glContext = canvas.getContext('webgl2', <String, dynamic>{'premultipliedAlpha': false})!,
|
||||
isOffscreen = true {
|
||||
_programCache = <String, GlProgram?>{};
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
GlContext._fromCanvasElement(html.CanvasElement canvas, bool useWebGl1)
|
||||
: glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2',
|
||||
<String, dynamic>{'premultipliedAlpha': false})!,
|
||||
isOffscreen = false {
|
||||
_programCache = <String, GlProgram?>{};
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
void setViewportSize(int width, int height) {
|
||||
_widthInPixels = width;
|
||||
_heightInPixels = height;
|
||||
}
|
||||
|
||||
/// Draws Gl context contents to canvas context.
|
||||
void drawImage(html.CanvasRenderingContext2D context,
|
||||
double left, double top) {
|
||||
// Actual size of canvas may be larger than viewport size. Use
|
||||
// source/destination to draw part of the image data.
|
||||
js_util.callMethod(context, 'drawImage',
|
||||
<dynamic>[_canvas, 0, 0, _widthInPixels, _heightInPixels,
|
||||
left, top, _widthInPixels, _heightInPixels]);
|
||||
}
|
||||
|
||||
GlProgram cacheProgram(
|
||||
String vertexShaderSource, String fragmentShaderSource) {
|
||||
String cacheKey = '$vertexShaderSource||$fragmentShaderSource';
|
||||
GlProgram? cachedProgram = _programCache[cacheKey];
|
||||
if (cachedProgram == null) {
|
||||
// Create and compile shaders.
|
||||
Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource);
|
||||
Object fragmentShader =
|
||||
compileShader('FRAGMENT_SHADER', fragmentShaderSource);
|
||||
// Create a gl program and link shaders.
|
||||
Object program = createProgram();
|
||||
attachShader(program, vertexShader);
|
||||
attachShader(program, fragmentShader);
|
||||
linkProgram(program);
|
||||
cachedProgram = GlProgram(program);
|
||||
_programCache[cacheKey] = cachedProgram;
|
||||
}
|
||||
return cachedProgram;
|
||||
}
|
||||
|
||||
Object compileShader(String shaderType, String source) {
|
||||
Object? shader = _createShader(shaderType);
|
||||
if (shader == null) {
|
||||
throw Exception(error);
|
||||
}
|
||||
js_util.callMethod(glContext, 'shaderSource', <dynamic>[shader, source]);
|
||||
js_util.callMethod(glContext, 'compileShader', <dynamic>[shader]);
|
||||
bool shaderStatus = js_util
|
||||
.callMethod(glContext, 'getShaderParameter', <dynamic>[shader, compileStatus]);
|
||||
if (!shaderStatus) {
|
||||
throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}');
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
Object createProgram() =>
|
||||
js_util.callMethod(glContext, 'createProgram', const <dynamic>[])!;
|
||||
|
||||
void attachShader(Object? program, Object shader) {
|
||||
js_util.callMethod(glContext, 'attachShader', <dynamic>[program, shader]);
|
||||
}
|
||||
|
||||
void linkProgram(Object program) {
|
||||
js_util.callMethod(glContext, 'linkProgram', <dynamic>[program]);
|
||||
if (!js_util
|
||||
.callMethod(glContext, 'getProgramParameter', <dynamic>[program, kLinkStatus])) {
|
||||
throw Exception(getProgramInfoLog(program));
|
||||
}
|
||||
}
|
||||
|
||||
void useProgram(GlProgram program) {
|
||||
js_util.callMethod(glContext, 'useProgram', <dynamic>[program.program]);
|
||||
}
|
||||
|
||||
Object? createBuffer() =>
|
||||
js_util.callMethod(glContext, 'createBuffer', const <dynamic>[]);
|
||||
|
||||
void bindArrayBuffer(Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindBuffer', <dynamic>[kArrayBuffer, buffer]);
|
||||
}
|
||||
|
||||
Object? createVertexArray() =>
|
||||
js_util.callMethod(glContext, 'createVertexArray', const <dynamic>[]);
|
||||
|
||||
void bindVertexArray(Object vertexObjectArray) {
|
||||
js_util.callMethod(glContext, 'bindVertexArray',
|
||||
<dynamic>[vertexObjectArray]);
|
||||
}
|
||||
|
||||
void unbindVertexArray() {
|
||||
js_util.callMethod(glContext, 'bindVertexArray',
|
||||
<dynamic>[null]);
|
||||
}
|
||||
|
||||
void bindElementArrayBuffer(Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindBuffer', <dynamic>[kElementArrayBuffer, buffer]);
|
||||
}
|
||||
|
||||
Object? createTexture() =>
|
||||
js_util.callMethod(glContext, 'createTexture', const <dynamic>[]);
|
||||
|
||||
void generateMipmap(dynamic target) =>
|
||||
js_util.callMethod(glContext, 'generateMipmap', <dynamic>[target]);
|
||||
|
||||
void bindTexture(dynamic target, Object? buffer) {
|
||||
js_util.callMethod(glContext, 'bindTexture', <dynamic>[target, buffer]);
|
||||
}
|
||||
|
||||
void activeTexture(int textureUnit) {
|
||||
js_util.callMethod(glContext, 'activeTexture', <dynamic>[textureUnit]);
|
||||
}
|
||||
|
||||
void texImage2D(dynamic target, int level, dynamic internalFormat,
|
||||
dynamic format, dynamic dataType,
|
||||
dynamic pixels, {int? width, int? height, int border = 0}) {
|
||||
if (width == null) {
|
||||
js_util.callMethod(glContext, 'texImage2D', <dynamic>[
|
||||
target, level, internalFormat, format, dataType, pixels]);
|
||||
} else {
|
||||
js_util.callMethod(glContext, 'texImage2D', <dynamic>[
|
||||
target, level, internalFormat, width, height, border, format, dataType,
|
||||
pixels]);
|
||||
}
|
||||
}
|
||||
|
||||
void texParameteri(dynamic target, dynamic parameterName, dynamic value) {
|
||||
js_util.callMethod(glContext, 'texParameteri', <dynamic>[
|
||||
target, parameterName, value]);
|
||||
}
|
||||
|
||||
void deleteBuffer(Object buffer) {
|
||||
js_util.callMethod(glContext, 'deleteBuffer', <dynamic>[buffer]);
|
||||
}
|
||||
|
||||
void bufferData(TypedData? data, dynamic type) {
|
||||
js_util.callMethod(glContext, 'bufferData', <dynamic>[kArrayBuffer, data, type]);
|
||||
}
|
||||
|
||||
void bufferElementData(TypedData? data, dynamic type) {
|
||||
js_util.callMethod(glContext, 'bufferData', <dynamic>[kElementArrayBuffer, data, type]);
|
||||
}
|
||||
|
||||
void enableVertexAttribArray(dynamic index) {
|
||||
js_util.callMethod(glContext, 'enableVertexAttribArray', <dynamic>[index]);
|
||||
}
|
||||
|
||||
/// Clear background.
|
||||
void clear() {
|
||||
js_util.callMethod(glContext, 'clear', <dynamic>[kColorBufferBit]);
|
||||
}
|
||||
|
||||
/// Destroys gl context.
|
||||
void dispose() {
|
||||
js_util.callMethod(_getExtension('WEBGL_lose_context'), 'loseContext', const <dynamic>[]);
|
||||
}
|
||||
|
||||
void deleteProgram(Object program) {
|
||||
js_util.callMethod(glContext, 'deleteProgram', <dynamic>[program]);
|
||||
}
|
||||
|
||||
void deleteShader(Object shader) {
|
||||
js_util.callMethod(glContext, 'deleteShader', <dynamic>[shader]);
|
||||
}
|
||||
|
||||
dynamic _getExtension(String extensionName) =>
|
||||
js_util.callMethod(glContext, 'getExtension', <dynamic>[extensionName]);
|
||||
|
||||
void drawTriangles(int triangleCount, ui.VertexMode vertexMode) {
|
||||
dynamic mode = _triangleTypeFromMode(vertexMode);
|
||||
js_util.callMethod(glContext, 'drawArrays', <dynamic>[mode, 0, triangleCount]);
|
||||
}
|
||||
|
||||
void drawElements(dynamic type, int indexCount, dynamic indexType) {
|
||||
js_util.callMethod(glContext, 'drawElements', <dynamic>[type, indexCount, indexType, 0]);
|
||||
}
|
||||
|
||||
/// Sets affine transformation from normalized device coordinates
|
||||
/// to window coordinates
|
||||
void viewport(double x, double y, double width, double height) {
|
||||
js_util.callMethod(glContext, 'viewport', <dynamic>[x, y, width, height]);
|
||||
}
|
||||
|
||||
dynamic _triangleTypeFromMode(ui.VertexMode mode) {
|
||||
switch (mode) {
|
||||
case ui.VertexMode.triangles:
|
||||
return kTriangles;
|
||||
case ui.VertexMode.triangleFan:
|
||||
return kTriangleFan;
|
||||
case ui.VertexMode.triangleStrip:
|
||||
return kTriangleStrip;
|
||||
}
|
||||
}
|
||||
|
||||
Object? _createShader(String shaderType) => js_util.callMethod(
|
||||
glContext, 'createShader', <dynamic>[js_util.getProperty(glContext, shaderType)]);
|
||||
|
||||
/// Error state of gl context.
|
||||
dynamic get error => js_util.callMethod(glContext, 'getError', const <dynamic>[]);
|
||||
|
||||
/// Shader compiler error, if this returns [kFalse], to get details use
|
||||
/// [getShaderInfoLog].
|
||||
dynamic get compileStatus =>
|
||||
_kCompileStatus ??= js_util.getProperty(glContext, 'COMPILE_STATUS');
|
||||
|
||||
dynamic get kArrayBuffer =>
|
||||
_kArrayBuffer ??= js_util.getProperty(glContext, 'ARRAY_BUFFER');
|
||||
|
||||
dynamic get kElementArrayBuffer =>
|
||||
_kElementArrayBuffer ??= js_util.getProperty(glContext,
|
||||
'ELEMENT_ARRAY_BUFFER');
|
||||
|
||||
dynamic get kLinkStatus =>
|
||||
_kLinkStatus ??= js_util.getProperty(glContext, 'LINK_STATUS');
|
||||
|
||||
dynamic get kFloat => _kFloat ??= js_util.getProperty(glContext, 'FLOAT');
|
||||
|
||||
dynamic get kRGBA => _kRGBA ??= js_util.getProperty(glContext, 'RGBA');
|
||||
|
||||
dynamic get kUnsignedByte =>
|
||||
_kUnsignedByte ??= js_util.getProperty(glContext, 'UNSIGNED_BYTE');
|
||||
|
||||
dynamic get kUnsignedShort =>
|
||||
_kUnsignedShort ??= js_util.getProperty(glContext, 'UNSIGNED_SHORT');
|
||||
|
||||
dynamic get kStaticDraw =>
|
||||
_kStaticDraw ??= js_util.getProperty(glContext, 'STATIC_DRAW');
|
||||
|
||||
dynamic get kTriangles =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLES');
|
||||
|
||||
dynamic get kTriangleFan =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_FAN');
|
||||
|
||||
dynamic get kTriangleStrip =>
|
||||
_kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_STRIP');
|
||||
|
||||
dynamic get kColorBufferBit =>
|
||||
_kColorBufferBit ??= js_util.getProperty(glContext, 'COLOR_BUFFER_BIT');
|
||||
|
||||
dynamic get kTexture2D =>
|
||||
_kTexture2D ??= js_util.getProperty(glContext, 'TEXTURE_2D');
|
||||
|
||||
int get kTexture0 =>
|
||||
_kTexture0 ??= js_util.getProperty(glContext, 'TEXTURE0') as int;
|
||||
|
||||
dynamic get kTextureWrapS =>
|
||||
_kTextureWrapS ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_S');
|
||||
|
||||
dynamic get kTextureWrapT =>
|
||||
_kTextureWrapT ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_T');
|
||||
|
||||
dynamic get kRepeat =>
|
||||
_kRepeat ??= js_util.getProperty(glContext, 'REPEAT');
|
||||
|
||||
dynamic get kClampToEdge =>
|
||||
_kClampToEdge ??= js_util.getProperty(glContext, 'CLAMP_TO_EDGE');
|
||||
|
||||
dynamic get kMirroredRepeat =>
|
||||
_kMirroredRepeat ??= js_util.getProperty(glContext, 'MIRRORED_REPEAT');
|
||||
|
||||
dynamic get kLinear =>
|
||||
_kLinear ??= js_util.getProperty(glContext, 'LINEAR');
|
||||
|
||||
dynamic get kTextureMinFilter =>
|
||||
_kTextureMinFilter ??= js_util.getProperty(glContext,
|
||||
'TEXTURE_MIN_FILTER');
|
||||
|
||||
/// Returns reference to uniform in program.
|
||||
Object getUniformLocation(Object program, String uniformName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getUniformLocation', <dynamic>[program, uniformName]);
|
||||
if (res == null) {
|
||||
throw Exception('$uniformName not found');
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if uniform exists.
|
||||
bool containsUniform(Object program, String uniformName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getUniformLocation', <dynamic>[program, uniformName]);
|
||||
return res != null;
|
||||
}
|
||||
|
||||
/// Returns reference to uniform in program.
|
||||
Object getAttributeLocation(Object program, String attribName) {
|
||||
Object? res = js_util
|
||||
.callMethod(glContext, 'getAttribLocation', <dynamic>[program, attribName]);
|
||||
if (res == null) {
|
||||
throw Exception('$attribName not found');
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets float uniform value.
|
||||
void setUniform1f(Object uniform, double value) {
|
||||
return js_util
|
||||
.callMethod(glContext, 'uniform1f', <dynamic>[uniform, value]);
|
||||
}
|
||||
|
||||
/// Sets vec2 uniform values.
|
||||
void setUniform2f(Object uniform, double value1, double value2) {
|
||||
return js_util
|
||||
.callMethod(glContext, 'uniform2f', <dynamic>[uniform, value1, value2]);
|
||||
}
|
||||
|
||||
/// Sets vec4 uniform values.
|
||||
void setUniform4f(Object uniform, double value1, double value2, double value3,
|
||||
double value4) {
|
||||
return js_util.callMethod(
|
||||
glContext, 'uniform4f', <dynamic>[uniform, value1, value2, value3, value4]);
|
||||
}
|
||||
|
||||
/// Sets mat4 uniform values.
|
||||
void setUniformMatrix4fv(Object uniform, bool transpose, Float32List value) {
|
||||
return js_util.callMethod(
|
||||
glContext, 'uniformMatrix4fv', <dynamic>[uniform, transpose, value]);
|
||||
}
|
||||
|
||||
/// Shader compile error log.
|
||||
dynamic getShaderInfoLog(Object glShader) {
|
||||
return js_util.callMethod(glContext, 'getShaderInfoLog', <dynamic>[glShader]);
|
||||
}
|
||||
|
||||
/// Errors that occurred during failed linking or validation of program
|
||||
/// objects. Typically called after [linkProgram].
|
||||
String? getProgramInfoLog(Object glProgram) {
|
||||
return js_util.callMethod(glContext, 'getProgramInfoLog', <dynamic>[glProgram]);
|
||||
}
|
||||
|
||||
int? get drawingBufferWidth =>
|
||||
js_util.getProperty(glContext, 'drawingBufferWidth');
|
||||
int? get drawingBufferHeight =>
|
||||
js_util.getProperty(glContext, 'drawingBufferWidth');
|
||||
|
||||
/// Reads gl contents as image data.
|
||||
///
|
||||
/// Warning: data is read bottom up (flipped).
|
||||
html.ImageData readImageData() {
|
||||
const int kBytesPerPixel = 4;
|
||||
final int bufferWidth = _widthInPixels!;
|
||||
final int bufferHeight = _heightInPixels!;
|
||||
if (browserEngine == BrowserEngine.webkit ||
|
||||
browserEngine == BrowserEngine.firefox) {
|
||||
final Uint8List pixels =
|
||||
Uint8List(bufferWidth * bufferHeight * kBytesPerPixel);
|
||||
js_util.callMethod(glContext, 'readPixels',
|
||||
<dynamic>[0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]);
|
||||
return html.ImageData(
|
||||
Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight);
|
||||
} else {
|
||||
final Uint8ClampedList pixels =
|
||||
Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel);
|
||||
js_util.callMethod(glContext, 'readPixels',
|
||||
<dynamic>[0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]);
|
||||
return html.ImageData(pixels, bufferWidth, bufferHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns image data in a form that can be used to create Canvas
|
||||
/// context patterns.
|
||||
Object? readPatternData() {
|
||||
// When using OffscreenCanvas and transferToImageBitmap is supported by
|
||||
// browser create ImageBitmap otherwise use more expensive canvas
|
||||
// allocation.
|
||||
if (_canvas != null &&
|
||||
js_util.hasProperty(_canvas!, 'transferToImageBitmap')) {
|
||||
js_util.callMethod(_canvas!, 'getContext', <dynamic>['webgl2']);
|
||||
Object?imageBitmap = js_util.callMethod(_canvas!, 'transferToImageBitmap',
|
||||
<dynamic>[]);
|
||||
return imageBitmap;
|
||||
} else {
|
||||
html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels);
|
||||
final html.CanvasRenderingContext2D ctx = canvas.context2D;
|
||||
drawImage(ctx, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns image data in data url format.
|
||||
String toImageUrl() {
|
||||
html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels);
|
||||
final html.CanvasRenderingContext2D ctx = canvas.context2D;
|
||||
drawImage(ctx, 0, 0);
|
||||
final String dataUrl = canvas.toDataUrl();
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
return dataUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates gl context from cached OffscreenCanvas for webgl rendering to image.
|
||||
class GlContextCache {
|
||||
static int _maxPixelWidth = 0;
|
||||
static int _maxPixelHeight = 0;
|
||||
static GlContext? _cachedContext;
|
||||
static OffScreenCanvas? _offScreenCanvas;
|
||||
|
||||
static void dispose() {
|
||||
_maxPixelWidth = 0;
|
||||
_maxPixelHeight = 0;
|
||||
_cachedContext = null;
|
||||
_offScreenCanvas?.dispose();
|
||||
}
|
||||
|
||||
static GlContext? createGlContext(int widthInPixels, int heightInPixels) {
|
||||
if (widthInPixels > _maxPixelWidth || heightInPixels > _maxPixelHeight) {
|
||||
_cachedContext?.dispose();
|
||||
_cachedContext = null;
|
||||
_offScreenCanvas = null;
|
||||
_maxPixelWidth = math.max(_maxPixelWidth, widthInPixels);
|
||||
_maxPixelHeight = math.max(_maxPixelHeight, widthInPixels);
|
||||
}
|
||||
_offScreenCanvas ??= OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
_cachedContext ??= GlContext(_offScreenCanvas!);
|
||||
_cachedContext!.setViewportSize(widthInPixels, heightInPixels);
|
||||
return _cachedContext;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user