Skip to content

Commit

Permalink
clipping drawing to image viewport; improving statusbar
Browse files Browse the repository at this point in the history
  • Loading branch information
wkjarosz committed Feb 10, 2024
1 parent 74bebbb commit f6fcc93
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 53 deletions.
112 changes: 67 additions & 45 deletions src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,37 +221,47 @@ HDRViewApp::HDRViewApp()

m_params.callbacks.ShowStatus = [this]()
{
const float y = ImGui::GetCursorPosY() - HelloImGui::EmSize(0.15f);
float x = ImGui::GetStyle().ItemSpacing.x;

auto sized_text = [&](float em_size, const string &text, float align = 1.f)
{
float item_width = HelloImGui::EmSize(em_size);
float text_width = ImGui::CalcTextSize(text.c_str()).x;
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;

ImGui::SameLine();
ImGui::SetCursorPos({x + align * (item_width - text_width), y});
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(text.c_str());
x += item_width + spacing;
};

if (auto img = current_image())
{
auto &io = ImGui::GetIO();

int2 p{pixel_at_app_pos(io.MousePos)};
ImGui::PushFont(m_mono_regular[14]);
auto p = int2{pixel_at_app_pos(io.MousePos)};

float4 color32 = image_pixel(p);
auto &group = img->groups[img->selected_group];

sized_text(3, fmt::format("({:>6d},", p.x), 0.f);
sized_text(3, fmt::format("{:>6d})", p.y), 0.f);

if (img->contains(p))
{
float4 color32 = image_pixel(p);
float4 color8 = linalg::clamp(color32 * pow(2.f, m_exposure) * 255, 0.f, 255.f);

// ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFontSize() * 0.15f);
ImGui::TextUnformatted(
fmt::format("({:>4d},{:>4d}) = ({:>6.3f},{:>6.3f},{:>6.3f},{:>6.3f})/({:>3d},{:>3d},{:>3d},{:>3d})",
p.x, p.y, color32.x, color32.y, color32.z, color32.w, (int)round(color8.x),
(int)round(color8.y), (int)round(color8.z), (int)round(color8.w))
.c_str());
// ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFontSize() * 0.15f);
sized_text(0.5f, "=", 0.5f);
for (int c = 0; c < group.num_channels; ++c)
sized_text(3.5f, fmt::format("{}{: 6.3f}{}", c == 0 ? "(" : "", color32[c],
c == group.num_channels - 1 ? ")" : ","));
}

// ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFontSize() * 0.15f);
float real_zoom = m_zoom * pixel_ratio();
int numer = (real_zoom < 1.0f) ? 1 : (int)round(real_zoom);
int denom = (real_zoom < 1.0f) ? (int)round(1.0f / real_zoom) : 1;
auto text = fmt::format("{:7.2f}% ({:d}:{:d})", real_zoom * 100, numer, denom);
float2 text_size = ImGui::CalcTextSize(text.c_str());
ImGui::SameLine(ImGui::GetIO().DisplaySize.x - text_size.x - 16.f * ImGui::GetFontSize());
ImGui::TextUnformatted(text.c_str());
// ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFontSize() * 0.15f);
ImGui::PopFont();
float real_zoom = m_zoom * pixel_ratio();
int numer = (real_zoom < 1.0f) ? 1 : (int)round(real_zoom);
int denom = (real_zoom < 1.0f) ? (int)round(1.0f / real_zoom) : 1;
x = ImGui::GetIO().DisplaySize.x - HelloImGui::EmSize(15.f);
sized_text(0.f, fmt::format("{:7.2f}% ({:d}:{:d})", real_zoom * 100, numer, denom));
}
};

Expand Down Expand Up @@ -797,7 +807,6 @@ void HDRViewApp::center() { m_offset = float2(0.f, 0.f); }

void HDRViewApp::fit()
{
// Calculate the appropriate scaling factor.
m_zoom = minelem(viewport_size() / current_image()->display_window.size());
center();
}
Expand All @@ -809,16 +818,16 @@ void HDRViewApp::set_zoom_level(float level)
m_zoom = std::clamp(std::pow(m_zoom_sensitivity, level) / pixel_ratio(), MIN_ZOOM, MAX_ZOOM);
}

void HDRViewApp::zoom_by(float amount, float2 focus_app_pos)
void HDRViewApp::zoom_at_vp_pos(float amount, float2 focus_vp_pos)
{
if (amount == 0.f)
return;

auto focused_pixel = pixel_at_app_pos(focus_app_pos); // save focused pixel coord before modifying zoom
auto focused_pixel = pixel_at_vp_pos(focus_vp_pos); // save focused pixel coord before modifying zoom
float scale_factor = std::pow(m_zoom_sensitivity, amount);
m_zoom = std::clamp(scale_factor * m_zoom, MIN_ZOOM, MAX_ZOOM);
// reposition so focused_pixel is still under focus_app_pos
reposition_pixel_to_vp_pos(vp_pos_at_app_pos(focus_app_pos), focused_pixel);
reposition_pixel_to_vp_pos(focus_vp_pos, focused_pixel);
}

