[GRASS5] [bug #2129] (grass) d.text.freetype: bad rendering

this bug's URL: http://intevation.de/rt/webrt?serial_num=2129
-------------------------------------------------------------------------

Subject: d.text.freetype: bad rendering

Platform: GNU/Linux/i386
grass obtained from: Trento Italy site
grass binary for platform: Compiled from Sources
GRASS Version: 5.3-cvs & 5.0.3rc

d.text.freetype creates jumbled snow where the letters should be.

Happens on Redhat Redhat 9 & Debian/testing, both with font=luxi* and path=/a/b/c/font.ttf. It doesn't seem to do it on Redhat 7.3 (not very much testing) with path= but I don't have the b&h luxi fonts installed there so font= doesn't exist as an option.

This seems to happen somewhat randomly. It will usually get at least one letter correct though. Resizing the monitor window vertically and redrawing will make other letters 'come in to focus' while letters that were good will frazzle. Resizing the monitor window horizontally doesn't change the it at all. It gets it right about 50% of the time, more when the monitor height is small, less when it is tall.

e.g.
http://bambi.otago.ac.nz/hamish/grass/bad_freetype.png

Notice the letters are almost right, it just seems to be wrapping the right hand side too soon and advancing the pixel buffer too much. At least it seems to be drawing it consistently for a given monitor-window size ..
I'm not sure if the buffer is filled too wide for the drawing-rectangle or the drawing loop is jumping to the next line too soon.

B&H Luxi is a Monospaced font, right? But that shouldn't matter.

I can't find any documentation for src/libes/raster/Raster.c R_raster_char() or really know how to pull apart draw_character() much more.

Making the following change makes the letters somewhat viewable on my system, but it certainly isn't correct. (note I've removed some white space to stop the patch line wrapping in the bug-input box)

$ cvs diff -u main.c
Index: main.c

RCS file: /home/grass/grassrepository/grass/src/display/d.text.freetype/main.c,v
retrieving revision 1.41
diff -u -r1.41 main.c
--- main.c 1 Apr 2003 06:51:32 -0000 1.41
+++ main.c 24 Sep 2003 10:39:41 -0000
@@ -998,7 +998,7 @@
     for(i = start_row; i < rows; i++)
         {
             R_move_abs(rect.l + start_col, rect.t + i);
- R_raster_char(w, 1, 0, buffer + width * i + start_col);
+ R_raster_char(w, 1, 0, buffer + (width+7) * i + start_col);
          }

#ifdef FLUSH_EACH_CHAR

??
Hamish

-------------------------------------------- Managed by Request Tracker

On Wed, Sep 24, 2003 at 01:31:52PM +0200, Request Tracker wrote:
[...]

I can't find any documentation for src/libes/raster/Raster.c
R_raster_char() or really know how to pull apart draw_character() much
more.

Right now I have sumbitted the first bunch of doxygen-style
function documentation to
src/libes/raster/

Unfortunately R_raster_char() remains undocumented. Whomever
knows what it is doing, please add the documentation into
src/libes/raster/Raster.c
(above is another function which demonstrates how to do that).

Markus

PS: More doxygen-style function documentation to be submitted soon.

Request Tracker wrote:

I can't find any documentation for src/libes/raster/Raster.c
R_raster_char() or really know how to pull apart draw_character() much
more.

R_raster_char() simply sends its arguments to the driver, preceded by
the RASTER_CHAR opcode; the actual work is done by the driver.

However, the net effect is to perform a raster drawing operation.

int R_raster_char(int num, int nrows, int withzero, unsigned char *ras);

"num" is the number of columns.

"nrows" is the number of rows to be drawn, all of which are identical
(this is used for vertical scaling).

"withzero" should be true (non-zero) if zero pixels are to be drawn in
colour zero, false (zero) if they are to be transparent (i.e. not
drawn).

"ras" should point to "num" bytes of data, which constitute the pixels
for a single row of a raster image.

The result is that a rectangular area of width "num" and height
"nrows", with its top-left corner at the current position, is filled
with "nrows" copies of the data pointed to by "ras".

Example: if you wanted to draw a byte-per-pixel image, you would use:

  unsigned char image[HEIGHT][WIDTH];

  for (y = 0; y < HEIGHT; y++)
  {
    R_move_abs(x_left, y_top + y);
    R_raster_char(WIDTH, 1, 1, image[y]);
  }

Looking at the d.text.freetype source code, it appears to be guessing
the pitch[1] of the rendered bitmap from the width, rather than
reading it from the FT_Bitmap structure. That would certainly explain
your symptoms, although there may be other problems.

[1] The number of bytes between the start of one row and the next.

Basically, draw_character() uses FT_Render_Glyph() to render the glyph
as a bitmap (1-bit-per-pixel); it then expands the image to
1-byte-per-pixel, and uses R_raster_char() to draw it.

The most likely mode of failure is that the conversion is wrong,
probably because it's making certain assumptions about the layout of
the bitmap data which turn out to be incorrect (at least, for you; the
minimal testing which I did worked, and presumably the same applies to
Huidae).

Actually fixing this will require reading the FreeType documentation
rather than trial-and-error.

--
Glynn Clements <glynn.clements@virgin.net>

Looking at the d.text.freetype source code, it appears to be guessing
the pitch[1] of the rendered bitmap from the width, rather than
reading it from the FT_Bitmap structure. That would certainly explain
your symptoms, although there may be other problems.

[1] The number of bytes between the start of one row and the next.

...

The most likely mode of failure is that the conversion is wrong,
probably because it's making certain assumptions about the layout of
the bitmap data which turn out to be incorrect (at least, for you; the
minimal testing which I did worked, and presumably the same applies to
Huidae).

Actually fixing this will require reading the FreeType documentation
rather than trial-and-error.

You're correct, it is the pitch.

The line should be j = face->glyph->bitmap.pitch;

I wonder if the remaining (7 ... % 8) etc in the bitwise algebra that
follows is damaging the crispness of the characters..

Anyway it seems to work with any TrueType font I have sitting around on
my system using the path=/a/b/c/fontname.ttf option.

The freetype documentation is actually extraordinarily good, and I
think it would not be all that hard to get libfreetype's anti-aliasing
or some crude alpha-blending to work if anyone felt so inclined.

What's the feeling on changing the meaning of parameters for 5.3?
d.text.freetype has a -p flag which changes the east_north= location-
setting option to use window-pixel coordinates instead. I'd prefer to
change this to mean "percent" not "pixel" or add an at= option like
d.barscale to set position as a percentage of the screen width/height.
Having all three -p, e_n=, and at= seems a bit crowded, but maybe
preferred?

thanks,
Hamish