TkGS Specification - Internals: TkGS_Objs

Introduction

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 main differences are:

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.

Structures


TkGS_InternalRep
Description
This structure is used to keep track of objects' internal representations. Objects can cache one or several internal reps simultaneously, depending on their base type.
This structure is very similar to that used by Tcl_Objs to store their internal representation.
Status
Complete.
Structure
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;

TkGS_Obj
Description
This structure is very similar to Tcl_Obj. It is not supposed to be used as is but rather as a base class for more specific objects (colors, drawables...). Thus it only defines room for type and internal representation, and no external representation.
Depending on their base type, objects may hold one or several internal representations.
Status
Complete.
Structure
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;

TkGS_ObjType
Description
This structure is very similar to Tcl_ObjType. The main differences are the lack of a duplication procedure and the baseTypePtr field.
The latter actually describe the real device-independent type of objects, since TkGS_Obj is only a "virtual class" (using OOP terminology). For example, an object can be a color, and its internal representation can be a device-specific color structure. The object's base type won't change, but the internal type may.
The lack of duplication procedure is due to the fact that only the public part can be safely duplicated.
Status
Complete.
Structure
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;

TkGS_BaseType
Description
This structure has no Tcl equivalent. This is due to the fact that all Tcl objects have string representations (the "everything is a string" paradigm). Thus this structure is used to describe the objects' public representation.
The size field is used to specify the byte size of the object of this type. Since TkGS_Obj is only a "virtual class", the real size of objects depends on their external representation.
The nbIntReps field is used to specify the number of cached internal reps. If it equals to one, then the internal rep will be stored in the single field of the TkGS_Obj structure. Else internal reps will be stored as elements of the multiple array field.
Status
Complete.
Structure
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;

Functions


TkGS_IncrRefCount
TkGS_DecrRefCount
Description
These procedures are the exact counterparts of the corresponding Tcl_Obj related procedures.
TkGS_IncrRefCount increments an object's reference count.
TkGS_DecrRefCount decrements an object's reference count, and if it reaches zero, frees the object.
Status
Specification complete. They are currently implemented as macros. Like in Tcl, we may add debugging-specific implementations.
Declaration
void TkGS_IncrRefCount(
    TkGS_Obj *objPtr
);
void TkGS_DecrRefCount(
    TkGS_Obj *objPtr
);
Arguments
objPtr: a pointer to the object.
Returned value
None.
Side effects
The object's reference count is incremented or decremented. If it falls below 1, the object is freed.

TkGS_IsShared
Description
This procedure is the exact counterpart of the corresponding Tcl_Obj related procedure. It checks whether the object is shared, ie if its refcount is at least 2. It is currently implemented as a macro.
Status
Complete.
Declaration
int TkGS_IsShared(
    TkGS_Obj *objPtr
);
Arguments
objPtr: a pointer to the object.
Returned value
1 (true) if the object's reference count is at least 2, else 0 (false).
Side effects
Memory is allocated.

TkGS_DuplicateObj
Description
This procedure is the exact counterpart of the corresponding Tcl_Obj related procedure. It creates a new object that has the exact type and external representation as the given object. It doesn't copy the internal part(s).
Status
Complete.
Declaration
int TkGS_DuplicateObj(
    TkGS_Obj *objPtr
);
Arguments
objPtr: a pointer to the object to duplicate.
Returned value
A newly allocated object that is the duplicate of objPtr.
Side effects
Memory is allocated.

TkGS_AddNewInternalRep
Description
This procedure is the counterpart of the Tcl_Obj-specific Tcl_ConvertToType. The difference is that it pushes a new internal representation in the object's cache, whereas the latter changes the (only) type of the internal representation. The new type must have the same base type as the object (one can't add a color representation to a drawable for example).
Status
Complete.
Declaration
TkGS_InternalRep * TkGS_AddNewInternalRep(
    Tcl_Interp   *interp,
    TkGS_Obj	 *objPtr,
    TkGS_ObjType *typePtr
);
Arguments
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.
Returned value
A pointer to the newly created internal rep if the conversion succeeds.
NULL if the conversion fails: if typePtr's and objPtr's base types differ, or if typePtr's conversion procedure fails. In the latter case, the interp's result is set to an error message.
Side effects
The object's internal representation may change.

TkGS_PushInternalRep
Description
This procedure pushes the specified internal representation in the object's cache stack.
Status
Complete.
Declaration
TkGS_InternalRep * TkGS_PushInternalRep(
    TkGS_Obj	     *objPtr,
    TkGS_InternalRep *intRepPtr
);
Arguments
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.
Returned value
A pointer to the newly created internal rep.
Side effects
The object's internal representation may change.

TkGS_FindInternalRep
Description
This procedure tries to find in the object cache an internal rep whose type is the one specified.
Status
Complete.
Declaration
TkGS_InternalRep * TkGS_FindInternalRep(
    TkGS_Obj	 *objPtr,
    TkGS_ObjType *typePtr
);
Arguments
objPtr: a pointer to the object.
typePtr: a pointer to the type of the internal rep to look for.
Returned value
A pointer to the internal rep if found, else NULL.
Side effects
None.

TkGSNewObj
Description
This procedure is used to allocate a new object of a given base type. It is not supposed to be called from outside TkGS.
Newly allocated objects' type is the default type defined by the base type's emptyTypePtr field. Thus all objects are guaranteed to have a proper type field set. This differs from Tcl_Objs which have their internal type set to NULL by default.
Status
Internal use only. Unlikely to change.
Declaration
TkGS_Obj *TkGSNewObj(
    TkGS_BaseType *baseTypePtr
);
Arguments
baseTypePtr: the object's base type descriptor.
Returned value
A pointer to the newly allocated object, or NULL in case of failure.
Side effects
Memory is allocated.

TkGSFreeObj
Description
This procedure is used to free an existing object. It is not supposed to be called from outside TkGS. Users should only call the public TkGS_IncrRefCount and TkGS_DecrRefCount for memory managemenet.
Status
Internal use only. Unlikely to change.
Declaration
void TkGSFreeObj(
    TkGS_Obj *objPtr
);
Arguments
objPtr: a pointer to the object.
Returned value
None.
Side effects
Memory is freed (internal and external representations).

TODO