a1e30a3a28
Also add some explanation to the image_sampling demo. Change-Id: Id20fe4d47c45b6a6b27e227871458bb862ec83d8 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/378936 Reviewed-by: Mike Reed <reed@google.com>
144 lines
5.4 KiB
HTML
144 lines
5.4 KiB
HTML
<!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.25.0/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.25.0/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 = sample(image, float2(p.x-0.5, p.y-0.5));
|
|
half4 pb = sample(image, float2(p.x+0.5, p.y-0.5));
|
|
half4 pc = sample(image, float2(p.x-0.5, p.y+0.5));
|
|
half4 pd = sample(image, 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], true, [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], true, [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>
|