image_loading.cc (4430B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include "tools/comparison_viewer/image_loading.h" 7 8 #include <jxl/cms.h> 9 10 #include <QRgb> 11 #include <QThread> 12 #include <cstdint> 13 #include <vector> 14 15 #include "lib/extras/codec.h" 16 #include "lib/extras/dec/color_hints.h" 17 #include "lib/jxl/image_bundle.h" 18 #include "lib/jxl/image_metadata.h" 19 #include "tools/file_io.h" 20 #include "tools/thread_pool_internal.h" 21 #include "tools/viewer/load_jxl.h" 22 23 namespace jpegxl { 24 namespace tools { 25 26 using jxl::CodecInOut; 27 using jxl::ColorEncoding; 28 using jxl::IccBytes; 29 using jxl::Image3F; 30 using jxl::ImageBundle; 31 using jxl::Rect; 32 using jxl::Span; 33 using jxl::Status; 34 using jxl::ThreadPool; 35 using jxl::extras::ColorHints; 36 37 namespace { 38 39 Status loadFromFile(const QString& filename, const ColorHints& color_hints, 40 CodecInOut* const decoded, ThreadPool* const pool) { 41 std::vector<uint8_t> compressed; 42 JXL_RETURN_IF_ERROR( 43 jpegxl::tools::ReadFile(filename.toStdString(), &compressed)); 44 const Span<const uint8_t> compressed_span(compressed); 45 return jxl::SetFromBytes(compressed_span, color_hints, decoded, pool, 46 nullptr); 47 } 48 49 } // namespace 50 51 bool canLoadImageWithExtension(QString extension) { 52 extension = extension.toLower(); 53 if (extension == "jxl" || extension == "j" || extension == "brn") { 54 return true; 55 } 56 const auto codec = jxl::extras::CodecFromPath("." + extension.toStdString()); 57 return codec != jxl::extras::Codec::kUnknown; 58 } 59 60 QImage loadImage(const QString& filename, const QByteArray& targetIccProfile, 61 const float intensityTarget, 62 const QString& sourceColorSpaceHint) { 63 qint64 elapsed; 64 QImage img = loadJxlImage(filename, targetIccProfile, &elapsed); 65 if (img.width() != 0 && img.height() != 0) { 66 return img; 67 } 68 static ThreadPoolInternal pool(QThread::idealThreadCount()); 69 70 CodecInOut decoded; 71 ColorHints color_hints; 72 if (!sourceColorSpaceHint.isEmpty()) { 73 color_hints.Add("color_space", sourceColorSpaceHint.toStdString()); 74 } 75 if (!loadFromFile(filename, color_hints, &decoded, &pool)) { 76 return QImage(); 77 } 78 decoded.metadata.m.SetIntensityTarget(intensityTarget); 79 const ImageBundle& ib = decoded.Main(); 80 81 ColorEncoding targetColorSpace; 82 bool use_fallback_profile = true; 83 if (!targetIccProfile.isEmpty()) { 84 IccBytes icc; 85 icc.assign(reinterpret_cast<const uint8_t*>(targetIccProfile.data()), 86 reinterpret_cast<const uint8_t*>(targetIccProfile.data() + 87 targetIccProfile.size())); 88 use_fallback_profile = 89 !targetColorSpace.SetICC(std::move(icc), JxlGetDefaultCms()); 90 } 91 if (use_fallback_profile) { 92 targetColorSpace = ColorEncoding::SRGB(ib.IsGray()); 93 } 94 Image3F converted; 95 if (!ib.CopyTo(Rect(ib), targetColorSpace, *JxlGetDefaultCms(), &converted, 96 &pool)) { 97 return QImage(); 98 } 99 100 QImage image(converted.xsize(), converted.ysize(), QImage::Format_ARGB32); 101 102 const auto ScaleAndClamp = [](const float x) { 103 return jxl::Clamp1(x * 255 + .5f, 0.f, 255.f); 104 }; 105 106 if (ib.HasAlpha()) { 107 for (int y = 0; y < image.height(); ++y) { 108 QRgb* const row = reinterpret_cast<QRgb*>(image.scanLine(y)); 109 const float* const alphaRow = ib.alpha().ConstRow(y); 110 const float* const redRow = converted.ConstPlaneRow(0, y); 111 const float* const greenRow = converted.ConstPlaneRow(1, y); 112 const float* const blueRow = converted.ConstPlaneRow(2, y); 113 for (int x = 0; x < image.width(); ++x) { 114 row[x] = qRgba(ScaleAndClamp(redRow[x]), ScaleAndClamp(greenRow[x]), 115 ScaleAndClamp(blueRow[x]), ScaleAndClamp(alphaRow[x])); 116 } 117 } 118 } else { 119 for (int y = 0; y < image.height(); ++y) { 120 QRgb* const row = reinterpret_cast<QRgb*>(image.scanLine(y)); 121 const float* const redRow = converted.ConstPlaneRow(0, y); 122 const float* const greenRow = converted.ConstPlaneRow(1, y); 123 const float* const blueRow = converted.ConstPlaneRow(2, y); 124 for (int x = 0; x < image.width(); ++x) { 125 row[x] = qRgb(ScaleAndClamp(redRow[x]), ScaleAndClamp(greenRow[x]), 126 ScaleAndClamp(blueRow[x])); 127 } 128 } 129 } 130 131 return image; 132 } 133 134 } // namespace tools 135 } // namespace jpegxl