Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PowerToys.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
</Project>
</Folder>
<Folder Name="/modules/MeasureTool/Tests/">
<Project Path="src/modules/MeasureTool/Tests/MeasureToolCore.UnitTests/MeasureToolCore.UnitTests.vcxproj" Id="1b76e770-b56f-486f-89d9-9dfe7c5289e8" />
<Project Path="src/modules/MeasureTool/Tests/ScreenRuler.UITests/ScreenRuler.UITests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
Expand Down
2 changes: 1 addition & 1 deletion doc/devdocs/modules/screenruler.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

## Overview

Screen Ruler (project name: MeasureTool or Measure 2) is a PowerToys module that allows users to measure pixel distances and detect color boundaries on the screen. The tool renders an overlay UI using DirectX and provides several measurement utilities.
Screen Ruler (project name: MeasureTool or Measure 2) is a PowerToys module that allows users to measure distances and detect color boundaries on the screen. Measurements are always displayed in physical pixels and can optionally include display-independent pixels (DIP), inches, centimeters, or millimeters. The tool renders an overlay UI using DirectX and provides several measurement utilities.

## Features

Expand Down
13 changes: 8 additions & 5 deletions src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

namespace
{
Measurement GetMeasurement(const CursorDrag& currentBounds, POINT cursorPos, float px2mmRatio)
Measurement GetMeasurement(const CursorDrag& currentBounds, POINT cursorPos, float px2mmRatio, float monitorDpi)
{
D2D1_RECT_F rect;
std::tie(rect.left, rect.right) =
std::minmax(static_cast<float>(cursorPos.x), currentBounds.startPos.x);
std::tie(rect.top, rect.bottom) =
std::minmax(static_cast<float>(cursorPos.y), currentBounds.startPos.y);

return Measurement(rect, px2mmRatio);
return Measurement(rect, px2mmRatio, monitorDpi);
}

void CopyToClipboard(HWND window, const BoundsToolState& toolState, POINT cursorPos)
Expand All @@ -30,7 +30,8 @@ namespace
if (handle == window && perScreen.currentBounds)
{
auto px2mmRatio = toolState.commonState->GetPhysicalPx2MmRatio(window);
allMeasurements.push_back(GetMeasurement(*perScreen.currentBounds, cursorPos, px2mmRatio));
auto monitorDpi = toolState.commonState->GetScreenDpi(window);
allMeasurements.push_back(GetMeasurement(*perScreen.currentBounds, cursorPos, px2mmRatio, monitorDpi));
}
}

Expand Down Expand Up @@ -87,7 +88,8 @@ namespace
if (const bool shiftPress = GetKeyState(VK_SHIFT) & 0x80000; shiftPress && perScreen.currentBounds)
{
auto px2mmRatio = toolState->commonState->GetPhysicalPx2MmRatio(window);
perScreen.measurements.push_back(GetMeasurement(*perScreen.currentBounds, cursorPos, px2mmRatio));
auto monitorDpi = toolState->commonState->GetScreenDpi(window);
perScreen.measurements.push_back(GetMeasurement(*perScreen.currentBounds, cursorPos, px2mmRatio, monitorDpi));
}

perScreen.currentBounds = std::nullopt;
Expand Down Expand Up @@ -317,6 +319,7 @@ void DrawBoundsToolTick(const CommonState& commonState,
std::tie(rect.left, rect.right) = std::minmax(perScreen.currentBounds->startPos.x, perScreen.currentBounds->currentPos.x);
std::tie(rect.top, rect.bottom) = std::minmax(perScreen.currentBounds->startPos.y, perScreen.currentBounds->currentPos.y);
auto px2mmRatio = toolState.commonState->GetPhysicalPx2MmRatio(window);
DrawMeasurement(Measurement{ rect, px2mmRatio }, commonState, window, d2dState, perScreen.currentBounds->currentPos);
auto monitorDpi = toolState.commonState->GetScreenDpi(window);
DrawMeasurement(Measurement{ rect, px2mmRatio, monitorDpi }, commonState, window, d2dState, perScreen.currentBounds->currentPos);
}
}
80 changes: 17 additions & 63 deletions src/modules/MeasureTool/MeasureToolCore/Measurement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,30 @@

#include <iostream>

Measurement::Measurement(RECT winRect, float px2mmRatio) :
px2mmRatio{ px2mmRatio }
Measurement::Measurement(RECT winRect, float px2mmRatio, float monitorDpi) :
px2mmRatio{ px2mmRatio }, monitorDpi{ monitorDpi }
{
rect.left = static_cast<float>(winRect.left);
rect.right = static_cast<float>(winRect.right);
rect.top = static_cast<float>(winRect.top);
rect.bottom = static_cast<float>(winRect.bottom);
}

