ladybird/Userland/Applications/Magnifier/MagnifierWidget.cpp
Sam Atkins 7cc118e84e Magnifier: Correct captured bitmap's clipping
Previously, we would get a line of junk pixels to the right or bottom of
the captured bitmap.

Two aspects to this:
- Use Painter's clip-rect functionality instead of trying to implement
  it ourselves.
- Switch from ScalingMode::NearestFractional to ::NearestNeighbor.
  Somehow, `draw_scaled_bitmap(NearestFractional)` draws outside of the
  Painter's clip-rect. I can't discern any difference in behavior
  between these two otherwise.

We also now use the same bitmap for drawing the grid as we used for the
bitmap, since this had issues where it wasn't drawn to the edge of the
bitmap.
2023-05-22 01:38:41 +02:00

170 lines
5 KiB
C++

/*
* Copyright (c) 2021, Valtteri Koskivuori <vkoskiv@gmail.com>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "MagnifierWidget.h"
#include <LibGUI/ConnectionToWindowServer.h>
#include <LibGUI/DisplayLink.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Window.h>
#include <LibGfx/Rect.h>
MagnifierWidget::MagnifierWidget()
{
GUI::DisplayLink::register_callback([this](auto) { sync(); });
}
void MagnifierWidget::set_scale_factor(int scale_factor)
{
VERIFY(scale_factor == 2 || scale_factor == 4 || scale_factor == 8);
if (m_scale_factor == scale_factor)
return;
m_scale_factor = scale_factor;
layout_relevant_change_occurred();
update();
}
void MagnifierWidget::lock_location(bool lock)
{
if (lock)
m_locked_location = GUI::ConnectionToWindowServer::the().get_global_cursor_position();
else
m_locked_location = {};
}
void MagnifierWidget::show_grid(bool new_value)
{
if (m_show_grid == new_value)
return;
m_show_grid = new_value;
update();
}
void MagnifierWidget::set_grid_color(Gfx::Color new_color)
{
if (m_grid_color == new_color)
return;
m_grid_color = new_color;
update();
}
void MagnifierWidget::set_color_filter(OwnPtr<Gfx::ColorBlindnessFilter> color_filter)
{
m_color_filter = move(color_filter);
sync();
}
void MagnifierWidget::display_previous_frame()
{
--m_frame_offset_from_head;
auto index = m_grabbed_bitmaps.head_index() + m_frame_offset_from_head;
m_grabbed_bitmap = m_grabbed_bitmaps.at(index);
update();
}
void MagnifierWidget::display_next_frame()
{
++m_frame_offset_from_head;
auto index = m_grabbed_bitmaps.head_index() + m_frame_offset_from_head;
m_grabbed_bitmap = m_grabbed_bitmaps.at(index);
update();
}
void MagnifierWidget::sync()
{
if (m_pause_capture)
return;
auto size = frame_inner_rect().size();
Gfx::IntSize grab_size { (size.width() + m_scale_factor - 1) / m_scale_factor, (size.height() + m_scale_factor - 1) / m_scale_factor };
VERIFY(grab_size.width() != 0 && grab_size.height() != 0);
if (m_locked_location.has_value()) {
m_grabbed_bitmap = GUI::ConnectionToWindowServer::the().get_screen_bitmap_around_location(grab_size, m_locked_location.value()).bitmap();
} else {
m_grabbed_bitmap = GUI::ConnectionToWindowServer::the().get_screen_bitmap_around_cursor(grab_size).bitmap();
}
m_grabbed_bitmaps.enqueue(m_grabbed_bitmap);
update();
}
void MagnifierWidget::paint_event(GUI::PaintEvent& event)
{
GUI::Frame::paint_event(event);
if (!m_grabbed_bitmap)
return;
GUI::Painter painter(*this);
auto frame_inner_rect = this->frame_inner_rect();
auto bitmap_rect = Gfx::IntRect { frame_inner_rect.top_left(), m_grabbed_bitmap->rect().size() * m_scale_factor };
painter.add_clip_rect(frame_inner_rect);
if (m_pause_capture)
painter.fill_rect(frame_inner_rect, Gfx::Color::Black);
if (m_grabbed_bitmap)
painter.draw_scaled_bitmap(bitmap_rect, *m_grabbed_bitmap, m_grabbed_bitmap->rect(), 1.0, Gfx::Painter::ScalingMode::NearestNeighbor);
if (m_show_grid) {
int start_y = bitmap_rect.top();
int start_x = bitmap_rect.left();
int end_y = bitmap_rect.bottom();
int end_x = bitmap_rect.right();
for (int current_y = start_y; current_y <= end_y; current_y += m_scale_factor)
painter.draw_line({ start_x, current_y }, { end_x, current_y }, m_grid_color);
for (int current_x = start_y; current_x <= end_x; current_x += m_scale_factor)
painter.draw_line({ current_x, start_y }, { current_x, end_y }, m_grid_color);
}
}
void MagnifierWidget::second_paint_event(GUI::PaintEvent&)
{
if (!m_color_filter)
return;
GUI::Painter painter(*this);
auto target = painter.target();
auto bitmap_clone_or_error = target->clone();
if (bitmap_clone_or_error.is_error())
return;
auto clone = bitmap_clone_or_error.release_value();
auto rect = target->rect();
m_color_filter->apply(*target, rect, *clone, rect);
}
void MagnifierWidget::mousemove_event(GUI::MouseEvent& event)
{
if (m_locked_location.has_value() && m_currently_dragging && !m_pause_capture) {
auto current_position = event.position();
auto difference = current_position - m_last_drag_position;
Gfx::IntPoint remainder = { difference.x() % m_scale_factor, difference.y() % m_scale_factor };
auto moved_by = difference / m_scale_factor;
m_locked_location = m_locked_location.value() - moved_by;
m_last_drag_position = current_position - remainder;
}
}
void MagnifierWidget::mousedown_event(GUI::MouseEvent& event)
{
if (event.button() == GUI::MouseButton::Primary && !m_pause_capture) {
m_currently_dragging = true;
m_last_drag_position = event.position();
}
}
void MagnifierWidget::mouseup_event(GUI::MouseEvent& event)
{
if (event.button() == GUI::MouseButton::Primary)
m_currently_dragging = false;
}