Clean up and fixes to fractions metrics and drawing

* Factor code for easier maintainance.

* Avoid computing metrics several times. This duplication explained the
  exponential blowup during the metrics phase for nested fractions (see
  b2b87330). This happened in particular when using lyxproofs which heavily uses
  nested \dfracs for on-screen drawing.

* Call MetricsBase::changeScript instead of MetricsBase::changeFrac for
  \nicefrac and \unitfrac.
This commit is contained in:
Guillaume Munch 2016-11-20 20:08:11 +01:00
parent a9eb87a89d
commit ffb865d6e8

View File

@ -152,62 +152,71 @@ void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
{
Dimension dim0, dim1, dim2;
// This could be simplified, including avoiding useless recalculation of
// cell metrics
if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
if (nargs() == 1) {
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
cell(0).metrics(mi, dim0);
dim.wid = dim0.width()+ 3;
dim.asc = dim0.asc;
dim.des = dim0.des;
} else if (nargs() == 2) {
cell(0).metrics(mi, dim0);
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
cell(1).metrics(mi, dim1);
dim.wid = dim0.width() + dim1.wid + 5;
dim.asc = max(dim0.asc, dim1.asc);
dim.des = max(dim0.des, dim1.des);
} else {
cell(2).metrics(mi, dim2);
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
Changer dummy2 = mi.base.changeFrac();
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
dim.asc = max(dim2.asc, dim0.height() + 5);
dim.des = max(dim2.des, dim1.height() - 5);
switch (kind_) {
case UNIT: {
// \unitone, \unittwo
dim.wid = 0;
int unit_cell = 0;
// is there an extra cell holding the value being given a dimension?
// (this is \unittwo)
if (nargs() == 2) {
cell(0).metrics(mi, dim1);
dim.wid += dim1.wid + 4;
unit_cell = 1;
}
} else {
// general cell metrics used for \frac
Changer dummy = mi.base.changeFrac();
// FIXME: Exponential blowup
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
cell(unit_cell).metrics(mi, dim0);
dim.wid += dim0.width() + 1;
dim.asc = max(dim0.asc, dim1.asc);
dim.des = max(dim0.des, dim1.des);
}
break;
case UNITFRAC:
case NICEFRAC: {
// \unitfrac, \unitfracthree, \nicefrac
dim.wid = 0;
// is there an extra cell holding the value being given a dimension?
// (this is \unitfracthree)
if (kind_ == UNITFRAC && nargs() == 3) {
cell(2).metrics(mi, dim2);
dim.wid += dim2.wid + 4;
}
Changer dummy = mi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
Changer dummy2 = mi.base.changeScript();
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
if (nargs() == 3)
cell(2).metrics(mi, dim2);
// metrics for special fraction types
if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
Changer dummy2 = mi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
dim.wid = dim0.width() + dim1.wid + 5;
dim.asc = dim0.height() + 5;
dim.des = dim1.height() - 5;
} else {
if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
|| kind_ == DFRAC || kind_ == TFRAC) {
// \cfrac and \dfrac are always in display size
// \tfrac is in always in text size
Changer dummy2 = mi.base.font.changeStyle((kind_ == TFRAC)
? LM_ST_SCRIPT
: LM_ST_DISPLAY);
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
}
dim.wid = max(dim0.wid, dim1.wid) + 2;
dim.asc = dim0.height() + 2 + 5;
dim.des = dim1.height() + 2 - 5;
}
dim.wid += dim0.wid + dim1.wid + 5;
dim.asc = max(dim2.asc, dim0.height() + 5);
dim.des = max(dim2.des, dim1.height() - 5);
}
break;
case FRAC:
case CFRAC:
case CFRACLEFT:
case CFRACRIGHT:
case DFRAC:
case TFRAC:
case OVER:
case ATOP: {
Changer dummy =
// \tfrac is always in text size
(kind_ == TFRAC) ? mi.base.font.changeStyle(LM_ST_SCRIPT) :
// \cfrac and \dfrac are always in display size
(kind_ == CFRAC
|| kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT
|| kind_ == DFRAC) ? mi.base.font.changeStyle(LM_ST_DISPLAY) :
// all others
mi.base.changeFrac();
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
dim.wid = max(dim0.wid, dim1.wid) + 2;
dim.asc = dim0.height() + 2 + 5;
dim.des = dim1.height() + 2 - 5;
}
} //switch (kind_)
metricsMarkers(mi, dim);
}
@ -217,74 +226,89 @@ void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
setPosCache(pi, x, y);
Dimension const dim = dimension(*pi.base.bv);
Dimension const dim0 = cell(0).dimension(*pi.base.bv);
if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) {
if (nargs() == 1) {
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
switch (kind_) {
case UNIT: {
// \unitone, \unittwo
int xx = x;
int unit_cell = 0;
// is there an extra cell holding the value being given a dimension?
// (this is \unittwo)
if (nargs() == 2) {
cell(0).draw(pi, x + 1, y);
} else if (nargs() == 2) {
cell(0).draw(pi, x + 1, y);
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
cell(1).draw(pi, x + dim0.width() + 5, y);
} else {
cell(2).draw(pi, x + 1, y);
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
Changer dummy2 = pi.base.changeFrac();
Dimension const dim1 = cell(1).dimension(*pi.base.bv);
Dimension const dim2 = cell(2).dimension(*pi.base.bv);
int xx = x + dim2.wid + 5;
cell(0).draw(pi, xx + 2,
y - dim0.des - 5);
cell(1).draw(pi, xx + dim0.width() + 5,
y + dim1.asc / 2);
xx += dim0.wid + 4;
unit_cell = 1;
}
} else {
Changer dummy = pi.base.changeFrac();
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
cell(unit_cell).draw(pi, xx + 1, y);
}
break;
case UNITFRAC:
case NICEFRAC: {
// \unitfrac, \unitfracthree, \nicefrac
int xx = x;
// is there an extra cell holding the value being given a dimension?
// (this is \unitfracthree)
if (kind_ == UNITFRAC && nargs() == 3) {
cell(2).draw(pi, x + 1, y);
xx += cell(2).dimension(*pi.base.bv).wid + 4;
}
Changer dummy = pi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
// nice fraction
// FIXME:
// * the solidus should be \kern-2mu/\kern-1mu.
// * the vertical offset of the first cell should be such that the
// top of M in the first cell matches the one of the
// surrounding text.
Changer dummy2 = pi.base.changeScript();
cell(0).draw(pi, xx + 2, y - dim0.des - 5);
Dimension const dim1 = cell(1).dimension(*pi.base.bv);
cell(1).draw(pi, xx + dim0.wid + 5, y + dim1.asc / 2);
// Diag line:
pi.pain.line(xx + dim0.wid + 1, y + dim.des - 2,
xx + dim0.wid + 6, y - dim.asc + 2,
pi.base.font.color());
}
break;
case FRAC:
case CFRAC:
case CFRACLEFT:
case CFRACRIGHT:
case DFRAC:
case TFRAC:
case OVER:
case ATOP: {
Changer dummy =
// \tfrac is always in text size
(kind_ == TFRAC) ? pi.base.font.changeStyle(LM_ST_SCRIPT) :
// \cfrac and \dfrac are always in display size
(kind_ == CFRAC
|| kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT
|| kind_ == DFRAC) ? pi.base.font.changeStyle(LM_ST_DISPLAY) :
// all others
pi.base.changeFrac();
Dimension const dim1 = cell(1).dimension(*pi.base.bv);
int m = x + dim.wid / 2;
if (kind_ == NICEFRAC) {
cell(0).draw(pi, x + 2,
y - dim0.des - 5);
cell(1).draw(pi, x + dim0.width() + 5,
y + dim1.asc / 2);
} else if (kind_ == UNITFRAC) {
Changer dummy2 = pi.base.font.changeShape(UP_SHAPE);
cell(0).draw(pi, x + 2, y - dim0.des - 5);
cell(1).draw(pi, x + dim0.width() + 5, y + dim1.asc / 2);
} else if (kind_ == FRAC || kind_ == ATOP || kind_ == OVER
|| kind_ == TFRAC) {
// tfrac is in always in text size
Changer dummy2 = pi.base.font.changeStyle(LM_ST_SCRIPT,
kind_ == TFRAC);
cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
} else {
// \cfrac and \dfrac are always in display size
Changer dummy2 = pi.base.font.changeStyle(LM_ST_DISPLAY);
if (kind_ == CFRAC || kind_ == DFRAC)
cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
else if (kind_ == CFRACLEFT)
cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
else if (kind_ == CFRACRIGHT)
cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
y - dim0.des - 2 - 5);
cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
}
int xx =
// align left
(kind_ == CFRACLEFT) ? x + 2 :
// align right
(kind_ == CFRACRIGHT) ? x + dim.wid - dim0.wid - 2 :
// center
m - dim0.wid / 2;
// FIXME: vertical offset should be based on ex
//int dy = theFontMetrics(pi.base.font).ex() / 2;
cell(0).draw(pi, xx, y - dim0.des - 2 - 5);
// center
cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
// horizontal line
if (kind_ != ATOP)
pi.pain.line(x + 1, y - 5,
x + dim.wid - 2, y - 5, pi.base.font.color());
}
if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
// Diag line:
int xx = x;
if (nargs() == 3)
xx += cell(2).dimension(*pi.base.bv).wid + 5;
pi.pain.line(xx + dim0.wid,
y + dim.des - 2,
xx + dim0.wid + 5,
y - dim.asc + 2, pi.base.font.color());
}
if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT || kind_ == DFRAC
|| kind_ == TFRAC || kind_ == OVER)
pi.pain.line(x + 1, y - 5,
x + dim.wid - 2, y - 5, pi.base.font.color());
} //switch (kind_)
drawMarkers(pi, x, y);
}