Serenity Operating System
at master 155 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2022, Torsten Engelmann <engelTorsten@gmx.de> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "LevelsDialog.h" 8#include <Applications/PixelPaint/LevelsDialogGML.h> 9#include <LibGUI/Button.h> 10#include <LibGUI/Label.h> 11#include <LibGUI/ValueSlider.h> 12 13namespace PixelPaint { 14 15LevelsDialog::LevelsDialog(GUI::Window* parent_window, ImageEditor* editor) 16 : GUI::Dialog(parent_window) 17{ 18 set_title("Levels"); 19 set_icon(parent_window->icon()); 20 21 auto main_widget = set_main_widget<GUI::Widget>().release_value_but_fixme_should_propagate_errors(); 22 main_widget->load_from_gml(levels_dialog_gml).release_value_but_fixme_should_propagate_errors(); 23 24 resize(305, 202); 25 set_resizable(false); 26 27 m_editor = editor; 28 29 m_brightness_slider = main_widget->find_descendant_of_type_named<GUI::ValueSlider>("brightness_slider"); 30 m_contrast_slider = main_widget->find_descendant_of_type_named<GUI::ValueSlider>("contrast_slider"); 31 m_gamma_slider = main_widget->find_descendant_of_type_named<GUI::ValueSlider>("gamma_slider"); 32 auto context_label = main_widget->find_descendant_of_type_named<GUI::Label>("context_label"); 33 auto apply_button = main_widget->find_descendant_of_type_named<GUI::Button>("apply_button"); 34 auto cancel_button = main_widget->find_descendant_of_type_named<GUI::Button>("cancel_button"); 35 36 VERIFY(m_brightness_slider); 37 VERIFY(m_contrast_slider); 38 VERIFY(m_gamma_slider); 39 VERIFY(context_label); 40 VERIFY(apply_button); 41 VERIFY(cancel_button); 42 VERIFY(m_editor->active_layer()); 43 44 context_label->set_text(DeprecatedString::formatted("Working on layer: {}", m_editor->active_layer()->name())); 45 m_gamma_slider->set_value(100); 46 47 m_brightness_slider->on_change = [this](auto) { 48 generate_new_image(); 49 }; 50 51 m_contrast_slider->on_change = [this](auto) { 52 generate_new_image(); 53 }; 54 55 m_gamma_slider->on_change = [this](auto) { 56 generate_new_image(); 57 }; 58 59 apply_button->on_click = [this](auto) { 60 if (m_did_change) 61 m_editor->did_complete_action("Levels"sv); 62 63 cleanup_resources(); 64 done(ExecResult::OK); 65 }; 66 67 cancel_button->on_click = [this](auto) { 68 done(ExecResult::Cancel); 69 }; 70} 71 72void LevelsDialog::revert_possible_changes() 73{ 74 // FIXME: Find a faster way to revert all the changes that we have done. 75 if (m_did_change && m_reference_bitmap) { 76 for (int x = 0; x < m_reference_bitmap->width(); x++) { 77 for (int y = 0; y < m_reference_bitmap->height(); y++) { 78 m_editor->active_layer()->content_bitmap().set_pixel(x, y, m_reference_bitmap->get_pixel(x, y)); 79 } 80 } 81 m_editor->layers_did_change(); 82 } 83 84 cleanup_resources(); 85} 86 87void LevelsDialog::generate_new_image() 88{ 89 (void)ensure_reference_bitmap(); 90 if (m_reference_bitmap.is_null()) 91 return; 92 93 generate_precomputed_color_correction(); 94 Color current_pixel_color; 95 Color new_pixel_color; 96 Gfx::StorageFormat storage_format = Gfx::determine_storage_format(m_editor->active_layer()->content_bitmap().format()); 97 98 for (int x = 0; x < m_reference_bitmap->width(); x++) { 99 for (int y = 0; y < m_reference_bitmap->height(); y++) { 100 current_pixel_color = m_reference_bitmap->get_pixel(x, y); 101 102 new_pixel_color.set_alpha(current_pixel_color.alpha()); 103 new_pixel_color.set_red(m_precomputed_color_correction[current_pixel_color.red()]); 104 new_pixel_color.set_green(m_precomputed_color_correction[current_pixel_color.green()]); 105 new_pixel_color.set_blue(m_precomputed_color_correction[current_pixel_color.blue()]); 106 107 switch (storage_format) { 108 case Gfx::StorageFormat::BGRx8888: 109 case Gfx::StorageFormat::BGRA8888: 110 m_editor->active_layer()->content_bitmap().scanline(y)[x] = new_pixel_color.value(); 111 break; 112 default: 113 m_editor->active_layer()->content_bitmap().set_pixel(x, y, new_pixel_color); 114 } 115 } 116 } 117 118 m_editor->active_layer()->did_modify_bitmap(); 119 m_did_change = true; 120} 121 122ErrorOr<void> LevelsDialog::ensure_reference_bitmap() 123{ 124 if (m_reference_bitmap.is_null()) 125 m_reference_bitmap = TRY(m_editor->active_layer()->content_bitmap().clone()); 126 127 return {}; 128} 129 130void LevelsDialog::cleanup_resources() 131{ 132 if (m_reference_bitmap) 133 m_reference_bitmap = nullptr; 134} 135 136void LevelsDialog::generate_precomputed_color_correction() 137{ 138 int delta_brightness = m_brightness_slider->value(); 139 float contrast_correction_factor = static_cast<float>(259 * (m_contrast_slider->value() + 255) / static_cast<float>(255 * (259 - m_contrast_slider->value()))); 140 float gamma_correction = 1 / (m_gamma_slider->value() / 100.0); 141 142 for (int color_val = 0; color_val < 256; color_val++) { 143 m_precomputed_color_correction[color_val] = color_val + delta_brightness; 144 m_precomputed_color_correction[color_val] = m_precomputed_color_correction[color_val] < 0 ? 0 : m_precomputed_color_correction[color_val]; 145 m_precomputed_color_correction[color_val] = m_precomputed_color_correction[color_val] > 255 ? 255 : m_precomputed_color_correction[color_val]; 146 147 m_precomputed_color_correction[color_val] = 255 * AK::pow<float>((m_precomputed_color_correction[color_val] / 255.0), gamma_correction); 148 149 m_precomputed_color_correction[color_val] = contrast_correction_factor * (m_precomputed_color_correction[color_val] - 128) + 128; 150 m_precomputed_color_correction[color_val] = m_precomputed_color_correction[color_val] < 0 ? 0 : m_precomputed_color_correction[color_val]; 151 m_precomputed_color_correction[color_val] = m_precomputed_color_correction[color_val] > 255 ? 255 : m_precomputed_color_correction[color_val]; 152 } 153} 154 155}