14.4 round 1: a quarter turn copies across only when image pixels map one to one onto device pixels; every layer and placed mask turns
This commit is contained in:
6 files changed
+86
-20
No files matched your search
@@ -59,7 +59,7 @@ void LayerRenderer::draw(const QImage &image, const LayerTransform &transform, Q
|
||||
placement.rotate(transform.degrees());
|
||||
placement.scale(transform.flipX ? -1 : 1, transform.flipY ? -1 : 1);
|
||||
const InterpolationQuality quality = interpolation(transform.sampling, width * device / std::max(1, image.width()) * (1 << source.level),
|
||||
upright(transform, placement, bounds, context));
|
||||
upright(transform, placement, coverage(source, bounds), source.image.size(), context));
|
||||
|
||||
if (options.mask.isNull() && options.clip.isNull() && !HslBlend::handles(options.blendMode)) {
|
||||
const QPainter::CompositionMode mode = compositionMode(options.blendMode);
|
||||
@@ -293,19 +293,20 @@ void LayerRenderer::drawCoverage(const QImage &image, const LayerTransform &tran
|
||||
}
|
||||
|
||||
// QPainter's fixed-point bilinear strays a level where CoreGraphics copies.
|
||||
bool LayerRenderer::upright(const LayerTransform &transform, const QTransform &placement, const QRectF &bounds, const QPainter &context)
|
||||
bool LayerRenderer::upright(const LayerTransform &transform, const QTransform &placement, const QRectF &target, QSize pixels, const QPainter &context)
|
||||
{
|
||||
if (transform.radians() == 0)
|
||||
return true;
|
||||
if (std::remainder(transform.rotation, 90) != 0)
|
||||
return false;
|
||||
const QTransform device = placement * context.deviceTransform();
|
||||
for (const QPointF corner : {bounds.topLeft(), bounds.bottomRight()}) {
|
||||
const QPointF mapped = device.map(corner);
|
||||
if (std::abs(mapped.x() - std::round(mapped.x())) > 0.001 || std::abs(mapped.y() - std::round(mapped.y())) > 0.001)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// Image pixels onto device pixels: a signed permutation, whole.
|
||||
const QTransform map = QTransform(target.width() / pixels.width(), 0, 0, target.height() / pixels.height(), target.left(), target.top()) * placement
|
||||
* context.deviceTransform();
|
||||
// Within a thousandth of a pixel across the whole image.
|
||||
const double tight = 0.001 / std::max(pixels.width(), pixels.height());
|
||||
const auto near = [](double value, double to, double within) { return std::abs(value - to) < within; };
|
||||
const bool straight = near(std::abs(map.m11()), 1, tight) && near(map.m12(), 0, tight) && near(map.m21(), 0, tight) && near(std::abs(map.m22()), 1, tight);
|
||||
const bool across = near(map.m11(), 0, tight) && near(std::abs(map.m12()), 1, tight) && near(std::abs(map.m21()), 1, tight) && near(map.m22(), 0, tight);
|
||||
return (straight || across) && near(map.m13(), 0, tight) && near(map.m23(), 0, tight) && near(map.dx(), std::round(map.dx()), 0.001)
|
||||
&& near(map.dy(), std::round(map.dy()), 0.001);
|
||||
}
|
||||
|
||||
InterpolationQuality LayerRenderer::interpolation(LayerSampling sampling, double finalFactor, bool upright)
|
||||
|
||||
@@ -59,8 +59,8 @@ void composite(QPainter &context, const QTransform &placement, const QRectF &ext
|
||||
const std::function<void(QPainter &)> &body, const std::function<void(QPainter &)> &veil = {});
|
||||
// An upright layer drawn pixel for pixel copies straight across.
|
||||
InterpolationQuality interpolation(LayerSampling sampling, double finalFactor, bool upright = false);
|
||||
// Swift's upright, or a quarter turn on whole pixels.
|
||||
bool upright(const LayerTransform &transform, const QTransform &placement, const QRectF &bounds, const QPainter &context);
|
||||
// Swift's upright, or image pixels onto device pixels, turned.
|
||||
bool upright(const LayerTransform &transform, const QTransform &placement, const QRectF &target, QSize pixels, const QPainter &context);
|
||||
Reduced reduced(const QImage &image, double width, double device, LayerSampling sampling);
|
||||
QRectF coverage(const Reduced &reduced, const QRectF &bounds);
|
||||
double deviceScale(const QPainter &context);
|
||||
|
||||
@@ -156,7 +156,7 @@ void withFrame(int pixelWidth, int pixelHeight, const QRectF &painted, const QRe
|
||||
placement.scale(transform.flipX ? -1 : 1, transform.flipY ? -1 : 1);
|
||||
const QRectF bounds(-width / 2, -height / 2, width, height);
|
||||
const InterpolationQuality quality = LayerRenderer::interpolation(transform.sampling, width * device / pixelWidth * (1 << level),
|
||||
LayerRenderer::upright(transform, placement, bounds, context));
|
||||
LayerRenderer::upright(transform, placement, bounds, QSize(pixelWidth, pixelHeight), context));
|
||||
const QRectF shown = placement.inverted().mapRect(BrushRaster::visibleRect(context));
|
||||
const double sx = pixelWidth / width, sy = pixelHeight / height;
|
||||
const Frame frame{bounds, double(pixelWidth), double(pixelHeight), level, device, transform.sampling,
|
||||
|
||||
Reference in new issue
Block a user