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