#include "image/ImageFileLoader.h" #include "image/ImageDecodeConfig.h" #include "large_image/VipsBackend.h" #include namespace core { namespace image_file { bool probeImagePixelSize(const QString& absolutePath, QSize* outSize) { if (!outSize || absolutePath.isEmpty()) { return false; } if (VipsBackend::isAvailable() && VipsBackend::probeSize(absolutePath, outSize)) { return outSize->isValid() && outSize->width() > 0 && outSize->height() > 0; } core::image_decode::prepareLargeImageReader(); QImageReader reader(absolutePath); reader.setAutoTransform(true); *outSize = reader.size(); return outSize->isValid() && outSize->width() > 0 && outSize->height() > 0; } QImage loadImageLimited(const QString& absolutePath, qint64 maxPixelBudget) { if (absolutePath.isEmpty()) { return {}; } QImage viaVips; if (VipsBackend::isAvailable() && VipsBackend::loadThumbnailQImage(absolutePath, maxPixelBudget, &viaVips) && !viaVips.isNull()) { return viaVips; } core::image_decode::prepareLargeImageReader(); QImageReader reader(absolutePath); reader.setAutoTransform(true); core::image_decode::downscaleReaderIfExceeds(reader, maxPixelBudget); return reader.read(); } QImage loadDepthMapAlignedToBackground(const QString& depthAbsolutePath, const QString& backgroundAbsolutePath) { if (depthAbsolutePath.isEmpty()) { return {}; } QImage g8 = loadImageLimited(depthAbsolutePath, core::image_decode::kWorkspaceMaxPixels); if (g8.isNull()) { return {}; } g8 = g8.convertToFormat(QImage::Format_Grayscale8); QSize logical; if (!backgroundAbsolutePath.isEmpty() && probeImagePixelSize(backgroundAbsolutePath, &logical) && logical.isValid() && logical.width() > 0 && logical.height() > 0 && g8.size() != logical) { g8 = g8.scaled(logical, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } return g8; } bool loadRegionToQImage(const QString& absolutePath, const QRect& rectLogical, int maxOutputWidth, int maxOutputHeight, QImage* out) { if (!out || absolutePath.isEmpty() || maxOutputWidth < 32 || maxOutputHeight < 32) { return false; } out->detach(); *out = QImage(); if (VipsBackend::isAvailable() && VipsBackend::loadRegionToQImage(absolutePath, rectLogical, maxOutputWidth, maxOutputHeight, out) && !out->isNull()) { return true; } QSize logical; if (!probeImagePixelSize(absolutePath, &logical)) { return false; } const QRect r = rectLogical.intersected(QRect(0, 0, logical.width(), logical.height())); if (r.isEmpty()) { return false; } QImage full = loadImageLimited(absolutePath, core::image_decode::kWorkspaceMaxPixels); if (full.isNull()) { return false; } const QRect r2 = core::image_decode::mapRectBetweenSizes(r, logical, full.size()); QImage piece = full.copy(r2); if (piece.isNull()) { return false; } // 与 vips 路径保持一致:输出永不超过 maxOutput*,避免后续绘制阶段二次缩放导致明显“发糊”。 if (piece.width() > maxOutputWidth || piece.height() > maxOutputHeight) { piece = piece.scaled(QSize(maxOutputWidth, maxOutputHeight), Qt::KeepAspectRatio, Qt::SmoothTransformation); } *out = piece; return !out->isNull(); } bool loadRegionToQImageExact(const QString& absolutePath, const QRect& rectLogical, QImage* out) { if (!out || absolutePath.isEmpty()) { return false; } out->detach(); *out = QImage(); QSize logical; if (!probeImagePixelSize(absolutePath, &logical)) { return false; } const QRect r = rectLogical.intersected(QRect(0, 0, logical.width(), logical.height())); if (r.isEmpty()) { return false; } // 1:1 输出:maxOutput 设为 region 本身大小,避免 vips/Qt 管线自动缩放导致坐标不一致。 const int w = std::max(1, r.width()); const int h = std::max(1, r.height()); return loadRegionToQImage(absolutePath, r, w, h, out); } } // namespace image_file } // namespace core