| <!DOCTYPE html> |
| <title>Image sampling techniques</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> |
| #draw { |
| border: 1px dashed grey; |
| } |
| figcaption { |
| max-width: 800px; |
| } |
| </style> |
| |
| <body> |
| <h1>Image sampling techniques</h1> |
| |
| <div class="slidecontainer"> |
| <input style="width:400px;" type="range" min="-10" max="10" value="0" step="0.01" |
| class="slider" id="scale_slider" title="Scale"> |
| </div> |
| |
| <figure> |
| <!-- width/height hard-coded for grid of 256px images. --> |
| <div ondrop="dropHandler(event);" ondragover="dragOverHandler(event);"> |
| <canvas id="draw" width=868 height=592></canvas> |
| </div> |
| <figcaption> |
| Drop an Image onto the rectangle above |
| </figcaption> |
| </figure> |
| |
| </body> |
| |
| <script type="text/javascript" charset="utf-8"> |
| function preventScrolling(elem) { |
| elem.addEventListener('touchmove', (e) => { |
| // Prevents touch events in the element from scrolling. |
| e.preventDefault(); |
| e.stopPropagation(); |
| }); |
| } |
| |
| function dragOverHandler(ev) { ev.preventDefault(); } |
| |
| // these can be changed with click/drag on the primary rectangle |
| let cell_width = 256, |
| cell_height = 256; |
| |
| let image = null; |
| let CK = null; |
| let surface = null; |
| let drawFrame = null; |
| |
| function dropHandler(ev) { |
| ev.preventDefault(); |
| |
| let file = null; |
| if (ev.dataTransfer.items) { |
| // Use DataTransferItemList interface to access the file(s) |
| for (item of ev.dataTransfer.items) { |
| // If dropped items aren't files, reject them |
| if (item.kind === 'file') { |
| file = item.getAsFile(); |
| break; |
| } |
| } |
| } else { |
| // Use DataTransfer interface to access the file(s) |
| for (f of ev.dataTransfer.files) { |
| file = f; |
| break; |
| } |
| } |
| if (file) { |
| file.arrayBuffer().then(buffer => { |
| image = CK.MakeImageFromEncoded(buffer); |
| surface.requestAnimationFrame(drawFrame); |
| }); |
| } |
| } |
| |
| const ckLoaded = CanvasKitInit({ locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.25.0/bin/full/' + file }); |
| |
| ckLoaded.then((_CK) => { |
| CK = _CK; |
| surface = CK.MakeCanvasSurface('draw'); |
| if (!surface) { |
| throw 'Could not make surface'; |
| } |
| |
| const font = new CK.Font(null, 15); |
| const paint = new CK.Paint(); |
| const textPaint = new CK.Paint(); |
| const TX = 20, |
| TY = 30; // where we start the first rectangle |
| |
| scale_slider.oninput = () => { surface.requestAnimationFrame(drawFrame); }; |
| |
| m33_scaled = function(sx, sy) { |
| if (!sy) sy = sx; |
| let m = new Float32Array(9); |
| m[0] = sx; m[4] = sy; m[8] = 1; |
| return m; |
| } |
| |
| drawFrame = function(canvas) { |
| if (!image) return; |
| |
| const scale = scale_slider.valueAsNumber < 0 ? 1 / (1 - scale_slider.valueAsNumber) |
| : (1 + scale_slider.valueAsNumber); |
| |
| const bounds = [0, 0, cell_width, cell_height]; |
| |
| const lm = m33_scaled(scale, scale); |
| |
| const samplings = [ |
| [CK.FilterMode.Nearest, CK.MipmapMode.None, 0, 0, "Nearest"], |
| [CK.FilterMode.Linear, CK.MipmapMode.None, 0, 0, "Bilerp"], |
| [CK.FilterMode.Linear, CK.MipmapMode.Linear, 0, 0, "Trilerp"], |
| [null, null, 0, 0.5, "CatmullRom"], |
| [null, null, 1/3.0, 1/3.0, "Mitchell"], |
| ]; |
| |
| const tile = CK.TileMode.Repeat; |
| |
| canvas.save(); |
| canvas.translate(TX, TY); |
| canvas.save(); |
| for (i in samplings) { |
| if (i == 3) { |
| canvas.restore(); |
| canvas.translate(0, bounds[3] - bounds[1] + 30); |
| canvas.save(); |
| } |
| |
| const s = samplings[i]; |
| const shader = s[0] ? image.makeShaderOptions(tile, tile, s[0], s[1], lm) |
| : image.makeShaderCubic( tile, tile, s[2], s[3], lm); |
| paint.setShader(shader); |
| canvas.drawRect(bounds, paint); |
| shader.delete(); |
| |
| canvas.drawText(s[4], 20, -8, textPaint, font); |
| |
| canvas.translate(bounds[2] - bounds[0] + 30, 0); |
| } |
| canvas.restore(); |
| canvas.restore(); |
| // draw the drag handle |
| if (true) { |
| canvas.save(); |
| paint.setShader(null); |
| paint.setColor(CK.Color4f(1, 0, 0)); |
| paint.setStrokeWidth(2); |
| canvas.translate(TX + cell_width + 4, TY + cell_height + 4); |
| canvas.drawLine(-12, 0, 0, 0, paint); |
| canvas.drawLine( 0, -12, 0, 0, paint); |
| canvas.restore() |
| } |
| }; |
| |
| // register our mouse handler |
| { |
| function len2(x, y) { |
| return x*x + y*y; |
| } |
| function hit_test(x,y, x1,y1) { |
| return len2(x-x1, y-y1) <= 10*10; |
| } |
| |
| let do_drag = false; |
| function pointer_up(e) { |
| do_drag = false; |
| } |
| function pointer_down(e) { |
| do_drag = hit_test(TX+cell_width, TY+cell_height, e.offsetX, e.offsetY); |
| } |
| function pointer_move(e) { |
| if (e.pressure && do_drag) { |
| cell_width = Math.max(e.offsetX - TX, 32); |
| cell_height = Math.max(e.offsetY - TY, 32); |
| surface.requestAnimationFrame(drawFrame); |
| } |
| } |
| |
| const elem = document.getElementById('draw'); |
| elem.addEventListener('pointermove', pointer_move); |
| elem.addEventListener('pointerdown', pointer_down); |
| elem.addEventListener('pointerup', pointer_up); |
| preventScrolling(elem); |
| } |
| |
| surface.requestAnimationFrame(drawFrame); |
| }); |
| |
| </script> |