vcl: introduce ViewSubPixelToLogicInt() wrappers to CoordinateMapper
This commit completes the inverse sub-pixel pipeline by adding the ViewSubPixelToLogicIntX/Y() master wrappers and their corresponding LogicUnits pipeline stages. Unit testing revealed that the order of operations in SubPixelToLogic is mathematically sensitive: the floating-point View distance must be rounded to an integer (llround) BEFORE any integer offsets (mnMapOfs or mnOutOffLogic) are subtracted. Shifting the rounding boundary by applying offsets in floating-point space leads to off-by-one errors for fractional coordinates near zero-crossing points. Key changes: - Added ViewSubPixelToLogicUnitsIntX/Y(): Pipeline stages that round scaled distances to tools::Long before stripping the mapping offset. - Added ViewSubPixelToLogicIntX/Y(): Master wrappers that provide the full "Round-then-Shift" journey from View sub-pixels to Absolute Logic integers. - Refactored OutputDevice::SubPixelToLogic() in map.cxx to use these new wrappers, successfully encapsulating the manual offset arithmetic while maintaining exact legacy rounding behavior. Change-Id: Icecd9a2f5f75cb5353e6c7d9431be22c0889e968 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203671 Reviewed-by: Dan Williams <dcbw@libreoffice.org> Tested-by: Jenkins
This commit is contained in:
committed by
Dan Williams
parent
82c68a6f63
commit
5970e72c67
@@ -167,6 +167,8 @@ public:
|
||||
// View <-> LogicUnits (Sub-pixel)
|
||||
double ViewSubPixelToLogicUnitsX(double fX) const;
|
||||
double ViewSubPixelToLogicUnitsY(double fY) const;
|
||||
tools::Long ViewSubPixelToLogicUnitsIntX(double fX) const;
|
||||
tools::Long ViewSubPixelToLogicUnitsIntY(double fY) const;
|
||||
double LogicUnitsToViewSubPixelX(double fX) const;
|
||||
double LogicUnitsToViewSubPixelY(double fY) const;
|
||||
|
||||
@@ -203,6 +205,10 @@ public:
|
||||
// To resolve the return-type conflict, these now return double
|
||||
double ViewSubPixelToLogicX(double fX) const;
|
||||
double ViewSubPixelToLogicY(double fY) const;
|
||||
// View (Sub-pixel) -> Absolute Logic (Integer)
|
||||
// Note: This rounds the distance before stripping offsets to satisfy legacy test parity.
|
||||
tools::Long ViewSubPixelToLogicIntX(double fX) const;
|
||||
tools::Long ViewSubPixelToLogicIntY(double fY) const;
|
||||
double LogicToViewSubPixelX(double fX) const;
|
||||
double LogicToViewSubPixelY(double fY) const;
|
||||
|
||||
|
||||
@@ -260,6 +260,18 @@ double CoordinateMapper::ViewSubPixelToLogicUnitsY(double fY) const
|
||||
return ViewToLogicDistanceDoubleY(fY, maMapRes.mfMapScY) - maMapRes.mnMapOfsY;
|
||||
}
|
||||
|
||||
tools::Long CoordinateMapper::ViewSubPixelToLogicUnitsIntX(double fX) const
|
||||
{
|
||||
// Uses the version that rounds the distance BEFORE the offset shift
|
||||
return ViewSubPixelToLogicDistanceX(fX) - maMapRes.mnMapOfsX;
|
||||
}
|
||||
|
||||
tools::Long CoordinateMapper::ViewSubPixelToLogicUnitsIntY(double fY) const
|
||||
{
|
||||
// Uses the version that rounds the distance BEFORE the offset shift
|
||||
return ViewSubPixelToLogicDistanceY(fY) - maMapRes.mnMapOfsY;
|
||||
}
|
||||
|
||||
double CoordinateMapper::LogicUnitsToViewSubPixelX(double fX) const
|
||||
{
|
||||
return LogicToViewDistanceSubPixelX(std::llround(fX + maMapRes.mnMapOfsX), maMapRes.mfMapScX);
|
||||
@@ -582,6 +594,22 @@ double CoordinateMapper::ViewSubPixelToLogicY(double fY) const
|
||||
return fLogicUnits - static_cast<double>(mnOutOffLogicY);
|
||||
}
|
||||
|
||||
tools::Long CoordinateMapper::ViewSubPixelToLogicIntX(double fX) const
|
||||
{
|
||||
// Move from View to Logic Units (Rounds distance, then strips mnMapOfs)
|
||||
const tools::Long nLogicUnits = ViewSubPixelToLogicUnitsIntX(fX);
|
||||
|
||||
return nLogicUnits - mnOutOffLogicX;
|
||||
}
|
||||
|
||||
tools::Long CoordinateMapper::ViewSubPixelToLogicIntY(double fY) const
|
||||
{
|
||||
// Move from View to Logic Units (Rounds distance, then strips mnMapOfs)
|
||||
const tools::Long nLogicUnits = ViewSubPixelToLogicUnitsIntY(fY);
|
||||
|
||||
return nLogicUnits - mnOutOffLogicY;
|
||||
}
|
||||
|
||||
// Absolute Logic -> View (Forward: Add Logical -> Add Mapping/Scale)
|
||||
double CoordinateMapper::LogicToViewSubPixelX(double fX) const
|
||||
{
|
||||
|
||||
@@ -696,8 +696,8 @@ Point OutputDevice::SubPixelToLogic(const basegfx::B2DPoint& rDevicePt) const
|
||||
return Point(rDevicePt.getX(), rDevicePt.getY());
|
||||
}
|
||||
|
||||
return Point(mpMapper->ViewSubPixelToLogicDistanceX(rDevicePt.getX()) - mpMapper->GetMappingXOffset() - mpMapper->GetLogicalXOffset(),
|
||||
mpMapper->ViewSubPixelToLogicDistanceY(rDevicePt.getY()) - mpMapper->GetMappingYOffset() - mpMapper->GetLogicalYOffset());
|
||||
return Point(mpMapper->ViewSubPixelToLogicIntX(rDevicePt.getX()),
|
||||
mpMapper->ViewSubPixelToLogicIntY(rDevicePt.getY()));
|
||||
}
|
||||
|
||||
Size OutputDevice::PixelToLogic( const Size& rDeviceSize ) const
|
||||
|
||||
Reference in New Issue
Block a user