| <!DOCTYPE html> |
| <title>Custom Image Upscaling</title> |
| <meta charset="utf-8" /> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <script type="text/javascript" src="https://unpkg.com/canvaskit-wasm@0.34.1/bin/full/canvaskit.js"></script> |
| |
| <style> |
| figcaption { |
| max-width: 800px; |
| } |
| </style> |
| |
| <body> |
| <h1>Custom Image Upscaling</h1> |
| |
| <div class="slidecontainer"> |
| <input type="range" min="0" max="1" value="0" step="0.01" class="slider" id="sharpen" |
| title="sharpen coefficient: 0 means nearest neighbor."> |
| <input type="range" min="0" max="1" value="0.3" step="0.01" class="slider" id="cubic_B" |
| title="cubic B"> |
| <input type="range" min="0" max="1" value="0.3" step="0.01" class="slider" id="cubic_C" |
| title="cubic C"> |
| </div> |
| |
| <figure> |
| <canvas id=draw width=820 height=820></canvas> |
| <figcaption> |
| This demo shows off a custom image upscaling algorithm written in SkSL. The algorithm |
| can be between nearest neighbor and linear interpolation, depending if the value of the |
| sharpen (i.e. the first) slider is 0 or 1, respectively. The upper left quadrant shows |
| the results of a 100x zoom in on a 4 pixel by 4 pixel image of random colors with this |
| algorithm. The lower left is the same algorithm with a smoothing curve applied. |
| <br> |
| For comparison, the upper right shows a stock linear interpolation and the lower right |
| shows a cubic interpolation with the B and C values controlled by the two remaining |
| sliders. |
| </figcaption> |
| </figure> |
| |
| </body> |
| |
| <script type="text/javascript" charset="utf-8"> |
| const ckLoaded = CanvasKitInit({ locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.34.1/bin/full/' + file }); |
| |
| ckLoaded.then((CanvasKit) => { |
| if (!CanvasKit.RuntimeEffect) { |
| throw 'Need RuntimeEffect'; |
| } |
| const surface = CanvasKit.MakeCanvasSurface('draw'); |
| if (!surface) { |
| throw 'Could not make surface'; |
| } |
| |
| const prog = ` |
| uniform shader image; |
| uniform float sharp; // 1/m 0 --> NN, 1 --> Linear |
| uniform float do_smooth; // bool |
| |
| float2 smooth(float2 t) { |
| return t * t * (3.0 - 2.0 * t); |
| } |
| |
| float2 sharpen(float2 w) { |
| return saturate(sharp * (w - 0.5) + 0.5); |
| } |
| |
| half4 main(float2 p) { |
| half4 pa = image.eval(float2(p.x-0.5, p.y-0.5)); |
| half4 pb = image.eval(float2(p.x+0.5, p.y-0.5)); |
| half4 pc = image.eval(float2(p.x-0.5, p.y+0.5)); |
| half4 pd = image.eval(float2(p.x+0.5, p.y+0.5)); |
| float2 w = sharpen(fract(p + 0.5)); |
| if (do_smooth > 0) { |
| w = smooth(w); |
| } |
| return mix(mix(pa, pb, w.x), mix(pc, pd, w.x), w.y); |
| } |
| `; |
| const effect = CanvasKit.RuntimeEffect.Make(prog); |
| |
| const paint = new CanvasKit.Paint(); |
| // image is a 4x4 image of 16 random colors. This very small image will be upscaled |
| // through various techniques. |
| const image = function() { |
| const surf = CanvasKit.MakeSurface(4, 4); |
| const c = surf.getCanvas(); |
| for (let y = 0; y < 4; y++) { |
| for (let x = 0; x < 4; x++) { |
| paint.setColor([Math.random(), Math.random(), Math.random(), 1]); |
| c.drawRect(CanvasKit.LTRBRect(x, y, x+1, y+1), paint); |
| } |
| } |
| return surf.makeImageSnapshot(); |
| }(); |
| |
| const imageShader = image.makeShaderOptions(CanvasKit.TileMode.Clamp, |
| CanvasKit.TileMode.Clamp, |
| CanvasKit.FilterMode.Nearest, |
| CanvasKit.MipmapMode.None); |
| |
| sharpen.oninput = () => { surface.requestAnimationFrame(drawFrame); }; |
| cubic_B.oninput = () => { surface.requestAnimationFrame(drawFrame); }; |
| cubic_C.oninput = () => { surface.requestAnimationFrame(drawFrame); }; |
| |
| const drawFrame = function(canvas) { |
| const v = sharpen.valueAsNumber; |
| const m = 1/Math.max(v, 0.00001); |
| const B = cubic_B.valueAsNumber; |
| const C = cubic_C.valueAsNumber; |
| |
| canvas.save(); |
| // Upscale all drawing by 100x; This is big enough to make the differences in technique |
| // more obvious. |
| const scale = 100; |
| canvas.scale(scale, scale); |
| |
| // Upper left, draw image using an algorithm (written in SkSL) between nearest neighbor and |
| // linear interpolation with no smoothing. |
| paint.setShader(effect.makeShaderWithChildren([m, 0], [imageShader], null)); |
| canvas.drawRect(CanvasKit.LTRBRect(0, 0, 4, 4), paint); |
| |
| // Lower left, draw image using an algorithm (written in SkSL) between nearest neighbor and |
| // linear interpolation with smoothing enabled. |
| canvas.save(); |
| canvas.translate(0, 4.1); |
| paint.setShader(effect.makeShaderWithChildren([m, 1], [imageShader], null)); |
| canvas.drawRect(CanvasKit.LTRBRect(0, 0, 4, 4), paint); |
| canvas.restore(); |
| |
| // Upper right, draw image with built-in linear interpolation. |
| canvas.drawImageOptions(image, 4.1, 0, CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None, null); |
| |
| // Lower right, draw image with configurable cubic interpolation. |
| canvas.drawImageCubic(image, 4.1, 4.1, B, C, null); |
| |
| canvas.restore(); |
| }; |
| |
| surface.requestAnimationFrame(drawFrame); |
| }); |
| |
| </script> |