void HDRViewApp::zoom_in()
Expand Down Expand Up @@ -906,6 +915,10 @@ void HDRViewApp::draw_pixel_grid() const
float factor = std::clamp((m_zoom - s_grid_threshold) / (2 * s_grid_threshold), 0.f, 1.f);
float alpha = lerp(0.0f, 1.0f, smoothstep(0.0f, 1.0f, factor));

ImDrawList *draw_list = ImGui::GetBackgroundDrawList();

// only draw within the image viewport
draw_list->PushClipRect(app_pos_at_vp_pos({0.f, 0.f}), app_pos_at_vp_pos(viewport_size()), true);
if (alpha > 0.0f)
{
ImColor col_fg(1.0f, 1.0f, 1.0f, alpha);
Expand All @@ -916,22 +929,23 @@ void HDRViewApp::draw_pixel_grid() const

// draw vertical lines
for (int x = bounds.min.x; x <= bounds.max.x; ++x)
ImGui::GetBackgroundDrawList()->AddLine(app_pos_at_pixel(float2(x, bounds.min.y)),
app_pos_at_pixel(float2(x, bounds.max.y)), col_bg, 4.f);
draw_list->AddLine(app_pos_at_pixel(float2(x, bounds.min.y)), app_pos_at_pixel(float2(x, bounds.max.y)),
col_bg, 4.f);

// draw horizontal lines
for (int y = bounds.min.y; y <= bounds.max.y; ++y)
ImGui::GetBackgroundDrawList()->AddLine(app_pos_at_pixel(float2(bounds.min.x, y)),
app_pos_at_pixel(float2(bounds.max.x, y)), col_bg, 4.f);
draw_list->AddLine(app_pos_at_pixel(float2(bounds.min.x, y)), app_pos_at_pixel(float2(bounds.max.x, y)),
col_bg, 4.f);

// and now again with the foreground color
for (int x = bounds.min.x; x <= bounds.max.x; ++x)
ImGui::GetBackgroundDrawList()->AddLine(app_pos_at_pixel(float2(x, bounds.min.y)),
app_pos_at_pixel(float2(x, bounds.max.y)), col_fg, 2.f);
draw_list->AddLine(app_pos_at_pixel(float2(x, bounds.min.y)), app_pos_at_pixel(float2(x, bounds.max.y)),
col_fg, 2.f);
for (int y = bounds.min.y; y <= bounds.max.y; ++y)
ImGui::GetBackgroundDrawList()->AddLine(app_pos_at_pixel(float2(bounds.min.x, y)),
app_pos_at_pixel(float2(bounds.max.x, y)), col_fg, 2.f);
draw_list->AddLine(app_pos_at_pixel(float2(bounds.min.x, y)), app_pos_at_pixel(float2(bounds.max.x, y)),
col_fg, 2.f);
}
draw_list->PopClipRect();
}

void HDRViewApp::draw_pixel_info() const
Expand Down Expand Up @@ -979,6 +993,8 @@ void HDRViewApp::draw_pixel_info() const

ImDrawList *draw_list = ImGui::GetBackgroundDrawList();

// only draw within the image viewport
draw_list->PushClipRect(app_pos_at_vp_pos({0.f, 0.f}), app_pos_at_vp_pos(viewport_size()), true);
if (alpha > 0.0f)
{
ImGui::PushFont(font);
Expand Down Expand Up @@ -1011,6 +1027,7 @@ void HDRViewApp::draw_pixel_info() const
}
ImGui::PopFont();
}
draw_list->PopClipRect();
}

void HDRViewApp::draw_image_border() const
Expand All @@ -1021,11 +1038,11 @@ void HDRViewApp::draw_image_border() const
constexpr float thickness = 3.f;
constexpr float2 fudge = float2{thickness * 0.5f - 0.5f, -(thickness * 0.5f - 0.5f)};
float2 pad = HelloImGui::EmToVec2({0.25, 0.125});
auto draw_list = ImGui::GetBackgroundDrawList();

auto draw_image_window =
[&](const Box2f &image_window, ImGuiCol col_idx, const string &text, const float2 &align, bool draw_label)
{
auto draw_list = ImGui::GetBackgroundDrawList();
Box2f window{app_pos_at_pixel(image_window.min), app_pos_at_pixel(image_window.max)};
draw_list->AddRect(window.min, window.max, ImGui::GetColorU32(col_idx), 0.f, ImDrawFlags_None, thickness);

Expand All @@ -1052,12 +1069,18 @@ void HDRViewApp::draw_image_border() const
ImGui::GetColorU32(ImGuiCol_Text, fade), text, align);
};

