TIL_Sequence
This class holds the high level description of the COP's data. This
information is constant over the frame range of the COP.
- Resolution
- Frame Range
- Range Extend Conditions
- Plane Composition
The resolution must be at least 1x1. The Frame Range can either be set
to a range or a single image. To specify range, call:
setSingleImage(false);
setStart(start_frame);
setLength(length);
In addition to the frame range, you can also specify how the compositer
renders frames outside that range with
setTemporalExtend().
You can
specify the extend condition for both before and after the range. The
extend conditions are:
- EXTEND_BLACK - Render
black frames. This is the default.
- EXTEND_CYCLE - Loop the
animation.
- EXTEND_MIRROR - Loop the
animation, reversing it every other loop.
- EXTEND_HOLD_FOREVER - Use
the first/last frame for all frames before/after the range.
- EXTEND_HOLD - Use the
first/last frame for a certain number of frames, then render black
frames. Another call to setTemporalHold() specifies the
number of
frames.
You can also set up the range as a single image. Single images are
time-independent, and exist at every frame. Non-animated mattes and
background plates fall into this category. To set up a single image,
call:
setSingleImage(true);
Once you have done this, it overrides the frame range amd extend
conditions.
Finally, you set up the image planes. Each image plane represents a
different type of data on the image - like Color, Alpha, Normal or
Depth. All the planes share the same resolution, frame range and extend
conditions. You can specify the plane name, vector size, component
name(s) and data format individually for each plane. All plane names
must be unique (or they will be made unique by appending a number to
the end). To add planes to a sequence, call:
TIL_Plane * addPlane(const char *name, TIL_DataFormat format,
const char *vn1 = 0,
const char *vn2 = 0,
const char *vn3 = 0,
const char *vn4 = 0,
bool reorder=false);
TIL_Plane * addPlane(const TIL_Plane *plane,
bool reorder=false);
The vector size is determine by the number of component names that are
filled in (vn1-4). The name of the plane can be custom, or one of the
standard plane names. You can get the plane name from
COP2_Node,
by
calling:
static const char *getColorPlaneName();
static const char *getAlphaPlaneName();
static const char *getMaskPlaneName();
static const char *getDepthPlaneName();
static const char *getLumPlaneName();
static const char *getBumpPlaneName();
static const char *getPointPlaneName();
static const char *getNormalPlaneName();
static const char *getVelocityPlaneName();
For more information on planes, see
TIL_Plane
below.
TIL_Plane
A
TIL_Plane structure contains the definition of an image
plane. Planes
can be scalar (like Alpha) or vector (like Color; R,G,B). Vectors of up
to 4 components are supported. You can also arrange planes into arrays,
though this is rarely used. Each plane has its own data format, and
names for its components.
The name of the plane can be custom, or one of the standard plane
names. You can get the plane name from
COP2_Node, by calling:
static const char *getColorPlaneName();
static const char *getAlphaPlaneName();
static const char *getMaskPlaneName();
static const char *getDepthPlaneName();
static const char *getLumPlaneName();
static const char *getBumpPlaneName();
static const char *getPointPlaneName();
static const char *getNormalPlaneName();
static const char *getVelocityPlaneName();
Next, specify the data format of the plane, which can be one of:
- TILE_INT8 - 8 bit integer
(0 to 255)
- TILE_INT16 - 16 bit
integer (0 to 65535)
- TILE_INT32 - 32 bit
integer (0 to 2^32-1)
- TILE_FLOAT32 - 32 bit
floating point
- TILE_FLOAT16 - 16 bit
floating point (-65535.0 to 65535.0)
Finally, you specify the names of the components. If your plane is a
scalar plane (vector size 1), you do not need to specify a name for the
first component. Common component names are r, g, b, x, y, z, and w.
For integer formats, you can also specify black and white points. By
default, they are set to the minimum and maximum representable values.
However, you can set them to any value in between to be able to
represent values greater than white or darker than black. To do this,
use:
void setBlackWhitePoints(unsigned int b, unsigned int w);
To unscope planes for cooking, use the following methods:
void setScoped(int enable);
void setPlaneMask(int enable, int i);
By default, all planes are scoped upon creation. You can unscope the
entire plane or just specific components. Unscoped components and
planes will not be modified by COP filters and passed through as-is.
TIL_Tile
TIL_Tile's contain the image data that the COPs engine
processes. Each
tile contains one component of one plane in an area of the output
image, at a certain frame. These tiles are arranged into
TIL_TileLists, which contain up to 4
tiles
(one for each component of a
TIL_Plane).
For the most part, you will only be interested in getting at the data
from the tiles with
getImageData(). By default, tiles are
200x200, but
near the edges of images this can change (and it is a preference as
well).
Tiles are very low level classes to work with, as the data is always in
the data format specified by the plane, and neighbouring pixel
algorithms are difficult to write within tiles. For this reason, it is
often easier to use
TIL_Region's to
get at
the input data, which allow you to grab an arbitrary area of the input
(and convert it to other formats as well).
The most important functions in
TIL_Tile are the
getImageData()
methods. Another important method is the
isConstantTile()
method, which returns true if the tile's image data is all set to a
single value. This can be used for optimizing algorithms. Most of the
other methods are only used by the compositing engine.
TIL_TileList
A
TIL_TileList is a simple vector wrapper for 1 to 4 tiles.
It contains a lot of useful member data collected from the tiles, and
some convienience functions. Whenever you request a 'tile', you will
get a tilelist back. Each tile contains one component of the plane,
listed in the tilelist. The important member data is:
TIL_Tile *myItems[PLANE_MAX_VECTOR_SIZE]
|
The list of tiles in the tilelist.
|
const TIL_Plane *myPlane
|
The plane that these tiles are from.
|
short myArrayIndex
|
The array index of the plane.
|
int mySize
|
The number of pixels in the tile.
|
int myX1, myY1, myX2, myY2
|
The area in the image that the tile represents.
|
char myUsePoints
|
Black/White point usage flag
|
unsigned int myBlack, myWhite
|
The black and white points of the data (integer
only)
|
The useful methods are:
Returns true if the tilelist contains the pixel (x,y).
Creates a TIL_Region which references the data
in the tiles. This is useful for running code on TIL_TileList's
that only works with TIL_Region's. Use TIL_Region::freeRegion()
to deallocate the region when finished.
This method checks to see if the region is
constant on all components. If it's not, it returns false. Otherwise,
it marks all the tiles as constant and fills them with the constant
color of the region (if black is false) or black (if black
is true). This is useful for optimizing algorithms. For example, a blur
operation can be optimized out if the input region is entirely
constant. An edge detect operation can be optimized out as well, but
instead of filling with the region color, it would fill with black.
Clears the tiles to black. Tiles are not cleared when you
receive them in your cook method, so if you don't want to produce any
output, you should clear them. If markconstant is true, the tiles are
also marked as constant tiles. You will want to pass markconstant as
false if you are just clearing them before operating on them.
Returns true if the TIL_TileList doesn't have
any tiles in it. You will rarely need to use this method (if ever).
TIL_Region
Regions are the most versatile way to access input data. They allow you
to grab any arbitrary area of the input, in any data format. Unlike
tiles, they contain all the components of the plane they are extracting
data from.
TIL_Region's are returned by the
COP2_Node
methods
inputRegion(), transformRegion() and
outputRegion().
Useful
TIL_Region methods:
Returns the plane that this region is fetching data from.
Useful for checking the data format.
Returns the area the region occupies, (x1,y1)-(x2,y2)
inclusive.
Gets the image data for the component specified (either
by index or name).
For more details on using
TIL_Region's for input, see
Cooking, Input Functions.
TIL_Fill
This class and its parameter class (
TIL_FillParms) are used to
fill an area with a different area, convert data types, clear data,
invert values, and multiply/divide by constants or arrays. It's sort of
a swiss army knife class for many common operations. The
TIL_Fill
'class' is actually a collection of static methods to do specific tasks:
static void fill(const TIL_FillParms &parms);
static void clear(const TIL_FillParms &parms);
static void invert(const TIL_FillParms &parms);
static void multiply(const TIL_FillParms &parms);
static void divide(const TIL_FillParms &parms);
The real interface is found in the
TIL_FillParms class, which
contains all the parameters for the operation (instead of having a
fill()
function with 30 parameters). The source and destination parameters are
almost mirrors of one another (the destination parms are shown):
const void *myDest;
TIL_DataFormat myDType;
int myDX1, myDY1;
int myDX2, myDY2;
unsigned int myDBlack, myDWhite;
int myDInc;
int myDFast;
myDest is the pointer to the destination pixel array.
myDType,
myDFast,
myDBlack and
myDWhite all determine the
data format of the pixels (
myDType =
TILE_INT8, TILE_INT16,
TILE_INT32, TILE_FLOAT16, or
TILE_FLOAT32; if
myDFast
is true, then black/white points are used for the integer formats,
which are specified in
myDBlack and
myDWhite). The
myDInc
parameter specifies the offset between adjacent pixel values; this is
useful for interleaving and deinterleaving data (to extract R out of
interleaved RGB, use mySInc = 1 and myDInc = 3). Finally, the
myDX1,
myDY1, myDX2, myDY2 parameters represent the area that the pixel
array represents.
For
fill(), the source and destination areas don't need to
match or even overlap - the filling will be handled appropriately (this
is useful for extracting areas out of a raster, or putting tiles back
into a raster). You can also set
mySVInc and
mySVOff,
to extract interlaced scanlines (
mySVInc = 2,
mySVOFF =
1 or 0, depending on whether you want the odd or even scanline).
You can also do some minor translates on the image, with the parameters
myXShift and
myYShift. The source area will be shifted
by these values (by default they are zero). You can also flip the
source area horizontally and/or vertically with
myXFlip and
myYFlip
(boolean values). A gamma can be applied to the fill using
myGamma.
For the
clear() method, the source area is the area to clear,
and the dest area is the size of the pixel area. You can clear
components (with
myDInc set to the vector size) and subareas
(with a smaller source area).
The
invert() method only uses the destination parms. All the
pixel values are inverted (1-p) and multiplied by the fill color (
myFillColor).
The
multiply() and
divide() methods use only the
destination parms and the multiplication parms (
myMultPerPixel,
myMultFactor, myMultData). If
myMultPerPixel is true, then
an array of floats the size of the destination area, stored in
myMultData,
is multiplied by destination pixels. If
myMultPerPixel is
false, the destination pixels are multiplied by a constant
myMultFactor.
For
divide(), division is performed instead of multiplication.
TIL_Pixel
This template class is used to convert single values to and from
floating point. To use it, declare the
TIL_Pixel with the
data type you want, and whether to use the 'fast' conversions (1) or
not (0). The fast conversions do not take the integer black/white
points into account. For example, to convert 8bit int to floating point:
TIL_Pixel<unsigned char, 1> pixel;
unsigned char data = 125;
pixel.set(data);
float_value = pixel;
You can also use
TIL_Pixel to convert back to the original
format:
pixel = float_value;
data = pixel.getValue();
To use
TIL_Pixel with black/white points, only a small change
is required to the declaration:
TIL_Pixel<unsigned char, 0> pixel(64, 192);
First is the change in the 'fast' template parameter, which tells it to
use the slower black/white point conversion methods. Second, the black
and white points are passed to the constructor. Floating point formats
do not support black/white points, so 'fast' and the black/white point
parameters are ignored.
TIL_Pixel's are very useful inside the templated RU_Algorithm
classes, as you can declare them as "
TIL_Pixel<type, fast>
pixel" and then use them as above.
RU (Raster Utility)
Library classes
RU_Algorithm
This is the interface for templated
algorithms. The RU_Algorithm class by itself isn't
templated; it manages the algorithm parameters and selects the
templates to use based on the input provided.
To create a templated algorithm, you need to create at least 2 classes
- one subclassed from RU_Algorithm (found in RU_Algorithm.h),
and one subclassed from one of the following template classes (found in
RU_AlgorithmTemplates.h). The RU_Algorithm subclass
should contain all the parameters required for the templated operation
- it will refer to this class for parameter data.
RU_GeneratorOp<class
Type, int fast>
This class provides a virtual method to generate data:
virtual int generate(TIL_TileList * output, float t, int thread, void *data)
Override it and define your operation, writing your data to the tiles
in output. The time (t) and thread you're cooking in (thread)
are also provided. The data parameter is discussed below.
Next, to call this method, you need to use the RU_Algorithm
method:
int generate (TIL_TileList *output, float t, void *ndata, int thread, void *data);
output is the tilelist to modify, t is the time, ndata
is a pointer to the COP node calling this method, thread is the
thread index that it is cooking in (from context.myThreadIndex) and data
is a pointer to user defined data that is passed to RU_GeneratorOp::generate()
as the last parameter.
RU_PixelOp<class
Type, int fast>
This class provides a virtual method for processing pixel
data on an input:
virtual int pixelAdjust(TIL_TileList *output, const TIL_TileList *input, float t, int thread, void *data)
Unlike the generator, the pixel operation has an input
tile list to use. To call this method in RU_Algorithm, use:
int pixelAdjust(TIL_TileList *output, const TIL_TileList *input, float t, void *ndata, int thread, void *data)
All the parms are the same as the generator version, except for input,
which are the input tiles to read from.
RU_FilterOp<class
Type, int fast>
This class is similar to RU_PixelOp, but
instead of an input tilelist, it uses regions. There are also two
signatures, one for an output tilelist, and one for an output region.
virtual int filter(TIL_TileList *output, const TIL_Region *input,float t, int thread, void *data)
virtual int filter(TIL_Region *output, const TIL_Region *input, float t, int thread, void *data)
To call one of these methods, use the corresponding RU_Algorithm
method:
int filter(TIL_TileList *output, const TIL_Region *input, float t, void *ndata, int thread, void *data)
int filter(TIL_Region *output, const TIL_Region *input, float t, void *ndata, int thread, void *data)
If you don't intend on calling the one of the signatures, you don't
need to implement that signature in your RU_FilterOp subclass.
RU_BinaryOp<class
Type, int fast>
Finally, the last template class is a binary operation,
with one output and two inputs. It also has two signatures, one for an
output tilelist and one for an output region.
virtual int binary(TIL_TileList *output, const TIL_Region *fore, const TIL_Region *back, float t,int thread, void *data)
virtual int binary(TIL_Region *output, const TIL_Region *fore, const TIL_Region *back, float t,int thread, void *data)
These correspond to the RU_Algorithm methods:
int binary(TIL_TileList *output,const TIL_Region *fore,const TIL_Region *back,float t,void *ndata,int thread,void *data)
int binary(TIL_Region *output,const TIL_Region *fore,const TIL_Region *back, float t,void *ndata,int thread,void *data)
Again, you only need to define the RU_BinaryOp method you are
going to use.
Putting the
Template Algorithm Together
You need to derive two classes, one from RU_Algorithm (the
public interface class) and one from one of the above template classes
(depending on what situation you have; 0, 1 or 2 inputs). The template
class is the one that does the work, and it is hidden behind the public
RU_Algorithm subclass.
A simple example:
// Declare our public interface class, a collection of parameters for the operation.
class RU_RandNoise : public RU_Algorithm
{
int mySeed;
float myScale;
float myShift;
};
// Declare our private worker template class.
template<class Type, int fast> class RU_RandNoiseOp : public RU_GeneratorOp<Type, fast>
{
RU_RandNoiseOp(RU_Algorithm *alg) : RU_GeneratorOp<Type,fast>(alg) {}
virtual int generate(TIL_TileList *output, float t, int thread, void *);
}
template<class Type,int fast> int
RU_NoiseOp<Type,fast>::generate(TIL_TileList *tilelist, float , int , void *)
{
RU_RandNoise *parms = dynamic_cast<RU_RandNoise *>(RU_GeneratorOp<Type, fast>::myAlg);
TIL_Pixel<Type, fast> pixel(tilelist->myBlack, tilelist->myWhite);
TIL_Tile *tile;
Type *data;
float noiseval;
FOR_EACH_UNCOOKED_TILE(tilelist, tile)
{
data = (Type *) tile->getImageData();
for(i=0; i<tilelist->mySize; i++)
{
// generate noise with parms->mySeed, parms->myScale, parms->myShift and stick the
// result in 'noiseval'.
pixel = noiseval;
data[i] = pixel.getValue();
}
}
return 1; // success
}
// How to call the algorithm from a cook method.
OP_ERROR
COP2_RandNoise::doCookMyTile(COP2_Context &context, TIL_TileList *tiles)
{
cop2_RandNoiseData *data = dynamic_cast<cop2_RandNoiseData *>(context.data());
// more likely, this would have been a member variable of our cop2_RandNoiseData context data,
// having been initialized in newContextData().
//
RU_RandNoise noisealg;
noisealg.mySeed = data->mySeed;
noisealg.myScale = data->myScale;
noisealg.myShift = data->myShift;
noisealg.generate(tiles, context.myTime, this, context.myThreadIndex);
return error();
}
RU_PixelFunction
Pixel Functions are very fast, input-to-output pixel algorithms. Gamma,
Brighten and HueShift are examples of pixel functions. They only
require a single input pixel (corresponding to the output pixel
produced) and some parameters.
This class is discussed thoroughly in context in
Pixel
Function COPs.
COP2 Library Classes
COP2_Context
The
COP2_Context class contains all the information that you
need to cook an image. It is generated by the COP engine based on the
requirements of the cook and passed to most cooking methods. You should
never create or delete one of these objects.
A
COP2_Context contains:
- A pointer to the context data structure of the node - data()
- A pointer to the plane that is currently being cooked - myPlane
and myArrayIndex
- The time the cook is occurring at - myTime
- The resolution of the image being cooked - myXres, myYres
- The size and origin of the image's canvas - myXsize, myYsize,
myXorig, myYorig
The only operation you will do on the
COP2_Context is to set
the image bounds, in the virtual method
COP2_Node::computeImageBounds(). All the rest of the information
is read-only.
COP2_ContextData
A
COP2_ContextData class is a base class used for storing
parameter values and pre-setup information needed for a cook. You will
need to derive a class from it if you would need to evaluate parameters
in the cook method (which is not allowed).
The base class provides some virtual methods to control the creation of
context data objects:
- virtual bool createPerPlane() const - Create a context
data for each plane that needs to be cooked (default false)
- virtual bool createPerRes() const - Create a context
data for each different resolution of the same image (default true)
- virtual bool createPerTime() const - Create a context
data for each different frame (default true)
- virtual bool createPerThread() const - Create a context
data for each thread (default false)
Why would you need to override these? Here are some situations:
- All your node's parameters are non-animatable (createPerTime()
= false)
- You are stashing data in the context data that cooking will
modify or use as scratch data (createPerThread() = true)
- Your operation differs depending on the plane type (createPerPlane()
= true)
- Your operation doesn't care about widths or heights or areas, it
operates the same on any sized image (createPerRes() = false)
When you create your new context data class for your node, you only
need to add data members which you'll be initializing in
COP2_Node::newContextData()
and reading in the cook methods. See
Context Data Creation for
more information.
COP2_CookAreaInfo
COP2_CookAreaInfo objects are used by the
COP2_Node
method
getInputDependenciesForOutputArea(). They are similar to
COP2_Context
objects as they share a lot of the same data, except that they
represent an area of an input plane to be cooked at a certain frame.
This object is an input dependency.
Most of the time, you will only need to use the
enlargeNeededArea()
and
expandNeededArea() methods to manipulate the area of an
COP2_CookAreaInfo
object. However, you also may need to create a new
COP2_CookAreaInfo
object to represent a new dependency (especially if you are referencing
a frame at a different time). In this case, you will need to create a
new
COP2_CookAreaInfo object. It is best to base it off of an
input
COP2_CookAreaInfo object that best matches the
dependency you want to create.
For example, to reference the color plane at the previous frame:
// cplanedep is the COP2_CookAreaInfo object that represents the color plane at the cook time.
time = cplanedep.getTime() - 1.0f / fps;
// grab the previous frame's bounds, in case they differ.
getInputBounds(0, cplanedep.getPlane(), cplanedep.getArrayIndex(), time, cplanedep.getFrameXRes(), cplanedep.getFrameYRes(),0,
bx1,by1,bx2,by2);
area = new COP2_CookAreaInfo(cplanedep.getNode(), cplanedep.getPlane(), cplanedep.getArrayIndex(), bx1, by1, bx2, by2,
cplanedep.getFrameXRes(), cplanedep.getFrameYRes(), cplanedep.getTime() - 1.0f / fps);
if(area)
area->enlargeNeededArea(output_area);
For more information on using this class, see
Determine Data
Dependencies.
COP2_TransformParms
This class is the parameter interface to doing 2D Transforms. You can
do a normal or motion blurred transform. This class is used by the
COP2_Node
method
transformRegion().
The normal 2D transform parameters are:
- myTX, myTY - translation (in pixels)
- mySX, mySY - scale
- myRot - rotation (in degrees)
- myPX, myPY - pivot (in pixels)
- myOrder - the Translate/Rotate/Scale
order (XYZ order is ignored).
There are also parameters to specify the filtering done, and how the
transformed image behaves outside its bounds:
- myFilterX, myFilterY - the filters to use for X and Y
(default Box)
- myFilterSizeX, myFilterSizeY - the filter size for each
pixel (default 1, in pixels). Increasing this value makes the image
blurrier.
- myWrapX, myWrapY - The horizontal and vertical behaviour
of the image outside the image bounds. Acceptable values are:
- RU_FILTER_REPEAT - The image is repeated again.
- RU_FILTER_CLAMP - The nearest edge pixel value is used
- RU_FILTER_BORDER - Black pixels are used
- RU_FILTER_MIRROR - The image is repeated, and reversed
every other repetition.
Finally, you can specify parameters for motion blur:
- myUseBlur - Set to true to enable motion blur.
- myBlur - The amount of motion blur to apply. A value of 1
means that the blur will end where the next frame's blur starts;
smaller values will make shorter blurs, and there will be small gaps in
the blurs of neighbouring frames.
- myBias - A bias of 0 centers the blur around the
transformed position at that frame. A bias of 1 makes the blur start at
the transformed position and go to the next frame's transformed
position, and a bias of -1 makes the blur start at the previous
transformed position and end at the current transformed position.
- mySegments - The number of segments to use when creating
the motion blur. More segments produce smoother blurs, but slow the
operation down more.
- myConstant - Set to true if the sequence you are using is
not animated (helps optimize deformation motion blur).
See
Input Functions for
more information on
transformRegion().