[smooth] Fix complex rendering at high ppem.

We used to split large glyphs into horizontal bands and continue
bisecting them still horizontally if that was not enough.  This is
guaranteed to fail when a single scanline cannot fit into the
rendering memory pool.  Now we bisect the bands vertically so that
the smallest unit is a column of the band height, which is guranteed
to fit into memory.

* src/smooth/ftgrays.c (gray_convert_glyph): Implement it.
This commit is contained in:
Alexei Podtelezhnikov 2017-11-01 22:51:03 -04:00
parent 91015cb41d
commit 816516360c
2 changed files with 41 additions and 36 deletions

View File

@ -1,3 +1,16 @@
2017-11-01 Alexei Podtelezhnikov <apodtele@gmail.com>
[smooth] Fix complex rendering at high ppem.
We used to split large glyphs into horizontal bands and continue
bisecting them still horizontally if that was not enough. This is
guaranteed to fail when a single scanline cannot fit into the
rendering memory pool. Now we bisect the bands vertically so that
the smallest unit is a column of the band height, which is guranteed
to fit into memory.
* src/smooth/ftgrays.c (gray_convert_glyph): Implement it.
2017-10-20 Alexei Podtelezhnikov <apodtele@gmail.com>
[smooth] Improve complex rendering at high ppem.

View File

@ -1736,35 +1736,43 @@ typedef ptrdiff_t FT_PtrDist;
static int
gray_convert_glyph( RAS_ARG )
{
const TCoord yMin = ras.min_ey;
const TCoord yMax = ras.max_ey;
const TCoord xMin = ras.min_ex;
const TCoord xMax = ras.max_ex;
TCell buffer[FT_MAX_GRAY_POOL];
TCoord band_size = FT_MAX_GRAY_POOL / 8;
TCoord count = ras.max_ey - ras.min_ey;
int num_bands;
TCoord min, max, max_y;
size_t height = (size_t)( yMax - yMin );
size_t n = FT_MAX_GRAY_POOL / 8;
TCoord y;
TCoord bands[32]; /* enough to accommodate bisections */
TCoord* band;
/* set up vertical bands */
if ( count > band_size )
if ( height > n )
{
/* two divisions rounded up */
num_bands = (int)( ( count + band_size - 1) / band_size );
band_size = ( count + num_bands - 1 ) / num_bands;
n = ( height + n - 1 ) / n;
height = ( height + n - 1 ) / n;
}
min = ras.min_ey;
max_y = ras.max_ey;
/* memory management */
n = ( height * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) / sizeof ( TCell );
for ( ; min < max_y; min = max )
ras.cells = buffer + n;
ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - n );
ras.ycells = (PCell*)buffer;
for ( y = yMin; y < yMax; )
{
max = min + band_size;
if ( max > max_y )
max = max_y;
ras.min_ey = y;
y += height;
ras.max_ey = FT_MIN( y, yMax );
band = bands;
band[1] = min;
band[0] = max;
band[1] = xMin;
band[0] = xMax;
do
{
@ -1772,27 +1780,12 @@ typedef ptrdiff_t FT_PtrDist;
int error;
/* memory management */
{
size_t ycount = (size_t)width;
size_t cell_start;
cell_start = ( ycount * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) /
sizeof ( TCell );
ras.cells = buffer + cell_start;
ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - cell_start );
ras.num_cells = 0;
ras.ycells = (PCell*)buffer;
while ( ycount )
ras.ycells[--ycount] = NULL;
}
FT_MEM_ZERO( ras.ycells, height * sizeof ( PCell ) );
ras.num_cells = 0;
ras.invalid = 1;
ras.min_ey = band[1];
ras.max_ey = band[0];
ras.min_ex = band[1];
ras.max_ex = band[0];
error = gray_convert_glyph_inner( RAS_VAR );
@ -1808,8 +1801,7 @@ typedef ptrdiff_t FT_PtrDist;
/* render pool overflow; we will reduce the render band by half */
width >>= 1;
/* This is too complex for a single scanline; there must */
/* be some problems. */
/* this should never happen even with tiny rendering pool */
if ( width == 0 )
{
FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" ));