tone_mapping.cc (4738B)
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 "lib/extras/tone_mapping.h" 7 8 #undef HWY_TARGET_INCLUDE 9 #define HWY_TARGET_INCLUDE "lib/extras/tone_mapping.cc" 10 #include <jxl/cms.h> 11 12 #include <hwy/foreach_target.h> 13 #include <hwy/highway.h> 14 15 #include "lib/jxl/cms/tone_mapping-inl.h" 16 #include "lib/jxl/image_bundle.h" 17 18 HWY_BEFORE_NAMESPACE(); 19 namespace jxl { 20 namespace HWY_NAMESPACE { 21 22 static constexpr Vector3 rec2020_luminances{0.2627f, 0.6780f, 0.0593f}; 23 24 Status ToneMapFrame(const std::pair<float, float> display_nits, 25 ImageBundle* const ib, ThreadPool* const pool) { 26 // Perform tone mapping as described in Report ITU-R BT.2390-8, section 5.4 27 // (pp. 23-25). 28 // https://www.itu.int/pub/R-REP-BT.2390-8-2020 29 30 HWY_FULL(float) df; 31 using V = decltype(Zero(df)); 32 33 ColorEncoding linear_rec2020; 34 linear_rec2020.SetColorSpace(ColorSpace::kRGB); 35 JXL_RETURN_IF_ERROR(linear_rec2020.SetPrimariesType(Primaries::k2100)); 36 JXL_RETURN_IF_ERROR(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); 37 linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); 38 JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC()); 39 JXL_RETURN_IF_ERROR( 40 ib->TransformTo(linear_rec2020, *JxlGetDefaultCms(), pool)); 41 42 Rec2408ToneMapper<decltype(df)> tone_mapper( 43 {ib->metadata()->tone_mapping.min_nits, 44 ib->metadata()->IntensityTarget()}, 45 display_nits, rec2020_luminances); 46 47 const auto process_row = [&](const uint32_t y, 48 size_t /* thread */) -> Status { 49 float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y); 50 float* const JXL_RESTRICT row_g = ib->color()->PlaneRow(1, y); 51 float* const JXL_RESTRICT row_b = ib->color()->PlaneRow(2, y); 52 for (size_t x = 0; x < ib->xsize(); x += Lanes(df)) { 53 V red = Load(df, row_r + x); 54 V green = Load(df, row_g + x); 55 V blue = Load(df, row_b + x); 56 tone_mapper.ToneMap(&red, &green, &blue); 57 Store(red, df, row_r + x); 58 Store(green, df, row_g + x); 59 Store(blue, df, row_b + x); 60 } 61 return true; 62 }; 63 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, ib->ysize(), ThreadPool::NoInit, 64 process_row, "ToneMap")); 65 return true; 66 } 67 68 Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation, 69 ThreadPool* const pool) { 70 HWY_FULL(float) df; 71 using V = decltype(Zero(df)); 72 73 ColorEncoding linear_rec2020; 74 linear_rec2020.SetColorSpace(ColorSpace::kRGB); 75 JXL_RETURN_IF_ERROR(linear_rec2020.SetPrimariesType(Primaries::k2100)); 76 JXL_RETURN_IF_ERROR(linear_rec2020.SetWhitePointType(WhitePoint::kD65)); 77 linear_rec2020.Tf().SetTransferFunction(TransferFunction::kLinear); 78 JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC()); 79 JXL_RETURN_IF_ERROR( 80 ib->TransformTo(linear_rec2020, *JxlGetDefaultCms(), pool)); 81 82 const auto process_row = [&](const uint32_t y, size_t /* thread*/) -> Status { 83 float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y); 84 float* const JXL_RESTRICT row_g = ib->color()->PlaneRow(1, y); 85 float* const JXL_RESTRICT row_b = ib->color()->PlaneRow(2, y); 86 for (size_t x = 0; x < ib->xsize(); x += Lanes(df)) { 87 V red = Load(df, row_r + x); 88 V green = Load(df, row_g + x); 89 V blue = Load(df, row_b + x); 90 GamutMap(&red, &green, &blue, rec2020_luminances, preserve_saturation); 91 Store(red, df, row_r + x); 92 Store(green, df, row_g + x); 93 Store(blue, df, row_b + x); 94 } 95 return true; 96 }; 97 98 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, ib->ysize(), ThreadPool::NoInit, 99 process_row, "GamutMap")); 100 101 return true; 102 } 103 104 // NOLINTNEXTLINE(google-readability-namespace-comments) 105 } // namespace HWY_NAMESPACE 106 } // namespace jxl 107 HWY_AFTER_NAMESPACE(); 108 109 #if HWY_ONCE 110 namespace jxl { 111 112 namespace { 113 HWY_EXPORT(ToneMapFrame); 114 HWY_EXPORT(GamutMapFrame); 115 } // namespace 116 117 Status ToneMapTo(const std::pair<float, float> display_nits, 118 CodecInOut* const io, ThreadPool* const pool) { 119 const auto tone_map_frame = HWY_DYNAMIC_DISPATCH(ToneMapFrame); 120 for (ImageBundle& ib : io->frames) { 121 JXL_RETURN_IF_ERROR(tone_map_frame(display_nits, &ib, pool)); 122 } 123 io->metadata.m.SetIntensityTarget(display_nits.second); 124 return true; 125 } 126 127 Status GamutMap(CodecInOut* const io, float preserve_saturation, 128 ThreadPool* const pool) { 129 const auto gamut_map_frame = HWY_DYNAMIC_DISPATCH(GamutMapFrame); 130 for (ImageBundle& ib : io->frames) { 131 JXL_RETURN_IF_ERROR(gamut_map_frame(&ib, preserve_saturation, pool)); 132 } 133 return true; 134 } 135 136 } // namespace jxl 137 #endif