Bug 563878. Part 7. Change frame, point, rect, region APIs. r=mats sr=roc

This commit is contained in:
Timothy Nikkel 2010-07-18 21:23:47 -05:00
parent 5bf17b6fc0
commit 460def762d
9 changed files with 221 additions and 12 deletions

View File

@ -438,6 +438,11 @@ inline PRInt32 NSAppUnitsToIntPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel);
}
inline float NSCoordScale(nscoord aCoord, PRInt32 aFromAPP, PRInt32 aToAPP)
{
return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP;
}
/// handy constants
#define TWIPS_PER_POINT_INT 20
#define TWIPS_PER_POINT_FLOAT 20.0f

View File

@ -83,6 +83,9 @@ struct nsPoint {
}
inline nsIntPoint ToNearestPixels(nscoord aAppUnitsPerPixel) const;
// Converts this point from aFromAPP, an appunits per pixel ratio, to aToAPP.
inline nsPoint ConvertAppUnits(PRInt32 aFromAPP, PRInt32 aToAPP) const;
};
struct nsIntPoint {
@ -128,4 +131,15 @@ nsPoint::ToNearestPixels(nscoord aAppUnitsPerPixel) const {
NSToIntRound(NSAppUnitsToFloatPixels(y, float(aAppUnitsPerPixel))));
}
inline nsPoint
nsPoint::ConvertAppUnits(PRInt32 aFromAPP, PRInt32 aToAPP) const {
if (aFromAPP != aToAPP) {
nsPoint point;
point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP));
point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP));
return point;
}
return *this;
}
#endif /* NSPOINT_H */

View File