Measurement::Measurement(D2D1_RECT_F d2dRect, float px2mmRatio) :
rect{ d2dRect }, px2mmRatio{ px2mmRatio }
Measurement::Measurement(D2D1_RECT_F d2dRect, float px2mmRatio, float monitorDpi) :
rect{ d2dRect }, px2mmRatio{ px2mmRatio }, monitorDpi{ monitorDpi }
{
}

namespace
{
inline float Convert(const float pixels, const Measurement::Unit units, float px2mmRatio)
{
if (px2mmRatio > 0)
{
switch (units)
{
case Measurement::Unit::Pixel:
return pixels;
case Measurement::Unit::Inch:
return pixels * px2mmRatio / 10.0f / 2.54f;
case Measurement::Unit::Centimetre:
return pixels * px2mmRatio / 10.0f;
case Measurement::Unit::Millimetre:
return pixels * px2mmRatio;
default:
return pixels;
}
}
else
{
switch (units)
{
case Measurement::Unit::Pixel:
return pixels;
case Measurement::Unit::Inch:
return pixels / 96.0f;
case Measurement::Unit::Centimetre:
return pixels / 96.0f * 2.54f;
case Measurement::Unit::Millimetre:
return pixels / 96.0f / 10.0f * 2.54f;
default:
return pixels;
}
}
}
}

winrt::hstring Measurement::abbreviations[4]{};
winrt::hstring Measurement::abbreviations[5]{};

inline float Measurement::Width(const Unit units) const
{
return Convert(rect.right - rect.left + 1.f, units, px2mmRatio);
return MeasurementLogic::Convert(rect.right - rect.left + 1.f, units, px2mmRatio, monitorDpi);
}

inline float Measurement::Height(const Unit units) const
{
return Convert(rect.bottom - rect.top + 1.f, units, px2mmRatio);
}

Measurement::Unit Measurement::GetUnitFromIndex(int index)
{
switch (index)
{
case 0:
return Measurement::Unit::Pixel;
case 1:
return Measurement::Unit::Inch;
case 2:
return Measurement::Unit::Centimetre;
case 3:
return Measurement::Unit::Millimetre;
default:
return Measurement::Unit::Pixel;
}
return MeasurementLogic::Convert(rect.bottom - rect.top + 1.f, units, px2mmRatio, monitorDpi);
}

void Measurement::InitResources()
Expand All @@ -94,6 +38,7 @@ void Measurement::InitResources()
abbreviations[1] = mm.GetValue(L"Resources/MeasurementUnitAbbrInch").ValueAsString();
abbreviations[2] = mm.GetValue(L"Resources/MeasurementUnitAbbrCentimetre").ValueAsString();
abbreviations[3] = mm.GetValue(L"Resources/MeasurementUnitAbbrMillimetre").ValueAsString();
abbreviations[4] = mm.GetValue(L"Resources/MeasurementUnitAbbrDip").ValueAsString();
}

const wchar_t* Measurement::GetUnitAbbreviation(Measurement::Unit units)
Expand All @@ -108,6 +53,8 @@ const wchar_t* Measurement::GetUnitAbbreviation(Measurement::Unit units)
return abbreviations[2].c_str();
case Unit::Millimetre:
return abbreviations[3].c_str();
case Unit::Dip:
return abbreviations[4].c_str();
default:
return L"??";
}
Expand Down Expand Up @@ -175,6 +122,12 @@ Measurement::PrintResult Measurement::Print(wchar_t* buf,
Measurement::GetUnitAbbreviation(unit));

