Subtract allocated parts before repeating division used for allocation to avoid rounding error of total. b=366865 r=bernd sr=roc

This commit is contained in:
dbaron%dbaron.org 2007-02-18 19:49:50 +00:00
parent 24eee9fe2e
commit e991cbf0ba
2 changed files with 143 additions and 57 deletions

View File

@ -80,7 +80,7 @@ random-if(MOZ_WIDGET_TOOLKIT=="windows") == bugs/359903-1.html bugs/359903-1-ref
!= bugs/315620-1b.html bugs/315620-1-ref.html
== bugs/315620-2a.xhtml bugs/315620-2-ref.xhtml
!= bugs/315620-2b.xhtml bugs/315620-2-ref.xhtml
random == bugs/363329-1.html bugs/363329-1-ref.html # bug 366865
== bugs/363329-1.html bugs/363329-1-ref.html
== bugs/363329-2.html bugs/363329-2-ref.html
== bugs/363637-1.html bugs/363637-1-ref.html
== bugs/364079-1.html bugs/364079-1-ref.html

View File

@ -328,8 +328,8 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
// Before looping over the spanned columns to distribute
// this cell's width over the columns it spans, we first
// compute that loop's invariants, which include totals over
// the spanned columns and ratios derived from those totals.
// compute totals over the spanned columns so we know how to
// allocate the space.
// Accumulate information about the spanned columns, and
// subtract the already-used space from |info|.
@ -372,24 +372,19 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
if (info.prefPercent < 0.0f)
info.prefPercent = 0.0f;
// Compute the ratios used to distribute this cell's width
// appropriately among the spanned columns.
float pctRatio = 0.0f;
if (nonPctCount && info.prefPercent > 0.0f) {
if (totalSNonPctPref > 0) {
pctRatio = info.prefPercent / float(totalSNonPctPref);
} else {
pctRatio = info.prefPercent / float(nonPctCount);
}
}
// The min-width of this cell that fits inside the
// pref-width of the spanned columns gets distributed
// according to different ratios.
nscoord minWithinPref =
PR_MIN(info.minCoord, totalSPref - totalSMin);
NS_ASSERTION(minWithinPref >= 0, "neither value can be negative");
info.minCoord -= minWithinPref;
nscoord minOutsidePref = info.minCoord - minWithinPref;
// Loop invariants (that we might get confused about as we
// subtract amounts for completed columns)
const PRBool spanHasNonPctPref = totalSNonPctPref > 0;
const PRBool spanHasPref = totalSPref > 0;
const PRBool spanHasNonPct = nonPctCount > 0;
// ... and actually do the distribution of the widths of
// this cell exceeding the totals already in the spanned
@ -405,15 +400,25 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
// the percentage width (only to columns that don't
// already have percentage widths, in proportion to
// the existing pref widths)
if (scolFrame->GetPrefPercent() == 0.0f) {
float spp;
if (totalSNonPctPref > 0) {
spp = pctRatio * scolFrame->GetPrefCoord();
float allocatedPct = 0.0f;
if (scolFrame->GetPrefPercent() == 0.0f &&
info.prefPercent != 0.0f) {
NS_ASSERTION((!spanHasNonPctPref ||
totalSNonPctPref != 0) &&
nonPctCount != 0,
"should not be zero if we haven't allocated "
"all pref percent");
if (spanHasNonPctPref) {
// Group so we're multiplying by 1.0f when we need
// to use up info.prefPercent.
allocatedPct = info.prefPercent *
(float(scolFrame->GetPrefCoord()) /
float(totalSNonPctPref));
} else {
// distribute equally when all pref widths are 0
spp = pctRatio;
allocatedPct = info.prefPercent / float(nonPctCount);
}
scolFrame->AddSpanPrefPercent(spp);
scolFrame->AddSpanPrefPercent(allocatedPct);
}
// the part of the min width that fits within the
@ -428,22 +433,55 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
// the rest of the min width, and the pref width (in
// proportion to the existing pref widths)
float coordRatio; // for both min and pref
if (totalSPref > 0) {
coordRatio = float(scolFrame->GetPrefCoord()) /
float(totalSPref);
if (spanHasPref) {
if (scolFrame->GetPrefCoord() == 0) {
// We might have already subtracted all of
// totalSPref.
coordRatio = 0.0f;
} else {
coordRatio = float(scolFrame->GetPrefCoord()) /
float(totalSPref);
}
} else {
// distribute equally when all pref widths are 0
coordRatio = 1.0f / float(colSpan);
coordRatio = 1.0f / float(scol_end - scol);
}
// combine the two min-width distributions, and record
// min and pref
scolFrame->AddSpanMinCoord(NSToCoordRound(
float(info.minCoord) * coordRatio +
float(minWithinPref) * minRatio));
scolFrame->AddSpanPrefCoord(NSToCoordRound(
float(info.prefCoord) * coordRatio));
nscoord allocatedMinWithinPref =
NSToCoordRound(float(minWithinPref) * minRatio);
nscoord allocatedMinOutsidePref =
NSToCoordRound(float(minOutsidePref) * coordRatio);
scolFrame->AddSpanMinCoord(allocatedMinWithinPref +
allocatedMinOutsidePref);
nscoord allocatedPref =
NSToCoordRound(float(info.prefCoord) * coordRatio);
scolFrame->AddSpanPrefCoord(allocatedPref);
// To avoid accumulating rounding error from division,
// subtract everything to do with the column we've
// passed from the totals.
minWithinPref -= allocatedMinWithinPref;
minOutsidePref -= allocatedMinOutsidePref;
info.prefCoord -= allocatedPref;
info.prefPercent -= allocatedPct;
totalSPref -= scolFrame->GetPrefCoord();
totalSMin -= scolFrame->GetMinCoord();
if (scolFrame->GetPrefPercent() == 0.0f) {
totalSNonPctPref -= scolFrame->GetPrefCoord();
--nonPctCount;
}
}
// Note that we only distribute the percentage if
// spanHasNonPct.
NS_ASSERTION(totalSPref == 0 && totalSMin == 0 &&
totalSNonPctPref == 0 && nonPctCount == 0 &&
minOutsidePref == 0 && minWithinPref == 0 &&
info.prefCoord == 0 &&
(info.prefPercent == 0.0f || !spanHasNonPct),
"didn't subtract all that we added");
} while ((item = item->next));
// Combine the results of the span analysis into the main results,
@ -734,46 +772,54 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
};
Loop2Type l2t;
float c; // the constant (over columns) for each case's math
// These are constants (over columns) for each case's math. We use
// a pair of nscoords rather than a float so that we can subtract
// each column's allocation so we avoid accumulating rounding error.
nscoord space; // the amount of extra width to allocate
union {
nscoord c;
float f;
} basis; // the sum of the statistic over columns to divide it
if (width < guess_pref) {
NS_ASSERTION(width >= guess_min, "bad width");
if (width < guess_min_pct) {
l2t = FLEX_PCT_SMALL;
c = float(width - guess_min) /
float(guess_min_pct - guess_min);
space = width - guess_min;
basis.c = guess_min_pct - guess_min;
} else if (width < guess_min_spec) {
l2t = FLEX_FIXED_SMALL;
c = float(width - guess_min_pct) /
float(guess_min_spec - guess_min_pct);
space = width - guess_min_pct;
basis.c = guess_min_spec - guess_min_pct;
} else {
l2t = FLEX_FLEX_SMALL;
c = float(width - guess_min_spec) /
float(guess_pref - guess_min_spec);
space = width - guess_min_spec;
basis.c = guess_pref - guess_min_spec;
}
} else {
space = width - guess_pref;
if (total_flex_pref > 0) {
l2t = FLEX_FLEX_LARGE;
c = float(width - guess_pref) / float(total_flex_pref);
basis.c = total_flex_pref;
} else if (total_fixed_pref > 0) {
l2t = FLEX_FIXED_LARGE;
c = float(width - guess_pref) / float(total_fixed_pref);
basis.c = total_fixed_pref;
} else if (total_pct > 0.0f) {
l2t = FLEX_PCT_LARGE;
c = float(width - guess_pref) / float(total_pct);
basis.f = total_pct;
} else {
l2t = FLEX_ALL_LARGE;
c = 1.0f / float(colCount);
basis.c = colCount;
}
}
#ifdef DEBUG_dbaron_off
printf("ComputeColumnWidths: %d columns in width %d,\n"
" guesses=[%d,%d,%d,%d], totals=[%d,%d,%f],\n"
" l2t=%d, c=%f\n",
" l2t=%d, space=%d, basis.c=%d\n",
colCount, width,
guess_min, guess_min_pct, guess_min_spec, guess_pref,
total_flex_pref, total_fixed_pref, total_pct,
l2t, c);
l2t, space, basis.c);
#endif
for (col = 0; col < colCount; ++col) {
@ -794,14 +840,18 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
col_width = colFrame->GetPrefCoord();
}
nscoord col_width_before_adjust = col_width;
switch (l2t) {
case FLEX_PCT_SMALL:
col_width = colFrame->GetMinCoord();
col_width = col_width_before_adjust = colFrame->GetMinCoord();
if (pct != 0.0f) {
nscoord width_from_pct = nscoord(float(width) * pct);
if (width_from_pct > col_width) {
col_width += NSToCoordRound(
float(width_from_pct - col_width) * c);
nscoord pct_minus_min =
nscoord(float(width) * pct) - col_width;
if (pct_minus_min > 0) {
float c = float(space) / float(basis.c);
basis.c -= pct_minus_min;
col_width += NSToCoordRound(float(pct_minus_min) * c);
}
}
break;
@ -811,10 +861,17 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
"wrong width assigned");
if (colFrame->GetHasSpecifiedCoord()) {
nscoord col_min = colFrame->GetMinCoord();
col_width = col_min + NSToCoordRound(
float(col_width - col_min) * c);
nscoord pref_minus_min = col_width - col_min;
col_width = col_width_before_adjust = col_min;
if (pref_minus_min != 0) {
float c = float(space) / float(basis.c);
basis.c -= pref_minus_min;
col_width += NSToCoordRound(
float(pref_minus_min) * c);
}
} else
col_width = colFrame->GetMinCoord();
col_width = col_width_before_adjust =
colFrame->GetMinCoord();
}
break;
case FLEX_FLEX_SMALL:
@ -823,8 +880,14 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
"wrong width assigned");
nscoord col_min = colFrame->GetMinCoord();
col_width = col_min + NSToCoordRound(
float(col_width - col_min) * c);
nscoord pref_minus_min = col_width - col_min;
col_width = col_width_before_adjust = col_min;
if (pref_minus_min != 0) {
float c = float(space) / float(basis.c);
basis.c -= pref_minus_min;
col_width += NSToCoordRound(
float(pref_minus_min) * c);
}
}
break;
case FLEX_FLEX_LARGE:
@ -832,7 +895,11 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
!colFrame->GetHasSpecifiedCoord()) {
NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
"wrong width assigned");
col_width += NSToCoordRound(float(col_width) * c);
if (col_width != 0) {
float c = float(space) / float(basis.c);
basis.c -= col_width;
col_width += NSToCoordRound(float(col_width) * c);
}
}
break;
case FLEX_FIXED_LARGE:
@ -842,19 +909,33 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
NS_ASSERTION(colFrame->GetHasSpecifiedCoord() ||
colFrame->GetPrefCoord() == 0,
"wrong case");
col_width += NSToCoordRound(float(col_width) * c);
if (col_width != 0) {
float c = float(space) / float(basis.c);
basis.c -= col_width;
col_width += NSToCoordRound(float(col_width) * c);
}
}
break;
case FLEX_PCT_LARGE:
NS_ASSERTION(pct != 0.0f || colFrame->GetPrefCoord() == 0,
"wrong case");
col_width += NSToCoordRound(pct * c);
if (pct != 0.0f) {
float c = float(space) / basis.f;
col_width += NSToCoordRound(pct * c);
basis.f -= pct;
}
break;
case FLEX_ALL_LARGE:
col_width += NSToCoordRound(float(width - guess_pref) * c);
{
float c = float(space) / float(basis.c);
col_width += NSToCoordRound(c);
--basis.c;
}
break;
}
space -= col_width - col_width_before_adjust;
NS_ASSERTION(col_width >= colFrame->GetMinCoord(),
"assigned width smaller than min");
@ -864,6 +945,11 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt
if (old_final != col_width)
mTableFrame->DidResizeColumns();
}
NS_ASSERTION(space == 0 &&
((l2t == FLEX_PCT_LARGE)
? (-0.001f < basis.f && basis.f < 0.001f)
: (basis.c == 0)),
"didn't subtract all that we added");
#ifdef DEBUG_TABLE_STRATEGY
printf("ComputeColumnWidths final\n");
mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);