Most public structures defined by TkGS, such as TkGS_Color, are in the same time device-independent from a user's point of view, and very platform- and device-dependent from the implementor's point of view. For example, a TkGS_Color specified by its RGB values could hold any device-independent value internally, be it an XColor or a Windows COLORREF or Pen Object. As the same object may be used on distinct devices, its internal representation may change as well, however its external representation remains the same, thus guaranteeing device-independence.
TkGS must then provide a structure that guarantees device-independence while providing the best overall performance. To do so, it introduces the TkGS_Obj structure. This structure is very much inspired from Tcl's Tcl_Obj, in the sense that it holds a device-independent visible part, and a device-dependent internal part that is subject to change during the object's lifetime. Both share many features, such as:
The latter can be seen as a limitation, but in practice generating an object's external representation from its internals may give unexpected results: a color's internal values may have been dithered to match the display's color model. The only possible interaction with objects is through device-independent calls, changing a color's RGB values for example.
Like with Tcl_Objs, the internal representation of TkGS_Objs may "shimmer" if subsequently used on different devices. However such shimmering should seldom occur given the way objects are used. An example is canvas printing: canvas items are most of the time drawn on a window, and once in a while output to a PostScript file. The colors' internal values may then change from screen device representation (eg. XColor) to PostScript color, and back to screen once printing is complete. Objects holding more than one internal representation are even less likely to shimmer.
This model provides the best compromise between performance and memory footprint, and device drivers can fine-tune their implementation depending on system peculiarities. For example, Object allocation on Windows are notably slow, but such objects can be used on any device, so caching them provides increased performances.
typedef struct TkGS_InternalRep {
TkGS_ObjType *typePtr; /* Denotes the object's type. Always
* corresponds to the type of the object's
* internal rep. NULL indicates the object
* has no internal rep (has no type). */
union {
long longValue; /* a long integer value */
unsigned long ulongValue; /* an unsigned long integer value */
double doubleValue; /* a double-precision floating value */
VOID *otherValuePtr; /* another, type-specific value */
struct { /* internal rep as two values */
union {
long longValue;
unsigned long ulongValue;
VOID* otherValuePtr;
} value1, value2;
} twoValue;
} value;
} TkGS_InternalRep;
typedef struct TkGS_Obj {
int refCount; /* When 0 the object will be freed. */
TkGS_BaseType *baseTypePtr; /* Base type, eg. GC, Drawable... */
union { /* The internal representation: */
TkGS_InternalRep single; /* - single internal rep. */
TkGS_InternalRep *multiple; /* - multiple internal reps: array of
* baseTypePtr->nbIntRep elements. */
} internalRep;
} TkGS_Obj;
typedef void (TkGS_FreeInternalRepProc) _ANSI_ARGS_((struct TkGS_InternalRep *intRepPtr));
typedef int (TkGS_SetFromAnyProc) _ANSI_ARGS_((Tcl_Interp *interp,
struct TkGS_Obj *objPtr,
struct TkGS_InternalRep *intRepPtr));
typedef struct TkGS_ObjType {
char *name; /* Name of the type, e.g. "XlibGC". */
TkGS_BaseType *baseTypePtr; /* Base type, eg. GC, Drawable... */
TkGS_FreeInternalRepProc *freeIntRepProc;
/* Called to free any storage for the type's
* internal rep. NULL if the internal rep
* does not need freeing. */
TkGS_SetFromAnyProc *setFromAnyProc;
/* Called to convert the object's internal
* rep to this type. Frees the internal rep
* of the old type. Returns TKGS_ERROR on
* failure, and TKGS_OK on success. NULL if
* the object is immutable */
} TkGS_ObjType;
typedef void (TkGS_FreeBaseProc) _ANSI_ARGS_((struct TkGS_Obj *objPtr));
typedef void (TkGS_DupBaseProc) _ANSI_ARGS_((struct TkGS_Obj *srcPtr,
struct TkGS_Obj *dupPtr));
typedef struct TkGS_BaseType {
char *name; /* Name of the base type, eg. "GC" */
int size; /* Byte size of TkGS_Objs of this type */
int nbIntReps; /* Number of cached internal reps */
TkGS_FreeBaseProc *freeBaseProc;
/* Called to free any storage for the type's
* base rep. NULL if the base rep does not
* need freeing. */
TkGS_DupBaseProc *dupBaseProc;
/* Called to create a new object as a copy
* of an existing object. It only copies the
* public part of the object, not the
* internal part */
} TkGS_BaseType;
void TkGS_IncrRefCount(
TkGS_Obj *objPtr
);
void TkGS_DecrRefCount(
TkGS_Obj *objPtr
);
objPtr: a pointer to the object.
int TkGS_IsShared(
TkGS_Obj *objPtr
);
objPtr: a pointer to the object.
int TkGS_DuplicateObj(
TkGS_Obj *objPtr
);
objPtr: a pointer to the object to duplicate.
TkGS_InternalRep * TkGS_AddNewInternalRep(
Tcl_Interp *interp,
TkGS_Obj *objPtr,
TkGS_ObjType *typePtr
);
interp: Tcl interpreter used for error reporting.
objPtr: a pointer to the object.
typePtr: the type of the new internal rep to push to
the object's cache.
TkGS_InternalRep * TkGS_PushInternalRep(
TkGS_Obj *objPtr,
TkGS_InternalRep *intRepPtr
);
objPtr: a pointer to the object.
intRepPtr: a pointer to the internal rep to push to the
object's cache. It is added at the top of the cache stack, and the last
element of the cache is removed.
TkGS_InternalRep * TkGS_FindInternalRep(
TkGS_Obj *objPtr,
TkGS_ObjType *typePtr
);
objPtr: a pointer to the object.
typePtr: a pointer to the type of the internal rep to
look for.
TkGS_Obj *TkGSNewObj(
TkGS_BaseType *baseTypePtr
);
baseTypePtr: the object's base type descriptor.
void TkGSFreeObj(
TkGS_Obj *objPtr
);
objPtr: a pointer to the object.