HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
COP2_FullImageFilter.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  * FullImageFilter Op.
27  */
28 #include <UT/UT_DSOVersion.h>
29 
30 #include <OP/OP_Context.h>
31 #include <OP/OP_OperatorTable.h>
32 
33 #include <SYS/SYS_Math.h>
34 #include <SYS/SYS_Floor.h>
35 
36 #include <PRM/PRM_Include.h>
37 #include <PRM/PRM_Parm.h>
38 
39 #include <TIL/TIL_Region.h>
40 #include <TIL/TIL_Plane.h>
41 #include <TIL/TIL_Sequence.h>
42 #include <TIL/TIL_Tile.h>
43 
44 #include <COP2/COP2_CookAreaInfo.h>
45 
46 #include "COP2_FullImageFilter.h"
47 
48 using namespace HDK_Sample;
49 
50 COP_MASK_SWITCHER(1, "Sample Full Image Filter");
51 
52 static PRM_Name names[] =
53 {
54  PRM_Name("size", "Size"),
55 };
56 
57 static PRM_Default sizeDef(10);
58 static PRM_Range sizeRange(PRM_RANGE_UI, 0, PRM_RANGE_UI, 100);
59 
62 {
64 
65  PRM_Template(PRM_FLT_J, TOOL_PARM, 1, &names[0], &sizeDef, 0,
66  &sizeRange),
67  PRM_Template(),
68 };
69 
73 
76 
78 {
79  "Image to Filter",
80  "Mask Input",
81  0
82 };
83 
84 OP_Node *
86  const char *name,
87  OP_Operator *op)
88 {
89  return new COP2_FullImageFilter(net, name, op);
90 }
91 
92 COP2_FullImageFilter::COP2_FullImageFilter(OP_Network *parent,
93  const char *name,
94  OP_Operator *entry)
95  : COP2_MaskOp(parent, name, entry)
96 {
97  // sets the default scope to only affect color and alpha. The global
98  // default is 'true, true, "*"', which affects color, alpha and all
99  // extra planes.
100  setDefaultScope(true, true, 0);
101 }
102 
104 {
105  ;
106 }
107 
108 // -----------------------------------------------------------------------
109 
110 
113  int /*arrayindex*/,
114  float t, int xres, int /*yres*/,
115  int /*thread*/, int /*maxthreads*/)
116 {
117  // This method evaluates and stashes parms and any other data that
118  // needs to be setup. Parms cannot be evaluated concurently in separate
119  // threads. This function is guaranteed to be single threaded.
121  int index = mySequence.getImageIndex(t);
122 
123  // xres may not be the full image res (if cooked at 1/2 or 1/4). Because
124  // we're dealing with a size, scale down the size based on our res.
125  // getXScaleFactor will return (xres / full_xres).
126  sdata->mySize = SIZE(t) * getXScaleFactor(xres)*getFrameScopeEffect(index);
127 
128  return sdata;
129 }
130 
131 
132 void
134 {
135  // if your algorthim increases the image bounds (like blurring or
136  // transforming) you can set the bounds here.
137 
138  // if you need to access your context data for some information to
139  // compute the bounds (like blur size), you can do it like:
140  // cop2_FullImageFilterData *sdata =
141  // (cop2_FullImageFilterData *) context.data();
142 
143  // SAMPLES:
144 
145  // expands or contracts the bounds to the visible image resolution
146  context.setImageBounds(0,0, context.myXres-1, context.myYres-1);
147 
148  // just copies the input bounds (ie this node don't modify it)
149  //copyInputBounds(0, context);
150 
151  // expands the input bounds by 5 pixels in each direction.
152  // copyInputBounds(0, context);
153  // int x1,y1,x2,y2;
154  // context.getImageBounds(x1,y1,x2,y2);
155  // context.setImageBounds(x1-5, y1-5, x2+5, y2+5);
156 
157 }
158 
159 void
161  COP2_CookAreaInfo &output_area,
162  const COP2_CookAreaList &input_areas,
163  COP2_CookAreaList &needed_areas)
164 {
166 
167  // for a given output area and plane, set up which input planes and areas
168  // it is dependent on. Basically, if you call inputTile or inputRegion in
169  // the cook, for each call you need to make a dependency here.
170 
171  // this makes a dependency on the input plane corresponding to the output
172  // area's plane.
174  output_area.getPlane().getName(),
175  output_area.getArrayIndex(),
176  output_area.getTime(),
177  input_areas, needed_areas);
178 
179  // Always check for null before setting the bounds of the input area.
180  // in this case, all of the input area is required.
181  if (area)
183 
184  // If the node depends on its input counterpart PLUS another plane,
185  // we need to add a dependency on that plane as well. In this case, we
186  // add an extra dependency on alpha (same input, same time).
187 
189  getAlphaPlaneName(), 0,
190  output_area.getTime(),
191  input_areas, needed_areas);
192  // again, we'll use all of the area.
193  if (area)
195 
196  getMaskDependency(output_area, input_areas, needed_areas);
197 
198 }
199 
200 OP_ERROR
202 {
203  // normally, this is where you would process your tile. However,
204  // cookFullImage() is a convenience function which assembles a full image
205  // and does all the proper locking for you, then calls your filter
206  // function.
207 
208  cop2_FullImageFilterData *sdata =
209  static_cast<cop2_FullImageFilterData *>(context.data());
210 
211  return cookFullImage(context, tiles, &COP2_FullImageFilter::filter,
212  sdata->myLock, true);
213 }
214 
215 OP_ERROR
217  const TIL_Region *input,
218  TIL_Region *output,
219  COP2_Node *me)
220 {
221  // since I don't like typing me-> constantly, just call a member function
222  // from this static function.
223  return ((COP2_FullImageFilter*)me)->filterImage(context, input, output);
224 
225 }
226 
227 OP_ERROR
229  const TIL_Region *input,
230  TIL_Region *output)
231 {
232  // retrieve my context data information (built in newContextData).
233  cop2_FullImageFilterData *sdata =
234  (cop2_FullImageFilterData *) context.data();
235 
236  // currently we have a blank output region, and an input region filled with
237  // whatever plane we've been told to cook. Both are in the same format, as
238  // this node didn't alter the data formats of any planes.
239 
240 
241  // we need the alpha plane, so grab it (generally, you'd want to check if
242  // context.myPlane->isAlphaPlane() first, and then just use the 'input'
243  // region if we were cooking alpha, but for simplicity's sake we won't
244  // bother). Oh, and we'll grab it as floating point.
245 
246  // make a copy of the alpha plane & set it to FP format.
248  alphaplane.setFormat(TILE_FLOAT32);
249  alphaplane.setScoped(1);
250 
251  TIL_Region *alpha = inputRegion(0, context, // input 0
252  &alphaplane,0, // FP alpha plane.
253  context.getTime(), // at current cook time
254  0, 0, // lower left corner
255  context.myXsize-1, context.myYsize-1); //UR
256  if(!alpha)
257  {
258  // something bad happened, possibly error, possibly user interruption.
259  return UT_ERROR_ABORT;
260  }
261 
262  int comp;
263  int x,y;
264  char *idata, *odata;
265  float *adata;
266 
267  // my silly algorithm is as follows: it will take the value of the alpha
268  // plane multiplied by the user defined size and move the source point
269  // up to that distance away from its original location. It just adds the
270  // pixel over any pixel at that location, for simplicities sake.
271 
272  adata = (float *) alpha->getImageData(0);
273 
274  // go component by component. PLANE_MAX_VECTOR_SIZE = 4.
275  for(comp = 0; comp < PLANE_MAX_VECTOR_SIZE; comp++)
276  {
277  idata = (char *) input->getImageData(comp);
278  odata = (char *) output->getImageData(comp);
279 
280  if(odata)
281  {
282  // since we aren't guarenteed to write to every pixel with this
283  // 'algorithm', the output data array needs to be zeroed.
284  memset(odata, 0, context.myXsize*context.myYsize * sizeof(float));
285  }
286 
287  if(idata && odata)
288  {
289  // myXsize & myYsize are the actual sizes of the large canvas,
290  // which may be different from the resolution (myXres, myYres).
291  for(y=0; y<context.myYsize; y++)
292  for(x=0; x<context.myXsize; x++)
293  {
294  float *pix = (float *) idata;
295  float *out = (float *) odata;
296 
297  unsigned seed = x * context.myYsize + y;
298  float dx = SYSrandomZero(seed);
299  float dy = SYSrandomZero(seed);
300  int idx, idy;
301  int nx, ny;
302 
303  dx *= adata[x + y * context.myXsize] * sdata->mySize;
304  dy *= adata[x + y * context.myXsize] * sdata->mySize;
305 
306  idx = (int) SYSrint(dx);
307  idy = (int) SYSrint(dy);
308 
309  nx = x+idx;
310  ny = y+idy;
311 
312  if(nx < 0 || nx >= context.myXsize ||
313  ny < 0 || ny >= context.myYsize)
314  continue;
315 
316  pix += (x+y*context.myXsize);
317  out += (nx+ny*context.myXsize);
318 
319  *out = *out + *pix;
320  }
321  }
322  }
323 
324 
325  // It is important to release regions and tiles you request with
326  // inputRegion & inputTile, otherwise they will just sit around until the
327  // end of the cook taking up memory. If someone puts down many of your
328  // nodes in a network, this could be problematic.
329  releaseRegion(alpha);
330 
331  // input and output are allocated & released by cookFullImage, so don't
332  // release them.
333 
334  return error();
335 }
336 
337 
338 void
340 {
341  table->addOperator(new OP_Operator("hdk_fullfilter",
342  "HDK Full Image Filter",
345  1,
346  2, // optional mask input.
348  0, // not generator
350 }
351 
fpreal getTime() const
virtual void setDefaultScope(bool color, bool alpha, const char *others)
TIL_Sequence mySequence
Definition: COP2_Node.h:1304
#define TILE_FLOAT32
Definition: TIL_Defines.h:68
static OP_TemplatePair myTemplatePair
OP_ERROR cookFullImage(COP2_Context &context, TIL_TileList *tiles, COP2_FullImageCB callback, UT_Lock &fullimagelock, bool use_float)
virtual float getFrameScopeEffect(int image_index)
fpreal getTime() const
Definition: COP2_Context.h:77
static OP_TemplatePair myTemplatePair
Definition: COP2_MaskOp.h:29
UT_ErrorSeverity
Definition: UT_Error.h:25
void setImageBounds(int x1, int y1, int x2, int y2)
GLint y
Definition: glcorearb.h:102
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
static OP_VariablePair myVariablePair
Definition: COP2_Node.h:628
void * getImageData(int index)
virtual void getInputDependenciesForOutputArea(COP2_CookAreaInfo &output_area, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas)
const char * getName() const
Definition: TIL_Plane.h:56
OP_ERROR filterImage(COP2_Context &context, const TIL_Region *input, TIL_Region *output)
virtual COP2_ContextData * newContextData(const TIL_Plane *p, int array_index, float t, int xres, int yres, int thread, int max_threads)
static OP_ERROR filter(COP2_Context &context, const TIL_Region *input, TIL_Region *output, COP2_Node *me)
float getXScaleFactor(int xres) const
#define PLANE_MAX_VECTOR_SIZE
Definition: TIL_Defines.h:156
COP2_ContextData * data()
GLfloat GLfloat GLfloat alpha
Definition: glcorearb.h:111
PRM_API PRM_Name PRMswitcherName
GLuint const GLchar * name
Definition: glcorearb.h:785
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
void newCop2Operator(OP_OperatorTable *table)
PRM_API const PRM_Type PRM_FLT_J
fpreal32 SYSrint(fpreal32 val)
Definition: SYS_Floor.h:163
PRM_API const PRM_Type PRM_SWITCHER
void releaseRegion(TIL_Region *, int output=0)
virtual OP_ERROR error()
Definition: COP2_Node.h:453
#define TOOL_PARM
Definition: COP2_Common.h:26
COP_MASK_SWITCHER(1,"Sample Full Image Filter")
void getMaskDependency(COP2_CookAreaInfo &output_area, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas)
const TIL_Plane & getPlane() const
TIL_Region * inputRegion(int input_index, COP2_Context &context, const TIL_Plane *plane, int array_index, float t, int xstart, int ystart, int xend, int yend, TIL_RegionExtend hold=TIL_BLACK, int share=1, void *regionmem[PLANE_MAX_VECTOR_SIZE]=0, bool correct_aspect=true, bool correct_bounds=true, int scan_alignment=0)
virtual void computeImageBounds(COP2_Context &context)
typedef int
Definition: png.h:1175
GLuint index
Definition: glcorearb.h:785
int getArrayIndex() const
GLint GLenum GLint x
Definition: glcorearb.h:408
static OP_VariablePair myVariablePair
void setFormat(TIL_DataFormat format)
COP2_CookAreaInfo * makeOutputAreaDependOnInputPlane(int input, const char *planename, int array_index, float t, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas)
TIL_Plane * getPlane(int index)
static const char * getAlphaPlaneName()
exint getImageIndex(double t, int clamp_range=1, int round_off=SEQUENCE_NEAREST) const
bool enlargeNeededAreaToBounds()
GA_API const UT_StringHolder area
virtual OP_ERROR doCookMyTile(COP2_Context &context, TIL_TileList *tiles)