When using the transform tool to rotate a QRCode on a picture (using Ctrl + click) I get aliased edged on the end result, even with bilinear or bicubic. And that makes me a sad panda, especially given I don’t see the same behavior in Photoshop.
That image from Photoshop has a canvas more than twice the size of the canvas shown in krita and the QR code occupies a greater proportion of the canvas.
Call it about 4 x the size.
Looking at it, there actually is antialiasing on the edges but to a lesser proportional amount than in krita, as might be expected given its relative larger size.
If you perform the transform on the same image as you did in krita, but using the same sizes in Photoshop, is it any better?
The aliasing effect is visible on the left edge of the QRCode. You would not get the same with Photoshop.
I suspect the workaround would be to make the transformation with twice the size and then resizing the image to force the filtering algorithm to kick in. But that’s not optimal.
Yes I can confirm, the left edge is aliased whatever the algorithm used.
I didn’t tried all possible angle but for the one I’ve tested, the aliasing is here.
And I can reproduce this from Krita 4.4 to 5.3
This is not normal a normal thing, there’s not reason to have the left edge aliased…
The left border is properly aliased
And then few millisecond later, it’s aliased…
(note: if QR code is on the left border, it’s immediately aliased, like if border of QR code was cropped… I think there’s a bug already opened for that, I’m searching)
Then, after ~1second, there’s a calculation made automatically: I’m not able to understand why this is executed and I currently didn’t found which event trigger this delayed recalculation.
But, when this recalculation is made:
the flag m_d->recalculatingStaticImage is true and condition is matched
– here, rendered tranformation is incorrect (top-left border is aliased)
qDebug trace:
The difference between first case and second one is in dst KisPainter: in the second one is truncated by one pixel QRect(13,28 466x454) instead of QRect(12,28 467x454)
Origin of this QRect is in KisPainter::copyAreaOptimizedImpl(), but from here it start to be complex and I’m not sure if this is the origin of problem (but probably, aliasing comes from here…)
At least there’s 2 problems:
a delayed calculation that is (here) not needed
calculation from this ‘static image’ that is not made properly (if m_d->recalculatingStaticImage is forced to true then image is updated properly and there’s no aliasing, but I’m sure it’s not the right solution )
@dkazakov or @tiar I may need help to dig this deeper; also it seems to be sensitive parts too and there’s a risk to break something else (in term of performance or rendering…)
Any tip to continue to try to understand these 2 problems?
I’ve found where the delayed update is triggered (a KisSignalCompressor with a delay of 3000ms)
So I’ve tried to deactivate it: it works.
More or less.
There’s another bug, but probably this another bug already exists, it just can be see due to the current aliasing bug…
As you can see, hidding the parent layer, there’s a remaining artifact:
I think as actually the anti-aliased part is truncated, this problem is not visible. Edit: the bug was introduced by me with some previous tests rollback on this change and hiding parent layer works properly…
I’m not sure about the role of this delayed update, I’ll try to continue to understand this.
The weird thing is, after a long time (maybe 10-15minutes), even if I don’t touch anything another event trigger the recalculation and the aliasing is back
So deactivating event is probably not the right way (even if I’m really curious about why there’s a recalculation after 3seconds…): I really need to understand why the ‘static image’ update doesn’t work…
I tried to reproduce this, and only managed it once, I don’t know what’s special about that layer to trigger it.
But, what I’m seeing is backward to what’s described here. When I hide and reshow the transform mask, it first shows aliased, then after a few seconds, antialiased and moved to the left by 1px.
My debug output looks like this (with additional qDebugs added):
It shows dst->exactBounds off by 1px vertically, but that can be explained by the anti-aliasing adding 1px to the height (the left side is on the top in this case). It doesn’t explain why it’s not aliased, or why it moves over 1px horizontally. So the problem must be elsewhere.
Output images for reference (zoom in to the top-left):
dd_13_partial_dst, dd_2_recalc_dst:
Arf weird.
On my side I reproduce this systematically…
I have the move by 1px too, but on my side it’s ‘antialiased’ then 3seconds after ‘aliased’
From my additional tests:
If I disable the static image recalculation that occurs 3seconds after, everything is fine
If I let it activated, the processing time (on a 8000x8000 image) of the ‘static image’ is not made through a worker and block krita for ~6seconds (first calculation I can see tiles being processed…) and result is not as expected…
It also is aliased first and antialiased after a while here. The reason is that there is 2 rendering paths, at least using the accurate option: one is used for the “inexpensive/fast” preview and the other for the high-quality transformation. The fast one always performs bilinear sampling (even if the filter is set to nearest neighbor in the options) and is used for example when moving the handles. Then, after some seconds of inactivity, the high-quality rendering is triggered.
So, I think I fixed the fast case, which showed aliasing. The issue was purely on how the sampling was implemented. There was an issue on how the dst device points were mapped to the src device pixels. It didn’t map the pixel centers, but the corners so some shifting was noticeable. And then there was an issue on how the bilinear filtering was implemented which also could introduce shifting and produced the aliasing, at least some.
But even then, there are discrepancies between the fast rendering and the high-quality one (comparing in bilinear mode since the fast code always renders using bilinear filtering). It seems like there is still some bad coordinate mapping in the high quality code. But that code is extra hard to comprehend tbh