1/*
2 * tkCanvBmap.c --
3 *
4 *	This file implements bitmap items for canvas widgets.
5 *
6 * Copyright (c) 1992-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkCanvBmap.c,v 1.7.2.2 2005/02/11 19:27:52 hobbs Exp $
13 */
14
15#include <stdio.h>
16#include "tkInt.h"
17#include "tkPort.h"
18#include "tkCanvas.h"
19
20/*
21 * The structure below defines the record for each bitmap item.
22 */
23
24typedef struct BitmapItem  {
25    Tk_Item header;		/* Generic stuff that's the same for all
26				 * types.  MUST BE FIRST IN STRUCTURE. */
27    double x, y;		/* Coordinates of positioning point for
28				 * bitmap. */
29    Tk_Anchor anchor;		/* Where to anchor bitmap relative to
30				 * (x,y). */
31    Pixmap bitmap;		/* Bitmap to display in window. */
32    Pixmap activeBitmap;	/* Bitmap to display in window. */
33    Pixmap disabledBitmap;	/* Bitmap to display in window. */
34    XColor *fgColor;		/* Foreground color to use for bitmap. */
35    XColor *activeFgColor;	/* Foreground color to use for bitmap. */
36    XColor *disabledFgColor;	/* Foreground color to use for bitmap. */
37    XColor *bgColor;		/* Background color to use for bitmap. */
38    XColor *activeBgColor;	/* Background color to use for bitmap. */
39    XColor *disabledBgColor;	/* Background color to use for bitmap. */
40    GC gc;			/* Graphics context to use for drawing
41				 * bitmap on screen. */
42} BitmapItem;
43
44/*
45 * Information used for parsing configuration specs:
46 */
47
48static Tk_CustomOption stateOption = {
49    (Tk_OptionParseProc *) TkStateParseProc,
50    TkStatePrintProc, (ClientData) 2
51};
52static Tk_CustomOption tagsOption = {
53    (Tk_OptionParseProc *) Tk_CanvasTagsParseProc,
54    Tk_CanvasTagsPrintProc, (ClientData) NULL
55};
56
57static Tk_ConfigSpec configSpecs[] = {
58    {TK_CONFIG_COLOR, "-activebackground", (char *) NULL, (char *) NULL,
59	(char *) NULL, Tk_Offset(BitmapItem, activeBgColor), TK_CONFIG_NULL_OK},
60    {TK_CONFIG_BITMAP, "-activebitmap", (char *) NULL, (char *) NULL,
61	(char *) NULL, Tk_Offset(BitmapItem, activeBitmap), TK_CONFIG_NULL_OK},
62    {TK_CONFIG_COLOR, "-activeforeground", (char *) NULL, (char *) NULL,
63	(char *) NULL, Tk_Offset(BitmapItem, activeFgColor), TK_CONFIG_NULL_OK},
64    {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
65	"center", Tk_Offset(BitmapItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
66    {TK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
67	(char *) NULL, Tk_Offset(BitmapItem, bgColor), TK_CONFIG_NULL_OK},
68    {TK_CONFIG_BITMAP, "-bitmap", (char *) NULL, (char *) NULL,
69	(char *) NULL, Tk_Offset(BitmapItem, bitmap), TK_CONFIG_NULL_OK},
70    {TK_CONFIG_COLOR, "-disabledbackground", (char *) NULL, (char *) NULL,
71	(char *) NULL, Tk_Offset(BitmapItem, disabledBgColor),
72	TK_CONFIG_NULL_OK},
73    {TK_CONFIG_BITMAP, "-disabledbitmap", (char *) NULL, (char *) NULL,
74	(char *) NULL, Tk_Offset(BitmapItem, disabledBitmap),
75	TK_CONFIG_NULL_OK},
76    {TK_CONFIG_COLOR, "-disabledforeground", (char *) NULL, (char *) NULL,
77	(char *) NULL, Tk_Offset(BitmapItem, disabledFgColor),
78	TK_CONFIG_NULL_OK},
79    {TK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
80	"black", Tk_Offset(BitmapItem, fgColor), 0},
81    {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL,
82	(char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK,
83	&stateOption},
84    {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
85	(char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
86    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
87	(char *) NULL, 0, 0}
88};
89
90/*
91 * Prototypes for procedures defined in this file:
92 */
93
94static int		BitmapCoords _ANSI_ARGS_((Tcl_Interp *interp,
95			    Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
96			    Tcl_Obj *CONST objv[]));
97static int		BitmapToArea _ANSI_ARGS_((Tk_Canvas canvas,
98			    Tk_Item *itemPtr, double *rectPtr));
99static double		BitmapToPoint _ANSI_ARGS_((Tk_Canvas canvas,
100			    Tk_Item *itemPtr, double *coordPtr));
101static int		BitmapToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
102			    Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
103static void		ComputeBitmapBbox _ANSI_ARGS_((Tk_Canvas canvas,
104			    BitmapItem *bmapPtr));
105static int		ConfigureBitmap _ANSI_ARGS_((Tcl_Interp *interp,
106			    Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
107			    Tcl_Obj *CONST objv[], int flags));
108static int		TkcCreateBitmap _ANSI_ARGS_((Tcl_Interp *interp,
109			    Tk_Canvas canvas, struct Tk_Item *itemPtr,
110			    int objc, Tcl_Obj *CONST objv[]));
111static void		DeleteBitmap _ANSI_ARGS_((Tk_Canvas canvas,
112			    Tk_Item *itemPtr, Display *display));
113static void		DisplayBitmap _ANSI_ARGS_((Tk_Canvas canvas,
114			    Tk_Item *itemPtr, Display *display, Drawable dst,
115			    int x, int y, int width, int height));
116static void		ScaleBitmap _ANSI_ARGS_((Tk_Canvas canvas,
117			    Tk_Item *itemPtr, double originX, double originY,
118			    double scaleX, double scaleY));
119static void		TranslateBitmap _ANSI_ARGS_((Tk_Canvas canvas,
120			    Tk_Item *itemPtr, double deltaX, double deltaY));
121
122/*
123 * The structures below defines the bitmap item type in terms of
124 * procedures that can be invoked by generic item code.
125 */
126
127Tk_ItemType tkBitmapType = {
128    "bitmap",				/* name */
129    sizeof(BitmapItem),			/* itemSize */
130    TkcCreateBitmap,			/* createProc */
131    configSpecs,			/* configSpecs */
132    ConfigureBitmap,			/* configureProc */
133    BitmapCoords,			/* coordProc */
134    DeleteBitmap,			/* deleteProc */
135    DisplayBitmap,			/* displayProc */
136    TK_CONFIG_OBJS,			/* flags */
137    BitmapToPoint,			/* pointProc */
138    BitmapToArea,			/* areaProc */
139    BitmapToPostscript,			/* postscriptProc */
140    ScaleBitmap,			/* scaleProc */
141    TranslateBitmap,			/* translateProc */
142    (Tk_ItemIndexProc *) NULL,		/* indexProc */
143    (Tk_ItemCursorProc *) NULL,		/* icursorProc */
144    (Tk_ItemSelectionProc *) NULL,	/* selectionProc */
145    (Tk_ItemInsertProc *) NULL,		/* insertProc */
146    (Tk_ItemDCharsProc *) NULL,		/* dTextProc */
147    (Tk_ItemType *) NULL,		/* nextPtr */
148};
149
150/*
151 *--------------------------------------------------------------
152 *
153 * TkcCreateBitmap --
154 *
155 *	This procedure is invoked to create a new bitmap
156 *	item in a canvas.
157 *
158 * Results:
159 *	A standard Tcl return value.  If an error occurred in
160 *	creating the item, then an error message is left in
161 *	the interp's result;  in this case itemPtr is left uninitialized,
162 *	so it can be safely freed by the caller.
163 *
164 * Side effects:
165 *	A new bitmap item is created.
166 *
167 *--------------------------------------------------------------
168 */
169
170static int
171TkcCreateBitmap(interp, canvas, itemPtr, objc, objv)
172    Tcl_Interp *interp;			/* Interpreter for error reporting. */
173    Tk_Canvas canvas;			/* Canvas to hold new item. */
174    Tk_Item *itemPtr;			/* Record to hold new item;  header
175					 * has been initialized by caller. */
176    int objc;				/* Number of arguments in objv. */
177    Tcl_Obj *CONST objv[];		/* Arguments describing rectangle. */
178{
179    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
180    int i;
181
182    if (objc == 0) {
183	Tcl_Panic("canvas did not pass any coords\n");
184    }
185
186    /*
187     * Initialize item's record.
188     */
189
190    bmapPtr->anchor = TK_ANCHOR_CENTER;
191    bmapPtr->bitmap = None;
192    bmapPtr->activeBitmap = None;
193    bmapPtr->disabledBitmap = None;
194    bmapPtr->fgColor = NULL;
195    bmapPtr->activeFgColor = NULL;
196    bmapPtr->disabledFgColor = NULL;
197    bmapPtr->bgColor = NULL;
198    bmapPtr->activeBgColor = NULL;
199    bmapPtr->disabledBgColor = NULL;
200    bmapPtr->gc = None;
201
202    /*
203     * Process the arguments to fill in the item record.
204     * Only 1 (list) or 2 (x y) coords are allowed.
205     */
206
207    if (objc == 1) {
208	i = 1;
209    } else {
210	char *arg = Tcl_GetString(objv[1]);
211	i = 2;
212	if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
213	    i = 1;
214	}
215    }
216    if (BitmapCoords(interp, canvas, itemPtr, i, objv) != TCL_OK) {
217	goto error;
218    }
219    if (ConfigureBitmap(interp, canvas, itemPtr, objc-i, objv+i, 0)
220	    == TCL_OK) {
221	return TCL_OK;
222    }
223
224    error:
225    DeleteBitmap(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
226    return TCL_ERROR;
227}
228
229/*
230 *--------------------------------------------------------------
231 *
232 * BitmapCoords --
233 *
234 *	This procedure is invoked to process the "coords" widget
235 *	command on bitmap items.  See the user documentation for
236 *	details on what it does.
237 *
238 * Results:
239 *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
240 *
241 * Side effects:
242 *	The coordinates for the given item may be changed.
243 *
244 *--------------------------------------------------------------
245 */
246
247static int
248BitmapCoords(interp, canvas, itemPtr, objc, objv)
249    Tcl_Interp *interp;			/* Used for error reporting. */
250    Tk_Canvas canvas;			/* Canvas containing item. */
251    Tk_Item *itemPtr;			/* Item whose coordinates are to be
252					 * read or modified. */
253    int objc;				/* Number of coordinates supplied in
254					 * objv. */
255    Tcl_Obj *CONST objv[];		/* Array of coordinates: x1, y1,
256					 * x2, y2, ... */
257{
258    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
259
260    if (objc == 0) {
261	Tcl_Obj *obj = Tcl_NewObj();
262	Tcl_Obj *subobj = Tcl_NewDoubleObj(bmapPtr->x);
263	Tcl_ListObjAppendElement(interp, obj, subobj);
264	subobj = Tcl_NewDoubleObj(bmapPtr->y);
265	Tcl_ListObjAppendElement(interp, obj, subobj);
266	Tcl_SetObjResult(interp, obj);
267    } else if (objc < 3) {
268	if (objc == 1) {
269	    if (Tcl_ListObjGetElements(interp, objv[0], &objc,
270		    (Tcl_Obj ***) &objv) != TCL_OK) {
271		return TCL_ERROR;
272	    } else if (objc != 2) {
273		char buf[64 + TCL_INTEGER_SPACE];
274
275		sprintf(buf, "wrong # coordinates: expected 2, got %d", objc);
276		Tcl_SetResult(interp, buf, TCL_VOLATILE);
277		return TCL_ERROR;
278	    }
279	}
280	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
281		&bmapPtr->x) != TCL_OK)
282		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
283			&bmapPtr->y) != TCL_OK)) {
284	    return TCL_ERROR;
285	}
286	ComputeBitmapBbox(canvas, bmapPtr);
287    } else {
288	char buf[64 + TCL_INTEGER_SPACE];
289
290	sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", objc);
291	Tcl_SetResult(interp, buf, TCL_VOLATILE);
292	return TCL_ERROR;
293    }
294    return TCL_OK;
295}
296
297/*
298 *--------------------------------------------------------------
299 *
300 * ConfigureBitmap --
301 *
302 *	This procedure is invoked to configure various aspects
303 *	of a bitmap item, such as its anchor position.
304 *
305 * Results:
306 *	A standard Tcl result code.  If an error occurs, then
307 *	an error message is left in the interp's result.
308 *
309 * Side effects:
310 *	Configuration information may be set for itemPtr.
311 *
312 *--------------------------------------------------------------
313 */
314
315static int
316ConfigureBitmap(interp, canvas, itemPtr, objc, objv, flags)
317    Tcl_Interp *interp;		/* Used for error reporting. */
318    Tk_Canvas canvas;		/* Canvas containing itemPtr. */
319    Tk_Item *itemPtr;		/* Bitmap item to reconfigure. */
320    int objc;			/* Number of elements in objv.  */
321    Tcl_Obj *CONST objv[];	/* Arguments describing things to configure. */
322    int flags;			/* Flags to pass to Tk_ConfigureWidget. */
323{
324    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
325    XGCValues gcValues;
326    GC newGC;
327    Tk_Window tkwin;
328    unsigned long mask;
329    XColor *fgColor;
330    XColor *bgColor;
331    Pixmap bitmap;
332    Tk_State state;
333
334    tkwin = Tk_CanvasTkwin(canvas);
335    if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
336	    (CONST char **) objv, (char *) bmapPtr, flags|TK_CONFIG_OBJS)) {
337	return TCL_ERROR;
338    }
339
340    /*
341     * A few of the options require additional processing, such as those
342     * that determine the graphics context.
343     */
344
345    state = itemPtr->state;
346
347    if (bmapPtr->activeFgColor!=NULL ||
348	    bmapPtr->activeBgColor!=NULL ||
349	    bmapPtr->activeBitmap!=None) {
350	itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
351    } else {
352	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
353    }
354
355    if (state == TK_STATE_NULL) {
356	state = ((TkCanvas *)canvas)->canvas_state;
357    }
358    if (state == TK_STATE_HIDDEN) {
359	ComputeBitmapBbox(canvas, bmapPtr);
360	return TCL_OK;
361    }
362    fgColor = bmapPtr->fgColor;
363    bgColor = bmapPtr->bgColor;
364    bitmap = bmapPtr->bitmap;
365    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
366	if (bmapPtr->activeFgColor!=NULL) {
367	    fgColor = bmapPtr->activeFgColor;
368	}
369	if (bmapPtr->activeBgColor!=NULL) {
370	    bgColor = bmapPtr->activeBgColor;
371	}
372	if (bmapPtr->activeBitmap!=None) {
373	    bitmap = bmapPtr->activeBitmap;
374	}
375    } else if (state == TK_STATE_DISABLED) {
376	if (bmapPtr->disabledFgColor!=NULL) {
377	    fgColor = bmapPtr->disabledFgColor;
378	}
379	if (bmapPtr->disabledBgColor!=NULL) {
380	    bgColor = bmapPtr->disabledBgColor;
381	}
382	if (bmapPtr->disabledBitmap!=None) {
383	    bitmap = bmapPtr->disabledBitmap;
384	}
385    }
386
387    if (bitmap == None) {
388	newGC = None;
389    } else {
390	gcValues.foreground = fgColor->pixel;
391	mask = GCForeground;
392	if (bgColor != NULL) {
393	    gcValues.background = bgColor->pixel;
394	    mask |= GCBackground;
395	} else {
396	    gcValues.clip_mask = bitmap;
397	    mask |= GCClipMask;
398	}
399	newGC = Tk_GetGC(tkwin, mask, &gcValues);
400    }
401    if (bmapPtr->gc != None) {
402	Tk_FreeGC(Tk_Display(tkwin), bmapPtr->gc);
403    }
404    bmapPtr->gc = newGC;
405
406    ComputeBitmapBbox(canvas, bmapPtr);
407    return TCL_OK;
408}
409
410/*
411 *--------------------------------------------------------------
412 *
413 * DeleteBitmap --
414 *
415 *	This procedure is called to clean up the data structure
416 *	associated with a bitmap item.
417 *
418 * Results:
419 *	None.
420 *
421 * Side effects:
422 *	Resources associated with itemPtr are released.
423 *
424 *--------------------------------------------------------------
425 */
426
427static void
428DeleteBitmap(canvas, itemPtr, display)
429    Tk_Canvas canvas;			/* Info about overall canvas widget. */
430    Tk_Item *itemPtr;			/* Item that is being deleted. */
431    Display *display;			/* Display containing window for
432					 * canvas. */
433{
434    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
435
436    if (bmapPtr->bitmap != None) {
437	Tk_FreeBitmap(display, bmapPtr->bitmap);
438    }
439    if (bmapPtr->activeBitmap != None) {
440	Tk_FreeBitmap(display, bmapPtr->activeBitmap);
441    }
442    if (bmapPtr->disabledBitmap != None) {
443	Tk_FreeBitmap(display, bmapPtr->disabledBitmap);
444    }
445    if (bmapPtr->fgColor != NULL) {
446	Tk_FreeColor(bmapPtr->fgColor);
447    }
448    if (bmapPtr->activeFgColor != NULL) {
449	Tk_FreeColor(bmapPtr->activeFgColor);
450    }
451    if (bmapPtr->disabledFgColor != NULL) {
452	Tk_FreeColor(bmapPtr->disabledFgColor);
453    }
454    if (bmapPtr->bgColor != NULL) {
455	Tk_FreeColor(bmapPtr->bgColor);
456    }
457    if (bmapPtr->activeBgColor != NULL) {
458	Tk_FreeColor(bmapPtr->activeBgColor);
459    }
460    if (bmapPtr->disabledBgColor != NULL) {
461	Tk_FreeColor(bmapPtr->disabledBgColor);
462    }
463    if (bmapPtr->gc != NULL) {
464	Tk_FreeGC(display, bmapPtr->gc);
465    }
466}
467
468/*
469 *--------------------------------------------------------------
470 *
471 * ComputeBitmapBbox --
472 *
473 *	This procedure is invoked to compute the bounding box of
474 *	all the pixels that may be drawn as part of a bitmap item.
475 *	This procedure is where the child bitmap's placement is
476 *	computed.
477 *
478 * Results:
479 *	None.
480 *
481 * Side effects:
482 *	The fields x1, y1, x2, and y2 are updated in the header
483 *	for itemPtr.
484 *
485 *--------------------------------------------------------------
486 */
487
488	/* ARGSUSED */
489static void
490ComputeBitmapBbox(canvas, bmapPtr)
491    Tk_Canvas canvas;			/* Canvas that contains item. */
492    BitmapItem *bmapPtr;		/* Item whose bbox is to be
493					 * recomputed. */
494{
495    int width, height;
496    int x, y;
497    Pixmap bitmap;
498    Tk_State state = bmapPtr->header.state;
499
500    if (state == TK_STATE_NULL) {
501	state = ((TkCanvas *)canvas)->canvas_state;
502    }
503    bitmap = bmapPtr->bitmap;
504    if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)bmapPtr) {
505	if (bmapPtr->activeBitmap!=None) {
506	    bitmap = bmapPtr->activeBitmap;
507	}
508    } else if (state==TK_STATE_DISABLED) {
509	if (bmapPtr->disabledBitmap!=None) {
510	    bitmap = bmapPtr->disabledBitmap;
511	}
512    }
513
514    x = (int) (bmapPtr->x + ((bmapPtr->x >= 0) ? 0.5 : - 0.5));
515    y = (int) (bmapPtr->y + ((bmapPtr->y >= 0) ? 0.5 : - 0.5));
516
517    if (state==TK_STATE_HIDDEN || bitmap == None) {
518	bmapPtr->header.x1 = bmapPtr->header.x2 = x;
519	bmapPtr->header.y1 = bmapPtr->header.y2 = y;
520	return;
521    }
522
523    /*
524     * Compute location and size of bitmap, using anchor information.
525     */
526
527    Tk_SizeOfBitmap(Tk_Display(Tk_CanvasTkwin(canvas)), bitmap,
528	    &width, &height);
529    switch (bmapPtr->anchor) {
530	case TK_ANCHOR_N:
531	    x -= width/2;
532	    break;
533	case TK_ANCHOR_NE:
534	    x -= width;
535	    break;
536	case TK_ANCHOR_E:
537	    x -= width;
538	    y -= height/2;
539	    break;
540	case TK_ANCHOR_SE:
541	    x -= width;
542	    y -= height;
543	    break;
544	case TK_ANCHOR_S:
545	    x -= width/2;
546	    y -= height;
547	    break;
548	case TK_ANCHOR_SW:
549	    y -= height;
550	    break;
551	case TK_ANCHOR_W:
552	    y -= height/2;
553	    break;
554	case TK_ANCHOR_NW:
555	    break;
556	case TK_ANCHOR_CENTER:
557	    x -= width/2;
558	    y -= height/2;
559	    break;
560    }
561
562    /*
563     * Store the information in the item header.
564     */
565
566    bmapPtr->header.x1 = x;
567    bmapPtr->header.y1 = y;
568    bmapPtr->header.x2 = x + width;
569    bmapPtr->header.y2 = y + height;
570}
571
572/*
573 *--------------------------------------------------------------
574 *
575 * DisplayBitmap --
576 *
577 *	This procedure is invoked to draw a bitmap item in a given
578 *	drawable.
579 *
580 * Results:
581 *	None.
582 *
583 * Side effects:
584 *	ItemPtr is drawn in drawable using the transformation
585 *	information in canvas.
586 *
587 *--------------------------------------------------------------
588 */
589
590static void
591DisplayBitmap(canvas, itemPtr, display, drawable, x, y, width, height)
592    Tk_Canvas canvas;			/* Canvas that contains item. */
593    Tk_Item *itemPtr;			/* Item to be displayed. */
594    Display *display;			/* Display on which to draw item. */
595    Drawable drawable;			/* Pixmap or window in which to draw
596					 * item. */
597    int x, y, width, height;		/* Describes region of canvas that
598					 * must be redisplayed (not used). */
599{
600    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
601    int bmapX, bmapY, bmapWidth, bmapHeight;
602    short drawableX, drawableY;
603    Pixmap bitmap;
604    Tk_State state = itemPtr->state;
605
606    /*
607     * If the area being displayed doesn't cover the whole bitmap,
608     * then only redisplay the part of the bitmap that needs
609     * redisplay.
610     */
611
612    if (state == TK_STATE_NULL) {
613	state = ((TkCanvas *)canvas)->canvas_state;
614    }
615    bitmap = bmapPtr->bitmap;
616    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
617	if (bmapPtr->activeBitmap!=None) {
618	    bitmap = bmapPtr->activeBitmap;
619	}
620    } else if (state == TK_STATE_DISABLED) {
621	if (bmapPtr->disabledBitmap!=None) {
622	    bitmap = bmapPtr->disabledBitmap;
623	}
624    }
625
626    if (bitmap != None) {
627	if (x > bmapPtr->header.x1) {
628	    bmapX = x - bmapPtr->header.x1;
629	    bmapWidth = bmapPtr->header.x2 - x;
630	} else {
631	    bmapX = 0;
632	    if ((x+width) < bmapPtr->header.x2) {
633		bmapWidth = x + width - bmapPtr->header.x1;
634	    } else {
635		bmapWidth = bmapPtr->header.x2 - bmapPtr->header.x1;
636	    }
637	}
638	if (y > bmapPtr->header.y1) {
639	    bmapY = y - bmapPtr->header.y1;
640	    bmapHeight = bmapPtr->header.y2 - y;
641	} else {
642	    bmapY = 0;
643	    if ((y+height) < bmapPtr->header.y2) {
644		bmapHeight = y + height - bmapPtr->header.y1;
645	    } else {
646		bmapHeight = bmapPtr->header.y2 - bmapPtr->header.y1;
647	    }
648	}
649	Tk_CanvasDrawableCoords(canvas,
650		(double) (bmapPtr->header.x1 + bmapX),
651		(double) (bmapPtr->header.y1 + bmapY),
652		&drawableX, &drawableY);
653
654	/*
655	 * Must modify the mask origin within the graphics context
656	 * to line up with the bitmap's origin (in order to make
657	 * bitmaps with "-background {}" work right).
658	 */
659
660	XSetClipOrigin(display, bmapPtr->gc, drawableX - bmapX,
661		drawableY - bmapY);
662	XCopyPlane(display, bitmap, drawable,
663		bmapPtr->gc, bmapX, bmapY, (unsigned int) bmapWidth,
664		(unsigned int) bmapHeight, drawableX, drawableY, 1);
665	XSetClipOrigin(display, bmapPtr->gc, 0, 0);
666    }
667}
668
669/*
670 *--------------------------------------------------------------
671 *
672 * BitmapToPoint --
673 *
674 *	Computes the distance from a given point to a given
675 *	rectangle, in canvas units.
676 *
677 * Results:
678 *	The return value is 0 if the point whose x and y coordinates
679 *	are coordPtr[0] and coordPtr[1] is inside the bitmap.  If the
680 *	point isn't inside the bitmap then the return value is the
681 *	distance from the point to the bitmap.
682 *
683 * Side effects:
684 *	None.
685 *
686 *--------------------------------------------------------------
687 */
688
689	/* ARGSUSED */
690static double
691BitmapToPoint(canvas, itemPtr, coordPtr)
692    Tk_Canvas canvas;		/* Canvas containing item. */
693    Tk_Item *itemPtr;		/* Item to check against point. */
694    double *coordPtr;		/* Pointer to x and y coordinates. */
695{
696    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
697    double x1, x2, y1, y2, xDiff, yDiff;
698
699    x1 = bmapPtr->header.x1;
700    y1 = bmapPtr->header.y1;
701    x2 = bmapPtr->header.x2;
702    y2 = bmapPtr->header.y2;
703
704    /*
705     * Point is outside rectangle.
706     */
707
708    if (coordPtr[0] < x1) {
709	xDiff = x1 - coordPtr[0];
710    } else if (coordPtr[0] > x2)  {
711	xDiff = coordPtr[0] - x2;
712    } else {
713	xDiff = 0;
714    }
715
716    if (coordPtr[1] < y1) {
717	yDiff = y1 - coordPtr[1];
718    } else if (coordPtr[1] > y2)  {
719	yDiff = coordPtr[1] - y2;
720    } else {
721	yDiff = 0;
722    }
723
724    return hypot(xDiff, yDiff);
725}
726
727/*
728 *--------------------------------------------------------------
729 *
730 * BitmapToArea --
731 *
732 *	This procedure is called to determine whether an item
733 *	lies entirely inside, entirely outside, or overlapping
734 *	a given rectangle.
735 *
736 * Results:
737 *	-1 is returned if the item is entirely outside the area
738 *	given by rectPtr, 0 if it overlaps, and 1 if it is entirely
739 *	inside the given area.
740 *
741 * Side effects:
742 *	None.
743 *
744 *--------------------------------------------------------------
745 */
746
747	/* ARGSUSED */
748static int
749BitmapToArea(canvas, itemPtr, rectPtr)
750    Tk_Canvas canvas;		/* Canvas containing item. */
751    Tk_Item *itemPtr;		/* Item to check against rectangle. */
752    double *rectPtr;		/* Pointer to array of four coordinates
753				 * (x1, y1, x2, y2) describing rectangular
754				 * area.  */
755{
756    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
757
758    if ((rectPtr[2] <= bmapPtr->header.x1)
759	    || (rectPtr[0] >= bmapPtr->header.x2)
760	    || (rectPtr[3] <= bmapPtr->header.y1)
761	    || (rectPtr[1] >= bmapPtr->header.y2)) {
762	return -1;
763    }
764    if ((rectPtr[0] <= bmapPtr->header.x1)
765	    && (rectPtr[1] <= bmapPtr->header.y1)
766	    && (rectPtr[2] >= bmapPtr->header.x2)
767	    && (rectPtr[3] >= bmapPtr->header.y2)) {
768	return 1;
769    }
770    return 0;
771}
772
773/*
774 *--------------------------------------------------------------
775 *
776 * ScaleBitmap --
777 *
778 *	This procedure is invoked to rescale a bitmap item in a
779 *	canvas.  It is one of the standard item procedures for
780 *	bitmap items, and is invoked by the generic canvas code.
781 *
782 * Results:
783 *	None.
784 *
785 * Side effects:
786 *	The item referred to by itemPtr is rescaled so that the
787 *	following transformation is applied to all point coordinates:
788 *		x' = originX + scaleX*(x-originX)
789 *		y' = originY + scaleY*(y-originY)
790 *
791 *--------------------------------------------------------------
792 */
793
794static void
795ScaleBitmap(canvas, itemPtr, originX, originY, scaleX, scaleY)
796    Tk_Canvas canvas;			/* Canvas containing rectangle. */
797    Tk_Item *itemPtr;			/* Rectangle to be scaled. */
798    double originX, originY;		/* Origin about which to scale item. */
799    double scaleX;			/* Amount to scale in X direction. */
800    double scaleY;			/* Amount to scale in Y direction. */
801{
802    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
803
804    bmapPtr->x = originX + scaleX*(bmapPtr->x - originX);
805    bmapPtr->y = originY + scaleY*(bmapPtr->y - originY);
806    ComputeBitmapBbox(canvas, bmapPtr);
807}
808
809/*
810 *--------------------------------------------------------------
811 *
812 * TranslateBitmap --
813 *
814 *	This procedure is called to move an item by a given amount.
815 *
816 * Results:
817 *	None.
818 *
819 * Side effects:
820 *	The position of the item is offset by (xDelta, yDelta), and
821 *	the bounding box is updated in the generic part of the item
822 *	structure.
823 *
824 *--------------------------------------------------------------
825 */
826
827static void
828TranslateBitmap(canvas, itemPtr, deltaX, deltaY)
829    Tk_Canvas canvas;			/* Canvas containing item. */
830    Tk_Item *itemPtr;			/* Item that is being moved. */
831    double deltaX, deltaY;		/* Amount by which item is to be
832					 * moved. */
833{
834    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
835
836    bmapPtr->x += deltaX;
837    bmapPtr->y += deltaY;
838    ComputeBitmapBbox(canvas, bmapPtr);
839}
840
841/*
842 *--------------------------------------------------------------
843 *
844 * BitmapToPostscript --
845 *
846 *	This procedure is called to generate Postscript for
847 *	bitmap items.
848 *
849 * Results:
850 *	The return value is a standard Tcl result.  If an error
851 *	occurs in generating Postscript then an error message is
852 *	left in the interp's result, replacing whatever used to be there.
853 *	If no error occurs, then Postscript for the item is appended
854 *	to the result.
855 *
856 * Side effects:
857 *	None.
858 *
859 *--------------------------------------------------------------
860 */
861
862static int
863BitmapToPostscript(interp, canvas, itemPtr, prepass)
864    Tcl_Interp *interp;			/* Leave Postscript or error message
865					 * here. */
866    Tk_Canvas canvas;			/* Information about overall canvas. */
867    Tk_Item *itemPtr;			/* Item for which Postscript is
868					 * wanted. */
869    int prepass;			/* 1 means this is a prepass to
870					 * collect font information;  0 means
871					 * final Postscript is being created. */
872{
873    BitmapItem *bmapPtr = (BitmapItem *) itemPtr;
874    double x, y;
875    int width, height, rowsAtOnce, rowsThisTime;
876    int curRow;
877    char buffer[100 + TCL_DOUBLE_SPACE * 2 + TCL_INTEGER_SPACE * 4];
878    XColor *fgColor;
879    XColor *bgColor;
880    Pixmap bitmap;
881    Tk_State state = itemPtr->state;
882
883    if (state == TK_STATE_NULL) {
884	state = ((TkCanvas *)canvas)->canvas_state;
885    }
886    fgColor = bmapPtr->fgColor;
887    bgColor = bmapPtr->bgColor;
888    bitmap = bmapPtr->bitmap;
889    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
890	if (bmapPtr->activeFgColor!=NULL) {
891	    fgColor = bmapPtr->activeFgColor;
892	}
893	if (bmapPtr->activeBgColor!=NULL) {
894	    bgColor = bmapPtr->activeBgColor;
895	}
896	if (bmapPtr->activeBitmap!=None) {
897	    bitmap = bmapPtr->activeBitmap;
898	}
899    } else if (state == TK_STATE_DISABLED) {
900	if (bmapPtr->disabledFgColor!=NULL) {
901	    fgColor = bmapPtr->disabledFgColor;
902	}
903	if (bmapPtr->disabledBgColor!=NULL) {
904	    bgColor = bmapPtr->disabledBgColor;
905	}
906	if (bmapPtr->disabledBitmap!=None) {
907	    bitmap = bmapPtr->disabledBitmap;
908	}
909    }
910
911    if (bitmap == None) {
912	return TCL_OK;
913    }
914
915    /*
916     * Compute the coordinates of the lower-left corner of the bitmap,
917     * taking into account the anchor position for the bitmp.
918     */
919
920    x = bmapPtr->x;
921    y = Tk_CanvasPsY(canvas, bmapPtr->y);
922    Tk_SizeOfBitmap(Tk_Display(Tk_CanvasTkwin(canvas)), bitmap,
923	    &width, &height);
924    switch (bmapPtr->anchor) {
925	case TK_ANCHOR_NW:			y -= height;		break;
926	case TK_ANCHOR_N:	x -= width/2.0; y -= height;		break;
927	case TK_ANCHOR_NE:	x -= width;	y -= height;		break;
928	case TK_ANCHOR_E:	x -= width;	y -= height/2.0;	break;
929	case TK_ANCHOR_SE:	x -= width;				break;
930	case TK_ANCHOR_S:	x -= width/2.0;				break;
931	case TK_ANCHOR_SW:						break;
932	case TK_ANCHOR_W:			y -= height/2.0;	break;
933	case TK_ANCHOR_CENTER:	x -= width/2.0; y -= height/2.0;	break;
934    }
935
936    /*
937     * Color the background, if there is one.
938     */
939
940    if (bgColor != NULL) {
941	sprintf(buffer,
942		"%.15g %.15g moveto %d 0 rlineto 0 %d rlineto %d %s\n",
943		x, y, width, height, -width, "0 rlineto closepath");
944	Tcl_AppendResult(interp, buffer, (char *) NULL);
945	if (Tk_CanvasPsColor(interp, canvas, bgColor) != TCL_OK) {
946	    return TCL_ERROR;
947	}
948	Tcl_AppendResult(interp, "fill\n", (char *) NULL);
949    }
950
951    /*
952     * Draw the bitmap, if there is a foreground color.  If the bitmap
953     * is very large, then chop it up into multiple bitmaps, each
954     * consisting of one or more rows.  This is needed because Postscript
955     * can't handle single strings longer than 64 KBytes long.
956     */
957
958    if (fgColor != NULL) {
959	if (Tk_CanvasPsColor(interp, canvas, fgColor) != TCL_OK) {
960	    return TCL_ERROR;
961	}
962	if (width > 60000) {
963	    Tcl_ResetResult(interp);
964	    Tcl_AppendResult(interp, "can't generate Postscript",
965		    " for bitmaps more than 60000 pixels wide",
966		    (char *) NULL);
967	    return TCL_ERROR;
968	}
969	rowsAtOnce = 60000/width;
970	if (rowsAtOnce < 1) {
971	    rowsAtOnce = 1;
972	}
973	sprintf(buffer, "%.15g %.15g translate\n", x, y+height);
974	Tcl_AppendResult(interp, buffer, (char *) NULL);
975	for (curRow = 0; curRow < height; curRow += rowsAtOnce) {
976	    rowsThisTime = rowsAtOnce;
977	    if (rowsThisTime > (height - curRow)) {
978		rowsThisTime = height - curRow;
979	    }
980	    sprintf(buffer, "0 -%.15g translate\n%d %d true matrix {\n",
981		    (double) rowsThisTime, width, rowsThisTime);
982	    Tcl_AppendResult(interp, buffer, (char *) NULL);
983	    if (Tk_CanvasPsBitmap(interp, canvas, bitmap,
984		    0, curRow, width, rowsThisTime) != TCL_OK) {
985		return TCL_ERROR;
986	    }
987	    Tcl_AppendResult(interp, "\n} imagemask\n", (char *) NULL);
988	}
989    }
990    return TCL_OK;
991}
992