1<!DOCTYPE html> 2<title>Custom Image Upscaling</title> 3<meta charset="utf-8" /> 4<meta http-equiv="X-UA-Compatible" content="IE=edge"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<script type="text/javascript" src="https://unpkg.com/canvaskit-wasm@0.25.0/bin/full/canvaskit.js"></script> 7 8<style> 9 figcaption { 10 max-width: 800px; 11 } 12</style> 13 14<body> 15 <h1>Custom Image Upscaling</h1> 16 17 <div class="slidecontainer"> 18 <input type="range" min="0" max="1" value="0" step="0.01" class="slider" id="sharpen" 19 title="sharpen coefficient: 0 means nearest neighbor."> 20 <input type="range" min="0" max="1" value="0.3" step="0.01" class="slider" id="cubic_B" 21 title="cubic B"> 22 <input type="range" min="0" max="1" value="0.3" step="0.01" class="slider" id="cubic_C" 23 title="cubic C"> 24 </div> 25 26 <figure> 27 <canvas id=draw width=820 height=820></canvas> 28 <figcaption> 29 This demo shows off a custom image upscaling algorithm written in SkSL. The algorithm 30 can be between nearest neighbor and linear interpolation, depending if the value of the 31 sharpen (i.e. the first) slider is 0 or 1, respectively. The upper left quadrant shows 32 the results of a 100x zoom in on a 4 pixel by 4 pixel image of random colors with this 33 algorithm. The lower left is the same algorithm with a smoothing curve applied. 34 <br> 35 For comparison, the upper right shows a stock linear interpolation and the lower right 36 shows a cubic interpolation with the B and C values controlled by the two remaining 37 sliders. 38 </figcaption> 39 </figure> 40 41</body> 42 43<script type="text/javascript" charset="utf-8"> 44 const ckLoaded = CanvasKitInit({ locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.25.0/bin/full/' + file }); 45 46 ckLoaded.then((CanvasKit) => { 47 if (!CanvasKit.RuntimeEffect) { 48 throw 'Need RuntimeEffect'; 49 } 50 const surface = CanvasKit.MakeCanvasSurface('draw'); 51 if (!surface) { 52 throw 'Could not make surface'; 53 } 54 55 const prog = ` 56 uniform shader image; 57 uniform float sharp; // 1/m 0 --> NN, 1 --> Linear 58 uniform float do_smooth; // bool 59 60 float2 smooth(float2 t) { 61 return t * t * (3.0 - 2.0 * t); 62 } 63 64 float2 sharpen(float2 w) { 65 return saturate(sharp * (w - 0.5) + 0.5); 66 } 67 68 half4 main(float2 p) { 69 half4 pa = sample(image, float2(p.x-0.5, p.y-0.5)); 70 half4 pb = sample(image, float2(p.x+0.5, p.y-0.5)); 71 half4 pc = sample(image, float2(p.x-0.5, p.y+0.5)); 72 half4 pd = sample(image, float2(p.x+0.5, p.y+0.5)); 73 float2 w = sharpen(fract(p + 0.5)); 74 if (do_smooth > 0) { 75 w = smooth(w); 76 } 77 return mix(mix(pa, pb, w.x), mix(pc, pd, w.x), w.y); 78 } 79 `; 80 const effect = CanvasKit.RuntimeEffect.Make(prog); 81 82 const paint = new CanvasKit.Paint(); 83 // image is a 4x4 image of 16 random colors. This very small image will be upscaled 84 // through various techniques. 85 const image = function() { 86 const surf = CanvasKit.MakeSurface(4, 4); 87 const c = surf.getCanvas(); 88 for (let y = 0; y < 4; y++) { 89 for (let x = 0; x < 4; x++) { 90 paint.setColor([Math.random(), Math.random(), Math.random(), 1]); 91 c.drawRect(CanvasKit.LTRBRect(x, y, x+1, y+1), paint); 92 } 93 } 94 return surf.makeImageSnapshot(); 95 }(); 96 97 const imageShader = image.makeShaderOptions(CanvasKit.TileMode.Clamp, 98 CanvasKit.TileMode.Clamp, 99 CanvasKit.FilterMode.Nearest, 100 CanvasKit.MipmapMode.None); 101 102 sharpen.oninput = () => { surface.requestAnimationFrame(drawFrame); }; 103 cubic_B.oninput = () => { surface.requestAnimationFrame(drawFrame); }; 104 cubic_C.oninput = () => { surface.requestAnimationFrame(drawFrame); }; 105 106 const drawFrame = function(canvas) { 107 const v = sharpen.valueAsNumber; 108 const m = 1/Math.max(v, 0.00001); 109 const B = cubic_B.valueAsNumber; 110 const C = cubic_C.valueAsNumber; 111 112 canvas.save(); 113 // Upscale all drawing by 100x; This is big enough to make the differences in technique 114 // more obvious. 115 const scale = 100; 116 canvas.scale(scale, scale); 117 118 // Upper left, draw image using an algorithm (written in SkSL) between nearest neighbor and 119 // linear interpolation with no smoothing. 120 paint.setShader(effect.makeShaderWithChildren([m, 0], true, [imageShader], null)); 121 canvas.drawRect(CanvasKit.LTRBRect(0, 0, 4, 4), paint); 122 123 // Lower left, draw image using an algorithm (written in SkSL) between nearest neighbor and 124 // linear interpolation with smoothing enabled. 125 canvas.save(); 126 canvas.translate(0, 4.1); 127 paint.setShader(effect.makeShaderWithChildren([m, 1], true, [imageShader], null)); 128 canvas.drawRect(CanvasKit.LTRBRect(0, 0, 4, 4), paint); 129 canvas.restore(); 130 131 // Upper right, draw image with built-in linear interpolation. 132 canvas.drawImageOptions(image, 4.1, 0, CanvasKit.FilterMode.Linear, CanvasKit.MipmapMode.None, null); 133 134 // Lower right, draw image with configurable cubic interpolation. 135 canvas.drawImageCubic(image, 4.1, 4.1, B, C, null); 136 137 canvas.restore(); 138 }; 139 140 surface.requestAnimationFrame(drawFrame); 141 }); 142 143</script> 144