2017-10-27 20:33:53 +03:00
|
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
/* class that manages rules for positioning floats */
|
|
|
|
|
|
|
|
|
|
#include "nsFloatManager.h"
|
2016-09-15 06:29:14 +03:00
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
2017-03-13 07:03:40 +03:00
|
|
|
|
#include <initializer_list>
|
2016-09-15 06:29:14 +03:00
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
#include "gfxContext.h"
|
2019-04-16 10:25:10 +03:00
|
|
|
|
#include "mozilla/PresShell.h"
|
2016-07-21 13:36:34 +03:00
|
|
|
|
#include "mozilla/ReflowInput.h"
|
2017-01-06 11:36:43 +03:00
|
|
|
|
#include "mozilla/ShapeUtils.h"
|
2016-09-15 09:32:12 +03:00
|
|
|
|
#include "nsBlockFrame.h"
|
2018-06-15 21:00:36 +03:00
|
|
|
|
#include "nsDeviceContext.h"
|
2012-07-27 18:03:27 +04:00
|
|
|
|
#include "nsError.h"
|
2018-01-25 09:55:18 +03:00
|
|
|
|
#include "nsImageRenderer.h"
|
2016-09-15 06:29:14 +03:00
|
|
|
|
#include "nsMemory.h"
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2010-03-29 05:46:55 +04:00
|
|
|
|
using namespace mozilla;
|
2018-06-15 21:00:36 +03:00
|
|
|
|
using namespace mozilla::image;
|
|
|
|
|
using namespace mozilla::gfx;
|
2010-03-29 05:46:55 +04:00
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
|
int32_t nsFloatManager::sCachedFloatManagerCount = 0;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// nsFloatManager
|
|
|
|
|
|
2019-04-16 10:25:10 +03:00
|
|
|
|
nsFloatManager::nsFloatManager(PresShell* aPresShell, WritingMode aWM)
|
2015-04-01 18:43:58 +03:00
|
|
|
|
:
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
mWritingMode(aWM),
|
|
|
|
|
#endif
|
2015-03-22 12:44:48 +03:00
|
|
|
|
mLineLeft(0),
|
|
|
|
|
mBlockStart(0),
|
2017-05-24 16:35:26 +03:00
|
|
|
|
mFloatDamage(aPresShell),
|
2011-10-17 18:59:28 +04:00
|
|
|
|
mPushedLeftFloatPastBreak(false),
|
|
|
|
|
mPushedRightFloatPastBreak(false),
|
|
|
|
|
mSplitLeftFloatAcrossBreak(false),
|
|
|
|
|
mSplitRightFloatAcrossBreak(false) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
MOZ_COUNT_CTOR(nsFloatManager);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsFloatManager::~nsFloatManager() { MOZ_COUNT_DTOR(nsFloatManager); }
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW {
|
|
|
|
|
if (sCachedFloatManagerCount > 0) {
|
|
|
|
|
// We have cached unused instances of this class, return a cached
|
|
|
|
|
// instance in stead of always creating a new one.
|
|
|
|
|
return sCachedFloatManagers[--sCachedFloatManagerCount];
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-24 12:47:33 +03:00
|
|
|
|
// The cache is empty, this means we have to create a new instance using
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// the global |operator new|.
|
2015-03-27 03:01:12 +03:00
|
|
|
|
return moz_xmalloc(aSize);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsFloatManager::operator delete(void* aPtr, size_t aSize) {
|
|
|
|
|
if (!aPtr) return;
|
|
|
|
|
// This float manager is no longer used, if there's still room in
|
|
|
|
|
// the cache we'll cache this float manager, unless the layout
|
|
|
|
|
// module was already shut down.
|
|
|
|
|
|
|
|
|
|
if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
|
|
|
|
|
sCachedFloatManagerCount >= 0) {
|
|
|
|
|
// There's still space in the cache for more instances, put this
|
|
|
|
|
// instance in the cache in stead of deleting it.
|
|
|
|
|
|
|
|
|
|
sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The cache is full, or the layout module has been shut down,
|
|
|
|
|
// delete this float manager.
|
2015-03-27 03:01:12 +03:00
|
|
|
|
free(aPtr);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
|
void nsFloatManager::Shutdown() {
|
|
|
|
|
// The layout module is being shut down, clean up the cache and
|
|
|
|
|
// disable further caching.
|
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
|
int32_t i;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < sCachedFloatManagerCount; i++) {
|
|
|
|
|
void* floatManager = sCachedFloatManagers[i];
|
2015-03-27 03:01:12 +03:00
|
|
|
|
if (floatManager) free(floatManager);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disable further caching.
|
|
|
|
|
sCachedFloatManagerCount = -1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 13:10:23 +03:00
|
|
|
|
#define CHECK_BLOCK_AND_LINE_DIR(aWM) \
|
|
|
|
|
NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir() && \
|
|
|
|
|
(aWM).IsLineInverted() == mWritingMode.IsLineInverted(), \
|
|
|
|
|
"incompatible writing modes")
|
2015-04-01 18:43:58 +03:00
|
|
|
|
|
2016-10-12 08:26:17 +03:00
|
|
|
|
nsFlowAreaRect nsFloatManager::GetFlowArea(
|
|
|
|
|
WritingMode aWM, nscoord aBCoord, nscoord aBSize,
|
2016-10-12 11:06:25 +03:00
|
|
|
|
BandInfoType aBandInfoType, ShapeType aShapeType, LogicalRect aContentArea,
|
2015-07-16 12:07:57 +03:00
|
|
|
|
SavedState* aState, const nsSize& aContainerSize) const {
|
2016-11-23 13:10:23 +03:00
|
|
|
|
CHECK_BLOCK_AND_LINE_DIR(aWM);
|
2014-10-22 02:16:12 +04:00
|
|
|
|
NS_ASSERTION(aBSize >= 0, "unexpected max block size");
|
|
|
|
|
NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
|
|
|
|
|
"unexpected content area inline size");
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2016-10-12 08:01:19 +03:00
|
|
|
|
nscoord blockStart = aBCoord + mBlockStart;
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (blockStart < nscoord_MIN) {
|
2009-01-07 02:21:00 +03:00
|
|
|
|
NS_WARNING("bad value");
|
2014-10-22 02:16:12 +04:00
|
|
|
|
blockStart = nscoord_MIN;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 00:52:36 +04:00
|
|
|
|
// Determine the last float that we should consider.
|
2012-08-22 19:56:38 +04:00
|
|
|
|
uint32_t floatCount;
|
2009-04-09 00:52:36 +04:00
|
|
|
|
if (aState) {
|
|
|
|
|
// Use the provided state.
|
|
|
|
|
floatCount = aState->mFloatInfoCount;
|
2015-02-10 01:34:50 +03:00
|
|
|
|
MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state");
|
2009-04-09 00:52:36 +04:00
|
|
|
|
} else {
|
|
|
|
|
// Use our current state.
|
|
|
|
|
floatCount = mFloats.Length();
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// If there are no floats at all, or we're below the last one, return
|
|
|
|
|
// quickly.
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (floatCount == 0 || (mFloats[floatCount - 1].mLeftBEnd <= blockStart &&
|
|
|
|
|
mFloats[floatCount - 1].mRightBEnd <= blockStart)) {
|
2016-10-12 08:01:19 +03:00
|
|
|
|
return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
|
2018-05-30 21:07:18 +03:00
|
|
|
|
aContentArea.ISize(aWM), aBSize,
|
2019-04-26 02:03:04 +03:00
|
|
|
|
nsFlowAreaRectFlags::NoFlags);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
nscoord blockEnd;
|
|
|
|
|
if (aBSize == nscoord_MAX) {
|
2009-07-09 05:10:29 +04:00
|
|
|
|
// This warning (and the two below) are possible to hit on pages
|
|
|
|
|
// with really large objects.
|
2016-10-12 08:26:17 +03:00
|
|
|
|
NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint,
|
|
|
|
|
"bad height");
|
2014-10-22 02:16:12 +04:00
|
|
|
|
blockEnd = nscoord_MAX;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
} else {
|
2014-10-22 02:16:12 +04:00
|
|
|
|
blockEnd = blockStart + aBSize;
|
|
|
|
|
if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
|
2009-01-07 02:21:00 +03:00
|
|
|
|
NS_WARNING("bad value");
|
2014-10-22 02:16:12 +04:00
|
|
|
|
blockEnd = nscoord_MAX;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-16 12:07:57 +03:00
|
|
|
|
nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
|
|
|
|
|
nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
|
2015-03-22 12:44:48 +03:00
|
|
|
|
if (lineRight < lineLeft) {
|
2009-01-07 02:21:00 +03:00
|
|
|
|
NS_WARNING("bad value");
|
2015-03-22 12:44:48 +03:00
|
|
|
|
lineRight = lineLeft;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Walk backwards through the floats until we either hit the front of
|
2014-10-22 02:16:12 +04:00
|
|
|
|
// the list or we're above |blockStart|.
|
2011-09-29 10:19:26 +04:00
|
|
|
|
bool haveFloats = false;
|
2018-05-30 21:07:18 +03:00
|
|
|
|
bool mayWiden = false;
|
2012-08-22 19:56:38 +04:00
|
|
|
|
for (uint32_t i = floatCount; i > 0; --i) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
const FloatInfo& fi = mFloats[i - 1];
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// There aren't any more floats that could intersect this band.
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
if (fi.IsEmpty(aShapeType)) {
|
2018-01-25 09:55:18 +03:00
|
|
|
|
// Ignore empty float areas.
|
|
|
|
|
// https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior
|
2009-01-05 03:39:54 +03:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-10-22 02:16:12 +04:00
|
|
|
|
|
2016-10-12 11:06:25 +03:00
|
|
|
|
nscoord floatBStart = fi.BStart(aShapeType);
|
|
|
|
|
nscoord floatBEnd = fi.BEnd(aShapeType);
|
2016-10-12 08:26:17 +03:00
|
|
|
|
if (blockStart < floatBStart &&
|
|
|
|
|
aBandInfoType == BandInfoType::BandFromPoint) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// This float is below our band. Shrink our band's height if needed.
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (floatBStart < blockEnd) {
|
|
|
|
|
blockEnd = floatBStart;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
2009-05-20 15:21:34 +04:00
|
|
|
|
}
|
2016-10-12 08:26:17 +03:00
|
|
|
|
// If blockStart == blockEnd (which happens only with WidthWithinHeight),
|
2009-05-20 15:21:34 +04:00
|
|
|
|
// we include floats that begin at our 0-height vertical area. We
|
2016-10-12 08:26:17 +03:00
|
|
|
|
// need to do this to satisfy the invariant that a
|
|
|
|
|
// WidthWithinHeight call is at least as narrow on both sides as a
|
|
|
|
|
// BandFromPoint call beginning at its blockStart.
|
2014-10-22 02:16:12 +04:00
|
|
|
|
else if (blockStart < floatBEnd &&
|
|
|
|
|
(floatBStart < blockEnd ||
|
|
|
|
|
(floatBStart == blockEnd && blockStart == blockEnd))) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// This float is in our band.
|
|
|
|
|
|
|
|
|
|
// Shrink our band's width if needed.
|
2018-07-16 19:50:18 +03:00
|
|
|
|
StyleFloat floatStyle = fi.mFrame->StyleDisplay()->mFloat;
|
2016-10-13 11:28:38 +03:00
|
|
|
|
|
|
|
|
|
// When aBandInfoType is BandFromPoint, we're only intended to
|
|
|
|
|
// consider a point along the y axis rather than a band.
|
|
|
|
|
const nscoord bandBlockEnd =
|
|
|
|
|
aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
|
2016-08-09 12:32:54 +03:00
|
|
|
|
if (floatStyle == StyleFloat::Left) {
|
2015-03-22 12:44:48 +03:00
|
|
|
|
// A left float
|
2016-10-13 11:28:38 +03:00
|
|
|
|
nscoord lineRightEdge =
|
2017-03-13 07:03:39 +03:00
|
|
|
|
fi.LineRight(aShapeType, blockStart, bandBlockEnd);
|
2015-03-22 12:44:48 +03:00
|
|
|
|
if (lineRightEdge > lineLeft) {
|
|
|
|
|
lineLeft = lineRightEdge;
|
2009-01-07 02:21:00 +03:00
|
|
|
|
// Only set haveFloats to true if the float is inside our
|
|
|
|
|
// containing block. This matches the spec for what some
|
|
|
|
|
// callers want and disagrees for other callers, so we should
|
|
|
|
|
// probably provide better information at some point.
|
2011-10-17 18:59:28 +04:00
|
|
|
|
haveFloats = true;
|
2018-05-30 21:07:18 +03:00
|
|
|
|
|
|
|
|
|
// Our area may widen in the block direction if this float may
|
|
|
|
|
// narrow in the block direction.
|
|
|
|
|
mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2015-03-22 12:44:48 +03:00
|
|
|
|
// A right float
|
2016-10-13 11:28:38 +03:00
|
|
|
|
nscoord lineLeftEdge =
|
2017-03-13 07:03:39 +03:00
|
|
|
|
fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
|
2015-03-22 12:44:48 +03:00
|
|
|
|
if (lineLeftEdge < lineRight) {
|
|
|
|
|
lineRight = lineLeftEdge;
|
2009-01-07 02:21:00 +03:00
|
|
|
|
// See above.
|
2011-10-17 18:59:28 +04:00
|
|
|
|
haveFloats = true;
|
2018-05-30 21:07:18 +03:00
|
|
|
|
mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-13 11:28:38 +03:00
|
|
|
|
|
|
|
|
|
// Shrink our band's height if needed.
|
|
|
|
|
if (floatBEnd < blockEnd &&
|
|
|
|
|
aBandInfoType == BandInfoType::BandFromPoint) {
|
|
|
|
|
blockEnd = floatBEnd;
|
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 02:16:13 +04:00
|
|
|
|
nscoord blockSize =
|
|
|
|
|
(blockEnd == nscoord_MAX) ? nscoord_MAX : (blockEnd - blockStart);
|
2015-03-22 12:44:48 +03:00
|
|
|
|
// convert back from LineLeft/Right to IStart
|
2015-07-16 12:08:05 +03:00
|
|
|
|
nscoord inlineStart =
|
|
|
|
|
aWM.IsBidiLTR()
|
2015-07-16 12:07:57 +03:00
|
|
|
|
? lineLeft - mLineLeft
|
2015-07-16 12:08:05 +03:00
|
|
|
|
: mLineLeft - lineRight + LogicalSize(aWM, aContainerSize).ISize(aWM);
|
2015-03-22 12:44:48 +03:00
|
|
|
|
|
2019-04-26 02:03:04 +03:00
|
|
|
|
nsFlowAreaRectFlags flags =
|
|
|
|
|
(haveFloats ? nsFlowAreaRectFlags::HasFloats
|
|
|
|
|
: nsFlowAreaRectFlags::NoFlags) |
|
|
|
|
|
(mayWiden ? nsFlowAreaRectFlags::MayWiden : nsFlowAreaRectFlags::NoFlags);
|
2018-05-30 21:07:18 +03:00
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
|
2018-05-30 21:07:18 +03:00
|
|
|
|
lineRight - lineLeft, blockSize, flags);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
void nsFloatManager::AddFloat(nsIFrame* aFloatFrame,
|
|
|
|
|
const LogicalRect& aMarginRect, WritingMode aWM,
|
2015-07-16 12:07:57 +03:00
|
|
|
|
const nsSize& aContainerSize) {
|
2016-11-23 13:10:23 +03:00
|
|
|
|
CHECK_BLOCK_AND_LINE_DIR(aWM);
|
2014-10-22 02:16:12 +04:00
|
|
|
|
NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
|
|
|
|
|
NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2016-10-12 09:26:26 +03:00
|
|
|
|
FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM,
|
|
|
|
|
aContainerSize);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
// Set mLeftBEnd and mRightBEnd.
|
2009-01-05 03:39:54 +03:00
|
|
|
|
if (HasAnyFloats()) {
|
|
|
|
|
FloatInfo& tail = mFloats[mFloats.Length() - 1];
|
2014-10-22 02:16:12 +04:00
|
|
|
|
info.mLeftBEnd = tail.mLeftBEnd;
|
|
|
|
|
info.mRightBEnd = tail.mRightBEnd;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
} else {
|
2014-10-22 02:16:12 +04:00
|
|
|
|
info.mLeftBEnd = nscoord_MIN;
|
|
|
|
|
info.mRightBEnd = nscoord_MIN;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
2018-07-16 19:50:18 +03:00
|
|
|
|
StyleFloat floatStyle = aFloatFrame->StyleDisplay()->mFloat;
|
2016-08-09 12:32:54 +03:00
|
|
|
|
MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
|
|
|
|
|
"Unexpected float style!");
|
|
|
|
|
nscoord& sideBEnd =
|
|
|
|
|
floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
|
2015-03-22 12:44:48 +03:00
|
|
|
|
nscoord thisBEnd = info.BEnd();
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (thisBEnd > sideBEnd) sideBEnd = thisBEnd;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2018-05-30 22:15:35 +03:00
|
|
|
|
mFloats.AppendElement(std::move(info));
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
// static
|
2014-10-22 02:16:12 +04:00
|
|
|
|
LogicalRect nsFloatManager::CalculateRegionFor(WritingMode aWM,
|
|
|
|
|
nsIFrame* aFloat,
|
|
|
|
|
const LogicalMargin& aMargin,
|
2015-07-16 12:07:57 +03:00
|
|
|
|
const nsSize& aContainerSize) {
|
2013-08-09 04:20:17 +04:00
|
|
|
|
// We consider relatively positioned frames at their original position.
|
2014-10-22 02:16:12 +04:00
|
|
|
|
LogicalRect region(aWM,
|
|
|
|
|
nsRect(aFloat->GetNormalPosition(), aFloat->GetSize()),
|
2015-07-16 12:07:57 +03:00
|
|
|
|
aContainerSize);
|
2009-07-15 09:19:31 +04:00
|
|
|
|
|
|
|
|
|
// Float region includes its margin
|
2014-10-22 02:16:12 +04:00
|
|
|
|
region.Inflate(aWM, aMargin);
|
2009-07-15 09:19:31 +04:00
|
|
|
|
|
|
|
|
|
// Don't store rectangles with negative margin-box width or height in
|
|
|
|
|
// the float manager; it can't deal with them.
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (region.ISize(aWM) < 0) {
|
2009-07-15 09:19:31 +04:00
|
|
|
|
// Preserve the right margin-edge for left floats and the left
|
|
|
|
|
// margin-edge for right floats
|
2013-08-09 04:20:17 +04:00
|
|
|
|
const nsStyleDisplay* display = aFloat->StyleDisplay();
|
2018-07-16 19:50:18 +03:00
|
|
|
|
StyleFloat floatStyle = display->mFloat;
|
2016-08-09 12:32:54 +03:00
|
|
|
|
if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
|
2014-10-22 02:16:12 +04:00
|
|
|
|
region.IStart(aWM) = region.IEnd(aWM);
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
2014-10-22 02:16:12 +04:00
|
|
|
|
region.ISize(aWM) = 0;
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (region.BSize(aWM) < 0) {
|
|
|
|
|
region.BSize(aWM) = 0;
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-28 06:23:59 +03:00
|
|
|
|
NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
|
2010-03-29 05:46:55 +04:00
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
LogicalRect nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
|
2015-07-16 12:07:57 +03:00
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
|
2017-05-27 14:36:00 +03:00
|
|
|
|
void* storedRegion = aFloat->GetProperty(FloatRegionProperty());
|
2009-07-15 09:19:31 +04:00
|
|
|
|
if (storedRegion) {
|
|
|
|
|
nsMargin margin = *static_cast<nsMargin*>(storedRegion);
|
2014-10-22 02:16:12 +04:00
|
|
|
|
region.Inflate(aWM, LogicalMargin(aWM, margin));
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
void nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
|
|
|
|
|
const LogicalRect& aRegion,
|
2015-07-16 12:07:57 +03:00
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
|
2009-07-15 09:19:31 +04:00
|
|
|
|
nsRect rect = aFloat->GetRect();
|
2014-10-22 02:16:12 +04:00
|
|
|
|
if (region.IsEqualEdges(rect)) {
|
2017-05-27 14:36:00 +03:00
|
|
|
|
aFloat->DeleteProperty(FloatRegionProperty());
|
2009-07-15 09:19:31 +04:00
|
|
|
|
} else {
|
2017-05-27 14:36:00 +03:00
|
|
|
|
nsMargin* storedMargin = aFloat->GetProperty(FloatRegionProperty());
|
2009-07-15 09:19:31 +04:00
|
|
|
|
if (!storedMargin) {
|
|
|
|
|
storedMargin = new nsMargin();
|
2017-05-27 14:36:00 +03:00
|
|
|
|
aFloat->SetProperty(FloatRegionProperty(), storedMargin);
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
2014-10-22 02:16:12 +04:00
|
|
|
|
*storedMargin = region - rect;
|
2009-07-15 09:19:31 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
nsresult nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList) {
|
|
|
|
|
if (!aFrameList) {
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
// This could be a good bit simpler if we could guarantee that the
|
|
|
|
|
// floats given were at the end of our list, so we could just search
|
|
|
|
|
// for the head of aFrameList. (But we can't;
|
|
|
|
|
// layout/reftests/bugs/421710-1.html crashes.)
|
2013-09-02 12:41:57 +04:00
|
|
|
|
nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
|
2011-11-09 00:24:37 +04:00
|
|
|
|
frameSet.PutEntry(f);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
|
uint32_t newLength = mFloats.Length();
|
2009-01-05 03:39:54 +03:00
|
|
|
|
while (newLength > 0) {
|
|
|
|
|
if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
--newLength;
|
|
|
|
|
}
|
2009-01-07 02:21:00 +03:00
|
|
|
|
mFloats.TruncateLength(newLength);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2012-08-22 19:56:38 +04:00
|
|
|
|
for (uint32_t i = 0; i < mFloats.Length(); ++i) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
NS_ASSERTION(
|
|
|
|
|
!frameSet.Contains(mFloats[i].mFrame),
|
|
|
|
|
"Frame region deletion was requested but we couldn't delete it");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsFloatManager::PushState(SavedState* aState) {
|
2018-04-28 22:50:58 +03:00
|
|
|
|
MOZ_ASSERT(aState, "Need a place to save state");
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
// This is a cheap push implementation, which
|
|
|
|
|
// only saves the (x,y) and last frame in the mFrameInfoMap
|
|
|
|
|
// which is enough info to get us back to where we should be
|
|
|
|
|
// when pop is called.
|
|
|
|
|
//
|
|
|
|
|
// This push/pop mechanism is used to undo any
|
|
|
|
|
// floats that were added during the unconstrained reflow
|
|
|
|
|
// in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
|
|
|
|
|
//
|
|
|
|
|
// It should also be noted that the state for mFloatDamage is
|
|
|
|
|
// intentionally not saved or restored in PushState() and PopState(),
|
|
|
|
|
// since that could lead to bugs where damage is missed/dropped when
|
|
|
|
|
// we move from position A to B (during the intermediate incremental
|
|
|
|
|
// reflow mentioned above) and then from B to C during the subsequent
|
|
|
|
|
// reflow. In the typical case A and C will be the same, but not always.
|
|
|
|
|
// Allowing mFloatDamage to accumulate the damage incurred during both
|
|
|
|
|
// reflows ensures that nothing gets missed.
|
2015-03-22 12:44:48 +03:00
|
|
|
|
aState->mLineLeft = mLineLeft;
|
|
|
|
|
aState->mBlockStart = mBlockStart;
|
2010-08-06 08:59:19 +04:00
|
|
|
|
aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
|
|
|
|
|
aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
|
2010-08-06 08:59:19 +04:00
|
|
|
|
aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
|
|
|
|
|
aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
aState->mFloatInfoCount = mFloats.Length();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsFloatManager::PopState(SavedState* aState) {
|
2018-04-28 22:50:58 +03:00
|
|
|
|
MOZ_ASSERT(aState, "No state to restore?");
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
mLineLeft = aState->mLineLeft;
|
|
|
|
|
mBlockStart = aState->mBlockStart;
|
2010-08-06 08:59:19 +04:00
|
|
|
|
mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
|
|
|
|
|
mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
|
2010-08-06 08:59:19 +04:00
|
|
|
|
mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
|
|
|
|
|
mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
|
|
|
|
|
"somebody misused PushState/PopState");
|
2009-01-07 02:21:00 +03:00
|
|
|
|
mFloats.TruncateLength(aState->mFloatInfoCount);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
nscoord nsFloatManager::GetLowestFloatTop() const {
|
2010-08-06 08:59:19 +04:00
|
|
|
|
if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
|
|
|
|
|
return nscoord_MAX;
|
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
if (!HasAnyFloats()) {
|
|
|
|
|
return nscoord_MIN;
|
|
|
|
|
}
|
2015-03-22 12:44:48 +03:00
|
|
|
|
return mFloats[mFloats.Length() - 1].BStart() - mBlockStart;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2014-01-06 03:31:14 +04:00
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
2009-01-05 03:39:54 +03:00
|
|
|
|
void DebugListFloatManager(const nsFloatManager* aFloatManager) {
|
|
|
|
|
aFloatManager->List(stdout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult nsFloatManager::List(FILE* out) const {
|
|
|
|
|
if (!HasAnyFloats()) return NS_OK;
|
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
|
for (uint32_t i = 0; i < mFloats.Length(); ++i) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
const FloatInfo& fi = mFloats[i];
|
2016-09-15 06:29:14 +03:00
|
|
|
|
fprintf_stderr(out,
|
|
|
|
|
"Float %u: frame=%p rect={%d,%d,%d,%d} BEnd={l:%d, r:%d}\n",
|
2014-01-27 02:07:02 +04:00
|
|
|
|
i, static_cast<void*>(fi.mFrame), fi.LineLeft(), fi.BStart(),
|
2015-03-22 12:44:48 +03:00
|
|
|
|
fi.ISize(), fi.BSize(), fi.mLeftBEnd, fi.mRightBEnd);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-09-07 05:20:17 +03:00
|
|
|
|
nscoord nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
|
2012-08-22 19:56:38 +04:00
|
|
|
|
uint32_t aFlags) const {
|
2010-08-06 08:59:19 +04:00
|
|
|
|
if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
|
2010-08-06 08:59:19 +04:00
|
|
|
|
return nscoord_MAX;
|
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
if (!HasAnyFloats()) {
|
2014-10-22 02:16:12 +04:00
|
|
|
|
return aBCoord;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
nscoord blockEnd = aBCoord + mBlockStart;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
|
|
|
|
const FloatInfo& tail = mFloats[mFloats.Length() - 1];
|
|
|
|
|
switch (aBreakType) {
|
2016-09-07 05:20:17 +03:00
|
|
|
|
case StyleClear::Both:
|
2014-10-22 02:16:12 +04:00
|
|
|
|
blockEnd = std::max(blockEnd, tail.mLeftBEnd);
|
|
|
|
|
blockEnd = std::max(blockEnd, tail.mRightBEnd);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
break;
|
2016-09-07 05:20:17 +03:00
|
|
|
|
case StyleClear::Left:
|
2015-03-22 12:44:48 +03:00
|
|
|
|
blockEnd = std::max(blockEnd, tail.mLeftBEnd);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
break;
|
2016-09-07 05:20:17 +03:00
|
|
|
|
case StyleClear::Right:
|
2015-03-22 12:44:48 +03:00
|
|
|
|
blockEnd = std::max(blockEnd, tail.mRightBEnd);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Do nothing
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
blockEnd -= mBlockStart;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2014-10-22 02:16:12 +04:00
|
|
|
|
return blockEnd;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 05:20:17 +03:00
|
|
|
|
bool nsFloatManager::ClearContinues(StyleClear aBreakType) const {
|
2010-08-06 08:59:19 +04:00
|
|
|
|
return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
|
2016-09-07 05:20:17 +03:00
|
|
|
|
(aBreakType == StyleClear::Both || aBreakType == StyleClear::Left)) ||
|
2010-08-06 08:59:19 +04:00
|
|
|
|
((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
|
2016-09-07 05:20:17 +03:00
|
|
|
|
(aBreakType == StyleClear::Both || aBreakType == StyleClear::Right));
|
2009-08-31 22:25:36 +04:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-19 10:06:10 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// ShapeInfo is an abstract class for implementing all the shapes in CSS
|
|
|
|
|
// Shapes Module. A subclass needs to override all the methods to adjust
|
|
|
|
|
// the flow area with respect to its shape.
|
|
|
|
|
//
|
|
|
|
|
class nsFloatManager::ShapeInfo {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~ShapeInfo() {}
|
|
|
|
|
|
|
|
|
|
virtual nscoord LineLeft(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const = 0;
|
|
|
|
|
virtual nscoord LineRight(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const = 0;
|
|
|
|
|
virtual nscoord BStart() const = 0;
|
|
|
|
|
virtual nscoord BEnd() const = 0;
|
|
|
|
|
virtual bool IsEmpty() const = 0;
|
|
|
|
|
|
2018-05-30 20:03:45 +03:00
|
|
|
|
// Does this shape possibly get inline narrower in the BStart() to BEnd()
|
|
|
|
|
// span when proceeding in the block direction? This is false for unrounded
|
|
|
|
|
// rectangles that span all the way to BEnd(), but could be true for other
|
|
|
|
|
// shapes. Note that we don't care if the BEnd() falls short of the margin
|
|
|
|
|
// rect -- the ShapeInfo can only affect float behavior in the span between
|
|
|
|
|
// BStart() and BEnd().
|
|
|
|
|
virtual bool MayNarrowInBlockDirection() const = 0;
|
|
|
|
|
|
2017-12-19 10:06:10 +03:00
|
|
|
|
// Translate the current origin by the specified offsets.
|
|
|
|
|
virtual void Translate(nscoord aLineLeft, nscoord aBlockStart) = 0;
|
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static LogicalRect ComputeShapeBoxRect(const StyleShapeSource& aShapeOutside,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
nsIFrame* const aFrame,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
|
|
|
|
WritingMode aWM);
|
2017-12-19 10:06:10 +03:00
|
|
|
|
|
|
|
|
|
// Convert the LogicalRect to the special logical coordinate space used
|
|
|
|
|
// in float manager.
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static nsRect ConvertToFloatLogical(const LogicalRect& aRect, WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
return nsRect(aRect.LineLeft(aWM, aContainerSize), aRect.BStart(aWM),
|
|
|
|
|
aRect.ISize(aWM), aRect.BSize(aWM));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreateShapeBox(nsIFrame* const aFrame,
|
2018-02-27 01:47:31 +03:00
|
|
|
|
nscoord aShapeMargin,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
|
|
|
|
WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreateBasicShape(
|
2018-11-02 21:19:06 +03:00
|
|
|
|
const StyleBasicShape& aBasicShape, nscoord aShapeMargin,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
nsIFrame* const aFrame, const LogicalRect& aShapeBoxRect,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
const LogicalRect& aMarginRect, WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreateInset(const StyleBasicShape& aBasicShape,
|
2018-04-13 21:29:14 +03:00
|
|
|
|
nscoord aShapeMargin,
|
|
|
|
|
nsIFrame* aFrame,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
|
|
|
|
WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreateCircleOrEllipse(
|
2018-11-02 21:19:06 +03:00
|
|
|
|
const StyleBasicShape& aBasicShape, nscoord aShapeMargin,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
nsIFrame* const aFrame, const LogicalRect& aShapeBoxRect, WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreatePolygon(const StyleBasicShape& aBasicShape,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
nscoord aShapeMargin,
|
|
|
|
|
nsIFrame* const aFrame,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
WritingMode aWM,
|
2017-12-19 10:06:10 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
static UniquePtr<ShapeInfo> CreateImageShape(const nsStyleImage& aShapeImage,
|
|
|
|
|
float aShapeImageThreshold,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
nscoord aShapeMargin,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
nsIFrame* const aFrame,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2017-12-19 10:06:10 +03:00
|
|
|
|
protected:
|
|
|
|
|
// Compute the minimum line-axis difference between the bounding shape
|
|
|
|
|
// box and its rounded corner within the given band (block-axis region).
|
|
|
|
|
// This is used as a helper function to compute the LineRight() and
|
|
|
|
|
// LineLeft(). See the picture in the implementation for an example.
|
|
|
|
|
// RadiusL and RadiusB stand for radius on the line-axis and block-axis.
|
|
|
|
|
//
|
|
|
|
|
// Returns radius-x diff on the line-axis, or 0 if there's no rounded
|
|
|
|
|
// corner within the given band.
|
|
|
|
|
static nscoord ComputeEllipseLineInterceptDiff(
|
|
|
|
|
const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
|
|
|
|
|
const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
|
|
|
|
|
const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
|
|
|
|
|
const nscoord aBandBStart, const nscoord aBandBEnd);
|
|
|
|
|
|
|
|
|
|
static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX,
|
|
|
|
|
const nscoord aRadiusY);
|
|
|
|
|
|
|
|
|
|
// Convert the physical point to the special logical coordinate space
|
|
|
|
|
// used in float manager.
|
|
|
|
|
static nsPoint ConvertToFloatLogical(const nsPoint& aPoint, WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize);
|
|
|
|
|
|
|
|
|
|
// Convert the half corner radii (nscoord[8]) to the special logical
|
|
|
|
|
// coordinate space used in float manager.
|
2017-12-19 10:22:36 +03:00
|
|
|
|
static UniquePtr<nscoord[]> ConvertToFloatLogical(const nscoord aRadii[8],
|
|
|
|
|
WritingMode aWM);
|
2018-04-12 00:05:06 +03:00
|
|
|
|
|
|
|
|
|
// Some ShapeInfo subclasses may define their float areas in intervals.
|
|
|
|
|
// Each interval is a rectangle that is one device pixel deep in the block
|
|
|
|
|
// axis. The values are stored as block edges in the y coordinates,
|
|
|
|
|
// and inline edges as the x coordinates. Interval arrays should be sorted
|
|
|
|
|
// on increasing y values. This function uses a binary search to find the
|
|
|
|
|
// first interval that contains aTargetY. If no such interval exists, this
|
|
|
|
|
// function returns aIntervals.Length().
|
|
|
|
|
static size_t MinIntervalIndexContainingY(const nsTArray<nsRect>& aIntervals,
|
|
|
|
|
const nscoord aTargetY);
|
2018-04-28 04:10:51 +03:00
|
|
|
|
|
2018-04-19 22:17:44 +03:00
|
|
|
|
// This interval function is designed to handle the arguments to ::LineLeft()
|
|
|
|
|
// and LineRight() and interpret them for the supplied aIntervals.
|
|
|
|
|
static nscoord LineEdge(const nsTArray<nsRect>& aIntervals,
|
|
|
|
|
const nscoord aBStart, const nscoord aBEnd,
|
|
|
|
|
bool aIsLineLeft);
|
|
|
|
|
|
2018-04-28 04:10:51 +03:00
|
|
|
|
// These types, constants, and functions are useful for ShapeInfos that
|
|
|
|
|
// allocate a distance field. Efficient distance field calculations use
|
|
|
|
|
// integer values that are 5X the Euclidean distance. MAX_MARGIN_5X is the
|
|
|
|
|
// largest possible margin that we can calculate (in 5X integer dev pixels),
|
|
|
|
|
// given these constraints.
|
|
|
|
|
typedef uint16_t dfType;
|
|
|
|
|
static const dfType MAX_CHAMFER_VALUE;
|
|
|
|
|
static const dfType MAX_MARGIN;
|
|
|
|
|
static const dfType MAX_MARGIN_5X;
|
|
|
|
|
|
|
|
|
|
// This function returns a typed, overflow-safe value of aShapeMargin in
|
|
|
|
|
// 5X integer dev pixels.
|
|
|
|
|
static dfType CalcUsedShapeMargin5X(nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel);
|
2017-12-19 10:06:10 +03:00
|
|
|
|
};
|
2017-03-13 07:03:40 +03:00
|
|
|
|
|
2018-04-28 04:10:51 +03:00
|
|
|
|
const nsFloatManager::ShapeInfo::dfType
|
|
|
|
|
nsFloatManager::ShapeInfo::MAX_CHAMFER_VALUE = 11;
|
|
|
|
|
|
|
|
|
|
const nsFloatManager::ShapeInfo::dfType nsFloatManager::ShapeInfo::MAX_MARGIN =
|
|
|
|
|
(std::numeric_limits<dfType>::max() - MAX_CHAMFER_VALUE) / 5;
|
|
|
|
|
|
|
|
|
|
const nsFloatManager::ShapeInfo::dfType
|
|
|
|
|
nsFloatManager::ShapeInfo::MAX_MARGIN_5X = MAX_MARGIN * 5;
|
|
|
|
|
|
2018-04-24 22:31:15 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// EllipseShapeInfo
|
|
|
|
|
//
|
|
|
|
|
// Implements shape-outside: circle() and shape-outside: ellipse().
|
|
|
|
|
//
|
|
|
|
|
class nsFloatManager::EllipseShapeInfo final
|
|
|
|
|
: public nsFloatManager::ShapeInfo {
|
|
|
|
|
public:
|
2018-02-27 00:13:03 +03:00
|
|
|
|
// Construct the float area using math to calculate the shape boundary.
|
|
|
|
|
// This is the fast path and should be used when shape-margin is negligible,
|
|
|
|
|
// or when the two values of aRadii are roughly equal. Those two conditions
|
|
|
|
|
// are defined by ShapeMarginIsNegligible() and RadiiAreRoughlyEqual(). In
|
|
|
|
|
// those cases, we can conveniently represent the entire float area using
|
|
|
|
|
// an ellipse.
|
2018-04-24 22:31:15 +03:00
|
|
|
|
EllipseShapeInfo(const nsPoint& aCenter, const nsSize& aRadii,
|
2018-02-27 00:13:03 +03:00
|
|
|
|
nscoord aShapeMargin);
|
2018-04-24 22:31:15 +03:00
|
|
|
|
|
2018-02-27 00:13:03 +03:00
|
|
|
|
// Construct the float area using rasterization to calculate the shape
|
|
|
|
|
// boundary. This constructor accounts for the fact that applying
|
|
|
|
|
// 'shape-margin' to an ellipse produces a shape that is not mathematically
|
|
|
|
|
// representable as an ellipse.
|
|
|
|
|
EllipseShapeInfo(const nsPoint& aCenter, const nsSize& aRadii,
|
|
|
|
|
nscoord aShapeMargin, int32_t aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
static bool ShapeMarginIsNegligible(nscoord aShapeMargin) {
|
|
|
|
|
// For now, only return true for a shape-margin of 0. In the future, if
|
|
|
|
|
// we want to enable use of the fast-path constructor more often, this
|
|
|
|
|
// limit could be increased;
|
|
|
|
|
static const nscoord SHAPE_MARGIN_NEGLIGIBLE_MAX(0);
|
|
|
|
|
return aShapeMargin <= SHAPE_MARGIN_NEGLIGIBLE_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool RadiiAreRoughlyEqual(const nsSize& aRadii) {
|
|
|
|
|
// For now, only return true when we are exactly equal. In the future, if
|
|
|
|
|
// we want to enable use of the fast-path constructor more often, this
|
|
|
|
|
// could be generalized to allow radii that are in some close proportion
|
|
|
|
|
// to each other.
|
|
|
|
|
return aRadii.width == aRadii.height;
|
|
|
|
|
}
|
2018-04-12 01:18:32 +03:00
|
|
|
|
nscoord LineEdge(const nscoord aBStart, const nscoord aBEnd,
|
|
|
|
|
bool aLeft) const;
|
2018-04-24 22:31:15 +03:00
|
|
|
|
nscoord LineLeft(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord LineRight(const nscoord aBStart, const nscoord aBEnd) const override;
|
2018-02-27 00:13:03 +03:00
|
|
|
|
nscoord BStart() const override {
|
|
|
|
|
return mCenter.y - mRadii.height - mShapeMargin;
|
|
|
|
|
}
|
|
|
|
|
nscoord BEnd() const override {
|
|
|
|
|
return mCenter.y + mRadii.height + mShapeMargin;
|
|
|
|
|
}
|
|
|
|
|
bool IsEmpty() const override {
|
2018-05-19 03:13:22 +03:00
|
|
|
|
// An EllipseShapeInfo is never empty, because an ellipse or circle with
|
|
|
|
|
// a zero radius acts like a point, and an ellipse with one zero radius
|
|
|
|
|
// acts like a line.
|
|
|
|
|
return false;
|
2018-02-27 00:13:03 +03:00
|
|
|
|
}
|
2018-05-30 20:03:45 +03:00
|
|
|
|
bool MayNarrowInBlockDirection() const override { return true; }
|
2018-04-17 21:39:50 +03:00
|
|
|
|
|
2018-04-24 22:31:15 +03:00
|
|
|
|
void Translate(nscoord aLineLeft, nscoord aBlockStart) override {
|
|
|
|
|
mCenter.MoveBy(aLineLeft, aBlockStart);
|
2018-02-27 00:13:03 +03:00
|
|
|
|
|
|
|
|
|
for (nsRect& interval : mIntervals) {
|
|
|
|
|
interval.MoveBy(aLineLeft, aBlockStart);
|
|
|
|
|
}
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-24 22:31:15 +03:00
|
|
|
|
private:
|
|
|
|
|
// The position of the center of the ellipse. The coordinate space is the
|
|
|
|
|
// same as FloatInfo::mRect.
|
|
|
|
|
nsPoint mCenter;
|
|
|
|
|
// The radii of the ellipse in app units. The width and height represent
|
|
|
|
|
// the line-axis and block-axis radii of the ellipse.
|
|
|
|
|
nsSize mRadii;
|
2018-02-27 00:13:03 +03:00
|
|
|
|
// The shape-margin of the ellipse in app units. If this value is greater
|
|
|
|
|
// than zero, then we calculate the bounds of the ellipse + margin using
|
|
|
|
|
// numerical methods and store the values in mIntervals.
|
|
|
|
|
nscoord mShapeMargin;
|
|
|
|
|
|
|
|
|
|
// An interval is slice of the float area defined by this EllipseShapeInfo.
|
|
|
|
|
// Each interval is a rectangle that is one pixel deep in the block
|
|
|
|
|
// axis. The values are stored as block edges in the y coordinates,
|
|
|
|
|
// and inline edges as the x coordinates.
|
|
|
|
|
|
|
|
|
|
// The intervals are stored in ascending order on y.
|
|
|
|
|
nsTArray<nsRect> mIntervals;
|
2018-04-24 22:31:15 +03:00
|
|
|
|
};
|
|
|
|
|
|
2018-02-27 00:13:03 +03:00
|
|
|
|
nsFloatManager::EllipseShapeInfo::EllipseShapeInfo(const nsPoint& aCenter,
|
|
|
|
|
const nsSize& aRadii,
|
|
|
|
|
nscoord aShapeMargin)
|
|
|
|
|
: mCenter(aCenter),
|
|
|
|
|
mRadii(aRadii),
|
|
|
|
|
mShapeMargin(
|
|
|
|
|
0) // We intentionally ignore the value of aShapeMargin here.
|
|
|
|
|
{
|
|
|
|
|
MOZ_ASSERT(
|
|
|
|
|
RadiiAreRoughlyEqual(aRadii) || ShapeMarginIsNegligible(aShapeMargin),
|
|
|
|
|
"This constructor should only be called when margin is "
|
|
|
|
|
"negligible or radii are roughly equal.");
|
|
|
|
|
|
|
|
|
|
// We add aShapeMargin into the radii, and we earlier stored a mShapeMargin
|
|
|
|
|
// of zero.
|
|
|
|
|
mRadii.width += aShapeMargin;
|
|
|
|
|
mRadii.height += aShapeMargin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsFloatManager::EllipseShapeInfo::EllipseShapeInfo(const nsPoint& aCenter,
|
|
|
|
|
const nsSize& aRadii,
|
|
|
|
|
nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel)
|
|
|
|
|
: mCenter(aCenter), mRadii(aRadii), mShapeMargin(aShapeMargin) {
|
|
|
|
|
if (RadiiAreRoughlyEqual(aRadii) || ShapeMarginIsNegligible(aShapeMargin)) {
|
|
|
|
|
// Mimic the behavior of the simple constructor, by adding aShapeMargin
|
|
|
|
|
// into the radii, and then storing mShapeMargin of zero.
|
|
|
|
|
mRadii.width += mShapeMargin;
|
|
|
|
|
mRadii.height += mShapeMargin;
|
|
|
|
|
mShapeMargin = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 01:18:32 +03:00
|
|
|
|
// We have to calculate a distance field from the ellipse edge, then build
|
|
|
|
|
// intervals based on pixels with less than aShapeMargin distance to an
|
|
|
|
|
// edge pixel.
|
|
|
|
|
|
|
|
|
|
// mCenter and mRadii have already been translated into logical coordinates.
|
|
|
|
|
// x = inline, y = block. Due to symmetry, we only need to calculate the
|
|
|
|
|
// distance field for one quadrant of the ellipse. We choose the positive-x,
|
|
|
|
|
// positive-y quadrant (the lower right quadrant in horizontal-tb writing
|
|
|
|
|
// mode). We choose this quadrant because it allows us to traverse our
|
|
|
|
|
// distance field in memory order, which is more cache efficient.
|
|
|
|
|
// When we apply these intervals in LineLeft() and LineRight(), we
|
|
|
|
|
// account for block ranges that hit other quadrants, or hit multiple
|
|
|
|
|
// quadrants.
|
|
|
|
|
|
|
|
|
|
// Given this setup, computing the distance field is a one-pass O(n)
|
|
|
|
|
// operation that runs from block top-to-bottom, inline left-to-right. We
|
|
|
|
|
// use a chamfer 5-7-11 5x5 matrix to compute minimum distance to an edge
|
|
|
|
|
// pixel. This integer math computation is reasonably close to the true
|
|
|
|
|
// Euclidean distance. The distances will be approximately 5x the true
|
|
|
|
|
// distance, quantized in integer units. The 5x is factored away in the
|
|
|
|
|
// comparison which builds the intervals.
|
2018-04-28 04:10:51 +03:00
|
|
|
|
dfType usedMargin5X =
|
|
|
|
|
CalcUsedShapeMargin5X(aShapeMargin, aAppUnitsPerDevPixel);
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
2018-05-08 21:11:47 +03:00
|
|
|
|
// Calculate the bounds of one quadrant of the ellipse, in integer device
|
|
|
|
|
// pixels. These bounds are equal to the rectangle defined by the radii,
|
|
|
|
|
// plus the shape-margin value in both dimensions.
|
2018-04-12 01:18:32 +03:00
|
|
|
|
const LayoutDeviceIntSize bounds =
|
2018-05-08 21:11:47 +03:00
|
|
|
|
LayoutDevicePixel::FromAppUnitsRounded(mRadii, aAppUnitsPerDevPixel) +
|
2018-04-27 23:10:08 +03:00
|
|
|
|
LayoutDeviceIntSize(usedMargin5X / 5, usedMargin5X / 5);
|
|
|
|
|
|
2018-04-12 01:18:32 +03:00
|
|
|
|
// Since our distance field is computed with a 5x5 neighborhood, but only
|
|
|
|
|
// looks in the negative block and negative inline directions, it is
|
|
|
|
|
// effectively a 3x3 neighborhood. We need to expand our distance field
|
|
|
|
|
// outwards by a further 2 pixels in both axes (on the minimum block edge
|
|
|
|
|
// and the minimum inline edge). We call this edge area the expanded region.
|
2018-04-20 23:43:36 +03:00
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
static const uint32_t iExpand = 2;
|
|
|
|
|
static const uint32_t bExpand = 2;
|
2018-04-28 04:10:51 +03:00
|
|
|
|
|
|
|
|
|
// Clamp the size of our distance field sizes to prevent multiplication
|
|
|
|
|
// overflow.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
static const uint32_t DF_SIDE_MAX =
|
2018-04-28 04:10:51 +03:00
|
|
|
|
floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
|
2018-04-30 22:25:13 +03:00
|
|
|
|
const uint32_t iSize = std::min(bounds.width + iExpand, DF_SIDE_MAX);
|
|
|
|
|
const uint32_t bSize = std::min(bounds.height + bExpand, DF_SIDE_MAX);
|
2018-04-12 01:18:32 +03:00
|
|
|
|
auto df = MakeUniqueFallible<dfType[]>(iSize * bSize);
|
|
|
|
|
if (!df) {
|
|
|
|
|
// Without a distance field, we can't reason about the float area.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Single pass setting distance field, in positive block direction, three
|
|
|
|
|
// cases:
|
|
|
|
|
// 1) Expanded region pixel: set to MAX_MARGIN_5X.
|
|
|
|
|
// 2) Pixel within the ellipse: set to 0.
|
|
|
|
|
// 3) Other pixel: set to minimum neighborhood distance value, computed
|
|
|
|
|
// with 5-7-11 chamfer.
|
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t b = 0; b < bSize; ++b) {
|
2018-04-12 01:18:32 +03:00
|
|
|
|
bool bIsInExpandedRegion(b < bExpand);
|
|
|
|
|
nscoord bInAppUnits = (b - bExpand) * aAppUnitsPerDevPixel;
|
|
|
|
|
bool bIsMoreThanEllipseBEnd(bInAppUnits > mRadii.height);
|
|
|
|
|
|
|
|
|
|
// Find the i intercept of the ellipse edge for this block row, and
|
|
|
|
|
// adjust it to compensate for the expansion of the inline dimension.
|
|
|
|
|
// If we're in the expanded region, or if we're using a b that's more
|
2018-05-08 21:11:47 +03:00
|
|
|
|
// than the bEnd of the ellipse, the intercept is nscoord_MIN.
|
2018-05-19 03:13:22 +03:00
|
|
|
|
// We have one other special case to consider: when the ellipse has no
|
|
|
|
|
// height. In that case we treat the bInAppUnits == 0 case as
|
|
|
|
|
// intercepting at the width of the ellipse. All other cases solve
|
|
|
|
|
// the intersection mathematically.
|
|
|
|
|
const int32_t iIntercept =
|
|
|
|
|
(bIsInExpandedRegion || bIsMoreThanEllipseBEnd)
|
|
|
|
|
? nscoord_MIN
|
2018-04-12 01:18:32 +03:00
|
|
|
|
: iExpand + NSAppUnitsToIntPixels(
|
2018-05-19 03:13:22 +03:00
|
|
|
|
(!!mRadii.height || bInAppUnits)
|
|
|
|
|
? XInterceptAtY(bInAppUnits, mRadii.width,
|
|
|
|
|
mRadii.height)
|
|
|
|
|
: mRadii.width,
|
2018-04-12 01:18:32 +03:00
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Set iMax in preparation for this block row.
|
|
|
|
|
int32_t iMax = iIntercept;
|
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t i = 0; i < iSize; ++i) {
|
|
|
|
|
const uint32_t index = i + b * iSize;
|
|
|
|
|
MOZ_ASSERT(index < (iSize * bSize),
|
2018-04-20 23:43:36 +03:00
|
|
|
|
"Our distance field index should be in-bounds.");
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
|
|
|
|
// Handle our three cases, in order.
|
|
|
|
|
if (i < iExpand || bIsInExpandedRegion) {
|
|
|
|
|
// Case 1: Expanded reqion pixel.
|
|
|
|
|
df[index] = MAX_MARGIN_5X;
|
2018-04-30 22:25:13 +03:00
|
|
|
|
} else if ((int32_t)i <= iIntercept) {
|
2018-05-19 03:13:22 +03:00
|
|
|
|
// Case 2: Pixel within the ellipse, or just outside the edge of it.
|
|
|
|
|
// Having a positive height indicates that there's an area we can
|
|
|
|
|
// be inside of.
|
|
|
|
|
df[index] = (!!mRadii.height) ? 0 : 5;
|
2018-04-12 01:18:32 +03:00
|
|
|
|
} else {
|
|
|
|
|
// Case 3: Other pixel.
|
|
|
|
|
|
|
|
|
|
// Backward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | |11| |
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// |11| 7| 5|
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | | 5| X|
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of the values of all of the numbered
|
|
|
|
|
// neighbors summed with the value in that chamfer cell.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
MOZ_ASSERT(index - iSize - 2 < (iSize * bSize) &&
|
|
|
|
|
index - (iSize * 2) - 1 < (iSize * bSize),
|
2018-04-20 23:43:36 +03:00
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(df[index - 1] + 5,
|
|
|
|
|
std::min<dfType>(df[index - iSize] + 5,
|
|
|
|
|
std::min<dfType>(df[index - iSize - 1] + 7,
|
2018-04-12 01:18:32 +03:00
|
|
|
|
std::min<dfType>(df[index - iSize - 2] + 11,
|
2018-12-05 21:44:05 +03:00
|
|
|
|
df[index - (iSize * 2) - 1] + 11))));
|
|
|
|
|
// clang-format on
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
|
|
|
|
// Check the df value and see if it's less than or equal to the
|
|
|
|
|
// usedMargin5X value.
|
|
|
|
|
if (df[index] <= usedMargin5X) {
|
2018-04-30 22:25:13 +03:00
|
|
|
|
MOZ_ASSERT(iMax < (int32_t)i);
|
2018-04-12 01:18:32 +03:00
|
|
|
|
iMax = i;
|
2018-05-08 21:11:47 +03:00
|
|
|
|
} else {
|
|
|
|
|
// Since we're computing the bottom-right quadrant, there's no way
|
|
|
|
|
// for a later i value in this row to be within the usedMargin5X
|
|
|
|
|
// value. Likewise, every row beyond us will encounter this
|
|
|
|
|
// condition with an i value less than or equal to our i value now.
|
|
|
|
|
// Since our chamfer only looks upward and leftward, we can stop
|
|
|
|
|
// calculating for the rest of the row, because the distance field
|
|
|
|
|
// values there will never be looked at in a later row's chamfer
|
|
|
|
|
// calculation.
|
|
|
|
|
break;
|
2018-04-12 01:18:32 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-08 21:11:47 +03:00
|
|
|
|
// It's very likely, though not guaranteed that we will find an pixel
|
|
|
|
|
// within the shape-margin distance for each block row. This may not
|
|
|
|
|
// always be true due to rounding errors.
|
2018-04-12 01:18:32 +03:00
|
|
|
|
if (iMax > nscoord_MIN) {
|
|
|
|
|
// Origin for this interval is at the center of the ellipse, adjusted
|
|
|
|
|
// in the positive block direction by bInAppUnits.
|
|
|
|
|
nsPoint origin(aCenter.x, aCenter.y + bInAppUnits);
|
|
|
|
|
// Size is an inline iMax plus 1 (to account for the whole pixel) dev
|
|
|
|
|
// pixels, by 1 block dev pixel. We convert this to app units.
|
|
|
|
|
nsSize size((iMax - iExpand + 1) * aAppUnitsPerDevPixel,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
mIntervals.AppendElement(nsRect(origin, size));
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-27 00:13:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 01:18:32 +03:00
|
|
|
|
nscoord nsFloatManager::EllipseShapeInfo::LineEdge(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd,
|
|
|
|
|
bool aIsLineLeft) const {
|
|
|
|
|
// If no mShapeMargin, just compute the edge using math.
|
2018-02-27 00:13:03 +03:00
|
|
|
|
if (mShapeMargin == 0) {
|
|
|
|
|
nscoord lineDiff = ComputeEllipseLineInterceptDiff(
|
|
|
|
|
BStart(), BEnd(), mRadii.width, mRadii.height, mRadii.width,
|
|
|
|
|
mRadii.height, aBStart, aBEnd);
|
2018-04-12 01:18:32 +03:00
|
|
|
|
return mCenter.x + (aIsLineLeft ? (-mRadii.width + lineDiff)
|
|
|
|
|
: (mRadii.width - lineDiff));
|
2018-02-27 00:13:03 +03:00
|
|
|
|
}
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
|
|
|
|
// We are checking against our intervals. Make sure we have some.
|
|
|
|
|
if (mIntervals.IsEmpty()) {
|
|
|
|
|
NS_WARNING("With mShapeMargin > 0, we can't proceed without intervals.");
|
2018-05-08 00:02:03 +03:00
|
|
|
|
return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
|
2018-04-12 01:18:32 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Map aBStart and aBEnd into our intervals. Our intervals are calculated
|
|
|
|
|
// for the lower-right quadrant (in terms of horizontal-tb writing mode).
|
|
|
|
|
// If aBStart and aBEnd span the center of the ellipse, then we know we
|
|
|
|
|
// are at the maximum displacement from the center.
|
|
|
|
|
bool bStartIsAboveCenter = (aBStart < mCenter.y);
|
|
|
|
|
bool bEndIsBelowOrAtCenter = (aBEnd >= mCenter.y);
|
|
|
|
|
if (bStartIsAboveCenter && bEndIsBelowOrAtCenter) {
|
|
|
|
|
return mCenter.x + (aIsLineLeft ? (-mRadii.width - mShapeMargin)
|
|
|
|
|
: (mRadii.width + mShapeMargin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// aBStart and aBEnd don't span the center. Since the intervals are
|
|
|
|
|
// strictly wider approaching the center (the start of the mIntervals
|
|
|
|
|
// array), we only need to find the interval at the block value closest to
|
|
|
|
|
// the center. We find the min of aBStart, aBEnd, and their reflections --
|
|
|
|
|
// whichever two of them are within the lower-right quadrant. When we
|
|
|
|
|
// reflect from the upper-right quadrant to the lower-right, we have to
|
|
|
|
|
// subtract 1 from the reflection, to account that block values are always
|
|
|
|
|
// addressed from the leading block edge.
|
|
|
|
|
|
|
|
|
|
// The key example is when we check with aBStart == aBEnd at the top of the
|
|
|
|
|
// intervals. That block line would be considered contained in the
|
|
|
|
|
// intervals (though it has no height), but its reflection would not be
|
|
|
|
|
// within the intervals unless we subtract 1.
|
|
|
|
|
nscoord bSmallestWithinIntervals = std::min(
|
|
|
|
|
bStartIsAboveCenter ? aBStart + (mCenter.y - aBStart) * 2 - 1 : aBStart,
|
|
|
|
|
bEndIsBelowOrAtCenter ? aBEnd : aBEnd + (mCenter.y - aBEnd) * 2 - 1);
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(bSmallestWithinIntervals >= mCenter.y &&
|
|
|
|
|
bSmallestWithinIntervals < BEnd(),
|
2018-05-08 00:02:03 +03:00
|
|
|
|
"We should have a block value within the float area.");
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
|
|
|
|
size_t index =
|
|
|
|
|
MinIntervalIndexContainingY(mIntervals, bSmallestWithinIntervals);
|
2018-05-08 00:02:03 +03:00
|
|
|
|
if (index >= mIntervals.Length()) {
|
|
|
|
|
// This indicates that our intervals don't cover the block value
|
|
|
|
|
// bSmallestWithinIntervals. This can happen when rounding error in the
|
|
|
|
|
// distance field calculation resulted in the last block pixel row not
|
|
|
|
|
// contributing to the float area. As long as we're within one block pixel
|
|
|
|
|
// past the last interval, this is an expected outcome.
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
nscoord onePixelPastLastInterval =
|
|
|
|
|
mIntervals[mIntervals.Length() - 1].YMost() +
|
|
|
|
|
mIntervals[mIntervals.Length() - 1].Height();
|
|
|
|
|
NS_WARNING_ASSERTION(bSmallestWithinIntervals < onePixelPastLastInterval,
|
|
|
|
|
"We should have found a matching interval for this "
|
|
|
|
|
"block value.");
|
|
|
|
|
#endif
|
|
|
|
|
return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
|
|
|
|
|
}
|
2018-04-12 01:18:32 +03:00
|
|
|
|
|
|
|
|
|
// The interval is storing the line right value. If aIsLineLeft is true,
|
|
|
|
|
// return the line right value reflected about the center. Since this is
|
|
|
|
|
// an inline measurement, it's just checking the distance to an edge, and
|
|
|
|
|
// not a collision with a specific pixel. For that reason, we don't need
|
|
|
|
|
// to subtract 1 from the reflection, as we did with the block reflection.
|
|
|
|
|
nscoord iLineRight = mIntervals[index].XMost();
|
|
|
|
|
return aIsLineLeft ? iLineRight - (iLineRight - mCenter.x) * 2 : iLineRight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::EllipseShapeInfo::LineLeft(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
|
|
|
|
return LineEdge(aBStart, aBEnd, true);
|
2018-04-24 22:31:15 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::EllipseShapeInfo::LineRight(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
2018-04-12 01:18:32 +03:00
|
|
|
|
return LineEdge(aBStart, aBEnd, false);
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:59:11 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// RoundedBoxShapeInfo
|
|
|
|
|
//
|
|
|
|
|
// Implements shape-outside: <shape-box> and shape-outside: inset().
|
|
|
|
|
//
|
|
|
|
|
class nsFloatManager::RoundedBoxShapeInfo final
|
|
|
|
|
: public nsFloatManager::ShapeInfo {
|
|
|
|
|
public:
|
|
|
|
|
RoundedBoxShapeInfo(const nsRect& aRect, UniquePtr<nscoord[]> aRadii)
|
2018-05-30 22:15:35 +03:00
|
|
|
|
: mRect(aRect), mRadii(std::move(aRadii)), mShapeMargin(0) {}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-04-13 21:29:14 +03:00
|
|
|
|
RoundedBoxShapeInfo(const nsRect& aRect, UniquePtr<nscoord[]> aRadii,
|
|
|
|
|
nscoord aShapeMargin, int32_t aAppUnitsPerDevPixel);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-04-17 21:59:11 +03:00
|
|
|
|
nscoord LineLeft(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord LineRight(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord BStart() const override { return mRect.y; }
|
|
|
|
|
nscoord BEnd() const override { return mRect.YMost(); }
|
2018-05-19 03:38:41 +03:00
|
|
|
|
bool IsEmpty() const override {
|
|
|
|
|
// A RoundedBoxShapeInfo is never empty, because if it is collapsed to
|
|
|
|
|
// zero area, it acts like a point. If it is collapsed further, to become
|
|
|
|
|
// inside-out, it acts like a rect in the same shape as the inside-out
|
|
|
|
|
// rect.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-05-30 20:03:45 +03:00
|
|
|
|
bool MayNarrowInBlockDirection() const override {
|
|
|
|
|
// Only possible to narrow if there are non-null mRadii.
|
|
|
|
|
return !!mRadii;
|
|
|
|
|
}
|
2018-04-17 21:59:11 +03:00
|
|
|
|
|
|
|
|
|
void Translate(nscoord aLineLeft, nscoord aBlockStart) override {
|
|
|
|
|
mRect.MoveBy(aLineLeft, aBlockStart);
|
2018-04-17 21:39:50 +03:00
|
|
|
|
|
|
|
|
|
if (mShapeMargin > 0) {
|
|
|
|
|
MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
|
|
|
|
|
mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
|
|
|
|
|
"If we have positive shape-margin, we should have corners.");
|
|
|
|
|
mLogicalTopLeftCorner->Translate(aLineLeft, aBlockStart);
|
|
|
|
|
mLogicalTopRightCorner->Translate(aLineLeft, aBlockStart);
|
|
|
|
|
mLogicalBottomLeftCorner->Translate(aLineLeft, aBlockStart);
|
|
|
|
|
mLogicalBottomRightCorner->Translate(aLineLeft, aBlockStart);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool EachCornerHasBalancedRadii(const nscoord* aRadii) {
|
|
|
|
|
return (aRadii[eCornerTopLeftX] == aRadii[eCornerTopLeftY] &&
|
|
|
|
|
aRadii[eCornerTopRightX] == aRadii[eCornerTopRightY] &&
|
|
|
|
|
aRadii[eCornerBottomLeftX] == aRadii[eCornerBottomLeftY] &&
|
|
|
|
|
aRadii[eCornerBottomRightX] == aRadii[eCornerBottomRightY]);
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// The rect of the rounded box shape in the float manager's coordinate
|
|
|
|
|
// space.
|
|
|
|
|
nsRect mRect;
|
|
|
|
|
// The half corner radii of the reference box. It's an nscoord[8] array
|
|
|
|
|
// in the float manager's coordinate space. If there are no radii, it's
|
|
|
|
|
// nullptr.
|
2018-04-13 21:29:14 +03:00
|
|
|
|
const UniquePtr<nscoord[]> mRadii;
|
|
|
|
|
|
2018-04-17 21:39:50 +03:00
|
|
|
|
// A shape-margin value extends the boundaries of the float area. When our
|
|
|
|
|
// first constructor is used, it is for the creation of rounded boxes that
|
|
|
|
|
// can ignore shape-margin -- either because it was specified as zero or
|
|
|
|
|
// because the box shape and radii can be inflated to account for it. When
|
|
|
|
|
// our second constructor is used, we store the shape-margin value here.
|
2018-04-13 21:29:14 +03:00
|
|
|
|
const nscoord mShapeMargin;
|
2018-04-17 21:39:50 +03:00
|
|
|
|
|
|
|
|
|
// If our second constructor is called (which implies mShapeMargin > 0),
|
|
|
|
|
// we will construct EllipseShapeInfo objects for each corner. We use the
|
|
|
|
|
// float logical naming here, where LogicalTopLeftCorner means the BStart
|
|
|
|
|
// LineLeft corner, and similarly for the other corners.
|
|
|
|
|
UniquePtr<EllipseShapeInfo> mLogicalTopLeftCorner;
|
|
|
|
|
UniquePtr<EllipseShapeInfo> mLogicalTopRightCorner;
|
|
|
|
|
UniquePtr<EllipseShapeInfo> mLogicalBottomLeftCorner;
|
|
|
|
|
UniquePtr<EllipseShapeInfo> mLogicalBottomRightCorner;
|
2018-04-17 21:59:11 +03:00
|
|
|
|
};
|
|
|
|
|
|
2018-04-13 21:29:14 +03:00
|
|
|
|
nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(
|
|
|
|
|
const nsRect& aRect, UniquePtr<nscoord[]> aRadii, nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel)
|
|
|
|
|
: mRect(aRect), mRadii(std::move(aRadii)), mShapeMargin(aShapeMargin) {
|
2018-04-17 21:39:50 +03:00
|
|
|
|
MOZ_ASSERT(mShapeMargin > 0 && !EachCornerHasBalancedRadii(mRadii.get()),
|
|
|
|
|
"Slow constructor should only be used for for shape-margin > 0 "
|
|
|
|
|
"and radii with elliptical corners.");
|
|
|
|
|
|
|
|
|
|
// Before we inflate mRect by mShapeMargin, construct each of our corners.
|
|
|
|
|
// If we do it in this order, it's a bit simpler to calculate the center
|
|
|
|
|
// of each of the corners.
|
|
|
|
|
mLogicalTopLeftCorner = MakeUnique<EllipseShapeInfo>(
|
|
|
|
|
nsPoint(mRect.X() + mRadii[eCornerTopLeftX],
|
|
|
|
|
mRect.Y() + mRadii[eCornerTopLeftY]),
|
|
|
|
|
nsSize(mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY]), mShapeMargin,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
mLogicalTopRightCorner = MakeUnique<EllipseShapeInfo>(
|
|
|
|
|
nsPoint(mRect.XMost() - mRadii[eCornerTopRightX],
|
|
|
|
|
mRect.Y() + mRadii[eCornerTopRightY]),
|
|
|
|
|
nsSize(mRadii[eCornerTopRightX], mRadii[eCornerTopRightY]), mShapeMargin,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
mLogicalBottomLeftCorner = MakeUnique<EllipseShapeInfo>(
|
|
|
|
|
nsPoint(mRect.X() + mRadii[eCornerBottomLeftX],
|
|
|
|
|
mRect.YMost() - mRadii[eCornerBottomLeftY]),
|
|
|
|
|
nsSize(mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY]),
|
|
|
|
|
mShapeMargin, aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
mLogicalBottomRightCorner = MakeUnique<EllipseShapeInfo>(
|
|
|
|
|
nsPoint(mRect.XMost() - mRadii[eCornerBottomRightX],
|
|
|
|
|
mRect.YMost() - mRadii[eCornerBottomRightY]),
|
|
|
|
|
nsSize(mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY]),
|
|
|
|
|
mShapeMargin, aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Now we inflate our mRect by mShapeMargin.
|
|
|
|
|
mRect.Inflate(mShapeMargin);
|
2018-04-13 21:29:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:59:11 +03:00
|
|
|
|
nscoord nsFloatManager::RoundedBoxShapeInfo::LineLeft(
|
|
|
|
|
const nscoord aBStart, const nscoord aBEnd) const {
|
2018-04-17 21:39:50 +03:00
|
|
|
|
if (mShapeMargin == 0) {
|
|
|
|
|
if (!mRadii) {
|
|
|
|
|
return mRect.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord lineLeftDiff = ComputeEllipseLineInterceptDiff(
|
|
|
|
|
mRect.y, mRect.YMost(), mRadii[eCornerTopLeftX],
|
|
|
|
|
mRadii[eCornerTopLeftY], mRadii[eCornerBottomLeftX],
|
|
|
|
|
mRadii[eCornerBottomLeftY], aBStart, aBEnd);
|
|
|
|
|
return mRect.x + lineLeftDiff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalBottomLeftCorner,
|
|
|
|
|
"If we have positive shape-margin, we should have corners.");
|
|
|
|
|
|
|
|
|
|
// Determine if aBEnd is within our top corner.
|
|
|
|
|
if (aBEnd < mLogicalTopLeftCorner->BEnd()) {
|
|
|
|
|
return mLogicalTopLeftCorner->LineLeft(aBStart, aBEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine if aBStart is within our bottom corner.
|
|
|
|
|
if (aBStart >= mLogicalBottomLeftCorner->BStart()) {
|
|
|
|
|
return mLogicalBottomLeftCorner->LineLeft(aBStart, aBEnd);
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:39:50 +03:00
|
|
|
|
// Either aBStart or aBEnd or both are within the flat part of our left
|
|
|
|
|
// edge. Because we've already inflated our mRect to encompass our
|
|
|
|
|
// mShapeMargin, we can just return the edge.
|
|
|
|
|
return mRect.X();
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::RoundedBoxShapeInfo::LineRight(
|
|
|
|
|
const nscoord aBStart, const nscoord aBEnd) const {
|
2018-04-17 21:39:50 +03:00
|
|
|
|
if (mShapeMargin == 0) {
|
|
|
|
|
if (!mRadii) {
|
|
|
|
|
return mRect.XMost();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord lineRightDiff = ComputeEllipseLineInterceptDiff(
|
|
|
|
|
mRect.y, mRect.YMost(), mRadii[eCornerTopRightX],
|
|
|
|
|
mRadii[eCornerTopRightY], mRadii[eCornerBottomRightX],
|
|
|
|
|
mRadii[eCornerBottomRightY], aBStart, aBEnd);
|
|
|
|
|
return mRect.XMost() - lineRightDiff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(mLogicalTopRightCorner && mLogicalBottomRightCorner,
|
|
|
|
|
"If we have positive shape-margin, we should have corners.");
|
|
|
|
|
|
|
|
|
|
// Determine if aBEnd is within our top corner.
|
|
|
|
|
if (aBEnd < mLogicalTopRightCorner->BEnd()) {
|
|
|
|
|
return mLogicalTopRightCorner->LineRight(aBStart, aBEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine if aBStart is within our bottom corner.
|
|
|
|
|
if (aBStart >= mLogicalBottomRightCorner->BStart()) {
|
|
|
|
|
return mLogicalBottomRightCorner->LineRight(aBStart, aBEnd);
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:39:50 +03:00
|
|
|
|
// Either aBStart or aBEnd or both are within the flat part of our right
|
|
|
|
|
// edge. Because we've already inflated our mRect to encompass our
|
|
|
|
|
// mShapeMargin, we can just return the edge.
|
|
|
|
|
return mRect.XMost();
|
2018-04-17 21:59:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-13 07:03:40 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// PolygonShapeInfo
|
2017-12-19 10:06:10 +03:00
|
|
|
|
//
|
|
|
|
|
// Implements shape-outside: polygon().
|
|
|
|
|
//
|
|
|
|
|
class nsFloatManager::PolygonShapeInfo final
|
|
|
|
|
: public nsFloatManager::ShapeInfo {
|
|
|
|
|
public:
|
|
|
|
|
explicit PolygonShapeInfo(nsTArray<nsPoint>&& aVertices);
|
2018-04-19 21:27:28 +03:00
|
|
|
|
PolygonShapeInfo(nsTArray<nsPoint>&& aVertices, nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel, const nsRect& aMarginRect);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2017-12-19 10:06:10 +03:00
|
|
|
|
nscoord LineLeft(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord LineRight(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord BStart() const override { return mBStart; }
|
|
|
|
|
nscoord BEnd() const override { return mBEnd; }
|
2018-05-23 01:54:21 +03:00
|
|
|
|
bool IsEmpty() const override {
|
|
|
|
|
// A PolygonShapeInfo is never empty, because the parser prevents us from
|
|
|
|
|
// creating a shape with no vertices. If we only have 1 vertex, the
|
|
|
|
|
// shape acts like a point. With 2 non-coincident vertices, the shape
|
|
|
|
|
// acts like a line.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-05-30 20:03:45 +03:00
|
|
|
|
bool MayNarrowInBlockDirection() const override { return true; }
|
2017-12-19 10:06:10 +03:00
|
|
|
|
|
|
|
|
|
void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
|
|
|
|
|
|
|
|
|
|
private:
|
2018-05-23 01:54:21 +03:00
|
|
|
|
// Helper method for determining the mBStart and mBEnd based on the
|
|
|
|
|
// vertices' y extent.
|
|
|
|
|
void ComputeExtent();
|
2018-04-19 21:27:28 +03:00
|
|
|
|
|
2017-12-19 10:06:10 +03:00
|
|
|
|
// Helper method for implementing LineLeft() and LineRight().
|
|
|
|
|
nscoord ComputeLineIntercept(
|
|
|
|
|
const nscoord aBStart, const nscoord aBEnd,
|
|
|
|
|
nscoord (*aCompareOp)(std::initializer_list<nscoord>),
|
|
|
|
|
const nscoord aLineInterceptInitialValue) const;
|
|
|
|
|
|
|
|
|
|
// Given a horizontal line y, and two points p1 and p2 forming a line
|
|
|
|
|
// segment L. Solve x for the intersection of y and L. This method
|
|
|
|
|
// assumes y and L do intersect, and L is *not* horizontal.
|
|
|
|
|
static nscoord XInterceptAtY(const nscoord aY, const nsPoint& aP1,
|
|
|
|
|
const nsPoint& aP2);
|
|
|
|
|
|
|
|
|
|
// The vertices of the polygon in the float manager's coordinate space.
|
|
|
|
|
nsTArray<nsPoint> mVertices;
|
|
|
|
|
|
2018-04-19 21:27:28 +03:00
|
|
|
|
// An interval is slice of the float area defined by this PolygonShapeInfo.
|
|
|
|
|
// These are only generated and used in float area calculations for
|
|
|
|
|
// shape-margin > 0. Each interval is a rectangle that is one device pixel
|
|
|
|
|
// deep in the block axis. The values are stored as block edges in the y
|
|
|
|
|
// coordinates, and inline edges as the x coordinates.
|
|
|
|
|
|
|
|
|
|
// The intervals are stored in ascending order on y.
|
|
|
|
|
nsTArray<nsRect> mIntervals;
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
// Computed block start and block end value of the polygon shape. These
|
|
|
|
|
// initial values are set to correct values in ComputeExtent(), which is
|
|
|
|
|
// called from all constructors. Afterwards, mBStart is guaranteed to be
|
|
|
|
|
// less than or equal to mBEnd.
|
2017-12-19 10:06:10 +03:00
|
|
|
|
nscoord mBStart = nscoord_MAX;
|
|
|
|
|
nscoord mBEnd = nscoord_MIN;
|
|
|
|
|
};
|
2017-03-13 07:03:40 +03:00
|
|
|
|
|
|
|
|
|
nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(
|
|
|
|
|
nsTArray<nsPoint>&& aVertices)
|
|
|
|
|
: mVertices(aVertices) {
|
2018-05-23 01:54:21 +03:00
|
|
|
|
ComputeExtent();
|
2018-04-19 21:27:28 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(
|
|
|
|
|
nsTArray<nsPoint>&& aVertices, nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel, const nsRect& aMarginRect)
|
|
|
|
|
: mVertices(aVertices) {
|
|
|
|
|
MOZ_ASSERT(aShapeMargin > 0,
|
|
|
|
|
"This constructor should only be used for a "
|
|
|
|
|
"polygon with a positive shape-margin.");
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
ComputeExtent();
|
2018-04-19 21:27:28 +03:00
|
|
|
|
|
2018-04-19 21:47:04 +03:00
|
|
|
|
// With a positive aShapeMargin, we have to calculate a distance
|
|
|
|
|
// field from the opaque pixels, then build intervals based on
|
|
|
|
|
// them being within aShapeMargin distance to an opaque pixel.
|
|
|
|
|
|
|
|
|
|
// Roughly: for each pixel in the margin box, we need to determine the
|
|
|
|
|
// distance to the nearest opaque image-pixel. If that distance is less
|
|
|
|
|
// than aShapeMargin, we consider this margin-box pixel as being part of
|
|
|
|
|
// the float area.
|
|
|
|
|
|
|
|
|
|
// Computing the distance field is a two-pass O(n) operation.
|
|
|
|
|
// We use a chamfer 5-7-11 5x5 matrix to compute minimum distance
|
|
|
|
|
// to an opaque pixel. This integer math computation is reasonably
|
|
|
|
|
// close to the true Euclidean distance. The distances will be
|
|
|
|
|
// approximately 5x the true distance, quantized in integer units.
|
|
|
|
|
// The 5x is factored away in the comparison used in the final
|
|
|
|
|
// pass which builds the intervals.
|
|
|
|
|
dfType usedMargin5X =
|
|
|
|
|
CalcUsedShapeMargin5X(aShapeMargin, aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Allocate our distance field. The distance field has to cover
|
|
|
|
|
// the entire aMarginRect, since aShapeMargin could bleed into it.
|
|
|
|
|
// Conveniently, our vertices have been converted into this same space,
|
|
|
|
|
// so if we cover the aMarginRect, we cover all the vertices.
|
|
|
|
|
const LayoutDeviceIntSize marginRectDevPixels =
|
|
|
|
|
LayoutDevicePixel::FromAppUnitsRounded(aMarginRect.Size(),
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Since our distance field is computed with a 5x5 neighborhood,
|
|
|
|
|
// we need to expand our distance field by a further 4 pixels in
|
|
|
|
|
// both axes, 2 on the leading edge and 2 on the trailing edge.
|
|
|
|
|
// We call this edge area the "expanded region".
|
|
|
|
|
static const uint32_t kiExpansionPerSide = 2;
|
|
|
|
|
static const uint32_t kbExpansionPerSide = 2;
|
|
|
|
|
|
|
|
|
|
// Clamp the size of our distance field sizes to prevent multiplication
|
|
|
|
|
// overflow.
|
|
|
|
|
static const uint32_t DF_SIDE_MAX =
|
|
|
|
|
floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
|
|
|
|
|
|
|
|
|
|
// Clamp the margin plus 2X the expansion values between expansion + 1 and
|
|
|
|
|
// DF_SIDE_MAX. This ensures that the distance field allocation doesn't
|
|
|
|
|
// overflow during multiplication, and the reverse iteration doesn't
|
|
|
|
|
// underflow.
|
|
|
|
|
const uint32_t iSize =
|
|
|
|
|
std::max(std::min(marginRectDevPixels.width + (kiExpansionPerSide * 2),
|
|
|
|
|
DF_SIDE_MAX),
|
|
|
|
|
kiExpansionPerSide + 1);
|
|
|
|
|
const uint32_t bSize =
|
|
|
|
|
std::max(std::min(marginRectDevPixels.height + (kbExpansionPerSide * 2),
|
|
|
|
|
DF_SIDE_MAX),
|
|
|
|
|
kbExpansionPerSide + 1);
|
|
|
|
|
|
|
|
|
|
// Since the margin-box size is CSS controlled, and large values will
|
|
|
|
|
// generate large iSize and bSize values, we do a fallible allocation for
|
|
|
|
|
// the distance field. If allocation fails, we early exit and layout will
|
|
|
|
|
// be wrong, but we'll avoid aborting from OOM.
|
|
|
|
|
auto df = MakeUniqueFallible<dfType[]>(iSize * bSize);
|
|
|
|
|
if (!df) {
|
|
|
|
|
// Without a distance field, we can't reason about the float area.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// First pass setting distance field, starting at top-left, three cases:
|
|
|
|
|
// 1) Expanded region pixel: set to MAX_MARGIN_5X.
|
|
|
|
|
// 2) Pixel within the polygon: set to 0.
|
|
|
|
|
// 3) Other pixel: set to minimum backward-looking neighborhood
|
|
|
|
|
// distance value, computed with 5-7-11 chamfer.
|
|
|
|
|
|
|
|
|
|
for (uint32_t b = 0; b < bSize; ++b) {
|
|
|
|
|
// Find the left and right i intercepts of the polygon edge for this
|
|
|
|
|
// block row, and adjust them to compensate for the expansion of the
|
|
|
|
|
// inline dimension. If we're in the expanded region, or if we're using
|
|
|
|
|
// a b that's less than the bStart of the polygon, the intercepts are
|
|
|
|
|
// the nscoord min and max limits.
|
|
|
|
|
nscoord bInAppUnits = (b - kbExpansionPerSide) * aAppUnitsPerDevPixel;
|
|
|
|
|
bool bIsInExpandedRegion(b < kbExpansionPerSide ||
|
|
|
|
|
b >= bSize - kbExpansionPerSide);
|
|
|
|
|
|
|
|
|
|
// We now figure out the i values that correspond to the left edge and
|
|
|
|
|
// the right edge of the polygon at one-dev-pixel-thick strip of b. We
|
|
|
|
|
// have a ComputeLineIntercept function that takes and returns app unit
|
|
|
|
|
// coordinates in the space of aMarginRect. So to pass in b values, we
|
|
|
|
|
// first have to add the aMarginRect.y value. And for the values that we
|
|
|
|
|
// get out, we have to subtract away the aMarginRect.x value before
|
|
|
|
|
// converting the app units to dev pixels.
|
|
|
|
|
nscoord bInAppUnitsMarginRect = bInAppUnits + aMarginRect.y;
|
2018-05-09 01:56:49 +03:00
|
|
|
|
bool bIsLessThanPolygonBStart(bInAppUnitsMarginRect < mBStart);
|
2018-05-23 01:54:21 +03:00
|
|
|
|
bool bIsMoreThanPolygonBEnd(bInAppUnitsMarginRect > mBEnd);
|
2018-04-19 21:47:04 +03:00
|
|
|
|
|
|
|
|
|
const int32_t iLeftEdge =
|
|
|
|
|
(bIsInExpandedRegion || bIsLessThanPolygonBStart ||
|
|
|
|
|
bIsMoreThanPolygonBEnd)
|
|
|
|
|
? nscoord_MAX
|
|
|
|
|
: kiExpansionPerSide +
|
|
|
|
|
NSAppUnitsToIntPixels(
|
|
|
|
|
ComputeLineIntercept(
|
|
|
|
|
bInAppUnitsMarginRect,
|
|
|
|
|
bInAppUnitsMarginRect + aAppUnitsPerDevPixel,
|
|
|
|
|
std::min<nscoord>, nscoord_MAX) -
|
|
|
|
|
aMarginRect.x,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-04-19 21:47:04 +03:00
|
|
|
|
const int32_t iRightEdge =
|
|
|
|
|
(bIsInExpandedRegion || bIsLessThanPolygonBStart ||
|
|
|
|
|
bIsMoreThanPolygonBEnd)
|
|
|
|
|
? nscoord_MIN
|
|
|
|
|
: kiExpansionPerSide +
|
|
|
|
|
NSAppUnitsToIntPixels(
|
|
|
|
|
ComputeLineIntercept(
|
|
|
|
|
bInAppUnitsMarginRect,
|
|
|
|
|
bInAppUnitsMarginRect + aAppUnitsPerDevPixel,
|
|
|
|
|
std::max<nscoord>, nscoord_MIN) -
|
|
|
|
|
aMarginRect.x,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < iSize; ++i) {
|
|
|
|
|
const uint32_t index = i + b * iSize;
|
|
|
|
|
MOZ_ASSERT(index < (iSize * bSize),
|
|
|
|
|
"Our distance field index should be in-bounds.");
|
|
|
|
|
|
|
|
|
|
// Handle our three cases, in order.
|
|
|
|
|
if (i < kiExpansionPerSide || i >= iSize - kiExpansionPerSide ||
|
|
|
|
|
bIsInExpandedRegion) {
|
|
|
|
|
// Case 1: Expanded pixel.
|
|
|
|
|
df[index] = MAX_MARGIN_5X;
|
2018-05-23 01:54:21 +03:00
|
|
|
|
} else if ((int32_t)i >= iLeftEdge && (int32_t)i <= iRightEdge) {
|
|
|
|
|
// Case 2: Polygon pixel, either inside or just adjacent to the right
|
|
|
|
|
// edge. We need this special distinction to detect a space between
|
|
|
|
|
// edges that is less than one dev pixel.
|
|
|
|
|
df[index] = (int32_t)i < iRightEdge ? 0 : 5;
|
2018-04-19 21:47:04 +03:00
|
|
|
|
} else {
|
|
|
|
|
// Case 3: Other pixel.
|
|
|
|
|
|
|
|
|
|
// Backward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | |11| |11| |
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// |11| 7| 5| 7|11|
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | | 5| X| | |
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of MAX_MARGIN_5X and the
|
|
|
|
|
// values of all of the numbered neighbors summed with the
|
|
|
|
|
// value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index - (iSize * 2) - 1 < (iSize * bSize) &&
|
|
|
|
|
index - iSize - 2 < (iSize * bSize),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(MAX_MARGIN_5X,
|
|
|
|
|
std::min<dfType>(df[index - (iSize * 2) - 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index - (iSize * 2) + 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index - iSize - 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index - iSize - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - iSize] + 5,
|
|
|
|
|
std::min<dfType>(df[index - iSize + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - iSize + 2] + 11,
|
|
|
|
|
df[index - 1] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-04-19 21:47:04 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Okay, time for the second pass. This pass is in reverse order from
|
|
|
|
|
// the first pass. All of our opaque pixels have been set to 0, and all
|
|
|
|
|
// of our expanded region pixels have been set to MAX_MARGIN_5X. Other
|
|
|
|
|
// pixels have been set to some value between those two (inclusive) but
|
|
|
|
|
// this hasn't yet taken into account the neighbors that were processed
|
|
|
|
|
// after them in the first pass. This time we reverse iterate so we can
|
|
|
|
|
// apply the forward-looking chamfer.
|
|
|
|
|
|
|
|
|
|
// This time, we constrain our outer and inner loop to ignore the
|
|
|
|
|
// expanded region pixels. For each pixel we iterate, we set the df value
|
|
|
|
|
// to the minimum forward-looking neighborhood distance value, computed
|
|
|
|
|
// with a 5-7-11 chamfer. We also check each df value against the
|
|
|
|
|
// usedMargin5X threshold, and use that to set the iMin and iMax values
|
|
|
|
|
// for the interval we'll create for that block axis value (b).
|
|
|
|
|
|
|
|
|
|
// At the end of each row, if any of the other pixels had a value less
|
|
|
|
|
// than usedMargin5X, we create an interval.
|
|
|
|
|
for (uint32_t b = bSize - kbExpansionPerSide - 1; b >= kbExpansionPerSide;
|
|
|
|
|
--b) {
|
|
|
|
|
// iMin tracks the first df pixel and iMax the last df pixel whose
|
|
|
|
|
// df[] value is less than usedMargin5X. Set iMin and iMax in
|
|
|
|
|
// preparation for this row or column.
|
|
|
|
|
int32_t iMin = iSize;
|
|
|
|
|
int32_t iMax = -1;
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = iSize - kiExpansionPerSide - 1; i >= kiExpansionPerSide;
|
|
|
|
|
--i) {
|
|
|
|
|
const uint32_t index = i + b * iSize;
|
|
|
|
|
MOZ_ASSERT(index < (iSize * bSize),
|
|
|
|
|
"Our distance field index should be in-bounds.");
|
|
|
|
|
|
|
|
|
|
// Only apply the chamfer calculation if the df value is not
|
|
|
|
|
// already 0, since the chamfer can only reduce the value.
|
|
|
|
|
if (df[index]) {
|
|
|
|
|
// Forward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | | | X| 5| |
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// |11| 7| 5| 7|11|
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | |11| |11| |
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of its current value and
|
|
|
|
|
// the values of all of the numbered neighbors summed with
|
|
|
|
|
// the value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index + (iSize * 2) + 1 < (iSize * bSize) &&
|
|
|
|
|
index + iSize + 2 < (iSize * bSize),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(df[index],
|
|
|
|
|
std::min<dfType>(df[index + (iSize * 2) + 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index + (iSize * 2) - 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index + iSize + 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index + iSize + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + iSize] + 5,
|
|
|
|
|
std::min<dfType>(df[index + iSize - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + iSize - 2] + 11,
|
|
|
|
|
df[index + 1] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-04-19 21:47:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally, we can check the df value and see if it's less than
|
|
|
|
|
// or equal to the usedMargin5X value.
|
|
|
|
|
if (df[index] <= usedMargin5X) {
|
|
|
|
|
if (iMax == -1) {
|
|
|
|
|
iMax = i;
|
|
|
|
|
}
|
|
|
|
|
MOZ_ASSERT(iMin > (int32_t)i);
|
|
|
|
|
iMin = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iMax != -1) {
|
|
|
|
|
// Our interval values, iMin, iMax, and b are all calculated from
|
|
|
|
|
// the expanded region, which is based on the margin rect. To create
|
|
|
|
|
// our interval, we have to subtract kiExpansionPerSide from iMin and
|
|
|
|
|
// iMax, and subtract kbExpansionPerSide from b to account for the
|
|
|
|
|
// expanded region edges. This produces coords that are relative to
|
|
|
|
|
// our margin-rect.
|
|
|
|
|
|
|
|
|
|
// Origin for this interval is at the aMarginRect origin, adjusted in
|
|
|
|
|
// the block direction by b in app units, and in the inline direction
|
|
|
|
|
// by iMin in app units.
|
|
|
|
|
nsPoint origin(
|
|
|
|
|
aMarginRect.x + (iMin - kiExpansionPerSide) * aAppUnitsPerDevPixel,
|
|
|
|
|
aMarginRect.y + (b - kbExpansionPerSide) * aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Size is the difference in iMax and iMin, plus 1 (to account for the
|
|
|
|
|
// whole pixel) dev pixels, by 1 block dev pixel. We don't bother
|
|
|
|
|
// subtracting kiExpansionPerSide from iMin and iMax in this case
|
|
|
|
|
// because we only care about the distance between them. We convert
|
|
|
|
|
// everything to app units.
|
|
|
|
|
nsSize size((iMax - iMin + 1) * aAppUnitsPerDevPixel,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
mIntervals.AppendElement(nsRect(origin, size));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reverse the intervals keep the array sorted on the block direction.
|
|
|
|
|
mIntervals.Reverse();
|
2018-04-19 21:27:28 +03:00
|
|
|
|
|
2018-04-19 21:47:04 +03:00
|
|
|
|
// Adjust our extents by aShapeMargin. This may cause overflow of some
|
|
|
|
|
// kind if aShapeMargin is large, so we do some clamping to maintain the
|
|
|
|
|
// invariant mBStart <= mBEnd.
|
|
|
|
|
mBStart = std::min(mBStart, mBStart - aShapeMargin);
|
|
|
|
|
mBEnd = std::max(mBEnd, mBEnd + aShapeMargin);
|
2018-04-19 21:27:28 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
|
|
|
|
// Use intervals if we have them.
|
|
|
|
|
if (!mIntervals.IsEmpty()) {
|
|
|
|
|
return LineEdge(mIntervals, aBStart, aBEnd, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We want the line-left-most inline-axis coordinate where the
|
|
|
|
|
// (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
|
|
|
|
|
// To get that, we start as line-right as possible (at nscoord_MAX). Then
|
|
|
|
|
// we iterate each line segment to compute its intersection point with the
|
|
|
|
|
// band (if any) and using std::min() successively to get the smallest
|
|
|
|
|
// inline-coordinates among those intersection points.
|
|
|
|
|
//
|
|
|
|
|
// Note: std::min<nscoord> means the function std::min() with template
|
|
|
|
|
// parameter nscoord, not the minimum value of nscoord.
|
|
|
|
|
return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
|
|
|
|
// Use intervals if we have them.
|
|
|
|
|
if (!mIntervals.IsEmpty()) {
|
|
|
|
|
return LineEdge(mIntervals, aBStart, aBEnd, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Similar to LineLeft(). Though here, we want the line-right-most
|
|
|
|
|
// inline-axis coordinate, so we instead start at nscoord_MIN and use
|
|
|
|
|
// std::max() to get the biggest inline-coordinate among those
|
|
|
|
|
// intersection points.
|
|
|
|
|
return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
void nsFloatManager::PolygonShapeInfo::ComputeExtent() {
|
2017-03-13 07:03:40 +03:00
|
|
|
|
// mBStart and mBEnd are the lower and the upper bounds of all the
|
|
|
|
|
// vertex.y, respectively. The vertex.y is actually on the block-axis of
|
|
|
|
|
// the float manager's writing mode.
|
|
|
|
|
for (const nsPoint& vertex : mVertices) {
|
|
|
|
|
mBStart = std::min(mBStart, vertex.y);
|
|
|
|
|
mBEnd = std::max(mBEnd, vertex.y);
|
|
|
|
|
}
|
2018-05-23 01:54:21 +03:00
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(mBStart <= mBEnd,
|
|
|
|
|
"Start of float area should be less than "
|
|
|
|
|
"or equal to the end.");
|
2017-03-13 07:03:40 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
|
|
|
|
|
const nscoord aBStart, const nscoord aBEnd,
|
|
|
|
|
nscoord (*aCompareOp)(std::initializer_list<nscoord>),
|
|
|
|
|
const nscoord aLineInterceptInitialValue) const {
|
|
|
|
|
MOZ_ASSERT(aBStart <= aBEnd,
|
|
|
|
|
"The band's block start is greater than its block end?");
|
|
|
|
|
|
|
|
|
|
const size_t len = mVertices.Length();
|
|
|
|
|
nscoord lineIntercept = aLineInterceptInitialValue;
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
// We have some special treatment of horizontal lines between vertices.
|
|
|
|
|
// Generally, we can ignore the impact of the horizontal lines since their
|
|
|
|
|
// endpoints will be included in the lines preceeding or following them.
|
|
|
|
|
// However, it's possible the polygon is entirely a horizontal line,
|
|
|
|
|
// possibly built from more than one horizontal segment. In such a case,
|
|
|
|
|
// we need to have the horizontal line(s) contribute to the line intercepts.
|
|
|
|
|
// We do this by accepting horizontal lines until we find a non-horizontal
|
|
|
|
|
// line, after which all further horizontal lines are ignored.
|
|
|
|
|
bool canIgnoreHorizontalLines = false;
|
|
|
|
|
|
2017-03-13 07:03:40 +03:00
|
|
|
|
// Iterate each line segment {p0, p1}, {p1, p2}, ..., {pn, p0}.
|
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
|
|
|
const nsPoint* smallYVertex = &mVertices[i];
|
|
|
|
|
const nsPoint* bigYVertex = &mVertices[(i + 1) % len];
|
|
|
|
|
|
|
|
|
|
// Swap the two points to satisfy the requirement for calling
|
|
|
|
|
// XInterceptAtY.
|
|
|
|
|
if (smallYVertex->y > bigYVertex->y) {
|
|
|
|
|
std::swap(smallYVertex, bigYVertex);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
// Generally, we need to ignore line segments that either don't intersect
|
|
|
|
|
// the band, or merely touch it. However, if the polygon has no block extent
|
|
|
|
|
// (it is a point, or a horizontal line), and the band touches the line
|
|
|
|
|
// segment, we let that line segment through.
|
|
|
|
|
if ((aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y) &&
|
|
|
|
|
!(mBStart == mBEnd && aBStart == bigYVertex->y)) {
|
|
|
|
|
// Skip computing the intercept if the band doesn't intersect the
|
|
|
|
|
// line segment.
|
2017-03-13 07:03:40 +03:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 01:54:21 +03:00
|
|
|
|
nscoord bStartLineIntercept;
|
|
|
|
|
nscoord bEndLineIntercept;
|
|
|
|
|
|
|
|
|
|
if (smallYVertex->y == bigYVertex->y) {
|
|
|
|
|
// The line is horizontal; see if we can ignore it.
|
|
|
|
|
if (canIgnoreHorizontalLines) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For a horizontal line that we can't ignore, we treat the two x value
|
|
|
|
|
// ends as the bStartLineIntercept and bEndLineIntercept. It doesn't
|
|
|
|
|
// matter which is applied to which, because they'll both be applied
|
|
|
|
|
// to aCompareOp.
|
|
|
|
|
bStartLineIntercept = smallYVertex->x;
|
|
|
|
|
bEndLineIntercept = bigYVertex->x;
|
|
|
|
|
} else {
|
|
|
|
|
// This is not a horizontal line. We can now ignore all future
|
|
|
|
|
// horizontal lines.
|
|
|
|
|
canIgnoreHorizontalLines = true;
|
|
|
|
|
|
|
|
|
|
bStartLineIntercept =
|
|
|
|
|
aBStart <= smallYVertex->y
|
|
|
|
|
? smallYVertex->x
|
|
|
|
|
: XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
|
|
|
|
|
bEndLineIntercept =
|
|
|
|
|
aBEnd >= bigYVertex->y
|
|
|
|
|
? bigYVertex->x
|
|
|
|
|
: XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
|
|
|
|
|
}
|
2017-03-13 07:03:40 +03:00
|
|
|
|
|
|
|
|
|
// If either new intercept is more extreme than lineIntercept (per
|
|
|
|
|
// aCompareOp), then update lineIntercept to that value.
|
|
|
|
|
lineIntercept =
|
|
|
|
|
aCompareOp({lineIntercept, bStartLineIntercept, bEndLineIntercept});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lineIntercept;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsFloatManager::PolygonShapeInfo::Translate(nscoord aLineLeft,
|
|
|
|
|
nscoord aBlockStart) {
|
|
|
|
|
for (nsPoint& vertex : mVertices) {
|
|
|
|
|
vertex.MoveBy(aLineLeft, aBlockStart);
|
|
|
|
|
}
|
2018-04-19 21:27:28 +03:00
|
|
|
|
for (nsRect& interval : mIntervals) {
|
|
|
|
|
interval.MoveBy(aLineLeft, aBlockStart);
|
|
|
|
|
}
|
2017-03-13 07:03:40 +03:00
|
|
|
|
mBStart += aBlockStart;
|
|
|
|
|
mBEnd += aBlockStart;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
nscoord nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY,
|
|
|
|
|
const nsPoint& aP1,
|
|
|
|
|
const nsPoint& aP2) {
|
2017-03-13 07:03:40 +03:00
|
|
|
|
// Solve for x in the linear equation: x = x1 + (y-y1) * (x2-x1) / (y2-y1),
|
|
|
|
|
// where aP1 = (x1, y1) and aP2 = (x2, y2).
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aP1.y <= aY && aY <= aP2.y,
|
|
|
|
|
"This function won't work if the horizontal line at aY and "
|
|
|
|
|
"the line segment (aP1, aP2) do not intersect!");
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aP1.y != aP2.y,
|
|
|
|
|
"A horizontal line segment results in dividing by zero error!");
|
|
|
|
|
|
|
|
|
|
return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// ImageShapeInfo
|
|
|
|
|
//
|
|
|
|
|
// Implements shape-outside: <image>
|
|
|
|
|
//
|
|
|
|
|
class nsFloatManager::ImageShapeInfo final : public nsFloatManager::ShapeInfo {
|
|
|
|
|
public:
|
|
|
|
|
ImageShapeInfo(uint8_t* aAlphaPixels, int32_t aStride,
|
|
|
|
|
const LayoutDeviceIntSize& aImageSize,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel, float aShapeImageThreshold,
|
|
|
|
|
nscoord aShapeMargin, const nsRect& aContentRect,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
const nsRect& aMarginRect, WritingMode aWM,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
const nsSize& aContainerSize);
|
|
|
|
|
|
|
|
|
|
nscoord LineLeft(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord LineRight(const nscoord aBStart, const nscoord aBEnd) const override;
|
|
|
|
|
nscoord BStart() const override { return mBStart; }
|
|
|
|
|
nscoord BEnd() const override { return mBEnd; }
|
|
|
|
|
bool IsEmpty() const override { return mIntervals.IsEmpty(); }
|
2018-05-30 20:03:45 +03:00
|
|
|
|
bool MayNarrowInBlockDirection() const override { return true; }
|
2018-01-25 09:55:18 +03:00
|
|
|
|
|
|
|
|
|
void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// An interval is slice of the float area defined by this ImageShapeInfo.
|
|
|
|
|
// Each interval is a rectangle that is one pixel deep in the block
|
|
|
|
|
// axis. The values are stored as block edges in the y coordinates,
|
|
|
|
|
// and inline edges as the x coordinates.
|
|
|
|
|
|
|
|
|
|
// The intervals are stored in ascending order on y.
|
|
|
|
|
nsTArray<nsRect> mIntervals;
|
|
|
|
|
|
|
|
|
|
nscoord mBStart = nscoord_MAX;
|
|
|
|
|
nscoord mBEnd = nscoord_MIN;
|
2018-02-28 21:43:43 +03:00
|
|
|
|
|
|
|
|
|
// CreateInterval transforms the supplied aIMin and aIMax and aB
|
|
|
|
|
// values into an interval that respects the writing mode. An
|
|
|
|
|
// aOffsetFromContainer can be provided if the aIMin, aIMax, aB
|
|
|
|
|
// values were generated relative to something other than the container
|
|
|
|
|
// rect (such as the content rect or margin rect).
|
|
|
|
|
void CreateInterval(int32_t aIMin, int32_t aIMax, int32_t aB,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel,
|
|
|
|
|
const nsPoint& aOffsetFromContainer, WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize);
|
2018-01-25 09:55:18 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
nsFloatManager::ImageShapeInfo::ImageShapeInfo(
|
|
|
|
|
uint8_t* aAlphaPixels, int32_t aStride,
|
|
|
|
|
const LayoutDeviceIntSize& aImageSize, int32_t aAppUnitsPerDevPixel,
|
|
|
|
|
float aShapeImageThreshold, nscoord aShapeMargin,
|
|
|
|
|
const nsRect& aContentRect, const nsRect& aMarginRect, WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
MOZ_ASSERT(aShapeImageThreshold >= 0.0 && aShapeImageThreshold <= 1.0,
|
|
|
|
|
"The computed value of shape-image-threshold is wrong!");
|
|
|
|
|
|
|
|
|
|
const uint8_t threshold = NSToIntFloor(aShapeImageThreshold * 255);
|
2018-04-30 22:25:13 +03:00
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aImageSize.width >= 0 && aImageSize.height >= 0,
|
|
|
|
|
"Image size must be non-negative for our math to work.");
|
|
|
|
|
const uint32_t w = aImageSize.width;
|
|
|
|
|
const uint32_t h = aImageSize.height;
|
2018-01-25 09:55:18 +03:00
|
|
|
|
|
2018-03-13 03:04:34 +03:00
|
|
|
|
if (aShapeMargin <= 0) {
|
|
|
|
|
// Without a positive aShapeMargin, all we have to do is a
|
|
|
|
|
// direct threshold comparison of the alpha pixels.
|
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number
|
|
|
|
|
|
|
|
|
|
// Scan the pixels in a double loop. For horizontal writing modes, we do
|
|
|
|
|
// this row by row, from top to bottom. For vertical writing modes, we do
|
|
|
|
|
// column by column, from left to right. We define the two loops
|
|
|
|
|
// generically, then figure out the rows and cols within the inner loop.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
const uint32_t bSize = aWM.IsVertical() ? w : h;
|
|
|
|
|
const uint32_t iSize = aWM.IsVertical() ? h : w;
|
|
|
|
|
for (uint32_t b = 0; b < bSize; ++b) {
|
2018-03-13 03:04:34 +03:00
|
|
|
|
// iMin and max store the start and end of the float area for the row
|
|
|
|
|
// or column represented by this iteration of the outer loop.
|
|
|
|
|
int32_t iMin = -1;
|
|
|
|
|
int32_t iMax = -1;
|
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t i = 0; i < iSize; ++i) {
|
|
|
|
|
const uint32_t col = aWM.IsVertical() ? b : i;
|
|
|
|
|
const uint32_t row = aWM.IsVertical() ? i : b;
|
|
|
|
|
const uint32_t index = col + row * aStride;
|
2018-03-13 03:04:34 +03:00
|
|
|
|
|
|
|
|
|
// Determine if the alpha pixel at this row and column has a value
|
2018-02-22 22:11:03 +03:00
|
|
|
|
// greater than the threshold. If it does, update our iMin and iMax
|
|
|
|
|
// values to track the edges of the float area for this row or column.
|
2018-03-13 03:04:34 +03:00
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number
|
2018-02-22 22:11:03 +03:00
|
|
|
|
const uint8_t alpha = aAlphaPixels[index];
|
2018-03-13 03:04:34 +03:00
|
|
|
|
if (alpha > threshold) {
|
|
|
|
|
if (iMin == -1) {
|
|
|
|
|
iMin = i;
|
|
|
|
|
}
|
2018-04-30 22:25:13 +03:00
|
|
|
|
MOZ_ASSERT(iMax < (int32_t)i);
|
2018-03-13 03:04:34 +03:00
|
|
|
|
iMax = i;
|
2018-04-24 22:31:15 +03:00
|
|
|
|
}
|
2018-03-13 03:04:34 +03:00
|
|
|
|
}
|
2018-01-25 09:55:18 +03:00
|
|
|
|
|
2018-03-13 03:04:34 +03:00
|
|
|
|
// At the end of a row or column; did we find something?
|
|
|
|
|
if (iMin != -1) {
|
|
|
|
|
// We need to supply an offset of the content rect top left, since
|
|
|
|
|
// our col and row have been calculated from the content rect,
|
|
|
|
|
// instead of the margin rect (against which floats are applied).
|
|
|
|
|
CreateInterval(iMin, iMax, b, aAppUnitsPerDevPixel,
|
|
|
|
|
aContentRect.TopLeft(), aWM, aContainerSize);
|
|
|
|
|
}
|
2018-02-22 22:11:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-13 03:04:34 +03:00
|
|
|
|
if (aWM.IsVerticalRL()) {
|
|
|
|
|
// vertical-rl or sideways-rl.
|
|
|
|
|
// Because we scan the columns from left to right, we need to reverse
|
|
|
|
|
// the array so that it's sorted (in ascending order) on the block
|
|
|
|
|
// direction.
|
|
|
|
|
mIntervals.Reverse();
|
|
|
|
|
}
|
2018-02-22 22:11:03 +03:00
|
|
|
|
} else {
|
|
|
|
|
// With a positive aShapeMargin, we have to calculate a distance
|
|
|
|
|
// field from the opaque pixels, then build intervals based on
|
|
|
|
|
// them being within aShapeMargin distance to an opaque pixel.
|
|
|
|
|
|
|
|
|
|
// Roughly: for each pixel in the margin box, we need to determine the
|
|
|
|
|
// distance to the nearest opaque image-pixel. If that distance is less
|
|
|
|
|
// than aShapeMargin, we consider this margin-box pixel as being part of
|
|
|
|
|
// the float area.
|
|
|
|
|
|
|
|
|
|
// Computing the distance field is a two-pass O(n) operation.
|
|
|
|
|
// We use a chamfer 5-7-11 5x5 matrix to compute minimum distance
|
|
|
|
|
// to an opaque pixel. This integer math computation is reasonably
|
|
|
|
|
// close to the true Euclidean distance. The distances will be
|
|
|
|
|
// approximately 5x the true distance, quantized in integer units.
|
|
|
|
|
// The 5x is factored away in the comparison used in the final
|
|
|
|
|
// pass which builds the intervals.
|
2018-04-28 04:10:51 +03:00
|
|
|
|
dfType usedMargin5X =
|
|
|
|
|
CalcUsedShapeMargin5X(aShapeMargin, aAppUnitsPerDevPixel);
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// Allocate our distance field. The distance field has to cover
|
|
|
|
|
// the entire aMarginRect, since aShapeMargin could bleed into it,
|
|
|
|
|
// beyond the content rect covered by aAlphaPixels. To make this work,
|
|
|
|
|
// we calculate a dfOffset value which is the top left of the content
|
|
|
|
|
// rect relative to the margin rect.
|
|
|
|
|
nsPoint offsetPoint = aContentRect.TopLeft() - aMarginRect.TopLeft();
|
|
|
|
|
LayoutDeviceIntPoint dfOffset = LayoutDevicePixel::FromAppUnitsRounded(
|
|
|
|
|
offsetPoint, aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Since our distance field is computed with a 5x5 neighborhood,
|
|
|
|
|
// we need to expand our distance field by a further 4 pixels in
|
|
|
|
|
// both axes, 2 on the leading edge and 2 on the trailing edge.
|
|
|
|
|
// We call this edge area the "expanded region".
|
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
// Our expansion amounts need to be the same for our math to work.
|
|
|
|
|
static uint32_t kExpansionPerSide = 2;
|
2018-02-22 22:11:03 +03:00
|
|
|
|
// Since dfOffset will be used in comparisons against expanded region
|
2018-04-21 00:04:15 +03:00
|
|
|
|
// pixel values, it's convenient to add expansion amounts to dfOffset in
|
|
|
|
|
// both axes, to simplify comparison math later.
|
|
|
|
|
dfOffset.x += kExpansionPerSide;
|
|
|
|
|
dfOffset.y += kExpansionPerSide;
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// In all these calculations, we purposely ignore aStride, because
|
|
|
|
|
// we don't have to replicate the packing that we received in
|
|
|
|
|
// aAlphaPixels. When we need to convert from df coordinates to
|
|
|
|
|
// alpha coordinates, we do that with math based on row and col.
|
|
|
|
|
const LayoutDeviceIntSize marginRectDevPixels =
|
|
|
|
|
LayoutDevicePixel::FromAppUnitsRounded(aMarginRect.Size(),
|
|
|
|
|
aAppUnitsPerDevPixel);
|
2018-04-30 22:25:13 +03:00
|
|
|
|
|
2018-04-28 04:10:51 +03:00
|
|
|
|
// Clamp the size of our distance field sizes to prevent multiplication
|
|
|
|
|
// overflow.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
static const uint32_t DF_SIDE_MAX =
|
2018-04-28 04:10:51 +03:00
|
|
|
|
floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
|
2018-04-30 22:25:13 +03:00
|
|
|
|
|
|
|
|
|
// Clamp the margin plus 2X the expansion values between expansion + 1
|
|
|
|
|
// and DF_SIDE_MAX. This ensures that the distance field allocation
|
|
|
|
|
// doesn't overflow during multiplication, and the reverse iteration
|
|
|
|
|
// doesn't underflow.
|
|
|
|
|
const uint32_t wEx =
|
|
|
|
|
std::max(std::min(marginRectDevPixels.width + (kExpansionPerSide * 2),
|
|
|
|
|
DF_SIDE_MAX),
|
|
|
|
|
kExpansionPerSide + 1);
|
|
|
|
|
const uint32_t hEx =
|
|
|
|
|
std::max(std::min(marginRectDevPixels.height + (kExpansionPerSide * 2),
|
|
|
|
|
DF_SIDE_MAX),
|
|
|
|
|
kExpansionPerSide + 1);
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// Since the margin-box size is CSS controlled, and large values will
|
|
|
|
|
// generate large wEx and hEx values, we do a falliable allocation for
|
|
|
|
|
// the distance field. If allocation fails, we early exit and layout will
|
|
|
|
|
// be wrong, but we'll avoid aborting from OOM.
|
|
|
|
|
auto df = MakeUniqueFallible<dfType[]>(wEx * hEx);
|
|
|
|
|
if (!df) {
|
|
|
|
|
// Without a distance field, we can't reason about the float area.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-30 22:25:13 +03:00
|
|
|
|
const uint32_t bSize = aWM.IsVertical() ? wEx : hEx;
|
|
|
|
|
const uint32_t iSize = aWM.IsVertical() ? hEx : wEx;
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// First pass setting distance field, starting at top-left, three cases:
|
|
|
|
|
// 1) Expanded region pixel: set to MAX_MARGIN_5X.
|
|
|
|
|
// 2) Image pixel with alpha greater than threshold: set to 0.
|
|
|
|
|
// 3) Other pixel: set to minimum backward-looking neighborhood
|
|
|
|
|
// distance value, computed with 5-7-11 chamfer.
|
|
|
|
|
|
|
|
|
|
// Scan the pixels in a double loop. For horizontal writing modes, we do
|
|
|
|
|
// this row by row, from top to bottom. For vertical writing modes, we do
|
|
|
|
|
// column by column, from left to right. We define the two loops
|
|
|
|
|
// generically, then figure out the rows and cols within the inner loop.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t b = 0; b < bSize; ++b) {
|
|
|
|
|
for (uint32_t i = 0; i < iSize; ++i) {
|
|
|
|
|
const uint32_t col = aWM.IsVertical() ? b : i;
|
|
|
|
|
const uint32_t row = aWM.IsVertical() ? i : b;
|
|
|
|
|
const uint32_t index = col + row * wEx;
|
|
|
|
|
MOZ_ASSERT(index < (wEx * hEx),
|
2018-04-21 00:04:15 +03:00
|
|
|
|
"Our distance field index should be in-bounds.");
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// Handle our three cases, in order.
|
2018-04-21 00:04:15 +03:00
|
|
|
|
if (col < kExpansionPerSide || col >= wEx - kExpansionPerSide ||
|
|
|
|
|
row < kExpansionPerSide || row >= hEx - kExpansionPerSide) {
|
2018-02-22 22:11:03 +03:00
|
|
|
|
// Case 1: Expanded pixel.
|
|
|
|
|
df[index] = MAX_MARGIN_5X;
|
2018-05-26 01:12:02 +03:00
|
|
|
|
} else if ((int32_t)col >= dfOffset.x &&
|
|
|
|
|
(int32_t)col < (dfOffset.x + aImageSize.width) &&
|
|
|
|
|
(int32_t)row >= dfOffset.y &&
|
|
|
|
|
(int32_t)row < (dfOffset.y + aImageSize.height) &&
|
2018-02-22 22:11:03 +03:00
|
|
|
|
aAlphaPixels[col - dfOffset.x +
|
|
|
|
|
(row - dfOffset.y) * aStride] > threshold) {
|
|
|
|
|
// Case 2: Image pixel that is opaque.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
DebugOnly<uint32_t> alphaIndex =
|
|
|
|
|
col - dfOffset.x + (row - dfOffset.y) * aStride;
|
|
|
|
|
MOZ_ASSERT(alphaIndex < (aStride * h),
|
2018-04-21 00:04:15 +03:00
|
|
|
|
"Our aAlphaPixels index should be in-bounds.");
|
|
|
|
|
|
2018-02-22 22:11:03 +03:00
|
|
|
|
df[index] = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// Case 3: Other pixel.
|
2018-05-01 03:01:24 +03:00
|
|
|
|
if (aWM.IsVertical()) {
|
|
|
|
|
// Column-by-column, starting at the left, each column
|
|
|
|
|
// top-to-bottom.
|
|
|
|
|
// Backward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | |11| | | +
|
|
|
|
|
// +--+--+--+ | /|
|
|
|
|
|
// |11| 7| 5| | / |
|
|
|
|
|
// +--+--+--+ | / V
|
|
|
|
|
// | | 5| X| |/
|
|
|
|
|
// +--+--+--+ +
|
|
|
|
|
// |11| 7| |
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | |11| |
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of MAX_MARGIN_5X and the
|
|
|
|
|
// values of all of the numbered neighbors summed with the
|
|
|
|
|
// value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index - wEx - 2 < (iSize * bSize) &&
|
|
|
|
|
index + wEx - 2 < (iSize * bSize) &&
|
|
|
|
|
index - (wEx * 2) - 1 < (iSize * bSize),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(MAX_MARGIN_5X,
|
|
|
|
|
std::min<dfType>(df[index - wEx - 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index + wEx - 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index - wEx - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - 1] + 5,
|
|
|
|
|
std::min<dfType>(df[index + wEx - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
|
|
|
|
|
df[index - wEx] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-05-01 03:01:24 +03:00
|
|
|
|
} else {
|
|
|
|
|
// Row-by-row, starting at the top, each row left-to-right.
|
|
|
|
|
// Backward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | |11| |11| | ----+
|
|
|
|
|
// +--+--+--+--+--+ /
|
|
|
|
|
// |11| 7| 5| 7|11| /
|
|
|
|
|
// +--+--+--+--+--+ /
|
|
|
|
|
// | | 5| X| | | +-->
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of MAX_MARGIN_5X and the
|
|
|
|
|
// values of all of the numbered neighbors summed with the
|
|
|
|
|
// value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index - (wEx * 2) - 1 < (iSize * bSize) &&
|
|
|
|
|
index - wEx - 2 < (iSize * bSize),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(MAX_MARGIN_5X,
|
|
|
|
|
std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index - wEx - 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index - wEx - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - wEx] + 5,
|
|
|
|
|
std::min<dfType>(df[index - wEx + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - wEx + 2] + 11,
|
|
|
|
|
df[index - 1] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-05-01 03:01:24 +03:00
|
|
|
|
}
|
2018-02-22 22:11:03 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Okay, time for the second pass. This pass is in reverse order from
|
|
|
|
|
// the first pass. All of our opaque pixels have been set to 0, and all
|
|
|
|
|
// of our expanded region pixels have been set to MAX_MARGIN_5X. Other
|
|
|
|
|
// pixels have been set to some value between those two (inclusive) but
|
|
|
|
|
// this hasn't yet taken into account the neighbors that were processed
|
|
|
|
|
// after them in the first pass. This time we reverse iterate so we can
|
|
|
|
|
// apply the forward-looking chamfer.
|
|
|
|
|
|
|
|
|
|
// This time, we constrain our outer and inner loop to ignore the
|
|
|
|
|
// expanded region pixels. For each pixel we iterate, we set the df value
|
|
|
|
|
// to the minimum forward-looking neighborhood distance value, computed
|
|
|
|
|
// with a 5-7-11 chamfer. We also check each df value against the
|
|
|
|
|
// usedMargin5X threshold, and use that to set the iMin and iMax values
|
|
|
|
|
// for the interval we'll create for that block axis value (b).
|
|
|
|
|
|
|
|
|
|
// At the end of each row (or column in vertical writing modes),
|
|
|
|
|
// if any of the other pixels had a value less than usedMargin5X,
|
2018-04-21 00:04:15 +03:00
|
|
|
|
// we create an interval. Note: "bSize - kExpansionPerSide - 1" is the
|
|
|
|
|
// index of the final row of pixels before the trailing expanded region.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t b = bSize - kExpansionPerSide - 1; b >= kExpansionPerSide;
|
2018-04-21 00:04:15 +03:00
|
|
|
|
--b) {
|
2018-02-22 22:11:03 +03:00
|
|
|
|
// iMin tracks the first df pixel and iMax the last df pixel whose
|
|
|
|
|
// df[] value is less than usedMargin5X. Set iMin and iMax in
|
|
|
|
|
// preparation for this row or column.
|
|
|
|
|
int32_t iMin = iSize;
|
|
|
|
|
int32_t iMax = -1;
|
|
|
|
|
|
2018-04-21 00:04:15 +03:00
|
|
|
|
// Note: "iSize - kExpansionPerSide - 1" is the index of the final row
|
|
|
|
|
// of pixels before the trailing expanded region.
|
2018-04-30 22:25:13 +03:00
|
|
|
|
for (uint32_t i = iSize - kExpansionPerSide - 1; i >= kExpansionPerSide;
|
2018-04-21 00:04:15 +03:00
|
|
|
|
--i) {
|
2018-04-30 22:25:13 +03:00
|
|
|
|
const uint32_t col = aWM.IsVertical() ? b : i;
|
|
|
|
|
const uint32_t row = aWM.IsVertical() ? i : b;
|
|
|
|
|
const uint32_t index = col + row * wEx;
|
|
|
|
|
MOZ_ASSERT(index < (wEx * hEx),
|
2018-04-21 00:04:15 +03:00
|
|
|
|
"Our distance field index should be in-bounds.");
|
2018-02-22 22:11:03 +03:00
|
|
|
|
|
|
|
|
|
// Only apply the chamfer calculation if the df value is not
|
|
|
|
|
// already 0, since the chamfer can only reduce the value.
|
|
|
|
|
if (df[index]) {
|
2018-05-01 03:01:24 +03:00
|
|
|
|
if (aWM.IsVertical()) {
|
|
|
|
|
// Column-by-column, starting at the right, each column
|
|
|
|
|
// bottom-to-top.
|
|
|
|
|
// Forward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | |11| | +
|
|
|
|
|
// +--+--+--+ /|
|
|
|
|
|
// | | 7|11| A / |
|
|
|
|
|
// +--+--+--+ | / |
|
|
|
|
|
// | X| 5| | |/ |
|
|
|
|
|
// +--+--+--+ + |
|
|
|
|
|
// | 5| 7|11|
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
// | |11| |
|
|
|
|
|
// +--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of its current value and
|
|
|
|
|
// the values of all of the numbered neighbors summed with
|
|
|
|
|
// the value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index + wEx + 2 < (wEx * hEx) &&
|
|
|
|
|
index + (wEx * 2) + 1 < (wEx * hEx) &&
|
|
|
|
|
index - (wEx * 2) + 1 < (wEx * hEx),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(df[index],
|
|
|
|
|
std::min<dfType>(df[index + wEx + 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index - wEx + 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index + wEx + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + 1] + 5,
|
|
|
|
|
std::min<dfType>(df[index - wEx + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
|
|
|
|
|
df[index + wEx] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-05-01 03:01:24 +03:00
|
|
|
|
} else {
|
|
|
|
|
// Row-by-row, starting at the bottom, each row right-to-left.
|
|
|
|
|
// Forward-looking neighborhood distance from target pixel X
|
|
|
|
|
// with chamfer 5-7-11 looks like:
|
|
|
|
|
//
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
// | | | X| 5| | <--+
|
|
|
|
|
// +--+--+--+--+--+ /
|
|
|
|
|
// |11| 7| 5| 7|11| /
|
|
|
|
|
// +--+--+--+--+--+ /
|
|
|
|
|
// | |11| |11| | +----
|
|
|
|
|
// +--+--+--+--+--+
|
|
|
|
|
//
|
|
|
|
|
// X should be set to the minimum of its current value and
|
|
|
|
|
// the values of all of the numbered neighbors summed with
|
|
|
|
|
// the value in that chamfer cell.
|
|
|
|
|
MOZ_ASSERT(index + (wEx * 2) + 1 < (wEx * hEx) &&
|
|
|
|
|
index + wEx + 2 < (wEx * hEx),
|
|
|
|
|
"Our distance field most extreme indices should be "
|
|
|
|
|
"in-bounds.");
|
|
|
|
|
|
2018-12-05 21:44:05 +03:00
|
|
|
|
// clang-format off
|
|
|
|
|
df[index] = std::min<dfType>(df[index],
|
|
|
|
|
std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
|
|
|
|
|
std::min<dfType>(df[index + wEx + 2] + 11,
|
|
|
|
|
std::min<dfType>(df[index + wEx + 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + wEx] + 5,
|
|
|
|
|
std::min<dfType>(df[index + wEx - 1] + 7,
|
|
|
|
|
std::min<dfType>(df[index + wEx - 2] + 11,
|
|
|
|
|
df[index + 1] + 5))))))));
|
|
|
|
|
// clang-format on
|
2018-05-01 03:01:24 +03:00
|
|
|
|
}
|
2018-02-22 22:11:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally, we can check the df value and see if it's less than
|
|
|
|
|
// or equal to the usedMargin5X value.
|
|
|
|
|
if (df[index] <= usedMargin5X) {
|
|
|
|
|
if (iMax == -1) {
|
|
|
|
|
iMax = i;
|
|
|
|
|
}
|
2018-04-30 22:25:13 +03:00
|
|
|
|
MOZ_ASSERT(iMin > (int32_t)i);
|
2018-02-22 22:11:03 +03:00
|
|
|
|
iMin = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iMax != -1) {
|
|
|
|
|
// Our interval values, iMin, iMax, and b are all calculated from
|
|
|
|
|
// the expanded region, which is based on the margin rect. To create
|
2018-04-21 00:04:15 +03:00
|
|
|
|
// our interval, we have to subtract kExpansionPerSide from (iMin,
|
|
|
|
|
// iMax, and b) to account for the expanded region edges. This
|
|
|
|
|
// produces coords that are relative to our margin-rect, so we pass
|
|
|
|
|
// in aMarginRect.TopLeft() to make CreateInterval convert to our
|
2018-02-22 22:11:03 +03:00
|
|
|
|
// container's coordinate space.
|
2018-04-21 00:04:15 +03:00
|
|
|
|
CreateInterval(iMin - kExpansionPerSide, iMax - kExpansionPerSide,
|
|
|
|
|
b - kExpansionPerSide, aAppUnitsPerDevPixel,
|
2018-02-22 22:11:03 +03:00
|
|
|
|
aMarginRect.TopLeft(), aWM, aContainerSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!aWM.IsVerticalRL()) {
|
|
|
|
|
// Anything other than vertical-rl or sideways-rl.
|
|
|
|
|
// Because we assembled our intervals on the bottom-up pass,
|
|
|
|
|
// they are reversed for most writing modes. Reverse them to
|
|
|
|
|
// keep the array sorted on the block direction.
|
|
|
|
|
mIntervals.Reverse();
|
|
|
|
|
}
|
2018-01-25 09:55:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!mIntervals.IsEmpty()) {
|
|
|
|
|
mBStart = mIntervals[0].Y();
|
|
|
|
|
mBEnd = mIntervals.LastElement().YMost();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-28 21:43:43 +03:00
|
|
|
|
|
|
|
|
|
void nsFloatManager::ImageShapeInfo::CreateInterval(
|
|
|
|
|
int32_t aIMin, int32_t aIMax, int32_t aB, int32_t aAppUnitsPerDevPixel,
|
|
|
|
|
const nsPoint& aOffsetFromContainer, WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
// Store an interval as an nsRect with our inline axis values stored in x
|
|
|
|
|
// and our block axis values stored in y. The position is dependent on
|
|
|
|
|
// the writing mode, but the size is the same for all writing modes.
|
|
|
|
|
|
|
|
|
|
// Size is the difference in inline axis edges stored as x, and one
|
|
|
|
|
// block axis pixel stored as y. For the inline axis, we add 1 to aIMax
|
|
|
|
|
// because we want to capture the far edge of the last pixel.
|
|
|
|
|
nsSize size(((aIMax + 1) - aIMin) * aAppUnitsPerDevPixel,
|
|
|
|
|
aAppUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Since we started our scanning of the image pixels from the top left,
|
|
|
|
|
// the interval position starts from the origin of the content rect,
|
|
|
|
|
// converted to logical coordinates.
|
|
|
|
|
nsPoint origin =
|
|
|
|
|
ConvertToFloatLogical(aOffsetFromContainer, aWM, aContainerSize);
|
|
|
|
|
|
|
|
|
|
// Depending on the writing mode, we now move the origin.
|
|
|
|
|
if (aWM.IsVerticalRL()) {
|
|
|
|
|
// vertical-rl or sideways-rl.
|
|
|
|
|
// These writing modes proceed from the top right, and each interval
|
|
|
|
|
// moves in a positive inline direction and negative block direction.
|
|
|
|
|
// That means that the intervals will be reversed after all have been
|
|
|
|
|
// constructed. We add 1 to aB to capture the end of the block axis pixel.
|
|
|
|
|
origin.MoveBy(aIMin * aAppUnitsPerDevPixel,
|
|
|
|
|
(aB + 1) * -aAppUnitsPerDevPixel);
|
2018-05-04 01:51:06 +03:00
|
|
|
|
} else if (aWM.IsVerticalLR() && !aWM.IsLineInverted()) {
|
2018-02-28 21:43:43 +03:00
|
|
|
|
// sideways-lr.
|
2018-05-04 01:51:06 +03:00
|
|
|
|
// Checking IsLineInverted is the only reliable way to distinguish
|
|
|
|
|
// vertical-lr from sideways-lr. IsSideways and IsInlineReversed are both
|
|
|
|
|
// affected by bidi and text-direction, and so complicate detection.
|
2018-02-28 21:43:43 +03:00
|
|
|
|
// These writing modes proceed from the bottom left, and each interval
|
|
|
|
|
// moves in a negative inline direction and a positive block direction.
|
|
|
|
|
// We add 1 to aIMax to capture the end of the inline axis pixel.
|
|
|
|
|
origin.MoveBy((aIMax + 1) * -aAppUnitsPerDevPixel,
|
|
|
|
|
aB * aAppUnitsPerDevPixel);
|
|
|
|
|
} else {
|
|
|
|
|
// horizontal-tb or vertical-lr.
|
|
|
|
|
// These writing modes proceed from the top left and each interval
|
|
|
|
|
// moves in a positive step in both inline and block directions.
|
|
|
|
|
origin.MoveBy(aIMin * aAppUnitsPerDevPixel, aB * aAppUnitsPerDevPixel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mIntervals.AppendElement(nsRect(origin, size));
|
|
|
|
|
}
|
2018-02-28 21:43:43 +03:00
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
nscoord nsFloatManager::ImageShapeInfo::LineLeft(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
2018-04-19 22:17:44 +03:00
|
|
|
|
return LineEdge(mIntervals, aBStart, aBEnd, true);
|
2018-01-25 09:55:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nscoord nsFloatManager::ImageShapeInfo::LineRight(const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
2018-04-19 22:17:44 +03:00
|
|
|
|
return LineEdge(mIntervals, aBStart, aBEnd, false);
|
2018-01-25 09:55:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsFloatManager::ImageShapeInfo::Translate(nscoord aLineLeft,
|
|
|
|
|
nscoord aBlockStart) {
|
|
|
|
|
for (nsRect& interval : mIntervals) {
|
|
|
|
|
interval.MoveBy(aLineLeft, aBlockStart);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mBStart += aBlockStart;
|
|
|
|
|
mBEnd += aBlockStart;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// FloatInfo
|
|
|
|
|
|
2015-03-22 12:44:48 +03:00
|
|
|
|
nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, nscoord aLineLeft,
|
2018-04-13 16:01:28 +03:00
|
|
|
|
nscoord aBlockStart,
|
2016-10-12 09:26:26 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize)
|
2015-03-22 12:44:48 +03:00
|
|
|
|
: mFrame(aFrame),
|
2018-07-12 09:42:14 +03:00
|
|
|
|
mLeftBEnd(nscoord_MIN),
|
|
|
|
|
mRightBEnd(nscoord_MIN),
|
2017-02-16 05:51:48 +03:00
|
|
|
|
mRect(ShapeInfo::ConvertToFloatLogical(aMarginRect, aWM, aContainerSize) +
|
|
|
|
|
nsPoint(aLineLeft, aBlockStart)) {
|
2009-01-05 03:39:54 +03:00
|
|
|
|
MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
|
2016-10-12 11:06:25 +03:00
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
if (IsEmpty()) {
|
|
|
|
|
// Per spec, a float area defined by a shape is clipped to the float’s
|
|
|
|
|
// margin box. Therefore, no need to create a shape info if the float's
|
|
|
|
|
// margin box is empty, since a float area can only be smaller than the
|
|
|
|
|
// margin box.
|
|
|
|
|
|
|
|
|
|
// https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 17:46:35 +03:00
|
|
|
|
const nsStyleDisplay* styleDisplay = mFrame->StyleDisplay();
|
|
|
|
|
const StyleShapeSource& shapeOutside = styleDisplay->mShapeOutside;
|
2016-10-12 11:06:25 +03:00
|
|
|
|
|
2018-02-27 01:47:31 +03:00
|
|
|
|
nscoord shapeMargin = (shapeOutside.GetType() == StyleShapeSourceType::None)
|
|
|
|
|
? 0
|
|
|
|
|
: nsLayoutUtils::ResolveToLength<true>(
|
2018-06-11 17:46:35 +03:00
|
|
|
|
styleDisplay->mShapeMargin,
|
2018-02-27 01:47:31 +03:00
|
|
|
|
LogicalSize(aWM, aContainerSize).ISize(aWM));
|
|
|
|
|
|
2017-11-17 11:34:37 +03:00
|
|
|
|
switch (shapeOutside.GetType()) {
|
|
|
|
|
case StyleShapeSourceType::None:
|
|
|
|
|
// No need to create shape info.
|
|
|
|
|
return;
|
|
|
|
|
|
2018-07-07 00:31:52 +03:00
|
|
|
|
case StyleShapeSourceType::Path:
|
|
|
|
|
MOZ_ASSERT_UNREACHABLE("shape-outside doesn't have Path source type!");
|
|
|
|
|
return;
|
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
case StyleShapeSourceType::Image: {
|
2018-06-11 17:46:35 +03:00
|
|
|
|
float shapeImageThreshold = styleDisplay->mShapeImageThreshold;
|
2018-11-02 21:19:06 +03:00
|
|
|
|
mShapeInfo = ShapeInfo::CreateImageShape(
|
|
|
|
|
shapeOutside.ShapeImage(), shapeImageThreshold, shapeMargin, mFrame,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
aMarginRect, aWM, aContainerSize);
|
|
|
|
|
if (!mShapeInfo) {
|
|
|
|
|
// Image is not ready, or fails to load, etc.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-17 11:34:37 +03:00
|
|
|
|
|
|
|
|
|
case StyleShapeSourceType::Box: {
|
|
|
|
|
// Initialize <shape-box>'s reference rect.
|
|
|
|
|
LogicalRect shapeBoxRect = ShapeInfo::ComputeShapeBoxRect(
|
|
|
|
|
shapeOutside, mFrame, aMarginRect, aWM);
|
2018-02-27 01:47:31 +03:00
|
|
|
|
mShapeInfo = ShapeInfo::CreateShapeBox(mFrame, shapeMargin, shapeBoxRect,
|
2017-11-21 13:24:34 +03:00
|
|
|
|
aWM, aContainerSize);
|
2017-11-17 11:34:37 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case StyleShapeSourceType::Shape: {
|
2018-11-02 21:19:06 +03:00
|
|
|
|
const StyleBasicShape& basicShape = shapeOutside.BasicShape();
|
2017-11-17 11:34:37 +03:00
|
|
|
|
// Initialize <shape-box>'s reference rect.
|
|
|
|
|
LogicalRect shapeBoxRect = ShapeInfo::ComputeShapeBoxRect(
|
|
|
|
|
shapeOutside, mFrame, aMarginRect, aWM);
|
2018-02-27 00:13:03 +03:00
|
|
|
|
mShapeInfo = ShapeInfo::CreateBasicShape(basicShape, shapeMargin, mFrame,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
shapeBoxRect, aMarginRect, aWM,
|
2017-11-17 11:34:37 +03:00
|
|
|
|
aContainerSize);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-12 11:06:25 +03:00
|
|
|
|
}
|
2017-01-23 12:17:37 +03:00
|
|
|
|
|
2017-02-02 06:53:06 +03:00
|
|
|
|
MOZ_ASSERT(mShapeInfo,
|
|
|
|
|
"All shape-outside values except none should have mShapeInfo!");
|
|
|
|
|
|
2017-01-23 12:17:37 +03:00
|
|
|
|
// Translate the shape to the same origin as nsFloatManager.
|
|
|
|
|
mShapeInfo->Translate(aLineLeft, aBlockStart);
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
2017-01-06 11:36:19 +03:00
|
|
|
|
nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther)
|
2018-05-30 22:15:35 +03:00
|
|
|
|
: mFrame(std::move(aOther.mFrame)),
|
|
|
|
|
mLeftBEnd(std::move(aOther.mLeftBEnd)),
|
|
|
|
|
mRightBEnd(std::move(aOther.mRightBEnd)),
|
|
|
|
|
mRect(std::move(aOther.mRect)),
|
|
|
|
|
mShapeInfo(std::move(aOther.mShapeInfo)) {
|
2009-01-05 04:25:58 +03:00
|
|
|
|
MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
nsFloatManager::FloatInfo::~FloatInfo() {
|
|
|
|
|
MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-03-13 07:03:39 +03:00
|
|
|
|
nscoord nsFloatManager::FloatInfo::LineLeft(ShapeType aShapeType,
|
2016-10-13 11:28:38 +03:00
|
|
|
|
const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
2016-10-12 11:06:25 +03:00
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return LineLeft();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
2017-01-06 11:36:30 +03:00
|
|
|
|
if (!mShapeInfo) {
|
2016-10-12 11:06:25 +03:00
|
|
|
|
return LineLeft();
|
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
// Clip the flow area to the margin-box because
|
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#relation-to-box-model-and-float-behavior
|
|
|
|
|
// says "When a shape is used to define a float area, the shape is clipped
|
|
|
|
|
// to the float’s margin box."
|
2017-03-13 07:03:39 +03:00
|
|
|
|
return std::max(LineLeft(), mShapeInfo->LineLeft(aBStart, aBEnd));
|
2016-10-12 11:06:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-13 07:03:39 +03:00
|
|
|
|
nscoord nsFloatManager::FloatInfo::LineRight(ShapeType aShapeType,
|
2016-10-13 11:28:38 +03:00
|
|
|
|
const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd) const {
|
2016-10-12 11:06:25 +03:00
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return LineRight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
2017-01-06 11:36:30 +03:00
|
|
|
|
if (!mShapeInfo) {
|
2016-10-12 11:06:25 +03:00
|
|
|
|
return LineRight();
|
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
// Clip the flow area to the margin-box. See LineLeft().
|
2017-03-13 07:03:39 +03:00
|
|
|
|
return std::min(LineRight(), mShapeInfo->LineRight(aBStart, aBEnd));
|
2017-01-06 11:36:30 +03:00
|
|
|
|
}
|
2016-10-12 11:06:25 +03:00
|
|
|
|
|
2017-01-06 11:36:30 +03:00
|
|
|
|
nscoord nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const {
|
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return BStart();
|
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
|
2017-01-06 11:36:30 +03:00
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
|
|
|
|
if (!mShapeInfo) {
|
|
|
|
|
return BStart();
|
2017-01-06 11:36:30 +03:00
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
// Clip the flow area to the margin-box. See LineLeft().
|
|
|
|
|
return std::max(BStart(), mShapeInfo->BStart());
|
2017-01-06 11:36:30 +03:00
|
|
|
|
}
|
2017-01-12 16:19:20 +03:00
|
|
|
|
|
2017-01-06 11:36:30 +03:00
|
|
|
|
nscoord nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const {
|
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return BEnd();
|
|
|
|
|
}
|
2017-01-12 16:19:20 +03:00
|
|
|
|
|
2017-01-06 11:36:30 +03:00
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
|
|
|
|
if (!mShapeInfo) {
|
|
|
|
|
return BEnd();
|
|
|
|
|
}
|
2017-01-06 11:36:43 +03:00
|
|
|
|
// Clip the flow area to the margin-box. See LineLeft().
|
|
|
|
|
return std::min(BEnd(), mShapeInfo->BEnd());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nsFloatManager::FloatInfo::IsEmpty(ShapeType aShapeType) const {
|
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return IsEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
|
|
|
|
if (!mShapeInfo) {
|
|
|
|
|
return IsEmpty();
|
|
|
|
|
}
|
|
|
|
|
return mShapeInfo->IsEmpty();
|
2016-10-12 11:06:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-30 20:03:45 +03:00
|
|
|
|
bool nsFloatManager::FloatInfo::MayNarrowInBlockDirection(
|
|
|
|
|
ShapeType aShapeType) const {
|
|
|
|
|
// This function mirrors the cases of the three argument versions of
|
|
|
|
|
// LineLeft() and LineRight(). This function returns true if and only if
|
|
|
|
|
// either of those functions could possibly return "narrower" values with
|
|
|
|
|
// increasing aBStart values. "Narrower" means closer to the far end of
|
|
|
|
|
// the float shape.
|
|
|
|
|
if (aShapeType == ShapeType::Margin) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
|
|
|
|
|
if (!mShapeInfo) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mShapeInfo->MayNarrowInBlockDirection();
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-23 12:18:07 +03:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// ShapeInfo
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
LogicalRect nsFloatManager::ShapeInfo::ComputeShapeBoxRect(
|
2017-02-21 18:26:17 +03:00
|
|
|
|
const StyleShapeSource& aShapeOutside, nsIFrame* const aFrame,
|
2017-12-19 10:22:36 +03:00
|
|
|
|
const LogicalRect& aMarginRect, WritingMode aWM) {
|
2017-02-16 05:51:48 +03:00
|
|
|
|
LogicalRect rect = aMarginRect;
|
|
|
|
|
|
|
|
|
|
switch (aShapeOutside.GetReferenceBox()) {
|
2017-03-16 10:18:10 +03:00
|
|
|
|
case StyleGeometryBox::ContentBox:
|
2017-02-16 05:51:48 +03:00
|
|
|
|
rect.Deflate(aWM, aFrame->GetLogicalUsedPadding(aWM));
|
|
|
|
|
MOZ_FALLTHROUGH;
|
2017-03-16 10:18:10 +03:00
|
|
|
|
case StyleGeometryBox::PaddingBox:
|
2017-02-16 05:51:48 +03:00
|
|
|
|
rect.Deflate(aWM, aFrame->GetLogicalUsedBorder(aWM));
|
|
|
|
|
MOZ_FALLTHROUGH;
|
2017-03-16 10:18:10 +03:00
|
|
|
|
case StyleGeometryBox::BorderBox:
|
2017-02-16 05:51:48 +03:00
|
|
|
|
rect.Deflate(aWM, aFrame->GetLogicalUsedMargin(aWM));
|
|
|
|
|
break;
|
2017-03-16 10:18:10 +03:00
|
|
|
|
case StyleGeometryBox::MarginBox:
|
2017-02-16 05:51:48 +03:00
|
|
|
|
// Do nothing. rect is already a margin rect.
|
|
|
|
|
break;
|
2017-02-21 18:26:17 +03:00
|
|
|
|
case StyleGeometryBox::NoBox:
|
|
|
|
|
default:
|
2017-02-16 05:51:48 +03:00
|
|
|
|
MOZ_ASSERT(aShapeOutside.GetType() != StyleShapeSourceType::Box,
|
|
|
|
|
"Box source type must have <shape-box> specified!");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 05:51:48 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreateShapeBox(nsIFrame* const aFrame,
|
2018-02-27 01:47:31 +03:00
|
|
|
|
nscoord aShapeMargin,
|
2017-02-16 05:51:48 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
nsRect logicalShapeBoxRect =
|
|
|
|
|
ConvertToFloatLogical(aShapeBoxRect, aWM, aContainerSize);
|
|
|
|
|
|
2018-02-27 01:47:31 +03:00
|
|
|
|
// Inflate logicalShapeBoxRect by aShapeMargin.
|
|
|
|
|
logicalShapeBoxRect.Inflate(aShapeMargin);
|
|
|
|
|
|
2017-02-16 05:51:48 +03:00
|
|
|
|
nscoord physicalRadii[8];
|
|
|
|
|
bool hasRadii = aFrame->GetShapeBoxBorderRadii(physicalRadii);
|
|
|
|
|
if (!hasRadii) {
|
2017-02-16 05:51:48 +03:00
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
|
|
|
|
|
UniquePtr<nscoord[]>());
|
2017-02-16 05:51:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-27 01:47:31 +03:00
|
|
|
|
// Add aShapeMargin to each of the radii.
|
|
|
|
|
for (nscoord& r : physicalRadii) {
|
|
|
|
|
r += aShapeMargin;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 05:51:48 +03:00
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(
|
|
|
|
|
logicalShapeBoxRect, ConvertToFloatLogical(physicalRadii, aWM));
|
2017-02-16 05:51:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-21 13:24:34 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreateBasicShape(const StyleBasicShape& aBasicShape,
|
2018-02-27 00:13:03 +03:00
|
|
|
|
nscoord aShapeMargin,
|
|
|
|
|
nsIFrame* const aFrame,
|
2017-11-21 13:24:34 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
2017-11-21 13:24:34 +03:00
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
2019-05-09 14:24:57 +03:00
|
|
|
|
switch (aBasicShape.tag) {
|
|
|
|
|
case StyleBasicShape::Tag::Polygon:
|
2018-04-19 21:27:28 +03:00
|
|
|
|
return CreatePolygon(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
|
|
|
|
|
aMarginRect, aWM, aContainerSize);
|
2019-05-09 14:24:57 +03:00
|
|
|
|
case StyleBasicShape::Tag::Circle:
|
|
|
|
|
case StyleBasicShape::Tag::Ellipse:
|
2018-02-27 00:13:03 +03:00
|
|
|
|
return CreateCircleOrEllipse(aBasicShape, aShapeMargin, aFrame,
|
|
|
|
|
aShapeBoxRect, aWM, aContainerSize);
|
2019-05-09 14:24:57 +03:00
|
|
|
|
case StyleBasicShape::Tag::Inset:
|
2018-04-13 21:29:14 +03:00
|
|
|
|
return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect, aWM,
|
|
|
|
|
aContainerSize);
|
2017-11-21 13:24:34 +03:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 05:51:49 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreateInset(const StyleBasicShape& aBasicShape,
|
2018-04-13 21:29:14 +03:00
|
|
|
|
nscoord aShapeMargin, nsIFrame* aFrame,
|
2017-02-16 05:51:49 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
// Use physical coordinates to compute inset() because the top, right,
|
|
|
|
|
// bottom and left offsets are physical.
|
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#funcdef-inset
|
|
|
|
|
nsRect physicalShapeBoxRect =
|
|
|
|
|
aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
|
|
|
|
|
nsRect insetRect =
|
|
|
|
|
ShapeUtils::ComputeInsetRect(aBasicShape, physicalShapeBoxRect);
|
|
|
|
|
|
|
|
|
|
nsRect logicalInsetRect = ConvertToFloatLogical(
|
|
|
|
|
LogicalRect(aWM, insetRect, aContainerSize), aWM, aContainerSize);
|
|
|
|
|
nscoord physicalRadii[8];
|
|
|
|
|
bool hasRadii = ShapeUtils::ComputeInsetRadii(
|
|
|
|
|
aBasicShape, insetRect, physicalShapeBoxRect, physicalRadii);
|
2018-04-13 21:29:14 +03:00
|
|
|
|
|
|
|
|
|
// With a zero shape-margin, we will be able to use the fast constructor.
|
|
|
|
|
if (aShapeMargin == 0) {
|
|
|
|
|
if (!hasRadii) {
|
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
|
|
|
|
|
UniquePtr<nscoord[]>());
|
|
|
|
|
}
|
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(
|
|
|
|
|
logicalInsetRect, ConvertToFloatLogical(physicalRadii, aWM));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// With a positive shape-margin, we might still be able to use the fast
|
|
|
|
|
// constructor. With no radii, we can build a rounded box by inflating
|
|
|
|
|
// logicalInsetRect, and supplying aShapeMargin as the radius for all
|
|
|
|
|
// corners.
|
2017-02-16 05:51:49 +03:00
|
|
|
|
if (!hasRadii) {
|
2018-04-13 21:29:14 +03:00
|
|
|
|
logicalInsetRect.Inflate(aShapeMargin);
|
|
|
|
|
auto logicalRadii = MakeUnique<nscoord[]>(8);
|
|
|
|
|
for (int32_t i = 0; i < 8; ++i) {
|
|
|
|
|
logicalRadii[i] = aShapeMargin;
|
|
|
|
|
}
|
2017-02-16 05:51:49 +03:00
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
|
2018-05-30 22:15:35 +03:00
|
|
|
|
std::move(logicalRadii));
|
2018-04-13 21:29:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:39:50 +03:00
|
|
|
|
// If we have radii, and they have balanced/equal corners, we can inflate
|
|
|
|
|
// both logicalInsetRect and all the radii and use the fast constructor.
|
|
|
|
|
if (RoundedBoxShapeInfo::EachCornerHasBalancedRadii(physicalRadii)) {
|
2018-04-13 21:29:14 +03:00
|
|
|
|
logicalInsetRect.Inflate(aShapeMargin);
|
|
|
|
|
for (nscoord& r : physicalRadii) {
|
|
|
|
|
r += aShapeMargin;
|
|
|
|
|
}
|
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(
|
|
|
|
|
logicalInsetRect, ConvertToFloatLogical(physicalRadii, aWM));
|
2017-02-16 05:51:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 21:39:50 +03:00
|
|
|
|
// With positive shape-margin and elliptical radii, we have to use the
|
2018-04-13 21:29:14 +03:00
|
|
|
|
// slow constructor.
|
|
|
|
|
nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
|
|
|
|
|
int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
|
2017-02-16 05:51:49 +03:00
|
|
|
|
return MakeUnique<RoundedBoxShapeInfo>(
|
|
|
|
|
logicalInsetRect, ConvertToFloatLogical(physicalRadii, aWM), aShapeMargin,
|
2018-04-13 21:29:14 +03:00
|
|
|
|
appUnitsPerDevPixel);
|
2017-02-16 05:51:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-25 11:01:51 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
|
2018-11-02 21:19:06 +03:00
|
|
|
|
const StyleBasicShape& aBasicShape, nscoord aShapeMargin,
|
2017-01-25 11:01:51 +03:00
|
|
|
|
nsIFrame* const aFrame, const LogicalRect& aShapeBoxRect, WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
// Use physical coordinates to compute the center of circle() or ellipse()
|
|
|
|
|
// since the <position> keywords such as 'left', 'top', etc. are physical.
|
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#funcdef-ellipse
|
|
|
|
|
nsRect physicalShapeBoxRect =
|
|
|
|
|
aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
|
|
|
|
|
nsPoint physicalCenter = ShapeUtils::ComputeCircleOrEllipseCenter(
|
|
|
|
|
aBasicShape, physicalShapeBoxRect);
|
2017-02-16 05:51:48 +03:00
|
|
|
|
nsPoint logicalCenter =
|
|
|
|
|
ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
|
2017-01-25 11:01:51 +03:00
|
|
|
|
|
|
|
|
|
// Compute the circle or ellipse radii.
|
|
|
|
|
nsSize radii;
|
2019-05-09 14:24:57 +03:00
|
|
|
|
if (aBasicShape.IsCircle()) {
|
2017-01-25 11:01:51 +03:00
|
|
|
|
nscoord radius = ShapeUtils::ComputeCircleRadius(
|
|
|
|
|
aBasicShape, physicalCenter, physicalShapeBoxRect);
|
2018-02-27 00:13:03 +03:00
|
|
|
|
// Circles can use the three argument, math constructor for
|
|
|
|
|
// EllipseShapeInfo.
|
2017-01-25 11:01:51 +03:00
|
|
|
|
radii = nsSize(radius, radius);
|
2018-02-27 00:13:03 +03:00
|
|
|
|
return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 14:24:57 +03:00
|
|
|
|
MOZ_ASSERT(aBasicShape.IsEllipse());
|
2018-02-27 00:13:03 +03:00
|
|
|
|
nsSize physicalRadii = ShapeUtils::ComputeEllipseRadii(
|
|
|
|
|
aBasicShape, physicalCenter, physicalShapeBoxRect);
|
|
|
|
|
LogicalSize logicalRadii(aWM, physicalRadii);
|
|
|
|
|
radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
|
|
|
|
|
|
|
|
|
|
// If radii are close to the same value, or if aShapeMargin is small
|
|
|
|
|
// enough (as specified in css pixels), then we can use the three argument
|
|
|
|
|
// constructor for EllipseShapeInfo, which uses math for a more efficient
|
|
|
|
|
// method of float area computation.
|
|
|
|
|
if (EllipseShapeInfo::ShapeMarginIsNegligible(aShapeMargin) ||
|
|
|
|
|
EllipseShapeInfo::RadiiAreRoughlyEqual(radii)) {
|
|
|
|
|
return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin);
|
2017-01-25 11:01:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-27 00:13:03 +03:00
|
|
|
|
// We have to use the full constructor for EllipseShapeInfo. This
|
|
|
|
|
// computes the float area using a rasterization method.
|
|
|
|
|
nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
|
|
|
|
|
int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
|
|
|
|
|
return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin,
|
|
|
|
|
appUnitsPerDevPixel);
|
2017-01-25 11:01:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-13 07:03:40 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreatePolygon(const StyleBasicShape& aBasicShape,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
nscoord aShapeMargin,
|
|
|
|
|
nsIFrame* const aFrame,
|
2017-03-13 07:03:40 +03:00
|
|
|
|
const LogicalRect& aShapeBoxRect,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
2017-03-13 07:03:40 +03:00
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
|
|
|
|
// Use physical coordinates to compute each (xi, yi) vertex because CSS
|
|
|
|
|
// represents them using physical coordinates.
|
|
|
|
|
// https://drafts.csswg.org/css-shapes-1/#funcdef-polygon
|
|
|
|
|
nsRect physicalShapeBoxRect =
|
|
|
|
|
aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
|
|
|
|
|
|
|
|
|
|
// Get physical vertices.
|
|
|
|
|
nsTArray<nsPoint> vertices =
|
|
|
|
|
ShapeUtils::ComputePolygonVertices(aBasicShape, physicalShapeBoxRect);
|
|
|
|
|
|
|
|
|
|
// Convert all the physical vertices to logical.
|
|
|
|
|
for (nsPoint& vertex : vertices) {
|
|
|
|
|
vertex = ConvertToFloatLogical(vertex, aWM, aContainerSize);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-19 21:27:28 +03:00
|
|
|
|
if (aShapeMargin == 0) {
|
2018-05-30 22:15:35 +03:00
|
|
|
|
return MakeUnique<PolygonShapeInfo>(std::move(vertices));
|
2018-04-19 21:27:28 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsRect marginRect = ConvertToFloatLogical(aMarginRect, aWM, aContainerSize);
|
|
|
|
|
|
|
|
|
|
// We have to use the full constructor for PolygonShapeInfo. This
|
|
|
|
|
// computes the float area using a rasterization method.
|
|
|
|
|
int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
2018-05-30 22:15:35 +03:00
|
|
|
|
return MakeUnique<PolygonShapeInfo>(std::move(vertices), aShapeMargin,
|
2018-04-19 21:27:28 +03:00
|
|
|
|
appUnitsPerDevPixel, marginRect);
|
2017-03-13 07:03:40 +03:00
|
|
|
|
}
|
2017-01-25 11:01:51 +03:00
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
|
|
|
|
|
nsFloatManager::ShapeInfo::CreateImageShape(const nsStyleImage& aShapeImage,
|
|
|
|
|
float aShapeImageThreshold,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
nscoord aShapeMargin,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
nsIFrame* const aFrame,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
const LogicalRect& aMarginRect,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
WritingMode aWM,
|
|
|
|
|
const nsSize& aContainerSize) {
|
2018-11-02 21:19:06 +03:00
|
|
|
|
MOZ_ASSERT(
|
|
|
|
|
&aShapeImage == &aFrame->StyleDisplay()->mShapeOutside.ShapeImage(),
|
2018-01-25 09:55:18 +03:00
|
|
|
|
"aFrame should be the frame that we got aShapeImage from");
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-11-02 21:19:06 +03:00
|
|
|
|
nsImageRenderer imageRenderer(aFrame, &aShapeImage,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
nsImageRenderer::FLAG_SYNC_DECODE_IMAGES);
|
|
|
|
|
|
|
|
|
|
if (!imageRenderer.PrepareImage()) {
|
2019-03-28 05:54:16 +03:00
|
|
|
|
// The image is not ready yet. Boost its loading priority since it will
|
|
|
|
|
// affect layout.
|
|
|
|
|
if (aShapeImage.GetType() == eStyleImageType_Image) {
|
|
|
|
|
if (imgRequestProxy* req = aShapeImage.GetImageData()) {
|
|
|
|
|
req->BoostPriority(imgIRequest::CATEGORY_SIZE_QUERY);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-25 09:55:18 +03:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsRect contentRect = aFrame->GetContentRect();
|
|
|
|
|
|
|
|
|
|
// Create a draw target and draw shape image on it.
|
|
|
|
|
nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
|
|
|
|
|
int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
|
|
|
|
|
LayoutDeviceIntSize contentSizeInDevPixels =
|
|
|
|
|
LayoutDeviceIntSize::FromAppUnitsRounded(contentRect.Size(),
|
|
|
|
|
appUnitsPerDevPixel);
|
|
|
|
|
|
|
|
|
|
// Use empty CSSSizeOrRatio to force set the preferred size as the frame's
|
|
|
|
|
// content box size.
|
|
|
|
|
imageRenderer.SetPreferredSize(CSSSizeOrRatio(), contentRect.Size());
|
|
|
|
|
|
|
|
|
|
RefPtr<gfx::DrawTarget> drawTarget =
|
|
|
|
|
gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(
|
|
|
|
|
contentSizeInDevPixels.ToUnknownSize(), gfx::SurfaceFormat::A8);
|
|
|
|
|
if (!drawTarget) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefPtr<gfxContext> context = gfxContext::CreateOrNull(drawTarget);
|
|
|
|
|
MOZ_ASSERT(context); // already checked the target above
|
|
|
|
|
|
|
|
|
|
ImgDrawResult result =
|
|
|
|
|
imageRenderer.DrawShapeImage(aFrame->PresContext(), *context);
|
|
|
|
|
|
|
|
|
|
if (result != ImgDrawResult::SUCCESS) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve the pixel image buffer to create the image shape info.
|
|
|
|
|
RefPtr<SourceSurface> sourceSurface = drawTarget->Snapshot();
|
|
|
|
|
RefPtr<DataSourceSurface> dataSourceSurface = sourceSurface->GetDataSurface();
|
|
|
|
|
DataSourceSurface::ScopedMap map(dataSourceSurface, DataSourceSurface::READ);
|
|
|
|
|
|
|
|
|
|
if (!map.IsMapped()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT(sourceSurface->GetSize() == contentSizeInDevPixels.ToUnknownSize(),
|
|
|
|
|
"Who changes the size?");
|
|
|
|
|
|
2018-03-13 03:04:34 +03:00
|
|
|
|
nsRect marginRect = aMarginRect.GetPhysicalRect(aWM, aContainerSize);
|
|
|
|
|
|
2018-01-25 09:55:18 +03:00
|
|
|
|
uint8_t* alphaPixels = map.GetData();
|
|
|
|
|
int32_t stride = map.GetStride();
|
|
|
|
|
|
|
|
|
|
// NOTE: ImageShapeInfo constructor does not keep a persistent copy of
|
|
|
|
|
// alphaPixels; it's only used during the constructor to compute pixel ranges.
|
|
|
|
|
return MakeUnique<ImageShapeInfo>(alphaPixels, stride, contentSizeInDevPixels,
|
|
|
|
|
appUnitsPerDevPixel, aShapeImageThreshold,
|
2018-03-13 03:04:34 +03:00
|
|
|
|
aShapeMargin, contentRect, marginRect, aWM,
|
2018-01-25 09:55:18 +03:00
|
|
|
|
aContainerSize);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
nscoord nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff(
|
2016-11-18 08:54:09 +03:00
|
|
|
|
const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
|
|
|
|
|
const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
|
|
|
|
|
const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
|
|
|
|
|
const nscoord aBandBStart, const nscoord aBandBEnd) {
|
|
|
|
|
// An example for the band intersecting with the top right corner of an
|
|
|
|
|
// ellipse with writing-mode horizontal-tb.
|
2016-10-13 11:28:38 +03:00
|
|
|
|
//
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// lineIntercept lineDiff
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// +---------------------------------|-------|-+---- aShapeBoxBStart
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | ##########^ | | |
|
|
|
|
|
// | ##############|#### | | |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// +---------#################|######|-------|-+---- aBandBStart
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | ###################|######|## | |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// | aBStartCornerRadiusB |######|### | |
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | ######################|######|##### | |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// +---#######################|<-----------><->^---- aBandBEnd
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | ########################|############## |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// | ########################|############## |---- b
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// | #########################|############### |
|
|
|
|
|
// | ######################## v<-------------->v
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// |###################### aBStartCornerRadiusL|
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// |###########################################|
|
|
|
|
|
// |###########################################|
|
|
|
|
|
// |###########################################|
|
|
|
|
|
// |###########################################|
|
|
|
|
|
// | ######################################### |
|
|
|
|
|
// | ######################################### |
|
|
|
|
|
// | ####################################### |
|
|
|
|
|
// | ####################################### |
|
|
|
|
|
// | ##################################### |
|
|
|
|
|
// | ################################### |
|
|
|
|
|
// | ############################### |
|
|
|
|
|
// | ############################# |
|
|
|
|
|
// | ######################### |
|
|
|
|
|
// | ################### |
|
|
|
|
|
// | ########### |
|
2016-11-18 08:54:09 +03:00
|
|
|
|
// +-------------------------------------------+----- aShapeBoxBEnd
|
|
|
|
|
|
|
|
|
|
NS_ASSERTION(aShapeBoxBStart <= aShapeBoxBEnd, "Bad shape box coordinates!");
|
|
|
|
|
NS_ASSERTION(aBandBStart <= aBandBEnd, "Bad band coordinates!");
|
|
|
|
|
|
|
|
|
|
nscoord lineDiff = 0;
|
|
|
|
|
|
|
|
|
|
// If the band intersects both the block-start and block-end corners, we
|
|
|
|
|
// don't need to enter either branch because the correct lineDiff is 0.
|
|
|
|
|
if (aBStartCornerRadiusB > 0 && aBandBEnd >= aShapeBoxBStart &&
|
|
|
|
|
aBandBEnd <= aShapeBoxBStart + aBStartCornerRadiusB) {
|
|
|
|
|
// The band intersects only the block-start corner.
|
|
|
|
|
nscoord b = aBStartCornerRadiusB - (aBandBEnd - aShapeBoxBStart);
|
|
|
|
|
nscoord lineIntercept =
|
|
|
|
|
XInterceptAtY(b, aBStartCornerRadiusL, aBStartCornerRadiusB);
|
|
|
|
|
lineDiff = aBStartCornerRadiusL - lineIntercept;
|
|
|
|
|
} else if (aBEndCornerRadiusB > 0 &&
|
|
|
|
|
aBandBStart >= aShapeBoxBEnd - aBEndCornerRadiusB &&
|
|
|
|
|
aBandBStart <= aShapeBoxBEnd) {
|
|
|
|
|
// The band intersects only the block-end corner.
|
|
|
|
|
nscoord b = aBEndCornerRadiusB - (aShapeBoxBEnd - aBandBStart);
|
|
|
|
|
nscoord lineIntercept =
|
|
|
|
|
XInterceptAtY(b, aBEndCornerRadiusL, aBEndCornerRadiusB);
|
|
|
|
|
lineDiff = aBEndCornerRadiusL - lineIntercept;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lineDiff;
|
2016-10-13 11:28:38 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
nscoord nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY,
|
|
|
|
|
const nscoord aRadiusX,
|
|
|
|
|
const nscoord aRadiusY) {
|
2016-10-13 11:28:38 +03:00
|
|
|
|
// Solve for x in the ellipse equation (x/radiusX)^2 + (y/radiusY)^2 = 1.
|
|
|
|
|
MOZ_ASSERT(aRadiusY > 0);
|
|
|
|
|
return aRadiusX * std::sqrt(1 - (aY * aY) / double(aRadiusY * aRadiusY));
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
nsPoint nsFloatManager::ShapeInfo::ConvertToFloatLogical(
|
2017-01-23 12:18:07 +03:00
|
|
|
|
const nsPoint& aPoint, WritingMode aWM, const nsSize& aContainerSize) {
|
|
|
|
|
LogicalPoint logicalPoint(aWM, aPoint, aContainerSize);
|
|
|
|
|
return nsPoint(logicalPoint.LineRelative(aWM, aContainerSize),
|
|
|
|
|
logicalPoint.B(aWM));
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 05:51:48 +03:00
|
|
|
|
/* static */ UniquePtr<nscoord[]>
|
|
|
|
|
nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8],
|
|
|
|
|
WritingMode aWM) {
|
|
|
|
|
UniquePtr<nscoord[]> logicalRadii(new nscoord[8]);
|
|
|
|
|
|
|
|
|
|
// Get the physical side for line-left and line-right since border radii
|
|
|
|
|
// are on the physical axis.
|
|
|
|
|
Side lineLeftSide =
|
|
|
|
|
aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
|
|
|
|
|
logicalRadii[eCornerTopLeftX] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineLeftSide, true, false)];
|
|
|
|
|
logicalRadii[eCornerTopLeftY] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineLeftSide, true, true)];
|
|
|
|
|
logicalRadii[eCornerBottomLeftX] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineLeftSide, false, false)];
|
|
|
|
|
logicalRadii[eCornerBottomLeftY] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineLeftSide, false, true)];
|
|
|
|
|
|
|
|
|
|
Side lineRightSide = aWM.PhysicalSide(
|
|
|
|
|
aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
|
|
|
|
|
logicalRadii[eCornerTopRightX] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineRightSide, false, false)];
|
|
|
|
|
logicalRadii[eCornerTopRightY] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineRightSide, false, true)];
|
|
|
|
|
logicalRadii[eCornerBottomRightX] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineRightSide, true, false)];
|
|
|
|
|
logicalRadii[eCornerBottomRightY] =
|
|
|
|
|
aRadii[SideToHalfCorner(lineRightSide, true, true)];
|
|
|
|
|
|
|
|
|
|
if (aWM.IsLineInverted()) {
|
|
|
|
|
// When IsLineInverted() is true, i.e. aWM is vertical-lr,
|
|
|
|
|
// line-over/line-under are inverted from block-start/block-end. So the
|
|
|
|
|
// relationship reverses between which corner comes first going
|
|
|
|
|
// clockwise, and which corner is block-start versus block-end. We need
|
|
|
|
|
// to swap the values stored in top and bottom corners.
|
|
|
|
|
std::swap(logicalRadii[eCornerTopLeftX], logicalRadii[eCornerBottomLeftX]);
|
|
|
|
|
std::swap(logicalRadii[eCornerTopLeftY], logicalRadii[eCornerBottomLeftY]);
|
|
|
|
|
std::swap(logicalRadii[eCornerTopRightX],
|
|
|
|
|
logicalRadii[eCornerBottomRightX]);
|
|
|
|
|
std::swap(logicalRadii[eCornerTopRightY],
|
|
|
|
|
logicalRadii[eCornerBottomRightY]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return logicalRadii;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
size_t nsFloatManager::ShapeInfo::MinIntervalIndexContainingY(
|
2018-04-19 22:17:44 +03:00
|
|
|
|
const nsTArray<nsRect>& aIntervals, const nscoord aTargetY) {
|
|
|
|
|
// Perform a binary search to find the minimum index of an interval
|
|
|
|
|
// that contains aTargetY. If no such interval exists, return a value
|
|
|
|
|
// equal to the number of intervals.
|
|
|
|
|
size_t startIdx = 0;
|
|
|
|
|
size_t endIdx = aIntervals.Length();
|
|
|
|
|
while (startIdx < endIdx) {
|
|
|
|
|
size_t midIdx = startIdx + (endIdx - startIdx) / 2;
|
|
|
|
|
if (aIntervals[midIdx].ContainsY(aTargetY)) {
|
|
|
|
|
return midIdx;
|
|
|
|
|
}
|
|
|
|
|
nscoord midY = aIntervals[midIdx].Y();
|
|
|
|
|
if (midY < aTargetY) {
|
|
|
|
|
startIdx = midIdx + 1;
|
|
|
|
|
} else {
|
|
|
|
|
endIdx = midIdx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return endIdx;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-26 01:09:24 +03:00
|
|
|
|
/* static */
|
|
|
|
|
nscoord nsFloatManager::ShapeInfo::LineEdge(const nsTArray<nsRect>& aIntervals,
|
|
|
|
|
const nscoord aBStart,
|
|
|
|
|
const nscoord aBEnd,
|
|
|
|
|
bool aIsLineLeft) {
|
2018-04-19 22:17:44 +03:00
|
|
|
|
MOZ_ASSERT(aBStart <= aBEnd,
|
|
|
|
|
"The band's block start is greater than its block end?");
|
|
|
|
|
|
|
|
|
|
// Find all the intervals whose rects overlap the aBStart to
|
|
|
|
|
// aBEnd range, and find the most constraining inline edge
|
|
|
|
|
// depending on the value of aLeft.
|
|
|
|
|
|
|
|
|
|
// Since the intervals are stored in block-axis order, we need
|
|
|
|
|
// to find the first interval that overlaps aBStart and check
|
|
|
|
|
// succeeding intervals until we get past aBEnd.
|
|
|
|
|
|
|
|
|
|
nscoord lineEdge = aIsLineLeft ? nscoord_MAX : nscoord_MIN;
|
|
|
|
|
|
|
|
|
|
size_t intervalCount = aIntervals.Length();
|
|
|
|
|
for (size_t i = MinIntervalIndexContainingY(aIntervals, aBStart);
|
|
|
|
|
i < intervalCount; ++i) {
|
|
|
|
|
// We can always get the bCoord from the intervals' mLineLeft,
|
|
|
|
|
// since the y() coordinate is duplicated in both points in the
|
|
|
|
|
// interval.
|
|
|
|
|
auto& interval = aIntervals[i];
|
|
|
|
|
nscoord bCoord = interval.Y();
|
2018-04-19 21:47:04 +03:00
|
|
|
|
if (bCoord >= aBEnd) {
|
2018-04-19 22:17:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Get the edge from the interval point indicated by aLeft.
|
|
|
|
|
if (aIsLineLeft) {
|
|
|
|
|
lineEdge = std::min(lineEdge, interval.X());
|
|
|
|
|
} else {
|
|
|
|
|
lineEdge = std::max(lineEdge, interval.XMost());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lineEdge;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-19 21:47:04 +03:00
|
|
|
|
/* static */ nsFloatManager::ShapeInfo::dfType
|
|
|
|
|
nsFloatManager::ShapeInfo::CalcUsedShapeMargin5X(nscoord aShapeMargin,
|
|
|
|
|
int32_t aAppUnitsPerDevPixel) {
|
|
|
|
|
// Our distance field has to be able to hold values equal to the
|
|
|
|
|
// maximum shape-margin value that we care about faithfully rendering,
|
|
|
|
|
// times 5. A 16-bit unsigned int can represent up to ~ 65K which means
|
|
|
|
|
// we can handle a margin up to ~ 13K device pixels. That's good enough
|
|
|
|
|
// for practical usage. Any supplied shape-margin value higher than this
|
|
|
|
|
// maximum will be clamped.
|
|
|
|
|
static const float MAX_MARGIN_5X_FLOAT = (float)MAX_MARGIN_5X;
|
|
|
|
|
|
|
|
|
|
// Convert aShapeMargin to dev pixels, convert that into 5x-dev-pixel
|
|
|
|
|
// space, then clamp to MAX_MARGIN_5X_FLOAT.
|
|
|
|
|
float shapeMarginDevPixels5X =
|
|
|
|
|
5.0f * NSAppUnitsToFloatPixels(aShapeMargin, aAppUnitsPerDevPixel);
|
|
|
|
|
NS_WARNING_ASSERTION(shapeMarginDevPixels5X <= MAX_MARGIN_5X_FLOAT,
|
|
|
|
|
"shape-margin is too large and is being clamped.");
|
|
|
|
|
|
|
|
|
|
// We calculate a minimum in float space, which takes care of any overflow
|
|
|
|
|
// or infinity that may have occurred earlier from multiplication of
|
|
|
|
|
// too-large aShapeMargin values.
|
|
|
|
|
float usedMargin5XFloat =
|
|
|
|
|
std::min(shapeMarginDevPixels5X, MAX_MARGIN_5X_FLOAT);
|
|
|
|
|
return (dfType)NSToIntRound(usedMargin5XFloat);
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
nsAutoFloatManager::~nsAutoFloatManager() {
|
2016-09-15 06:29:14 +03:00
|
|
|
|
// Restore the old float manager in the reflow input if necessary.
|
2009-01-05 03:39:54 +03:00
|
|
|
|
if (mNew) {
|
2016-09-15 09:32:12 +03:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
if (nsBlockFrame::gNoisyFloatManager) {
|
|
|
|
|
printf("restoring old float manager %p\n", mOld);
|
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
#endif
|
|
|
|
|
|
2016-07-21 13:36:39 +03:00
|
|
|
|
mReflowInput.mFloatManager = mOld;
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2016-09-15 09:32:12 +03:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
if (nsBlockFrame::gNoisyFloatManager) {
|
|
|
|
|
if (mOld) {
|
|
|
|
|
mReflowInput.mFrame->ListTag(stdout);
|
|
|
|
|
printf(": float manager %p after reflow\n", mOld);
|
|
|
|
|
mOld->List(stdout);
|
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsAutoFloatManager::CreateFloatManager(nsPresContext* aPresContext) {
|
2017-02-09 12:57:16 +03:00
|
|
|
|
MOZ_ASSERT(!mNew, "Redundant call to CreateFloatManager!");
|
|
|
|
|
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// Create a new float manager and install it in the reflow
|
2016-09-15 06:29:14 +03:00
|
|
|
|
// input. `Remember' the old float manager so we can restore it
|
2009-01-05 03:39:54 +03:00
|
|
|
|
// later.
|
2017-02-09 12:57:16 +03:00
|
|
|
|
mNew = MakeUnique<nsFloatManager>(aPresContext->PresShell(),
|
|
|
|
|
mReflowInput.GetWritingMode());
|
2009-01-05 03:39:54 +03:00
|
|
|
|
|
2016-09-15 09:32:12 +03:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
if (nsBlockFrame::gNoisyFloatManager) {
|
|
|
|
|
printf("constructed new float manager %p (replacing %p)\n", mNew.get(),
|
2017-02-09 12:57:16 +03:00
|
|
|
|
mReflowInput.mFloatManager);
|
2016-09-15 09:32:12 +03:00
|
|
|
|
}
|
2009-01-05 03:39:54 +03:00
|
|
|
|
#endif
|
|
|
|
|
|
2016-09-15 06:29:14 +03:00
|
|
|
|
// Set the float manager in the existing reflow input.
|
2016-07-21 13:36:39 +03:00
|
|
|
|
mOld = mReflowInput.mFloatManager;
|
2017-02-09 12:57:16 +03:00
|
|
|
|
mReflowInput.mFloatManager = mNew.get();
|
2009-01-05 03:39:54 +03:00
|
|
|
|
}
|