TkGS Specification - Internals: TkGS_Objs

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 color 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.

This model provides the best compromise between performance and memory footprint, and device drivers can fine-tune their implementation depending on system specificities. 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_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.
Status
Complete.
Structure
typedef struct TkGS_Obj {
    int refCount;		    /* When 0 the object will be freed. */
    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 {			    /* The internal representation: */
	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;
    } 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
Nearly complete. The procedure prototypes may change (for example, we may add more explicit error reporting to TkGS_SetFromAnyProc).
Structure
typedef void (TkGS_FreeInternalRepProc) _ANSI_ARGS_((struct TkGS_Obj *objPtr));
typedef int  (TkGS_SetFromAnyProc)      _ANSI_ARGS_((struct TkGS_Obj *objPtr));

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.
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 */
    struct TkGS_ObjType *emptyTypePtr;	
				/* Used to initialize objects with no internal
				 * representation */

    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
This 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.
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_ConvertToType
Description
This procedure is the exact counterpart of the corresponding Tcl_Obj related procedure. It tries to convert an object's internal part to a specific type. The new type must have the same base type as the object (one can't convert a color to a drawable for example).
Status
Nearly complete. The procedure prototypes may change (for example, we may add more explicit error reporting).
Declaration
int TkGS_ConvertToType(
    TkGS_Obj	 *objPtr,
    TkGS_ObjType *typePtr
);
Arguments
objPtr: a pointer to the object.
typePtr: the new object type.
Returned value
TKGS_OK if the conversion succeeds.
TKGS_ERROR if the conversion fails: if typePtr's and objPtr's base types differ, or if typePtr's conversion procedure fails.
Side effects
The object's internal representation may change.

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.
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.

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