HistogramWidget.cpp 5.4 KB

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