HistogramWidget.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. ErrorOr<void> HistogramWidget::rebuild_histogram_data()
  16. {
  17. if (!m_image)
  18. return {};
  19. auto full_bitmap = TRY(m_image->compose_bitmap(Gfx::BitmapFormat::BGRA8888));
  20. m_data.red.clear_with_capacity();
  21. m_data.green.clear_with_capacity();
  22. m_data.blue.clear_with_capacity();
  23. m_data.brightness.clear_with_capacity();
  24. for (int i = 0; i < 256; i++) {
  25. m_data.red.append(0);
  26. m_data.green.append(0);
  27. m_data.blue.append(0);
  28. m_data.brightness.append(0);
  29. }
  30. Color pixel_color;
  31. for (int x = 0; x < full_bitmap->width(); x++) {
  32. for (int y = 0; y < full_bitmap->height(); y++) {
  33. pixel_color = full_bitmap->get_pixel(x, y);
  34. if (!pixel_color.alpha())
  35. continue;
  36. m_data.red[pixel_color.red()]++;
  37. m_data.green[pixel_color.green()]++;
  38. m_data.blue[pixel_color.blue()]++;
  39. m_data.brightness[pixel_color.luminosity()]++;
  40. }
  41. }
  42. int max_brightness_frequency = 0;
  43. int max_color_frequency = 0;
  44. for (int i = 0; i < 256; i++) {
  45. if (m_data.red[i] > max_color_frequency)
  46. max_color_frequency = m_data.red[i];
  47. if (m_data.green[i] > max_color_frequency)
  48. max_color_frequency = m_data.green[i];
  49. if (m_data.blue[i] > max_color_frequency)
  50. max_color_frequency = m_data.blue[i];
  51. if (m_data.brightness[i] > max_brightness_frequency)
  52. max_brightness_frequency = m_data.brightness[i];
  53. }
  54. // Scale the frequency values to fit the widgets height.
  55. auto widget_height = height();
  56. for (int i = 0; i < 256; i++) {
  57. m_data.red[i] = m_data.red[i] != 0 ? (static_cast<float>(m_data.red[i]) / max_color_frequency) * widget_height : 0;
  58. m_data.green[i] = m_data.green[i] != 0 ? (static_cast<float>(m_data.green[i]) / max_color_frequency) * widget_height : 0;
  59. m_data.blue[i] = m_data.blue[i] != 0 ? (static_cast<float>(m_data.blue[i]) / max_color_frequency) * widget_height : 0;
  60. m_data.brightness[i] = m_data.brightness[i] != 0 ? (static_cast<float>(m_data.brightness[i]) / max_brightness_frequency) * widget_height : 0;
  61. }
  62. return {};
  63. }
  64. void HistogramWidget::paint_event(GUI::PaintEvent& event)
  65. {
  66. GUI::Painter painter(*this);
  67. painter.add_clip_rect(event.rect());
  68. if (!m_image)
  69. return;
  70. int bottom_line = height() - 1;
  71. float step_width = static_cast<float>(width()) / 256;
  72. Gfx::Path brightness_path;
  73. Gfx::Path red_channel_path;
  74. Gfx::Path green_channel_path;
  75. Gfx::Path blue_channel_path;
  76. red_channel_path.move_to({ 0, bottom_line - m_data.red[0] });
  77. green_channel_path.move_to({ 0, bottom_line - m_data.green[0] });
  78. blue_channel_path.move_to({ 0, bottom_line - m_data.blue[0] });
  79. brightness_path.move_to({ 0, bottom_line });
  80. brightness_path.line_to({ 0, bottom_line });
  81. float current_x_as_float = 0;
  82. int current_x_as_int = 0;
  83. int last_drawn_x = -1;
  84. for (int data_column = 0; data_column < 256; data_column++) {
  85. current_x_as_int = static_cast<int>(current_x_as_float);
  86. // we would like to skip values that map to the same x position as it does not look so good in the final result
  87. if (current_x_as_int == last_drawn_x) {
  88. current_x_as_float += step_width;
  89. continue;
  90. }
  91. red_channel_path.line_to({ current_x_as_int, bottom_line - m_data.red[data_column] });
  92. green_channel_path.line_to({ current_x_as_int, bottom_line - m_data.green[data_column] });
  93. blue_channel_path.line_to({ current_x_as_int, bottom_line - m_data.blue[data_column] });
  94. brightness_path.line_to({ current_x_as_int, bottom_line - m_data.brightness[data_column] });
  95. current_x_as_float += step_width;
  96. last_drawn_x = current_x_as_int;
  97. }
  98. brightness_path.line_to({ last_drawn_x, bottom_line });
  99. brightness_path.close();
  100. painter.fill_path(brightness_path, Color::MidGray, Gfx::Painter::WindingRule::EvenOdd);
  101. painter.stroke_path(red_channel_path, Color(Color::NamedColor::Red).with_alpha(90), 2);
  102. painter.stroke_path(green_channel_path, Color(Color::NamedColor::Green).with_alpha(90), 2);
  103. painter.stroke_path(blue_channel_path, Color(Color::NamedColor::Blue).with_alpha(90), 2);
  104. if (m_color_at_mouseposition != Color::Transparent) {
  105. int x = m_color_at_mouseposition.luminosity() * step_width;
  106. painter.draw_line({ x, 0 }, { x, bottom_line }, Color::from_hsl(45, 1, .7), 1);
  107. }
  108. }
  109. void HistogramWidget::image_changed()
  110. {
  111. (void)rebuild_histogram_data();
  112. update();
  113. }
  114. }