diff --git a/src/browser/canvas/WebGLRenderingContext.zig b/src/browser/canvas/WebGLRenderingContext.zig new file mode 100644 index 000000000..47f9df207 --- /dev/null +++ b/src/browser/canvas/WebGLRenderingContext.zig @@ -0,0 +1,145 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); + +const WebGLRenderingContext = @This(); +_: u8 = 0, + +/// On Chrome and Safari, a call to `getSupportedExtensions` returns total of 39. +/// The reference for it lists lesser number of extensions: +/// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions#extension_list +pub const Extension = union(enum) { + ANGLE_instanced_arrays: void, + EXT_blend_minmax: void, + EXT_clip_control: void, + EXT_color_buffer_half_float: void, + EXT_depth_clamp: void, + EXT_disjoint_timer_query: void, + EXT_float_blend: void, + EXT_frag_depth: void, + EXT_polygon_offset_clamp: void, + EXT_shader_texture_lod: void, + EXT_texture_compression_bptc: void, + EXT_texture_compression_rgtc: void, + EXT_texture_filter_anisotropic: void, + EXT_texture_mirror_clamp_to_edge: void, + EXT_sRGB: void, + KHR_parallel_shader_compile: void, + OES_element_index_uint: void, + OES_fbo_render_mipmap: void, + OES_standard_derivatives: void, + OES_texture_float: void, + OES_texture_float_linear: void, + OES_texture_half_float: void, + OES_texture_half_float_linear: void, + OES_vertex_array_object: void, + WEBGL_blend_func_extended: void, + WEBGL_color_buffer_float: void, + WEBGL_compressed_texture_astc: void, + WEBGL_compressed_texture_etc: void, + WEBGL_compressed_texture_etc1: void, + WEBGL_compressed_texture_pvrtc: void, + WEBGL_compressed_texture_s3tc: void, + WEBGL_compressed_texture_s3tc_srgb: void, + WEBGL_debug_renderer_info: Type.WEBGL_debug_renderer_info, + WEBGL_debug_shaders: void, + WEBGL_depth_texture: void, + WEBGL_draw_buffers: void, + WEBGL_lose_context: Type.WEBGL_lose_context, + WEBGL_multi_draw: void, + WEBGL_polygon_mode: void, + + /// Reified enum type from the fields of this union. + const Kind = blk: { + const info = @typeInfo(Extension).@"union"; + const fields = info.fields; + var items: [fields.len]std.builtin.Type.EnumField = undefined; + for (fields, 0..) |field, i| { + items[i] = .{ .name = field.name, .value = i }; + } + + break :blk @Type(.{ + .@"enum" = .{ + .tag_type = std.math.IntFittingRange(0, if (fields.len == 0) 0 else fields.len - 1), + .fields = &items, + .decls = &.{}, + .is_exhaustive = true, + }, + }); + }; + + /// Returns the `Extension.Kind` by its name. + fn find(name: []const u8) ?Kind { + // Just to make you really sad, this function has to be case-insensitive. + // So here we copy what's being done in `std.meta.stringToEnum` but replace + // the comparison function. + const kvs = comptime build_kvs: { + const T = Extension.Kind; + const EnumKV = struct { []const u8, T }; + var kvs_array: [@typeInfo(T).@"enum".fields.len]EnumKV = undefined; + for (@typeInfo(T).@"enum".fields, 0..) |enumField, i| { + kvs_array[i] = .{ enumField.name, @field(T, enumField.name) }; + } + break :build_kvs kvs_array[0..]; + }; + const Map = std.StaticStringMapWithEql(Extension.Kind, std.static_string_map.eqlAsciiIgnoreCase); + const map = Map.initComptime(kvs); + return map.get(name); + } + + /// Extension types. + pub const Type = struct { + pub const WEBGL_debug_renderer_info = struct { + pub const UNMASKED_VENDOR_WEBGL: u64 = 0x9245; + pub const UNMASKED_RENDERER_WEBGL: u64 = 0x9246; + + pub fn get_UNMASKED_VENDOR_WEBGL() u64 { + return UNMASKED_VENDOR_WEBGL; + } + + pub fn get_UNMASKED_RENDERER_WEBGL() u64 { + return UNMASKED_RENDERER_WEBGL; + } + }; + + pub const WEBGL_lose_context = struct { + _: u8 = 0, + pub fn _loseContext(_: *const WEBGL_lose_context) void {} + pub fn _restoreContext(_: *const WEBGL_lose_context) void {} + }; + }; +}; + +/// Enables a WebGL extension. +pub fn _getExtension(self: *const WebGLRenderingContext, name: []const u8) ?Extension { + _ = self; + + const tag = Extension.find(name) orelse return null; + + return switch (tag) { + .WEBGL_debug_renderer_info => @unionInit(Extension, "WEBGL_debug_renderer_info", .{}), + .WEBGL_lose_context => @unionInit(Extension, "WEBGL_lose_context", .{}), + inline else => |comptime_enum| @unionInit(Extension, @tagName(comptime_enum), {}), + }; +} + +/// Returns a list of all the supported WebGL extensions. +pub fn _getSupportedExtensions(_: *const WebGLRenderingContext) []const []const u8 { + return std.meta.fieldNames(Extension.Kind); +} diff --git a/src/browser/canvas/root.zig b/src/browser/canvas/root.zig index ad4157457..fbb51caab 100644 --- a/src/browser/canvas/root.zig +++ b/src/browser/canvas/root.zig @@ -1,6 +1,13 @@ //! Canvas API. //! https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API +const CanvasRenderingContext2D = @import("CanvasRenderingContext2D.zig"); +const WebGLRenderingContext = @import("WebGLRenderingContext.zig"); +const Extension = WebGLRenderingContext.Extension; + pub const Interfaces = .{ - @import("./CanvasRenderingContext2D.zig"), + CanvasRenderingContext2D, + WebGLRenderingContext, + Extension.Type.WEBGL_debug_renderer_info, + Extension.Type.WEBGL_lose_context, }; diff --git a/src/browser/html/elements.zig b/src/browser/html/elements.zig index 037356e79..1180d1228 100644 --- a/src/browser/html/elements.zig +++ b/src/browser/html/elements.zig @@ -33,6 +33,7 @@ const DataSet = @import("DataSet.zig"); const StyleSheet = @import("../cssom/StyleSheet.zig"); const CSSStyleDeclaration = @import("../cssom/CSSStyleDeclaration.zig"); const CanvasRenderingContext2D = @import("../canvas/CanvasRenderingContext2D.zig"); +const WebGLRenderingContext = @import("../canvas/WebGLRenderingContext.zig"); const WalkerChildren = @import("../dom/walker.zig").WalkerChildren; @@ -497,15 +498,21 @@ pub const HTMLCanvasElement = struct { color_space: []const u8 = "srgb", }; + /// Returns a drawing context on the canvas, or null if the context identifier + /// is not supported, or the canvas has already been set to a different context mode. pub fn _getContext( ctx_type: []const u8, _: ?ContextAttributes, - ) !CanvasRenderingContext2D { - if (!std.mem.eql(u8, ctx_type, "2d")) { - return error.NotSupported; + ) ?union(enum) { @"2d": CanvasRenderingContext2D, webgl: WebGLRenderingContext } { + if (std.mem.eql(u8, ctx_type, "2d")) { + return .{ .@"2d" = .{} }; } - return .{}; + if (std.mem.eql(u8, ctx_type, "webgl") or std.mem.eql(u8, ctx_type, "experimental-webgl")) { + return .{ .webgl = .{} }; + } + + return null; } }; diff --git a/src/tests/html/canvas.html b/src/tests/html/canvas.html index ab076487c..529813f4b 100644 --- a/src/tests/html/canvas.html +++ b/src/tests/html/canvas.html @@ -1,7 +1,7 @@ - - + + + +