Import Cobalt 25.master.0.1033734
diff --git a/third_party/skia/demos.skia.org/demos/image_sampling/index.html b/third_party/skia/demos.skia.org/demos/image_sampling/index.html
new file mode 100644
index 0000000..6626630
--- /dev/null
+++ b/third_party/skia/demos.skia.org/demos/image_sampling/index.html
@@ -0,0 +1,143 @@
+<!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 = 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>