#include "UI/CanvasThumbnail.h" #include "Document/BrushStroke.h" #include "IO/ImageExporter.h" #include "Logging.h" #include "Rendering/LayerRenderer.h" #include #include namespace { // A canvas-shaped picture; `draw` gets pixels and their scale. QPixmap render(QSizeF canvas, double box, const std::function &draw) { const QSize points = CanvasThumbnail::fittedSize(canvas, box); const QSize pixels = points * CanvasThumbnail::backingScale; QImage image; try { image = BrushRaster::context(pixels.width(), pixels.height(), false); } catch (const ExportError &error) { qCWarning(lcRendering) << "a thumbnail could not be allocated:" << pixels << error.what(); QPixmap empty(pixels); empty.fill(Qt::transparent); empty.setDevicePixelRatio(CanvasThumbnail::backingScale); return empty; } { QPainter painter(&image); draw(painter, pixels, canvas.width() > 0 ? pixels.width() / canvas.width() : 1); } QPixmap result = QPixmap::fromImage(image); result.setDevicePixelRatio(CanvasThumbnail::backingScale); return result; } // The image where the transform puts it, as the canvas. void place(const QImage &image, const LayerTransform &transform, double scale, QPainter &painter) { LayerRenderer::draw(image, transform, QPointF(transform.center().x() * scale, transform.center().y() * scale), painter, {.scale = scale}); } } QSize CanvasThumbnail::fittedSize(QSizeF canvas, double box) { if (!(canvas.width() > 0) || !(canvas.height() > 0) || !std::isfinite(canvas.width()) || !std::isfinite(canvas.height())) return QSize(int(box), int(box)); const double scale = box / std::max(canvas.width(), canvas.height()); return QSize(std::max(1, int(std::round(canvas.width() * scale))), std::max(1, int(std::round(canvas.height() * scale)))); } QPixmap CanvasThumbnail::layer(const QImage &image, const LayerTransform &transform, QSizeF canvas, double box) { return render(canvas, box, [&](QPainter &painter, QSize size, double scale) { painter.fillRect(QRect(QPoint(0, 0), size), QColor::fromRgbF(0.22, 0.22, 0.22)); const double tile = 6 * backingScale; for (int row = 0; row < int(std::ceil(size.height() / tile)); ++row) { for (int column = 0; column < int(std::ceil(size.width() / tile)); ++column) { if ((row + column) % 2 == 0) painter.fillRect(QRectF(column * tile, row * tile, tile, tile), QColor::fromRgbF(0.32, 0.32, 0.32)); } } if (!image.isNull()) place(image, transform, scale, painter); }); } QPixmap CanvasThumbnail::mask(const QImage &image, const LayerTransform &transform, QSizeF canvas, double box) { return render(canvas, box, [&](QPainter &painter, QSize size, double scale) { const double tone = edgeTone(image); painter.fillRect(QRect(QPoint(0, 0), size), QColor::fromRgbF(tone, tone, tone)); place(image, transform, scale, painter); }); } double CanvasThumbnail::edgeTone(const QImage &image) { const QImage gray = image.convertToFormat(QImage::Format_Grayscale8); if (gray.width() <= 0 || gray.height() <= 0) return 1; qint64 total = 0, count = 0; for (int y = 0; y < gray.height(); ++y) { const uchar *row = gray.constScanLine(y); const bool edgeRow = y == 0 || y == gray.height() - 1; for (int x = 0; x < gray.width(); ++x) { if (edgeRow || x == 0 || x == gray.width() - 1) { total += row[x]; ++count; } } } return count == 0 ? 1 : double(total) / double(count) / 255; }