[GRASS-dev] discussion: replacing ps.map

Glynn Clements glynn at gclements.plus.com
Fri Apr 6 13:59:51 EDT 2007

Dylan Beaudette wrote:

> > It would certainly be *easier* to change ps.map, as ps.map generates
> > PostScript directly.
> Would we then be limited to the functionality of ps.map?

Well, you would be limited to whatever functionality it currently
possesses plus whatever functionality you are willing to add.

Mostly, you're limited by the fact that it's a monolithic application,
so adding new functionality means re-compiling it, whereas with
separate d.* modules you can just add new ones.

In practical terms, you're also limited by the existing structure. The
main point is that ps.map commands simply store their data; PostScript
generation only commences once the entire file has been read.

> > d.* commands are limited to whatever the raster library provides. If
> > you want to add an additional operation to the graphics API, you
> > currently have to:
> >
> >  1. Add a R_* function to lib/raster/com_proto.c
> >  2. Add its prototype to include/raster.h
> >  3. Add a field to "struct transport" in lib/raster/transport.h
> >  4. Add an intitialiser for the field to "loc_trans" in lib/raster/com_io.c
> >  5. Add an intitialiser for the field to "rem_trans" in lib/raster/com_io.c
> >  6. Add a LOC_* version to lib/raster/loc_proto.c
> >  7. Add a REM_* version to lib/raster/rem_proto.c
> >  8. Add an opcode to include/graphics.h
> >  9. Add a "case" to lib/driver/command.c
> > 10. Add a COM_* version to lib/driver/<whatever>.c
> > 11. Add its prototype to lib/driver/driver.h
> > 12. Add a field to "struct driver" in lib/driver/driver.h
> > 13. Add a PNG_* version to lib/pngdriver/<whatever>.c
> > 14. Add the prototype to lib/pngdriver/pngdriver.h
> > 15. Add an intitialiser for the field in lib/pngdriver/Driver.c
> > 16. Add a PS_* version to lib/psdriver/<whatever>.c
> > 17. Add the prototype to lib/psdriver/psdriver.h
> > 18. Add an intitialiser for the field in lib/psdriver/Driver.c
> > 19. Add a XD_* version to display/drivers/XDRIVER/<whatever>.c
> > 20. Add the prototype to display/drivers/XDRIVER/XDRIVER.h
> > 21. Add an intitialiser for the field in display/drivers/XDRIVER/main.c
> > 22. Add a NULL intitialiser for the field in display/drivers/HTMLMAP/main.c
> >
> > If we can eliminate monitors in favour of direct rendering, we can
> > eliminate steps 3 through 12. The R_* function would become the COM_*
> > function, with no need to dispatch based upon local/remote transport.
> >
> > If we can eliminate XDRIVER in favour of a GUI which uses PPM/PNG
> > images, that loses 3 steps. If we can eliminate the PNG driver in
> > favour of "gs -sDEVICE=ppmraw", that loses another 3 (as well as
> > giving better quality).
> I think that striving for quality would be a good thing. I usually compose 
> maps at 2x the resolution and then downscale with the current PNG driver.

Oversampling with the PNG driver is less than ideal, due to the fact
that most modules which draw lines use single-pixel lines.

This is also an issue for the PS driver; at 2400dpi, "single pixel" is
a synonym for "invisible". If necessary, you can modify the WIDTH
procedure in psdriver.ps to set a reasonable minimum value. Also, the
line width will initially start at PostScript's default of 1 point
(again, this is easy to change by editing the prolog).

> > Ultimately, if the PS driver was the only driver, we'd be down to just
> > steps 1 and 2. The R_* function would become the PS_* function; there
> > would be no need to dispatch based upon the current driver.
> If this were the only driver this might solve a couple of issues in a single 
> step: high quality on screen output, AND high quality print output. The 
> slowness of the PS driver might make working with data more cumbersome, 
> however the current monitors are a bit slow anyways...

The driver itself shouldn't be particularly slow, but actually
rendering the PostScript might be.

One issue is that the PS driver doesn't undersample the data based
upon the output resolution, as it doesn't know the output resolution
when it's generating the data.

It wouldn't be hard to add an option to do this, although for rasters
you can obtain the same effect by changing the region resolution.

> > More significantly, additional cases wouldn't need necessarily need
> > new R_* functions; the module could just generate PostScript code
> > directly.
> This would be nice if we knew how long it would take to compose the image. 
> Here are some more time comparisons for composition of separate RGB channels 
> with d.rgb.
> region:
> rows:       1638
> cols:       2160
> Xmon
> real    0m1.439s
> user    0m1.288s
> sys     0m0.048s
> real    0m1.419s
> user    0m1.288s
> sys     0m0.032s
> PS
> real    0m4.651s
> user    0m3.836s
> sys     0m0.132s

Timings for raster output (d.rast, d.rgb, d.his) are largely
meaningless unless you know the region dimensions (rows x cols)
relative to the screen size.

The reason is that D_draw_raster() and D_draw_raster_RGB() return the
next source row which will actually be displayed. If the region
resolution is higher than the screen resolution (downsampling), d.rast
etc will only read the rows which are actually going to be displayed.

PostScript conceptually has infinite resolution, so d.rast etc can
never skip rows when using the PostScript driver.

As both the raster I/O and colour lookups can be quite expensive, this
can make a significant difference.

> I also noticed that the PS driver only supports grayscale, perhaps this was 
> mentioned previously.

You have to explicitly set GRASS_TRUECOLOR=TRUE to get colour output. 

Glynn Clements <glynn at gclements.plus.com>

More information about the grass-dev mailing list