Serenity Operating System
at master 139 lines 5.0 kB view raw
1/* 2 * Copyright (c) 2022, Torsten Engelmann 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "HistogramWidget.h" 8#include "Image.h" 9#include "ImageEditor.h" 10#include "Layer.h" 11#include <LibGUI/Painter.h> 12#include <LibGfx/Palette.h> 13#include <LibGfx/Path.h> 14 15REGISTER_WIDGET(PixelPaint, HistogramWidget); 16 17namespace PixelPaint { 18 19ErrorOr<void> HistogramWidget::rebuild_histogram_data() 20{ 21 if (!m_image) 22 return {}; 23 24 auto full_bitmap = TRY(m_image->compose_bitmap(Gfx::BitmapFormat::BGRA8888)); 25 26 m_data.red.clear_with_capacity(); 27 m_data.green.clear_with_capacity(); 28 m_data.blue.clear_with_capacity(); 29 m_data.brightness.clear_with_capacity(); 30 31 for (int i = 0; i < 256; i++) { 32 m_data.red.append(0); 33 m_data.green.append(0); 34 m_data.blue.append(0); 35 m_data.brightness.append(0); 36 } 37 38 Color pixel_color; 39 for (int x = 0; x < full_bitmap->width(); x++) { 40 for (int y = 0; y < full_bitmap->height(); y++) { 41 pixel_color = full_bitmap->get_pixel(x, y); 42 if (!pixel_color.alpha()) 43 continue; 44 45 m_data.red[pixel_color.red()]++; 46 m_data.green[pixel_color.green()]++; 47 m_data.blue[pixel_color.blue()]++; 48 m_data.brightness[pixel_color.luminosity()]++; 49 } 50 } 51 int max_brightness_frequency = 0; 52 int max_color_frequency = 0; 53 for (int i = 0; i < 256; i++) { 54 if (m_data.red[i] > max_color_frequency) 55 max_color_frequency = m_data.red[i]; 56 if (m_data.green[i] > max_color_frequency) 57 max_color_frequency = m_data.green[i]; 58 if (m_data.blue[i] > max_color_frequency) 59 max_color_frequency = m_data.blue[i]; 60 if (m_data.brightness[i] > max_brightness_frequency) 61 max_brightness_frequency = m_data.brightness[i]; 62 } 63 64 // Scale the frequency values to fit the widgets height. 65 auto widget_height = height(); 66 67 for (int i = 0; i < 256; i++) { 68 m_data.red[i] = m_data.red[i] != 0 ? (static_cast<float>(m_data.red[i]) / max_color_frequency) * widget_height : 0; 69 m_data.green[i] = m_data.green[i] != 0 ? (static_cast<float>(m_data.green[i]) / max_color_frequency) * widget_height : 0; 70 m_data.blue[i] = m_data.blue[i] != 0 ? (static_cast<float>(m_data.blue[i]) / max_color_frequency) * widget_height : 0; 71 m_data.brightness[i] = m_data.brightness[i] != 0 ? (static_cast<float>(m_data.brightness[i]) / max_brightness_frequency) * widget_height : 0; 72 } 73 74 return {}; 75} 76 77void HistogramWidget::paint_event(GUI::PaintEvent& event) 78{ 79 GUI::Painter painter(*this); 80 painter.add_clip_rect(event.rect()); 81 82 if (!m_image) 83 return; 84 85 int bottom_line = height() - 1; 86 float step_width = static_cast<float>(width()) / 256; 87 88 Gfx::Path brightness_path; 89 Gfx::Path red_channel_path; 90 Gfx::Path green_channel_path; 91 Gfx::Path blue_channel_path; 92 red_channel_path.move_to({ 0, bottom_line - m_data.red[0] }); 93 green_channel_path.move_to({ 0, bottom_line - m_data.green[0] }); 94 blue_channel_path.move_to({ 0, bottom_line - m_data.blue[0] }); 95 brightness_path.move_to({ 0, bottom_line }); 96 brightness_path.line_to({ 0, bottom_line }); 97 98 float current_x_as_float = 0; 99 int current_x_as_int = 0; 100 int last_drawn_x = -1; 101 102 for (int data_column = 0; data_column < 256; data_column++) { 103 current_x_as_int = static_cast<int>(current_x_as_float); 104 // we would like to skip values that map to the same x position as it does not look so good in the final result 105 if (current_x_as_int == last_drawn_x) { 106 current_x_as_float += step_width; 107 continue; 108 } 109 110 red_channel_path.line_to({ current_x_as_int, bottom_line - m_data.red[data_column] }); 111 green_channel_path.line_to({ current_x_as_int, bottom_line - m_data.green[data_column] }); 112 blue_channel_path.line_to({ current_x_as_int, bottom_line - m_data.blue[data_column] }); 113 brightness_path.line_to({ current_x_as_int, bottom_line - m_data.brightness[data_column] }); 114 115 current_x_as_float += step_width; 116 last_drawn_x = current_x_as_int; 117 } 118 119 brightness_path.line_to({ last_drawn_x, bottom_line }); 120 brightness_path.close(); 121 122 painter.fill_path(brightness_path, Color::MidGray, Gfx::Painter::WindingRule::EvenOdd); 123 painter.stroke_path(red_channel_path, Color(Color::NamedColor::Red).with_alpha(90), 2); 124 painter.stroke_path(green_channel_path, Color(Color::NamedColor::Green).with_alpha(90), 2); 125 painter.stroke_path(blue_channel_path, Color(Color::NamedColor::Blue).with_alpha(90), 2); 126 127 if (m_color_at_mouseposition != Color::Transparent) { 128 int x = m_color_at_mouseposition.luminosity() * step_width; 129 painter.draw_line({ x, 0 }, { x, bottom_line }, Color::from_hsl(45, 1, .7), 1); 130 } 131} 132 133void HistogramWidget::image_changed() 134{ 135 (void)rebuild_histogram_data(); 136 update(); 137} 138 139}