palette.cc (7795B)
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/jxl/modular/transform/palette.h" 7 8 #include <jxl/memory_manager.h> 9 10 #include "lib/jxl/base/status.h" 11 #include "lib/jxl/modular/transform/transform.h" // CheckEqualChannels 12 13 namespace jxl { 14 15 Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, 16 uint32_t nb_deltas, Predictor predictor, 17 const weighted::Header &wp_header, ThreadPool *pool) { 18 JxlMemoryManager *memory_manager = input.memory_manager(); 19 if (input.nb_meta_channels < 1) { 20 return JXL_FAILURE("Error: Palette transform without palette."); 21 } 22 int nb = input.channel[0].h; 23 uint32_t c0 = begin_c + 1; 24 if (c0 >= input.channel.size()) { 25 return JXL_FAILURE("Channel is out of range."); 26 } 27 size_t w = input.channel[c0].w; 28 size_t h = input.channel[c0].h; 29 if (nb < 1) return JXL_FAILURE("Corrupted transforms"); 30 for (int i = 1; i < nb; i++) { 31 JXL_ASSIGN_OR_RETURN(Channel c, Channel::Create(memory_manager, w, h, 32 input.channel[c0].hshift, 33 input.channel[c0].vshift)); 34 input.channel.insert(input.channel.begin() + c0 + 1, std::move(c)); 35 } 36 const Channel &palette = input.channel[0]; 37 const pixel_type *JXL_RESTRICT p_palette = input.channel[0].Row(0); 38 intptr_t onerow = input.channel[0].plane.PixelsPerRow(); 39 intptr_t onerow_image = input.channel[c0].plane.PixelsPerRow(); 40 const int bit_depth = std::min(input.bitdepth, 24); 41 42 if (w == 0) { 43 // Nothing to do. 44 // Avoid touching "empty" channels with non-zero height. 45 } else if (nb_deltas == 0 && predictor == Predictor::Zero) { 46 if (nb == 1) { 47 const auto process_row = [&](const uint32_t task, 48 size_t /* thread */) -> Status { 49 const size_t y = task; 50 pixel_type *p = input.channel[c0].Row(y); 51 for (size_t x = 0; x < w; x++) { 52 const int index = 53 Clamp1<int>(p[x], 0, static_cast<pixel_type>(palette.w) - 1); 54 p[x] = palette_internal::GetPaletteValue(p_palette, index, /*c=*/0, 55 /*palette_size=*/palette.w, 56 /*onerow=*/onerow, 57 /*bit_depth=*/bit_depth); 58 } 59 return true; 60 }; 61 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, h, ThreadPool::NoInit, process_row, 62 "UndoChannelPalette")); 63 } else { 64 const auto process_row = [&](const uint32_t task, 65 size_t /* thread */) -> Status { 66 const size_t y = task; 67 std::vector<pixel_type *> p_out(nb); 68 const pixel_type *p_index = input.channel[c0].Row(y); 69 for (int c = 0; c < nb; c++) p_out[c] = input.channel[c0 + c].Row(y); 70 for (size_t x = 0; x < w; x++) { 71 const int index = p_index[x]; 72 for (int c = 0; c < nb; c++) { 73 p_out[c][x] = palette_internal::GetPaletteValue( 74 p_palette, index, /*c=*/c, 75 /*palette_size=*/palette.w, 76 /*onerow=*/onerow, /*bit_depth=*/bit_depth); 77 } 78 } 79 return true; 80 }; 81 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, h, ThreadPool::NoInit, process_row, 82 "UndoPalette")); 83 } 84 } else { 85 // Parallelized per channel. 86 ImageI indices; 87 ImageI &plane = input.channel[c0].plane; 88 JXL_ASSIGN_OR_RETURN( 89 indices, ImageI::Create(memory_manager, plane.xsize(), plane.ysize())); 90 plane.Swap(indices); 91 if (predictor == Predictor::Weighted) { 92 const auto process_row = [&](const uint32_t c, 93 size_t /* thread */) -> Status { 94 Channel &channel = input.channel[c0 + c]; 95 weighted::State wp_state(wp_header, channel.w, channel.h); 96 for (size_t y = 0; y < channel.h; y++) { 97 pixel_type *JXL_RESTRICT p = channel.Row(y); 98 const pixel_type *JXL_RESTRICT idx = indices.Row(y); 99 for (size_t x = 0; x < channel.w; x++) { 100 int index = idx[x]; 101 pixel_type_w val = 0; 102 const pixel_type palette_entry = palette_internal::GetPaletteValue( 103 p_palette, index, /*c=*/c, 104 /*palette_size=*/palette.w, /*onerow=*/onerow, 105 /*bit_depth=*/bit_depth); 106 if (index < static_cast<int32_t>(nb_deltas)) { 107 PredictionResult pred = PredictNoTreeWP( 108 channel.w, p + x, onerow_image, x, y, predictor, &wp_state); 109 val = pred.guess + palette_entry; 110 } else { 111 val = palette_entry; 112 } 113 p[x] = val; 114 wp_state.UpdateErrors(p[x], x, y, channel.w); 115 } 116 } 117 return true; 118 }; 119 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, nb, ThreadPool::NoInit, 120 process_row, "UndoDeltaPaletteWP")); 121 } else { 122 const auto process_row = [&](const uint32_t c, 123 size_t /* thread */) -> Status { 124 Channel &channel = input.channel[c0 + c]; 125 for (size_t y = 0; y < channel.h; y++) { 126 pixel_type *JXL_RESTRICT p = channel.Row(y); 127 const pixel_type *JXL_RESTRICT idx = indices.Row(y); 128 for (size_t x = 0; x < channel.w; x++) { 129 int index = idx[x]; 130 pixel_type_w val = 0; 131 const pixel_type palette_entry = palette_internal::GetPaletteValue( 132 p_palette, index, /*c=*/c, 133 /*palette_size=*/palette.w, 134 /*onerow=*/onerow, /*bit_depth=*/bit_depth); 135 if (index < static_cast<int32_t>(nb_deltas)) { 136 PredictionResult pred = PredictNoTreeNoWP( 137 channel.w, p + x, onerow_image, x, y, predictor); 138 val = pred.guess + palette_entry; 139 } else { 140 val = palette_entry; 141 } 142 p[x] = val; 143 } 144 } 145 return true; 146 }; 147 JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, nb, ThreadPool::NoInit, 148 process_row, "UndoDeltaPaletteNoWP")); 149 } 150 } 151 if (c0 >= input.nb_meta_channels) { 152 // Palette was done on normal channels 153 input.nb_meta_channels--; 154 } else { 155 // Palette was done on metachannels 156 JXL_ENSURE(static_cast<int>(input.nb_meta_channels) >= 2 - nb); 157 input.nb_meta_channels -= 2 - nb; 158 JXL_ENSURE(begin_c + nb - 1 < input.nb_meta_channels); 159 } 160 input.channel.erase(input.channel.begin(), input.channel.begin() + 1); 161 return true; 162 } 163 164 Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c, 165 uint32_t nb_colors, uint32_t nb_deltas, bool lossy) { 166 JXL_RETURN_IF_ERROR(CheckEqualChannels(input, begin_c, end_c)); 167 JxlMemoryManager *memory_manager = input.memory_manager(); 168 169 size_t nb = end_c - begin_c + 1; 170 if (begin_c >= input.nb_meta_channels) { 171 // Palette was done on normal channels 172 input.nb_meta_channels++; 173 } else { 174 // Palette was done on metachannels 175 JXL_ENSURE(end_c < input.nb_meta_channels); 176 // we remove nb-1 metachannels and add one 177 input.nb_meta_channels += 2 - nb; 178 } 179 input.channel.erase(input.channel.begin() + begin_c + 1, 180 input.channel.begin() + end_c + 1); 181 JXL_ASSIGN_OR_RETURN( 182 Channel pch, Channel::Create(memory_manager, nb_colors + nb_deltas, nb)); 183 pch.hshift = -1; 184 pch.vshift = -1; 185 input.channel.insert(input.channel.begin(), std::move(pch)); 186 return true; 187 } 188 189 } // namespace jxl