• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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