break;
case Measurement::Unit::Dip:
result.strLen += swprintf_s(buf + result.strLen,
bufSize - result.strLen,
L" %s",
Measurement::GetUnitAbbreviation(unit));
break;
}
if (paren)
{
Expand All @@ -185,6 +138,7 @@ Measurement::PrintResult Measurement::Print(wchar_t* buf,
int count = 0;
const Measurement::Unit allUnits[] = {
Measurement::Unit::Pixel,
Measurement::Unit::Dip,
Measurement::Unit::Millimetre,
Measurement::Unit::Inch,
Measurement::Unit::Centimetre,
Expand Down
23 changes: 12 additions & 11 deletions src/modules/MeasureTool/MeasureToolCore/Measurement.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,23 @@
#include <windef.h>
#include <iosfwd>

#include "MeasurementLogic.h"

struct Measurement
{
enum Unit
{
Pixel = 1,
Inch = 2,
Centimetre = 4,
Millimetre = 8,
};
using Unit = MeasurementLogic::Unit;

D2D1_RECT_F rect = {}; // corners are inclusive

float px2mmRatio = 0;
static winrt::hstring abbreviations[4]; // Abbreviations of units.
float monitorDpi = MeasurementLogic::DefaultDpi;
static winrt::hstring abbreviations[5]; // Abbreviations of units.

Measurement(const Measurement&) = default;
Measurement& operator=(const Measurement&) = default;

explicit Measurement(D2D1_RECT_F d2dRect, float px2mmRatio);
explicit Measurement(RECT winRect, float px2mmRatio);
explicit Measurement(D2D1_RECT_F d2dRect, float px2mmRatio, float monitorDpi);
explicit Measurement(RECT winRect, float px2mmRatio, float monitorDpi);

float Width(const Unit units) const;
float Height(const Unit units) const;
Expand All @@ -36,7 +33,11 @@ struct Measurement
};

static void InitResources();
static Unit GetUnitFromIndex(int index);
static constexpr Unit GetUnitFromIndex(const int index) noexcept
{
return MeasurementLogic::GetUnitFromIndex(index);
}

static const wchar_t* GetUnitAbbreviation(const Unit units);

PrintResult Print(wchar_t* buf,
Expand Down
74 changes: 74 additions & 0 deletions src/modules/MeasureTool/MeasureToolCore/MeasurementLogic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

namespace MeasurementLogic
{
constexpr float DefaultDpi = 96.0f;

enum Unit
{
Pixel = 1,
Inch = 2,
Centimetre = 4,
Millimetre = 8,
Dip = 16,
};

constexpr Unit GetUnitFromIndex(const int index) noexcept
{
switch (index)
{
case 0:
return Unit::Pixel;
case 1:
return Unit::Inch;
case 2:
return Unit::Centimetre;
case 3:
return Unit::Millimetre;
case 4:
return Unit::Dip;
default:
return Unit::Pixel;
}
}

constexpr float Convert(const float pixels, const Unit units, const float px2mmRatio, const float monitorDpi) noexcept
{
if (units == Unit::Pixel)
{
return pixels;
}

if (units == Unit::Dip)
{
return monitorDpi > 0 ? pixels * DefaultDpi / monitorDpi : pixels;
}

if (px2mmRatio > 0)
{
switch (units)
{
case Unit::Inch:
return pixels * px2mmRatio / 25.4f;
case Unit::Centimetre:
return pixels * px2mmRatio / 10.0f;
case Unit::Millimetre:
return pixels * px2mmRatio;
default:
return pixels;
}
}

switch (units)
{
case Unit::Inch:
return pixels / DefaultDpi;
case Unit::Centimetre:
return pixels / DefaultDpi * 2.54f;
case Unit::Millimetre:
return pixels / DefaultDpi * 25.4f;
default:
return pixels;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<ClInclude Include="D2DState.h" />
<ClInclude Include="DxgiAPI.h" />
<ClInclude Include="Measurement.h" />
<ClInclude Include="MeasurementLogic.h" />
<ClInclude Include="MeasureToolOverlayUI.h" />
<ClInclude Include="PerGlyphOpacityTextRender.h" />
<ClInclude Include="resource.h" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<ClInclude Include="resource.h" />
<ClInclude Include="CoordinateSystemConversion.h" />
<ClInclude Include="Measurement.h" />
<ClInclude Include="MeasurementLogic.h" />
<ClInclude Include="DxgiAPI.h" />
</ItemGroup>
<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion src/modules/MeasureTool/MeasureToolCore/ScreenCapturing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ void UpdateCaptureState(const CommonState& commonState,
perColorChannelEdgeDetection,
pixelTolerance);
auto px2mmRatio = commonState.GetPhysicalPx2MmRatio(window);
auto monitorDpi = commonState.GetScreenDpi(window);

#if defined(DEBUG_EDGES)
char buffer[256];
Expand All @@ -312,7 +313,7 @@ void UpdateCaptureState(const CommonState& commonState,
OutputDebugStringA(buffer);
#endif
state.Access([&](MeasureToolState& state) {
state.perScreen[window].measuredEdges = Measurement{ bounds, px2mmRatio };
state.perScreen[window].measuredEdges = Measurement{ bounds, px2mmRatio, monitorDpi };
});
}

Expand Down
8 changes: 8 additions & 0 deletions src/modules/MeasureTool/MeasureToolCore/ToolState.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <d2d1helper.h>
#include <dCommon.h>

#include <common/Display/dpi_aware.h>
#include <common/Display/monitors.h>
#include <common/utils/serialized.h>

Expand Down Expand Up @@ -48,6 +49,13 @@ struct CommonState
}
return ratio;
}

float GetScreenDpi(HWND window) const
{
unsigned int dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForWindow(window, dpi);
return static_cast<float>(dpi);
}
};

struct CursorDrag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
<data name="MeasurementUnitAbbrPixel" xml:space="preserve">
<value>px</value>
</data>
<data name="MeasurementUnitAbbrDip" xml:space="preserve">
<value>DIP</value>
</data>
<data name="MeasurementUnitAbbrInch" xml:space="preserve">
<value>in</value>
</data>
Expand Down
Loading