VectorscopeWidget.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "VectorscopeWidget.h"
  7. #include "Layer.h"
  8. #include <AK/Math.h>
  9. #include <AK/Types.h>
  10. #include <LibGUI/Event.h>
  11. #include <LibGUI/Painter.h>
  12. #include <LibGUI/Widget.h>
  13. #include <LibGfx/AntiAliasingPainter.h>
  14. #include <LibGfx/Bitmap.h>
  15. #include <LibGfx/Palette.h>
  16. #include <LibGfx/SystemTheme.h>
  17. #include <LibGfx/TextAlignment.h>
  18. #include <LibGfx/TextElision.h>
  19. REGISTER_WIDGET(PixelPaint, VectorscopeWidget);
  20. namespace PixelPaint {
  21. void VectorscopeWidget::image_changed()
  22. {
  23. (void)rebuild_vectorscope_data();
  24. rebuild_vectorscope_image();
  25. update();
  26. }
  27. ErrorOr<void> VectorscopeWidget::rebuild_vectorscope_data()
  28. {
  29. if (!m_image)
  30. return {};
  31. m_vectorscope_data.fill({});
  32. VERIFY(AK::abs(m_vectorscope_data[0][0]) < 0.01f);
  33. auto full_bitmap = TRY(m_image->compose_bitmap(Gfx::BitmapFormat::BGRA8888));
  34. for (size_t x = 0; x < static_cast<size_t>(full_bitmap->width()); ++x) {
  35. for (size_t y = 0; y < static_cast<size_t>(full_bitmap->height()); ++y) {
  36. auto yuv = full_bitmap->get_pixel(x, y).to_yuv();
  37. auto u_index = u_v_to_index(yuv.u);
  38. auto v_index = u_v_to_index(yuv.v);
  39. m_vectorscope_data[u_index][v_index]++;
  40. }
  41. }
  42. auto maximum = full_bitmap->width() * full_bitmap->height() * pixel_percentage_for_max_brightness * pixel_percentage_for_max_brightness;
  43. for (size_t i = 0; i < m_vectorscope_data.size(); ++i) {
  44. for (size_t j = 0; j < m_vectorscope_data[i].size(); ++j) {
  45. m_vectorscope_data[i][j] = AK::sqrt(m_vectorscope_data[i][j]) / maximum;
  46. }
  47. }
  48. return {};
  49. }
  50. void VectorscopeWidget::rebuild_vectorscope_image()
  51. {
  52. m_vectorscope_image = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size()));
  53. m_vectorscope_image->fill(Color::Transparent);
  54. Gfx::Painter base_painter(*m_vectorscope_image);
  55. Gfx::AntiAliasingPainter painter(base_painter);
  56. auto const scope_size = min(height(), width());
  57. auto const min_scope_size = parent_widget()->min_height().as_int();
  58. auto const color_vector_scale = scope_size / static_cast<float>(min_scope_size);
  59. auto const size_1x1 = Gfx::FloatSize { 2.5f, 2.5f } * static_cast<float>(color_vector_scale);
  60. base_painter.translate(width() / 2, height() / 2);
  61. painter.translate(static_cast<float>(width()) / 2.0f, static_cast<float>(height()) / 2.0f);
  62. for (size_t u_index = 0; u_index < u_v_steps; ++u_index) {
  63. for (size_t v_index = 0; v_index < u_v_steps; ++v_index) {
  64. auto const color_vector = ColorVector::from_indices(u_index, v_index);
  65. auto const brightness = m_vectorscope_data[u_index][v_index];
  66. if (brightness < 0.0001f)
  67. continue;
  68. auto const pseudo_rect = Gfx::FloatRect::centered_at(color_vector.to_vector(scope_size) * 2.0f, size_1x1);
  69. auto color = Color::from_yuv(0.6f, color_vector.u, color_vector.v);
  70. color = color.saturated_to(1.0f - min(brightness, 1.0f));
  71. color.set_alpha(static_cast<u8>(min(AK::sqrt(brightness), alpha_range) * NumericLimits<u8>::max() / alpha_range));
  72. painter.fill_rect(pseudo_rect, color);
  73. }
  74. }
  75. }
  76. void VectorscopeWidget::paint_event(GUI::PaintEvent& event)
  77. {
  78. GUI::Painter base_painter(*this);
  79. Gfx::AntiAliasingPainter painter(base_painter);
  80. base_painter.add_clip_rect(event.rect());
  81. // From this point on we're working with 0,0 as the scope center to make things easier.
  82. base_painter.translate(width() / 2, height() / 2);
  83. painter.translate(static_cast<float>(width()) / 2.0f, static_cast<float>(height()) / 2.0f);
  84. auto const graticule_color = Color::White;
  85. auto const scope_size = min(height(), width());
  86. auto const graticule_size = scope_size / 6;
  87. auto const graticule_thickness = graticule_size / 12;
  88. auto const entire_scope_rect = Gfx::FloatRect::centered_at({ 0, 0 }, { scope_size, scope_size });
  89. painter.fill_ellipse(entire_scope_rect.to_rounded<int>().shrunken(graticule_thickness * 2, graticule_thickness * 2), Color::Black);
  90. // Main scope data
  91. if (m_image) {
  92. if (m_vectorscope_image->size() != this->size())
  93. rebuild_vectorscope_image();
  94. base_painter.blit({ -width() / 2, -height() / 2 }, *m_vectorscope_image, m_vectorscope_image->rect());
  95. }
  96. // Graticule(s)
  97. painter.draw_ellipse(entire_scope_rect.to_rounded<int>(), graticule_color, graticule_thickness);
  98. // FIXME: Translation calls to the painters don't appear to work correctly, and I figured out a combination of calls through trial and error that do what I want, but I don't know how they do that.
  99. // Translation does work correctly with things like rectangle and text drawing, so it's very strange.
  100. painter.translate(-static_cast<float>(width()) / 2.0f, -static_cast<float>(height()) / 2.0f);
  101. // We intentionally draw the skin tone line much further than the actual color we're using for it.
  102. painter.draw_line({ 0, 0 }, skin_tone_color.to_vector(scope_size) * 2.0, graticule_color);
  103. painter.translate(-static_cast<float>(width()) / 2.0f, -static_cast<float>(height()) / 2.0f);
  104. for (auto const& primary_color : primary_colors) {
  105. auto center = primary_color.to_vector(scope_size);
  106. auto center_rounded = center.to_rounded<int>();
  107. // Box color
  108. Gfx::Color corner_color = Gfx::Color::from_yuv(0.5f, primary_color.u, primary_color.v).saturated_to(0.5);
  109. // Bracket vertex calculations
  110. int left_outer_vertex = center_rounded.x() - graticule_size / 2;
  111. int right_outer_vertex = center_rounded.x() + graticule_size / 2;
  112. int top_outer_vertex = center_rounded.y() - graticule_size / 2;
  113. int bottom_outer_vertex = center_rounded.y() + graticule_size / 2;
  114. int left_inner_vertex = center_rounded.x() - graticule_size / 3;
  115. int right_inner_vertex = center_rounded.x() + graticule_size / 3;
  116. int top_inner_vertex = center_rounded.y() - graticule_size / 3;
  117. int bottom_inner_vertex = center_rounded.y() + graticule_size / 3;
  118. // Top Left Corner
  119. base_painter.draw_line(Gfx::IntPoint(left_outer_vertex, top_outer_vertex), Gfx::IntPoint(left_inner_vertex, top_outer_vertex), corner_color, graticule_thickness);
  120. base_painter.draw_line(Gfx::IntPoint(left_outer_vertex, top_outer_vertex), Gfx::IntPoint(left_outer_vertex, top_inner_vertex), corner_color, graticule_thickness);
  121. // Top Right Corner
  122. base_painter.draw_line(Gfx::IntPoint(right_outer_vertex, top_outer_vertex), Gfx::IntPoint(right_inner_vertex, top_outer_vertex), corner_color, graticule_thickness);
  123. base_painter.draw_line(Gfx::IntPoint(right_outer_vertex, top_outer_vertex), Gfx::IntPoint(right_outer_vertex, top_inner_vertex), corner_color, graticule_thickness);
  124. // Bottom Left Corner
  125. base_painter.draw_line(Gfx::IntPoint(left_outer_vertex, bottom_outer_vertex), Gfx::IntPoint(left_inner_vertex, center_rounded.y() + graticule_size / 2), corner_color, graticule_thickness);
  126. base_painter.draw_line(Gfx::IntPoint(left_outer_vertex, center_rounded.y() + graticule_size / 2), Gfx::IntPoint(left_outer_vertex, bottom_inner_vertex), corner_color, graticule_thickness);
  127. // Bottom Right Corner
  128. base_painter.draw_line(Gfx::IntPoint(right_outer_vertex, center_rounded.y() + graticule_size / 2), Gfx::IntPoint(right_inner_vertex, center_rounded.y() + graticule_size / 2), corner_color, graticule_thickness);
  129. base_painter.draw_line(Gfx::IntPoint(right_outer_vertex, center_rounded.y() + graticule_size / 2), Gfx::IntPoint(right_outer_vertex, bottom_inner_vertex), corner_color, graticule_thickness);
  130. // Add text label to vectorscope
  131. auto text_rect = Gfx::FloatRect::centered_at(center, { graticule_size, graticule_size }).to_rounded<int>().translated(-(graticule_thickness + 1), -(graticule_thickness + 1));
  132. base_painter.draw_text(text_rect, StringView { &primary_color.symbol, 1 }, Gfx::TextAlignment::BottomRight, graticule_color);
  133. }
  134. if (m_color_at_mouseposition != Color::Transparent) {
  135. auto color_vector = ColorVector { m_color_at_mouseposition };
  136. painter.draw_ellipse(Gfx::FloatRect::centered_at(color_vector.to_vector(scope_size) * 2.0, { graticule_size, graticule_size }).to_rounded<int>(), graticule_color, graticule_thickness);
  137. }
  138. }
  139. }