Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perspective transforms image smoothing not working properly #85

Closed
ewanhowell5195 opened this issue Feb 10, 2022 · 1 comment
Closed
Labels
enhancement New feature or request

Comments

@ewanhowell5195
Copy link

ewanhowell5195 commented Feb 10, 2022

Left image produced using skia-canvas, right image using ImageMagick
comparison
The outer edge of the image seems to work correctly however

Changing the imageSmoothingQuality doesn't seem to improve anything

This doesn't seem to affect drawing things like rectangles and text, only images and canvases
out

The higher resolution the input image, the worse it gets

256x265 input image
squares
out

1024x1024 input image
squares
out

Code i tested with:

import {Canvas, loadImage} from "skia-canvas"

const img = await loadImage("squares.png")

const canvas = new Canvas(256, 256)
const ctx = canvas.getContext("2d")

ctx.setTransform(ctx.createProjection([65, 10, 212, 112, 250, 200, 0, 256]))

ctx.imageSmoothingQuality = "high"

ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

canvas.saveAs("out.png")
@samizdatco
Copy link
Owner

This seems to be a known issue with Skia itself—specifically that it doesn't use a high-quality image filter when downscaling images, despite doing so when upscaling. I've tried fiddling with the SamplingOptions values that are used for the different imageSmoothingQuality settings, but (as described in this discussion and this one) the cubic resampler is only applied when upsampling. Unfortunately, enabling bi-linear filtering didn't have a noticeable effect against your sample code.

SkiaSharp has also been wrestling with the for some time.

So if there is a solution to this problem, it's going to be more complicated. Some possibilities that might be worth investigating:

  1. Using GPU-backed rendering (which, I believe, is why these artifacts aren't seen in Chrome)
  2. Applying transforms using an ImageFilter rather than leaving that to the canvas
  3. Manually applying a convolution when downscaling images (as suggested here)

In the meantime, the best workaround might be to generate an interim bitmap of your canvas at a higher resolution (e.g., using 3 or 4 for the density export argument), then scale that image down to your desired size:

const img = await loadImage("squares.png")
const canvas = new Canvas(256, 256)
const ctx = canvas.getContext("2d")

ctx.setTransform(ctx.createProjection([65, 10, 212, 112, 250, 200, 0, 256]))
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
let snapshot = await canvas.toBuffer('png', {density:4})

ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.resetTransform()
ctx.drawImage(await loadImage(snapshot), 0, 0, canvas.width, canvas.height)
canvas.saveAs("out.png")

out@2x

@samizdatco samizdatco added the enhancement New feature or request label Feb 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants