home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1991 David Elworthy. All rights reserved.
- Distributed by Free Software Foundation, Inc.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* gdevarc.c */
- /* Ghostscript driver for Acorn Archimedes (RISC OS) */
- /* The code here does output to a sprite, but none of the sprite display,
- or the general user interface stuff.
- */
-
- /*
- 1.0 -- April 1991 -- First working version
- 2.0 -- June 1991 -- Changed for GhostScript 2.2
- 2.1 -- Nov 1991 -- Some code moved to gp_arc
- Split into two parts
-
- If the symbol GS$Options is defined, up to four floats may be read from it, setting
- height and width in inches, and the x and y resolutions in dpi. The output is in the
- current mode. Finally, there may be an integer, which is non zero for save only mode.
- The defaults are A4, and 180 dpi. Specifying -1 for a variable gets you the default.
- From version 2.2, a menu for the read and print windows allows this to be altered.
-
- From version 2.0 onwards, the size of the window is changed to fit the sprite, and
- the sprite is redraw manually, rather than being attached to an icon.
-
- If the size is too large or the resolution too high, GS may fall over, probably not
- cleanly.
- */
-
- #include <stdlib.h>
- #include <string.h>
-
- #include "wimpc.h"
-
- #include "gx.h"
- #include "gsmatrix.h" /* for gxdevice.h */
- #include "gxbitmap.h"
- #include "gxdevice.h"
- /* Do all allocation directly */
- #include "malloc_.h"
-
- #include "h.os"
- #include "bbc.h"
- #include "wimp.h"
- #include "wimpt.h"
- #include "win.h"
- #include "event.h"
- #include "wimpio.h"
- #include "colourtran.h"
- #include "sprite.h"
- #include "werr.h"
- #include "saveas.h"
-
- #include "gdevarc.h"
- #include "gp_arc.h"
-
- /*--- Device description --- */
-
- /* See gxdevice.h for the definitions of the procedures. */
- /* Define all the procedures to start with - we can go back
- to defaults for any we turn out not to use */
- dev_proc_open_device(arc_open);
- dev_proc_get_initial_matrix(arc_get_initial_matrix);
- dev_proc_sync_output(arc_sync_output);
- dev_proc_output_page(arc_output_page);
- dev_proc_close_device(arc_close);
- dev_proc_map_rgb_color(arc_map_rgb_color);
- dev_proc_map_color_rgb(arc_map_color_rgb);
- dev_proc_fill_rectangle(arc_fill_rectangle);
- dev_proc_tile_rectangle(arc_tile_rectangle);
- dev_proc_copy_mono(arc_copy_mono);
- dev_proc_copy_color(arc_copy_color);
- dev_proc_draw_line(arc_draw_line);
- dev_proc_fill_trapezoid(arc_fill_trapezoid);
- dev_proc_tile_trapezoid(arc_tile_trapezoid);
-
- /* The device descriptor */
- static gx_device_procs arc_procs = {
- arc_open,
- arc_get_initial_matrix,
- arc_sync_output,
- arc_output_page,
- arc_close,
- arc_map_rgb_color,
- arc_map_color_rgb,
- arc_fill_rectangle,
- arc_tile_rectangle,
- arc_copy_mono,
- arc_copy_color,
- arc_draw_line,
- arc_fill_trapezoid,
- arc_tile_trapezoid
- };
-
- gx_device_arc gs_arc_device = {
- sizeof(gx_device_arc),
- &arc_procs,
- "arc",
- /* Width and height are actually in OS units, not pixels */
- 0, 0, /* width and height in pixels */
- 0, 0, /* density (pixels per inch) */
- no_margins, /* margins around imageable area (inches) */
- 0, /* has_color */
- 255, /* max_rgb_value */
- 1, /* bits per color pixel */
- 0, /* not open yet */
- 0, /* mode */
- 1, 1, /* OS/pixel factors */
- -1, /* Page window handle */
- NULL, /* Address of sprite area */
- 0, /* Space allocated */
- {0, 0}, /* Sprite id - gets filled in later */
- {1, 1, 1, 1}, /* Sprite scaling */
- 0, /* Are we in a 256 colour mode? */
- TRUE, /* Page closed flag */
- 0 /* Save only mode? */
- /* Palette table follows */
- };
-
- /*--- Miscellaneous macros, parameters and globals ---*/
-
- #define CheckPoint if (!gp_arc_fast) wimpc_checkpoint()
-
- #if (0)
- /* Client name for allocators */
- char *arc_client = "GS_ARC";
- #endif
-
- /* Code marked with the define AllowModeChange allows the sprite mode to be
- changed. This code is not complete; I decided not to finish it because it slows
- things down too much. I think all that needs doing is fixing the set_colour macro
- */
- #undef AllowModeChange
-
- /*------------------------------- Device control code -------------------------*/
-
- /* Open the device: do any initialization associated with making the
- device instance valid. This must be done before any output to the device.
- We assume output will be in the current mode, and set the relevant parameters.
- */
- int arc_open(gx_device *dev)
- {
- adev_needed(dev);
- float width = -1, height = -1;
- float xres = -1, yres = -1;
- int saveonly = 0;
- int r0, r1;
- int mode;
- char opt_buffer[MAX_OPTIONS + 1];
-
- /* Find current mode */
- mode = wimpt_mode();
-
- /* Read settings from options string, if it exists */
- os_read_var_val("GS$Options", opt_buffer, MAX_OPTIONS);
- sscanf(opt_buffer, "%f %f %f %f %d", &width, &height, &xres, &yres, &saveonly);
-
- arc_set_options(adev, mode, width, height, xres, yres);
-
- dev->has_color = (bbc_modevar(mode, bbc_NColour) > 1);
- dev->bits_per_color_pixel = 1 << bbc_modevar(mode, bbc_Log2BPP);
- adev->save_only = (saveonly != 0);
-
- /* There are no modes with more than 8bpp at present, but we should warn
- in case of future expansion. Copy color won't work for more than this
- */
- if (dev->bits_per_color_pixel > 8)
- {
- printf("Warning: there are more than 8 bits per pixel\n");
- printf("The current implementation may fail on some colour images\n");
- }
-
- if (!arc_create_op(arc_dev(dev)))
- {
- werr(0, "Unable to create output (probably short of memory)");
- return 1;
- }
-
- /* First create the window and register event handler */
- { char title[100];
- arc_set_title(adev, title);
- if ((adev->page_window = w_create(title, "page", NULL)) == -1)
- return 0;
- }
-
- win_register_event_handler(adev->page_window, arc_page_event_handler, adev);
- gp_arc_device_menu(arc_device_options, adev);
-
- /* Claim unknown events on the page window, so we can get palette changes */
- win_claim_unknown_events (adev->page_window);
-
- /* Set the extent to the size of the sprite */
- arc_set_window_extent(adev);
-
- /* Set the close string */
- w_close_string(NULL);
-
- dev->is_open = 1;
- return 0;
- }
-
-
- /* Construct the initial transformation matrix mapping user
- coordinates (nominally 1/72" per unit) to device coordinates.
- The Archimedes has (0,0) at the bottom left. Hence we end up with
- positive xx and yy values, and zero for everything else.
- */
- void arc_get_initial_matrix(gx_device *dev, gs_matrix *mat)
- {
- mat->xx = dev->x_pixels_per_inch/72.0;
- mat->xy = 0;
- mat->yx = 0;
- mat->yy = dev->y_pixels_per_inch/72.0;
- mat->tx = 0;
- mat->ty = 0;
- }
-
-
- /* Synchronize the device. If any output to the device has been
- buffered, send / write it now. Note that this may be called several times
- in the process of constructing a page, so printer drivers should NOT
- implement this by printing the page.
- OPTIONAL
- */
- int arc_sync_output(gx_device *dev)
- {
- /* Use this as a chance for a checkpoint */
- wimpc_checkpoint();
- return gx_default_sync_output(dev);
- }
-
- /* Output a fully composed page to the device. The default
- definition just calls sync_output. Printer drivers should implement this
- by printing and ejecting the page.
- */
- int arc_output_page(gx_device *dev)
- {
- adev_needed(dev);
-
- /* Check for save only */
- if (adev->save_only)
- {
- saveas(file_type, "GSOutput", adev->sarea_size, arc_saver, 0, 0, (void *)adev);
- }
- else
- {
- /* Put up the window if not save only */
- wimp_wstate state;
-
- event_attachmenumaker(adev->page_window, arc_create_page_menu,
- arc_process_page_menu, (void *)adev);
- if (wimpt_complain(wimp_get_wind_state(adev->page_window, &state)) == 0)
- {
- state.o.behind = -1; /* Make sure window is opened in front */
- wimpt_noerr(wimp_open_wind(&state.o));
- adev->page_closed = FALSE;
- }
-
- /* Wait until closed */
- while (!adev->page_closed) event_process();
- wread_fake_input("\n");
- }
-
- return 0;
- }
-
-
- /* Close the device: release any associated resources. After this,
- output to the device is no longer allowed.
- For notes on memory bug, see arc_create_op
- */
- int arc_close(gx_device *dev)
- {
- adev_needed(dev);
- wimpt_noerr(wimp_delete_wind(adev->page_window));
- win_activedec();
-
- if (adev->sarea)
- free((char *)(adev->sarea));
- adev->sarea_size = 0;
- adev->is_open = 0;
- return 0;
- }
-
- /* Map a RGB color to a device color. The default algorithm simply
- returns the brightness, i.e. max(red, green, blue). The range of legal
- values of the RGB arguments is given by the max_rgb_value element of the
- device parameter structure. Ghostscript assumes that for devices that
- have color capability (i.e., has_color is true), map_rgb_color returns a
- color index for a gray level (as opposed to a non-gray color) iff red =
- green = blue.
- */
- gx_color_index arc_map_rgb_color(gx_device *dev, unsigned short red,
- unsigned short green, unsigned short blue)
- {
- adev_needed(dev);
- wimp_paletteword rgb;
- int colour;
-
- rgb.bytes.red = red;
- rgb.bytes.blue = blue;
- rgb.bytes.green = green;
- #ifdef AllowModeChange
- rgb.bytes.gcol = 0;
- return ((gx_color_index)rgb.word);
- #else
- colourtran_return_GCOLformode(rgb, adev->mode,
- (wimp_paletteword *)adev->paltab, &colour);
- return ((gx_color_index)colour);
- #endif
- }
-
-
- /* Map a device color code to RGB values. The default algorithm
- simply sets all three elements of the result to the color.
- OPTIONAL
- */
- int arc_map_color_rgb(gx_device *dev, gx_color_index color,
- unsigned short rgb[3])
- {
- return gx_default_map_color_rgb(dev, color, rgb);
- }
-
- /*------------------------------- Rendering code --------------------------*/
-
- /*--- Low level functions and macros ---*/
-
- /* Start and end output: direct output to sprite, and save context */
- /* This is quite inefficient */
- static sprite_state sstate;
- #define start_op sprite_outputtosprite(adev->sarea, &(adev->sprite), (int *)0, &sstate);
- #define end_op sprite_restorestate(sstate);
-
- static void arc_set_colour(gx_device_arc *dev, int col)
- {
- #ifdef AllowModeChange
- wimp_paletteword pw;
-
- pw.word = col;
- colourtran_return_GCOLformode(pw, dev->mode,
- (wimp_paletteword *)dev->paltab, &col);
- #endif
-
- if (arc_is_col256(dev))
- {
- bbc_gcol(0, col >> 2);
- bbc_tint(2, col & 3);
- }
- else
- bbc_gcol(0, col);
- }
-
- static void out25(char c1,int x,int y)
- { bbc_vdu(25); bbc_vdu(c1), bbc_vduw(x), bbc_vduw(y); }
-
- /* Macros to adjust coordinates: pixel to OS */
- #define adjust_coord(x,y) x = x << adev->xeig; y = y << adev->yeig
- #define adjust_x(x) x = x << adev->xeig
-
- #define fg(x,y) sprite_writepixel(adev->sarea, &(adev->sprite), x, y, &col1)
- #define bg(x,y) sprite_writepixel(adev->sarea, &(adev->sprite), x, y, &col0)
-
- #define set_colour(d) if (d != last_col) { if (arc_is_col256(adev)) { col1.colour = d >> 2; \
- col1.tint = (d & 3) << 6; } else col1.colour = d; last_col = d; }
-
- /*--- Device interface routines ---*/
-
- /* Fill a rectangle with a color. The set of pixels filled is
- {(px,py) | x <= px < x + width and y <= py < y + height}. If width <= 0
- or height <= 0, nothing should be drawn.
-
- We must subtract 1 from width and height, because bbc-rectangle includes
- both limits.
- */
- int arc_fill_rectangle(gx_device *dev, int x, int y,
- int width, int height, gx_color_index color)
- {
- adev_needed(dev);
- CheckPoint;
-
- if (x + width > dev->width) width = dev->width - x;
- if (y + height > dev->height) height = dev->height - y;
- if (width <=0 || height <= 0) return 0;
- start_op
- arc_set_colour(arc_dev(dev), (int)color);
-
- adjust_coord(x,y);
- adjust_coord(width,height);
-
- out25(4,x,y);
- out25(97,width-1,height-1);
- end_op
- return 0;
- }
-
- /* Draw a minimum-thickness line from (x0,y0) to (x1,y1). The
- precise set of points to be filled is defined as follows. First, if y1 <
- y0, swap (x0,y0) and (x1,y1). Then the line includes the point (x0,y0)
- but not the point (x1,y1).
- */
- #define swap(a, b, temp) temp=a;a=b;b=temp;
- int arc_draw_line(gx_device *dev, int x0, int y0, int x1, int y1,
- gx_color_index color)
- {
- adev_needed(dev);
- CheckPoint;
-
- if (y1 < y0)
- { int temp;
- swap(x0, x1, temp);
- swap(y0, y1, temp);
- }
-
- start_op
- arc_set_colour(arc_dev(dev), (int)color);
-
- adjust_coord(x0,y0);
- adjust_coord(x1,y1);
-
- out25(4,x0,y0);
- out25(13,x1,y1);
- end_op
-
- return 0;
- }
-
-
- /* Fill a trapezoid whose corners are (x0,y0), (x0+width0,y0),
- (x1,y1), and (x1+width1,y1), where y0 < y1. If width0 < 0, width1 < 0,
- width0 = width1 = 0, or y0 >= y1, nothing should be drawn; however, width0
- = 0 or width1 = 0 is valid, and should result in a triangle being filled.
- The points (x0+width0,y0) and (*,y1) are not included.
- ??? I think this comment underspecifies the display.
-
- We implement this by drawing two triangles: (x0,y0):(x0+w0-1,y0):(x1,y1-1)
- and (x0+w0-1,y0):(x1,y1-1):(x1+w1-1,y1-1): the limit pixels are thus excluded
- (subject to rounding errors). If width1 = 0, we omit the second. If width0=0,
- we plot (x0,y0):(x1,y1-1):(x1+w1-1,y1-1).
-
- The result is stlightly inaccurate (but so is the default routine).
- */
- int arc_fill_trapezoid(gx_device *dev, int x0, int y0, int width0,
- int x1, int y1, int width1, gx_color_index color)
- {
- adev_needed(dev);
- CheckPoint;
-
- if (width0 < 0 || width1 < 0 || (width0 == 0 && width1 == 0) || y0 >= y1)
- return 0;
-
- start_op
- arc_set_colour(arc_dev(dev), (int)color);
-
- adjust_coord(x0,y0);
- adjust_coord(x1,y1);
- adjust_x(width0);
- adjust_x(width1);
-
- out25(4,x0,y0);
- out25(4,x1,y1-1);
- if (width0 == 0)
- {
- /* Plot one triangle only */
- out25(85,x1+width1-1,y1-1);
- }
- else
- {
- /* Plot first triangle */
- out25(85,x0+width0-1,y0);
- if (width1 != 0)
- /* Plot second triangle */
- out25(85,x1+width1-1,y1-1);
- }
- end_op
-
- return 0;
- }
-
-
- /* Copy a monochrome image (similar to the PostScript image
- operator). Each scan line is raster bytes wide. Copying begins at
- (data_x,0) and transfers a rectangle of the given width at height to the
- device at device coordinate (x,y). (If the transfer should start at some
- non-zero y value in the data, the caller can adjust the data address by
- the appropriate multiple of the raster.) The copying operation writes
- device color color0 at each 0-bit, and color1 at each 1-bit: if color0 or
- color1 is gx_no_color_index, the device pixel is unaffected if the image
- bit is 0 or 1 respectively.
-
- This operation is the workhorse for most of Ghostscript, so
- implementing it efficiently is very important.
-
- I tried various experiments, expressing this code in other ways. But they all
- take about the same time.
- */
-
- int arc_copy_mono(gx_device *dev, unsigned char *data, int data_x,
- int raster, int x, int y, int width, int height,
- gx_color_index color0, gx_color_index color1)
- {
- adev_needed(dev);
- int base_x = x;
- int h, w;
- int plot0 = (color0 != gx_no_color_index);
- int plot1 = (color1 != gx_no_color_index);
- unsigned char *from;
- int d, m, c;
- int part_add, part_x;
- sprite_colour col0, col1;
- CheckPoint;
-
- /* Set fg and bg colours */
- if (arc_is_col256(adev))
- {
- col0.colour = (int)color0 >> 2;
- col0.tint = ((int)color0 & 3) << 6;
- col1.colour = (int)color1 >> 2;
- col1.tint = ((int)color1 & 3) << 6;
- }
- else
- {
- col0.colour = (int)color0;
- col1.colour = (int)color1;
- }
-
- start_op
- part_add = data_x / 8;
- part_x = 8 - (data_x % 8);
-
- /* Scan across each line in turn */
- for (h = 0 ; h < height ; h++, data += raster)
- {
- x = base_x;
- from = data + part_add;
- w = width;
-
- /* Display any partial byte, if data_x is not a multiple of 8 */
- if (part_x != 8) /* Yes, that really is 8 and not 0 */
- {
- d = *from++;
- m = 1 << part_x;
-
- for ( ; w > 0 && m != 0 ; w--, m = m >> 1, x++)
- {
- c = d & m;
- if ((c != 0) && plot1) /* 1 bit */
- fg(x,y);
- else if ((c == 0) && plot0) /* 0 bit */
- bg(x,y);
- }
- }
-
- /* Display remaining bytes */
- while (w > 0)
- {
- d = *from++;
-
- for (m = 0x80 ; w > 0 && m != 0 ; w--, m = m >> 1, x++)
- {
- /* See what bit we have */
- c = d & m;
- if ((c != 0) && plot1) /* 1 bit */
- fg(x,y);
- else if ((c == 0) && plot0) /* 0 bit */
- bg(x,y);
- }
- }
-
- /* Next screen line */
- y++;
- }
-
- end_op
- return 0;
- }
-
-
- /* Tile a rectangle. Tiling consists of doing multiple copy_mono
- operations to fill the rectangle with copies of the tile. The tiles are
- aligned with the device coordinate system, to avoid "seams".
-
- If color0 and color1 are both gx_no_color_index, then the tile is
- a color pixmap, not a bitmap: see the next section.
- OPTIONAL
- */
- int arc_tile_rectangle(gx_device *dev, gx_bitmap *tile,
- int x, int y, int width, int height,
- gx_color_index color0, gx_color_index color1,
- int phase_x, int phase_y)
- {
- return gx_default_tile_rectangle(dev,tile,x,y,width,height,color0,color1,
- phase_x, phase_y);
- }
-
-
- /* Tile a trapezoid similarly.
- OPTIONAL
- */
- int arc_tile_trapezoid(gx_device *dev, gx_bitmap *tile,
- int x0, int y0, int width0, int x1, int y1, int width1,
- gx_color_index color0, gx_color_index color1,
- int phase_x, int phase_y)
- {
- return gx_default_tile_trapezoid(dev,tile,x0,y0,width0,x1,y1,width1,color0,color1,
- phase_x, phase_y);
- }
-
-
- /* Copy a color image with multiple bits per pixel. The raster is in
- bytes, but x and width are in pixels, not bits.
-
- Do this by plotting pixels in the sprite.
- This routine is untested. It will only work with less than or equal to
- 8 bits per pixel. However, you can't get more than that on the Archimedes
- at present, using standard hardware.
- */
-
- int arc_copy_color(gx_device *dev, unsigned char *data, int data_x,
- int raster, int x, int y, int width, int height)
- {
- adev_needed(dev);
- int bpp = dev->bits_per_color_pixel;
- int h,w;
- unsigned char *from;
- sprite_colour col1;
- gx_color_index last_col = gx_no_color_index;
- int base_x = x;
- char d;
- CheckPoint;
-
- start_op
-
- /* For 8bpp, one byte = one colour, so we can optimise */
- if (bpp == 8)
- {
- from = data;
-
- /* Scan across each line in turn */
- for (h = 0 ; h < height ; h++, data += raster)
- {
- x = base_x;
- for (w = width, x = base_x ; w > 0 ; w--, x++)
- {
- d = *from++;
- set_colour(d);
-
- fg(x,y);
- }
-
- /* Next screen line */
- y++;
- }
- }
- else
- {
- int initial_mask = (bpp == 1) ? 0x80 :
- (bpp == 2) ? 0xc0 : 0xf0;
- int byte_part = 8 >> (bpp-1);
- int part_add, part_x, m;
-
- /* The following loop is similar to copy_mono */
- part_add = data_x / byte_part;
- part_x = data_x % byte_part;
-
- /* Scan across each line in turn */
- for (h = 0 ; h < height ; h++, data += raster)
- {
- x = base_x;
- from = data + part_add;
- w = width;
-
- /* Display any partial byte */
- if (part_x != 0)
- {
- d = *from++;
- m = initial_mask >> byte_part;
-
- for ( ; w > 0 && m != 0 ; w--, m = m >> bpp, x++)
- {
- set_colour(d & m);
- fg(x,y);
- }
- }
- }
-
- /* Display remaining bytes */
- while (w > 0)
- {
- d = *from++;
-
- for (m = initial_mask ; w > 0 && m != 0 ; w--, m = m >> bpp, x++)
- {
- set_colour(d & m);
- fg(x,y);
- }
- }
-
- /* Next screen line */
- y++;
- }
-
- end_op
-
- return 0; /* No default */
-
- }
-