bool non_trivial = current_image()->data_window != current_image()->display_window ||
current_image()->data_window.min != int2{0, 0};
draw_image_window(Box2f{current_image()->data_window}, ImGuiCol_TabActive, "Data window", {0.f, 0.f}, non_trivial);
if (non_trivial)
draw_image_window(Box2f{current_image()->display_window}, ImGuiCol_TabUnfocused, "Display window", {1.f, 1.f},
true);
// only draw within the image viewport
draw_list->PushClipRect(app_pos_at_vp_pos({0.f, 0.f}), app_pos_at_vp_pos(viewport_size()), true);
{
bool non_trivial = current_image()->data_window != current_image()->display_window ||
current_image()->data_window.min != int2{0, 0};
draw_image_window(Box2f{current_image()->data_window}, ImGuiCol_TabActive, "Data window", {0.f, 0.f},
non_trivial);
if (non_trivial)
draw_image_window(Box2f{current_image()->display_window}, ImGuiCol_TabUnfocused, "Display window",
{1.f, 1.f}, true);
}
draw_list->PopClipRect();
}

void HDRViewApp::draw_image() const
Expand Down Expand Up @@ -1192,9 +1215,8 @@ void HDRViewApp::draw_background()

if (!io.WantCaptureMouse)
{
auto app_mouse_pos = float2{io.MousePos};
auto vp_mouse_pos = vp_pos_at_app_pos(app_mouse_pos);
auto scroll = float2{io.MouseWheelH, io.MouseWheel};
auto vp_mouse_pos = vp_pos_at_app_pos(io.MousePos);
auto scroll = float2{io.MouseWheelH, io.MouseWheel};
#if defined(__EMSCRIPTEN__)
scroll *= 10.0f;
#endif
Expand All @@ -1208,7 +1230,7 @@ void HDRViewApp::draw_background()
// panning
reposition_pixel_to_vp_pos(vp_mouse_pos + scroll * 4.f, pixel_at_vp_pos(vp_mouse_pos));
else
zoom_by(scroll.y / 4.f, app_mouse_pos);
zoom_at_vp_pos(scroll.y / 4.f, vp_mouse_pos);
}

//
Expand Down
17 changes: 9 additions & 8 deletions src/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class HDRViewApp

/// Reposition the image so that the specified image pixel coordinate lies under the provided viewport position
void reposition_pixel_to_vp_pos(float2 vp_pos, float2 pixel);

float pixel_ratio() const;
float2 viewport_size() const { return m_viewport_size; }
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
Expand All @@ -93,11 +96,11 @@ class HDRViewApp
/// Centers and scales the image so that it fits inside the widget.
void fit();
/**
Changes the scale factor by the provided amount modified by the zoom sensitivity member variable.
The scaling occurs such that the image pixel coordinate under the focused app position remains in
the same place before and after the scaling.
Changes the zoom factor by the provided amount modified by the zoom sensitivity member variable.
The scaling occurs such that the image pixel coordinate under focus_vp_pos remains in
the same place before and after the zoom.
*/
void zoom_by(float amount, float2 focus_app_pos);
void zoom_at_vp_pos(float amount, float2 focus_vp_pos);
/// Zoom in to the next power of two
void zoom_in();
/// Zoom out to the previous power of two
Expand All @@ -107,8 +110,6 @@ class HDRViewApp
//-----------------------------------------------------------------------------

private:
float pixel_ratio() const;
float2 viewport_size() const { return m_viewport_size; }
float2 center_offset(ConstImagePtr img = nullptr) const;
Box2f scaled_display_window(ConstImagePtr img = nullptr) const;
Box2f scaled_data_window(ConstImagePtr img = nullptr) const;
Expand Down Expand Up @@ -155,8 +156,8 @@ class HDRViewApp
// Image display parameters.
float m_zoom_sensitivity = 1.0717734625f;

float m_zoom = 1.f; ///< The scale/zoom of the image
float2 m_offset = {0.f, 0.f}; ///< The panning offset of the
float m_zoom = 1.f; ///< The zoom factor (image pixel size / logical pixel size)
float2 m_offset = {0.f, 0.f}; ///< The panning offset of the image
EChannel m_channel = EChannel::RGB; ///< Which channel to display
EBlendMode m_blend_mode = EBlendMode::NORMAL_BLEND; ///< How to blend the current and reference images
EBGMode m_bg_mode = EBGMode::BG_DARK_CHECKER; ///< How the background around the image should be rendered
Expand Down

0 comments on commit f6fcc93

Please sign in to comment.