@ -178,10 +178,17 @@ struct NS_GFX nsRect {
nsRect operator+(const nsMargin& aMargin) const { return nsRect(*this) += aMargin; }
nsRect operator-(const nsMargin& aMargin) const { return nsRect(*this) -= aMargin; }
// Scale by aScale, converting coordinates to integers so that the result
// is the smallest integer-coordinate rectangle containing the unrounded result
// Scale by aScale, converting coordinates to integers so that the result is
// the smallest integer-coordinate rectangle containing the unrounded result.
nsRect& ScaleRoundOut(float aScale);
// Converts this rect from aFromAPP, an appunits per pixel ratio, to aToAPP.
// In the RoundOut version we make the rect the smallest rect containing the
// unrounded result. In the RoundIn version we make the rect the largest rect
// contained in the unrounded result.
inline nsRect ConvertAppUnitsRoundOut(PRInt32 aFromAPP, PRInt32 aToAPP) const;
inline nsRect ConvertAppUnitsRoundIn(PRInt32 aFromAPP, PRInt32 aToAPP) const;
// Helpers for accessing the vertices
nsPoint TopLeft() const { return nsPoint(x, y); }
nsPoint TopRight() const { return nsPoint(XMost(), y); }
@ -333,6 +340,43 @@ struct NS_GFX nsIntRect {
/*
* App Unit/Pixel conversions
*/
inline nsRect
nsRect::ConvertAppUnitsRoundOut(PRInt32 aFromAPP, PRInt32 aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
nsRect rect;
nscoord right = NSToCoordCeil(NSCoordScale(XMost(), aFromAPP, aToAPP));
nscoord bottom = NSToCoordCeil(NSCoordScale(YMost(), aFromAPP, aToAPP));
rect.x = NSToCoordFloor(NSCoordScale(x, aFromAPP, aToAPP));
rect.y = NSToCoordFloor(NSCoordScale(y, aFromAPP, aToAPP));
rect.width = (right - rect.x);
rect.height = (bottom - rect.y);
return rect;
}
inline nsRect
nsRect::ConvertAppUnitsRoundIn(PRInt32 aFromAPP, PRInt32 aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
nsRect rect;
nscoord right = NSToCoordFloor(NSCoordScale(XMost(), aFromAPP, aToAPP));
nscoord bottom = NSToCoordFloor(NSCoordScale(YMost(), aFromAPP, aToAPP));
rect.x = NSToCoordCeil(NSCoordScale(x, aFromAPP, aToAPP));
rect.y = NSToCoordCeil(NSCoordScale(y, aFromAPP, aToAPP));
rect.width = (right - rect.x);
rect.height = (bottom - rect.y);
return rect;
}
// scale the rect but round to preserve centers
inline nsIntRect
nsRect::ToNearestPixels(nscoord aAppUnitsPerPixel) const

View File

@ -1289,6 +1289,44 @@ void nsRegion::MoveBy (nsPoint aPt)
}
}
nsRegion nsRegion::ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
// Do it in a simplistic and slow way to avoid any weird behaviour with
// rounding causing rects to overlap. Should be fast enough for what we need.
nsRegion region;
nsRegionRectIterator iter(*this);
for (;;) {
const nsRect* r = iter.Next();
if (!r)
break;
nsRect rect = r->ConvertAppUnitsRoundOut(aFromAPP, aToAPP);
region.Or(region, rect);
}
return region;
}
nsRegion nsRegion::ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
// Do it in a simplistic and slow way to avoid any weird behaviour with
// rounding causing rects to overlap. Should be fast enough for what we need.
nsRegion region;
nsRegionRectIterator iter(*this);
for (;;) {
const nsRect* r = iter.Next();
if (!r)
break;
nsRect rect = r->ConvertAppUnitsRoundIn(aFromAPP, aToAPP);
region.Or(region, rect);
}
return region;
}
nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
{
nsIntRegion result;

View File

@ -178,6 +178,11 @@ public:
PRBool IsEqual (const nsRegion& aRegion) const;
PRUint32 GetNumRects () const { return mRectCount; }
const nsRect& GetBounds () const { return mBoundRect; }
// Converts this region from aFromAPP, an appunits per pixel ratio, to
// aToAPP. This applies nsRect::ConvertAppUnitsRoundOut/In to each rect of
// the region.
nsRegion ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const;
nsRegion ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const;
nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
nsRect GetLargestRectangle () const;

View File

@ -69,6 +69,9 @@ struct nsSize {
nsSize& operator+=(const nsSize& aSize) {width += aSize.width;
height += aSize.height;
return *this;}
// Converts this size from aFromAPP, an appunits per pixel ratio, to aToAPP.
inline nsSize ConvertAppUnits(PRInt32 aFromAPP, PRInt32 aToAPP) const;
};
struct nsIntSize {
@ -90,4 +93,15 @@ struct nsIntSize {
void SizeTo(PRInt32 aWidth, PRInt32 aHeight) {width = aWidth; height = aHeight;}
};
inline nsSize
nsSize::ConvertAppUnits(PRInt32 aFromAPP, PRInt32 aToAPP) const {
if (aFromAPP != aToAPP) {
nsSize size;
size.width = NSToCoordRound(NSCoordScale(width, aFromAPP, aToAPP));
size.height = NSToCoordRound(NSCoordScale(height, aFromAPP, aToAPP));
return size;
}
return *this;
}
#endif /* NSSIZE_H */

View File

@ -205,11 +205,12 @@ public:
/**
* @return a point pt such that adding pt to a coordinate relative to aFrame
* makes it relative to ReferenceFrame(), i.e., returns
* aFrame->GetOffsetTo(ReferenceFrame()). It may be optimized to be faster
* than aFrame->GetOffsetTo(ReferenceFrame()) (but currently isn't).
* aFrame->GetOffsetToCrossDoc(ReferenceFrame()). The returned point is in
* the appunits of aFrame. It may be optimized to be faster than
* aFrame->GetOffsetToCrossDoc(ReferenceFrame()) (but currently isn't).
*/
nsPoint ToReferenceFrame(const nsIFrame* aFrame) {
return aFrame->GetOffsetTo(ReferenceFrame());
return aFrame->GetOffsetToCrossDoc(ReferenceFrame());
}
/**
* When building the display list, the scrollframe aFrame will be "ignored"

View File

@ -3539,23 +3539,87 @@ nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const
{
NS_PRECONDITION(aOther,
"Must have frame for destination coordinate system!");
//NS_ASSERTION(PresContext() == aOther->PresContext(),
// "GetOffsetTo called on frames in different documents");
//XXX sometime in the near future once we are confident that all GetOffsetTo
// callers pass frames that are really in the same doc we can get rid of this
// check.
if (PresContext() != aOther->PresContext()) {
return GetOffsetToCrossDoc(aOther);
}
nsPoint offset(0, 0);
const nsIFrame* f;
for (f = this; f != aOther && f;
f = nsLayoutUtils::GetCrossDocParentFrame(f, &offset)) {
for (f = this; f != aOther && f; f = f->GetParent()) {
offset += f->GetPosition();
}
if (f != aOther) {
// Looks like aOther wasn't an ancestor of |this|. So now we have
// the root-document-relative position of |this| in |offset|. Convert back
// the root-frame-relative position of |this| in |offset|. Convert back
// to the coordinates of aOther
nsPoint negativeOffset(0,0);
while (aOther) {
offset -= aOther->GetPosition();
aOther = nsLayoutUtils::GetCrossDocParentFrame(aOther, &negativeOffset);
aOther = aOther->GetParent();
}
offset -= negativeOffset;
}
return offset;
}
nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const
{
return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
}
nsPoint
nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const PRInt32 aAPD) const
{
NS_PRECONDITION(aOther,
"Must have frame for destination coordinate system!");
NS_ASSERTION(PresContext()->GetRootPresContext() ==
aOther->PresContext()->GetRootPresContext(),
"trying to get the offset between frames in different document "
"hierarchies?");
const nsIFrame* root = nsnull;
// offset will hold the final offset
// docOffset holds the currently accumulated offset at the current APD, it
// will be converted and added to offset when the current APD changes.
nsPoint offset(0, 0), docOffset(0, 0);
const nsIFrame* f = this;
PRInt32 currAPD = PresContext()->AppUnitsPerDevPixel();
while (f && f != aOther) {
docOffset += f->GetPosition();
nsIFrame* parent = f->GetParent();
if (parent) {
f = parent;
} else {
nsPoint newOffset(0, 0);
root = f;
f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
PRInt32 newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
if (!f || newAPD != currAPD) {
// Convert docOffset to the right APD and add it to offset.
offset += docOffset.ConvertAppUnits(currAPD, aAPD);
docOffset.x = docOffset.y = 0;
}
currAPD = newAPD;
docOffset += newOffset;
}
}
if (f == aOther) {
offset += docOffset.ConvertAppUnits(currAPD, aAPD);
} else {
// Looks like aOther wasn't an ancestor of |this|. So now we have
// the root-document-relative position of |this| in |offset|. Subtract the
// root-document-relative position of |aOther| from |offset|.
// This call won't try to recurse again because root is an ancestor of
// aOther.
nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
offset -= negOffset;
}
return offset;

View File

@ -1656,7 +1656,8 @@ public:
*
* This function is fastest when aOther is an ancestor of |this|.
*
* This function works across document boundaries.
* This function _DOES NOT_ work across document boundaries.
* Use this function only when |this| and aOther are in the same document.
*
* NOTE: this actually returns the offset from aOther to |this|, but
* that offset is added to transform _coordinates_ from |this| to
@ -1665,6 +1666,28 @@ public:
nsPoint GetOffsetTo(const nsIFrame* aOther) const;
virtual nsPoint GetOffsetToExternal(const nsIFrame* aOther) const;
/**
* Get the offset between the coordinate systems of |this| and aOther
* expressed in appunits per dev pixel of |this|' document. Adding the return
* value to a point that is relative to the origin of |this| will make the
* point relative to the origin of aOther but in the appunits per dev pixel
* ratio of |this|.
*
* aOther must be non-null.
*
* This function is fastest when aOther is an ancestor of |this|.
*
* This function works across document boundaries.
*
* Because this function may cross document boundaries that have different
* app units per dev pixel ratios it needs to be used very carefully.
*
* NOTE: this actually returns the offset from aOther to |this|, but
* that offset is added to transform _coordinates_ from |this| to
* aOther.
*/
nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther) const;
/**
* Get the screen rect of the frame in pixels.
* @return the pixel rect of the frame in screen coordinates.
@ -2572,6 +2595,7 @@ protected:
private:
nsRect* GetOverflowAreaProperty(PRBool aCreateIfNecessary = PR_FALSE);
void SetOverflowRect(const nsRect& aRect);
nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const PRInt32 aAPD) const;
#ifdef NS_DEBUG
public: