NAME

Rivl_Buf, Rivl_BufNew, Rivl_BufFree, Rivl_BufType, Rivl_BufBox, Rivl_BufSetOffset, Rivl_BufRef, Rivl_BufPtr, Rivl_BufCopy, Rivl_BufScale, Rivl_BufDup - procedures for managing buffers

SYNOPSIS

#include <rivl.h>
Rivl_Box
Rivl_BufBox(buf)
Rivl_BufCopy(destBuf, srcBuf, region)
Rivl_Buf
Rivl_BufDup(buf)
void
Rivl_BufFree(buf)
Rivl_Buf
Rivl_BufNew(interp, valueType, box, bufPtr)
Rivl_Buf
Rivl_BufNewFromRegion(interp, valueType, region, bufPtr)
pointer type
Rivl_BufPtr(buf, x, y)
element type
Rivl_BufRef(buf, x, y)
Rivl_BufSetOffset(buf, ox, oy)
Rivl_BufScale(destBuf, srcBuf, sx, sy, region)
Rivl_ValueType
Rivl_BufType(buf)

ARGUMENTS

Tcl_Interp *interp (in)
The Tcl interpreter.
Rivl_Buf buf (in)
A buffer.
Rivl_ValueType valueType (in)
Type of new buffer.
Rivl_Buf *bufPtr (out)
Pointer to location in which to store new buffer.
Rivl_Buf destBuf, srcBuf (in)
Destination and source buffers of data transfer.
Rivl_Region region (in)
A region.
double sx, sy (in)
Factors to scale buffer by.
int ox, oy (in)
New offset coordinates of buffer.
Rivl_Box box (in)
Box describing offset and size of new buffer.

INTRODUCTION

The Rivl_Buf facility provides routines to create and manipulate two-dimensional buffers. In Rivl, buffers are used primarily to hold regions of data computed from signals.

Rivl_Buf was built directly on top of an existing image manipulation package by William Rucklidge. Users of William's module should see the last section for more details.

OVERVIEW

A buffer stores a rectangular area of values. The rectangle's corners can be any four integers; it need not originate at (0,0). We refer to the rectangle's origin as its offset.

The values of a given buffer are all the same type, so we can talk about the type of a buffer as being the type of its elements. There is one buffer type for every Rivl value type. All operations described in this document work on any type of buffer. The table below lists each buffer type along with the tag and C type of the value type it contains.

Buffer type              Tag		C Type
                         
Rivl_ByteBuf             RIVL_BYTE	unsigned char 
Rivl_GrayBuf             RIVL_GRAY	unsigned char 
Rivl_ShortBuf            RIVL_SHORT	short 
Rivl_LongBuf             RIVL_LONG	long 
Rivl_FloatBuf            RIVL_FLOAT	float 
Rivl_DoubleBuf           RIVL_DOUBLE	double 
Rivl_RGBBuf              Rivl_RGB	Rivl_RGB
Rivl_PtrBuf              RIVL_PTR	void * 
Note that Rivl_GrayBuf and RIVL_GRAY are aliases for Rivl_ByteBuf and RIVL_BYTE. The Rivl_Buf type is used to declare a buffer of unknown value type, serving as a kind of void pointer for buffers. You can safely cast Rivl_Buf to and from the specific buffer types, but you you cannot access the values of a Rivl_Buf.

COMMANDS

Rivl_BufNew creates a new blank buffer. valueType is a Rivl_ValueType tag such as RIVL_BYTE. box indicates the offset and size of the buffer's area. Under normal circumstances, Rivl_BufNew sets *bufPtr to the new buffer and returns TCL_OK. If there is insufficient memory to create the buffer or if the area is deemed too large, then TCL_ERROR is returned and an error message is left in interp->result.

Rivl_BufNewFromRegion is the same as Rivl_BufNew, except that the buffer's area is determined by the clip box of region.

New buffers are guaranteed to be cleared to zero. For pointer buffers, this means all values are NULL.

Rivl_BufFree destroys buf and releases its storage.

Rivl_BufType returns the type of buf as a Rivl_ValueType.

Rivl_BufBox returns the rectangular area of buf.

Rivl_BufSetOffset changes the offset of buf. Since the size stays the same, both the upper and lower corners of the buffer's area are shifted in tandem. There is no way to change the size of an existing buffer.

Rivl_BufDup creates and returns a duplicate of buf with the same type, area, and contents. It must be freed eventually with Rivl_BufFree.

Rivl_BufCopy copies the data from srcBuf into destBuf wherever srcBuf's area, destBuf's area, and region intersect. If the intersection is empty then no copying occurs.

Rivl_BufScale scales the data from srcBuf into destBuf by scale factors sx and sy, which may be any positive or negative real values. Data is only written where destBuf's area intersects with region.

ACCESSING BUFFER VALUES

There are two ways to access values in a buffer. The first way is via the macro Rivl_BufRef, which resembles an array reference. For example, the code

    Rivl_Buf buf;
    int x,y;
    .
    .
    Rivl_BufRef(buf, x, y) = Rivl_Buf(buf, x, y) + 1;
adds one to the buffer value at (x, y). Rivl_BufRef yields an lvalue of the type appropriate for the specified buffer (here, an unsigned char). The coordinates given must be within the buffer's rectangle; giving invalid coordinates is equivalent to an out-of-bounds array access.

Normally you loop over a rectangular area of a buffer, performing a single task at each cell. The following loop adds 1 to every value in a rectangular area specified by a Rivl_Box structure:

    Rivl_Box box;
    Rivl_ByteBuf buf;
    int x, y;
    .
    .
    for (y = box.y1; y < box.y2; y++){
        for (x = box.x1; x < box.x2; x++){
            Rivl_BufRef(buf, x, y) = Rivl_BufRef(buf, x, y) + 1;
        }
    }
Note the order of the loops: y on the outside, x on the inside. Because buffers are stored in row-major order, all double loops should be ordered this way. Note also that the lower right corner (x2, y2) of a Rivl_Box is not considered part of its area, so we do not touch that row and column.

The second way to access buffer values is to obtain a pointer to the buffer, and then manipulate and dereference the pointer. The macro Rivl_BufPtr has the same form as Rivl_BufRef, but returns a pointer to the specified value. Again, the type of the pointer depends on the type of the buffer given: a pointer to unsigned char for a byte buffer, a pointer to long for a long buffer, and so on. When given coordinates outside the buffer's rectangle, Rivl_BufPtr does not crash, but the pointer it returns is useless even for comparison with other pointers.

The only difference between using Rivl_BufRef and Rivl_BufPtr is efficiency. Rivl_BufRef is fairly efficient; in particular, it makes use of row pointers in the buffer to avoid a multiplication by y. However, on many architectures, more efficient loops can be constructed using pointers. The trade-off is that the loops are harder to create and read. Here is the above loop written with pointers:

    Rivl_Box box;
    Rivl_ByteBuf buf;
    int x, y;
    unsigned char *p, *pEnd;

    .
    .
	
    for (y = box.y1; y < box.y2; y++){
	p = Rivl_BufPtr(buf, box.x1, y);
	pEnd = Rivl_BufPtr(buf, box.x2-1, y);
	while (1) {
	    *p = *p + n;
	    if (p==pEnd)
		break;
	    p++;
	}
    }
At the start of each inner loop, we generate two pointers: one at the beginning of the row (p) and one at the end (pEnd). Since buf is a byte buffer, the p and pEnd are pointers to unsigned chars. The condition pd==pEnd tests whether we have reached the end of the row.

You might wonder why we don't use the simpler inner loop

    pEnd = Rivl_BufPtr(buf, box.x2, y);
    while (p < pEnd) {
        *p = *p + n;
        p++;
    }
The problem is that if box.x2 is past the right edge of the buffer, the pointer returned from Rivl_BufPtr is invalid even for comparison.

WRITING POLYMORPHIC CODE

Most Rivl signal commands operate on more than one type of signal. For example, sig_mult operates on signals of type byte, float, double, etc. This means that the routine which actually computes the data must be polymorphic.

Unfortunately, the C language and these routines do not lend themselves well to polymorphic operations which require examining or setting buffer values. You can pass around a buffer of unknown type as a Rivl_Buf, but it is impossible to access the values inside it with Rivl_BufRef or Rivl_BufPtr. A buffer must be cast to one of the specific buffer types - Rivl_ByteBuf, Rivl_FloatBuf, etc. - before it can be passed to Rivl_BufRef or Rivl_BufPtr.

To write a polymorphic low-level buffer operation that examines or sets buffer values, you must check the type of the buffer with Rivl_BufType, and write a switch statement with a case for each type. Judicious use of macros can reduce the tedium of writing inner loops. For examples, see the Rivl source code for sig_hash or sig_construct.

CREDITS

Rivl_Buf is built directly on top of William Rucklidge's image datatype. Only the names have changed, to match the rest of Rivl's external interface and to replace the term "image" with the more appropriate "buffer". In fact, you can access the original function names (imNew, imRef, etc.) by including <wjr-image-bindings.h> after <rivl.h>. This should allow existing code that uses the original bindings to work with Rivl. Of course, we cannot guarantee this support indefinitely as the implementations will invariably drift apart.

This documentation was adapted in part from William's original manual.