r105: This commit was manufactured by cvs2svn to create tag
[cinelerra_cv/mob.git] / hvirtual / cinelerra / maskengine.C
blob6180fb95807dd277e4747619f714c0c02f1cfe0d
1 #include "clip.h"
2 #include "maskauto.h"
3 #include "maskautos.h"
4 #include "maskengine.h"
5 #include "mutex.h"
6 #include "vframe.h"
8 #include <math.h>
9 #include <stdint.h>
10 #include <string.h>
12 MaskPackage::MaskPackage()
14         apply_mutex = new Mutex;
17 MaskPackage::~MaskPackage()
19         delete apply_mutex;
28 MaskUnit::MaskUnit(MaskEngine *engine)
29  : LoadClient(engine)
31         this->engine = engine;
32         this->temp = 0;
36 MaskUnit::~MaskUnit()
38         if(temp) delete temp;
41 #ifndef SQR
42 #define SQR(x) ((x) * (x))
43 #endif
45 #define OVERSAMPLE 8
59 #define DRAW_LINE_CLAMPED(type, value) \
60 { \
61         type **rows = (type**)frame->get_rows(); \
62  \
63         if(draw_y2 != draw_y1) \
64         { \
65                 float slope = ((float)draw_x2 - draw_x1) / ((float)draw_y2 - draw_y1); \
66                 int w = frame->get_w() - 1; \
67                 int h = frame->get_h(); \
68  \
69                 for(float y = draw_y1; y < draw_y2; y++) \
70                 { \
71                         if(y >= 0 && y < h) \
72                         { \
73                                 int x = (int)((y - draw_y1) * slope + draw_x1); \
74                                 int y_i = (int)y; \
75                                 int x_i = CLIP(x, 0, w); \
76  \
77                                 if(rows[y_i][x_i] == value) \
78                                         rows[y_i][x_i] = 0; \
79                                 else \
80                                         rows[y_i][x_i] = value; \
81                         } \
82                 } \
83         } \
88 void MaskUnit::draw_line_clamped(VFrame *frame, 
89         int &x1, 
90         int &y1, 
91         int x2, 
92         int y2,
93         unsigned char k)
95 //printf("MaskUnit::draw_line_clamped 1 %d %d %d %d\n", x1, y1, x2, y2);
96         int draw_x1;
97         int draw_y1;
98         int draw_x2;
99         int draw_y2;
100         unsigned char value;
102         if(y2 < y1)
103         {
104                 draw_x1 = x2;
105                 draw_y1 = y2;
106                 draw_x2 = x1;
107                 draw_y2 = y1;
108         }
109         else
110         {
111                 draw_x1 = x1;
112                 draw_y1 = y1;
113                 draw_x2 = x2;
114                 draw_y2 = y2;
115         }
117         switch(frame->get_color_model())
118         {
119                 case BC_A8:
120                         DRAW_LINE_CLAMPED(unsigned char, k);
121                         break;
122                 
123                 case BC_A16:
124                         DRAW_LINE_CLAMPED(uint16_t, k);
125                         break;
126         }
129 void MaskUnit::blur_strip(float *val_p, 
130         float *val_m, 
131         float *dst, 
132         float *src, 
133         int size,
134         int max)
136         float *sp_p = src;
137         float *sp_m = src + size - 1;
138         float *vp = val_p;
139         float *vm = val_m + size - 1;
140         float initial_p = sp_p[0];
141         float initial_m = sp_m[0];
143 //printf("MaskUnit::blur_strip %d\n", size);
144         for(int k = 0; k < size; k++)
145         {
146                 int terms = (k < 4) ? k : 4;
147                 int l;
148                 for(l = 0; l <= terms; l++)
149                 {
150                         *vp += n_p[l] * sp_p[-l] - d_p[l] * vp[-l];
151                         *vm += n_m[l] * sp_m[l] - d_m[l] * vm[l];
152                 }
154                 for( ; l <= 4; l++)
155                 {
156                         *vp += (n_p[l] - bd_p[l]) * initial_p;
157                         *vm += (n_m[l] - bd_m[l]) * initial_m;
158                 }
159                 sp_p++;
160                 sp_m--;
161                 vp++;
162                 vm--;
163         }
165         for(int i = 0; i < size; i++)
166         {
167                 float sum = val_p[i] + val_m[i];
168                 CLAMP(sum, 0, max);
169                 dst[i] = sum;
170         }
173 void MaskUnit::do_feather(VFrame *output,
174         VFrame *input, 
175         float feather, 
176         int start_out, 
177         int end_out)
179 //printf("MaskUnit::do_feather %f\n", feather);
180 // Get constants
181         double constants[8];
182         double div;
183         double std_dev = sqrt(-(double)(feather * feather) / (2 * log(1.0 / 255.0)));
184         div = sqrt(2 * M_PI) * std_dev;
185         constants[0] = -1.783 / std_dev;
186         constants[1] = -1.723 / std_dev;
187         constants[2] = 0.6318 / std_dev;
188         constants[3] = 1.997  / std_dev;
189         constants[4] = 1.6803 / div;
190         constants[5] = 3.735 / div;
191         constants[6] = -0.6803 / div;
192         constants[7] = -0.2598 / div;
194         n_p[0] = constants[4] + constants[6];
195         n_p[1] = exp(constants[1]) *
196                                 (constants[7] * sin(constants[3]) -
197                                 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
198                                 exp(constants[0]) *
199                                 (constants[5] * sin(constants[2]) -
200                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
202         n_p[2] = 2 * exp(constants[0] + constants[1]) *
203                                 ((constants[4] + constants[6]) * cos(constants[3]) * 
204                                 cos(constants[2]) - constants[5] * 
205                                 cos(constants[3]) * sin(constants[2]) -
206                                 constants[7] * cos(constants[2]) * sin(constants[3])) +
207                                 constants[6] * exp(2 * constants[0]) +
208                                 constants[4] * exp(2 * constants[1]);
210         n_p[3] = exp(constants[1] + 2 * constants[0]) *
211                                 (constants[7] * sin(constants[3]) - 
212                                 constants[6] * cos(constants[3])) +
213                                 exp(constants[0] + 2 * constants[1]) *
214                                 (constants[5] * sin(constants[2]) - constants[4] * 
215                                 cos(constants[2]));
216         n_p[4] = 0.0;
218         d_p[0] = 0.0;
219         d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
220                                 2 * exp(constants[0]) * cos(constants[2]);
222         d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) * 
223                                 exp(constants[0] + constants[1]) +
224                                 exp(2 * constants[1]) + exp (2 * constants[0]);
226         d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
227                                 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
229         d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
231         for(int i = 0; i < 5; i++) d_m[i] = d_p[i];
233         n_m[0] = 0.0;
234         for(int i = 1; i <= 4; i++)
235                 n_m[i] = n_p[i] - d_p[i] * n_p[0];
237         double sum_n_p, sum_n_m, sum_d;
238         double a, b;
240         sum_n_p = 0.0;
241         sum_n_m = 0.0;
242         sum_d = 0.0;
243         for(int i = 0; i < 5; i++)
244         {
245                 sum_n_p += n_p[i];
246                 sum_n_m += n_m[i];
247                 sum_d += d_p[i];
248         }
250         a = sum_n_p / (1 + sum_d);
251         b = sum_n_m / (1 + sum_d);
253         for(int i = 0; i < 5; i++)
254         {
255                 bd_p[i] = d_p[i] * a;
256                 bd_m[i] = d_m[i] * b;
257         }
280 #define DO_FEATHER(type, max) \
281 { \
282         int frame_w = input->get_w(); \
283         int frame_h = input->get_h(); \
284         int size = MAX(frame_w, frame_h); \
285         float *src = new float[size]; \
286         float *dst = new float[size]; \
287         float *val_p = new float[size]; \
288         float *val_m = new float[size]; \
289         int start_in = start_out - (int)feather; \
290         int end_in = end_out + (int)feather; \
291         if(start_in < 0) start_in = 0; \
292         if(end_in > frame_h) end_in = frame_h; \
293         int strip_size = end_in - start_in; \
294         type **in_rows = (type**)input->get_rows(); \
295         type **out_rows = (type**)output->get_rows(); \
296         int j; \
298 /* printf("DO_FEATHER 1\n"); */ \
299         for(j = 0; j < frame_w; j++) \
300         { \
301 /* printf("DO_FEATHER 1.1 %d\n", j); */ \
302                 bzero(val_p, sizeof(float) * (end_in - start_in)); \
303                 bzero(val_m, sizeof(float) * (end_in - start_in)); \
304                 for(int l = 0, k = start_in; k < end_in; l++, k++) \
305                 { \
306                         src[l] = (float)in_rows[k][j]; \
307                 } \
309                 blur_strip(val_p, val_m, dst, src, strip_size, max); \
311                 for(int l = start_out - start_in, k = start_out; k < end_out; l++, k++) \
312                 { \
313                         out_rows[k][j] = (type)dst[l]; \
314                 } \
315         } \
317         for(j = start_out; j < end_out; j++) \
318         { \
319 /* printf("DO_FEATHER 2 %d\n", j); */ \
320                 bzero(val_p, sizeof(float) * frame_w); \
321                 bzero(val_m, sizeof(float) * frame_w); \
322                 for(int k = 0; k < frame_w; k++) \
323                 { \
324                         src[k] = (float)out_rows[j][k]; \
325                 } \
327                 blur_strip(val_p, val_m, dst, src, frame_w, max); \
329                 for(int k = 0; k < frame_w; k++) \
330                 { \
331                         out_rows[j][k] = (type)dst[k]; \
332                 } \
333         } \
335 /* printf("DO_FEATHER 3\n"); */ \
337         delete [] src; \
338         delete [] dst; \
339         delete [] val_p; \
340         delete [] val_m; \
341 /* printf("DO_FEATHER 4\n"); */ \
351 //printf("do_feather %d\n", frame->get_color_model());
352         switch(input->get_color_model())
353         {
354                 case BC_A8:
355                         DO_FEATHER(unsigned char, 0xff);
356                         break;
357                 
358                 case BC_A16:
359                         DO_FEATHER(uint16_t, 0xffff);
360                         break;
361         }
368 void MaskUnit::process_package(LoadPackage *package)
370         MaskPackage *ptr = (MaskPackage*)package;
372         if(engine->recalculate && ptr->part == RECALCULATE_PART)
373         {
374                 VFrame *mask;
375 //printf("MaskUnit::process_package 1 %d\n", get_package_number());
376                 if(engine->feather > 0) 
377                         mask = engine->temp_mask;
378                 else
379                         mask = engine->mask;
381 // Generated oversampling frame
382                 int mask_w = mask->get_w();
383                 int mask_h = mask->get_h();
384                 int oversampled_package_w = mask_w * OVERSAMPLE;
385                 int oversampled_package_h = (ptr->row2 - ptr->row1) * OVERSAMPLE;
386 //printf("MaskUnit::process_package 1\n");
388                 if(temp && 
389                         (temp->get_w() != oversampled_package_w ||
390                         temp->get_h() != oversampled_package_h))
391                 {
392                         delete temp;
393                         temp = 0;
394                 }
395 //printf("MaskUnit::process_package 1\n");
397                 if(!temp)
398                 {
399                         temp = new VFrame(0, 
400                                 oversampled_package_w, 
401                                 oversampled_package_h,
402                                 BC_A8);
403                 }
405                 temp->clear_frame();
406 //printf("MaskUnit::process_package 1 %d\n", engine->point_sets.total);
409 // Draw oversampled region of polygons on temp
410                 for(int k = 0; k < engine->point_sets.total; k++)
411                 {
412                         int old_x, old_y;
413                         unsigned char max = k + 1;
414                         ArrayList<MaskPoint*> *points = engine->point_sets.values[k];
416                         if(points->total < 3) continue;
417 //printf("MaskUnit::process_package 2 %d %d\n", k, points->total);
418                         for(int i = 0; i < points->total; i++)
419                         {
420                                 MaskPoint *point1 = points->values[i];
421                                 MaskPoint *point2 = (i >= points->total - 1) ? 
422                                         points->values[0] : 
423                                         points->values[i + 1];
425                                 float x, y;
426                                 int segments = (int)(sqrt(SQR(point1->x - point2->x) + SQR(point1->y - point2->y)));
427                                 float x0 = point1->x;
428                                 float y0 = point1->y;
429                                 float x1 = point1->x + point1->control_x2;
430                                 float y1 = point1->y + point1->control_y2;
431                                 float x2 = point2->x + point2->control_x1;
432                                 float y2 = point2->y + point2->control_y1;
433                                 float x3 = point2->x;
434                                 float y3 = point2->y;
436                                 for(int j = 0; j <= segments; j++)
437                                 {
438                                         float t = (float)j / segments;
439                                         float tpow2 = t * t;
440                                         float tpow3 = t * t * t;
441                                         float invt = 1 - t;
442                                         float invtpow2 = invt * invt;
443                                         float invtpow3 = invt * invt * invt;
445                                         x = (        invtpow3 * x0
446                                                 + 3 * t     * invtpow2 * x1
447                                                 + 3 * tpow2 * invt     * x2 
448                                                 +     tpow3            * x3);
449                                         y = (        invtpow3 * y0 
450                                                 + 3 * t     * invtpow2 * y1
451                                                 + 3 * tpow2 * invt     * y2 
452                                                 +     tpow3            * y3);
454                                         y -= ptr->row1;
455                                         x *= OVERSAMPLE;
456                                         y *= OVERSAMPLE;
458                                         if(j > 0)
459                                         {
460                                                 draw_line_clamped(temp, old_x, old_y, (int)x, (int)y, max);
461                                         }
463                                         old_x = (int)x;
464                                         old_y = (int)y;
465                                 }
466                         }
468 //printf("MaskUnit::process_package 1\n");
474 #define FILL_ROWS(type) \
475 for(int i = 0; i < oversampled_package_h; i++) \
476 { \
477         type *row = (type*)temp->get_rows()[i]; \
478         int value = 0x0; \
479         int total = 0; \
481         for(int j = 0; j < oversampled_package_w; j++) \
482                 if(row[j] == max) total++; \
484         if(total > 1) \
485         { \
486                 if(total & 0x1) total--; \
487                 for(int j = 0; j < oversampled_package_w; j++) \
488                 { \
489                         if(row[j] == max && total > 0) \
490                         { \
491                                 if(value)  \
492                                         value = 0x0; \
493                                 else \
494                                         value = max; \
495                                 total--; \
496                         } \
497                         else \
498                         { \
499                                 if(value) row[j] = value; \
500                         } \
501                 } \
502         } \
506 // Fill in the polygon in the horizontal direction
507                         switch(temp->get_color_model())
508                         {
509                                 case BC_A8:
510                                         FILL_ROWS(unsigned char);
511                                         break;
513                                 case BC_A16:
514                                         FILL_ROWS(uint16_t);
515                                         break;
516                         }
517                 }
526 #define DOWNSAMPLE(type, value) \
527 for(int i = 0; i < ptr->row2 - ptr->row1; i++) \
528 { \
529         type *output_row = (type*)mask->get_rows()[i + ptr->row1]; \
530         unsigned char **input_rows = (unsigned char**)temp->get_rows() + i * OVERSAMPLE; \
533         for(int j = 0; j < mask_w; j++) \
534         { \
535                 int64_t total = 0; \
537 /* Accumulate pixel */ \
538                 for(int k = 0; k < OVERSAMPLE; k++) \
539                 { \
540                         unsigned char *input_vector = input_rows[k] + j * OVERSAMPLE; \
541                         for(int l = 0; l < OVERSAMPLE; l++) \
542                         { \
543                                 total += (input_vector[l] ? value : 0); \
544                         } \
545                 } \
547 /* Divide pixel */ \
548                 if(OVERSAMPLE == 8) \
549                         total >>= 6; \
550                 else \
551                 if(OVERSAMPLE == 4) \
552                         total >>= 2; \
553                 else \
554                 if(OVERSAMPLE == 2) \
555                         total >>= 2; \
556                 else \
557                         total /= OVERSAMPLE * OVERSAMPLE; \
559                 output_row[j] = total; \
560         } \
564 // Downsample polygon
565                 switch(mask->get_color_model())
566                 {
567                         case BC_A8:
568                         {
569                                 unsigned char value;
570                                 value = (int)((float)engine->value / 100 * 0xff);
571                                 DOWNSAMPLE(unsigned char, value);
572                                 break;
573                         }
575                         case BC_A16:
576                         {
577                                 uint16_t value;
578                                 value = (int)((float)engine->value / 100 * 0xffff);
579                                 DOWNSAMPLE(uint16_t, value);
580                                 break;
581                         }
582                 }
584         }
587         if(ptr->part == RECALCULATE_PART)
588         {
589 // The feather could span more than one package so can't do it until
590 // all packages are drawn.
591                 if(get_package_number() >= engine->get_total_packages() / 2 - 1)
592                 {
593                         for(int i = engine->get_total_packages() / 2; 
594                                 i < engine->get_total_packages();
595                                 i++)
596                         {
597                                 MaskPackage *package = (MaskPackage*)engine->get_package(i);
598                                 package->apply_mutex->unlock();
599                         }
600                 }
602         }
604 //printf("MaskUnit::process_package 2\n");
606         if(ptr->part == APPLY_PART)
607         {
608 //printf("MaskUnit::process_package 2.1\n");
609                 ptr->apply_mutex->lock();
610                 ptr->apply_mutex->unlock();
611 //printf("MaskUnit::process_package 2.2\n");
613                 if(engine->recalculate)
614                 {
615 // Feather polygon
616                         if(engine->feather > 0) do_feather(engine->mask, 
617                                 engine->temp_mask, 
618                                 engine->feather, 
619                                 ptr->row1, 
620                                 ptr->row2);
622                 }
623 //printf("MaskUnit::process_package 3 %f\n", engine->feather);
628 // Apply mask
629                 int mask_w = engine->mask->get_w();
632 #define APPLY_MASK_SUBTRACT_ALPHA(type, max, components, do_yuv) \
633 { \
634         type *output_row = (type*)engine->output->get_rows()[i]; \
635         type *mask_row = (type*)engine->mask->get_rows()[i]; \
636         int chroma_offset = (max + 1) / 2; \
638         for(int j  = 0; j < mask_w; j++) \
639         { \
640                 if(components == 4) \
641                 { \
642                         output_row[j * 4 + 3] = output_row[j * 4 + 3] * (max - mask_row[j]) / max; \
643                 } \
644                 else \
645                 { \
646                         output_row[j * 3] = output_row[j * 3] * (max - mask_row[j]) / max; \
648                         output_row[j * 3 + 1] = output_row[j * 3 + 1] * (max - mask_row[j]) / max; \
649                         output_row[j * 3 + 2] = output_row[j * 3 + 2] * (max - mask_row[j]) / max; \
651                         if(do_yuv) \
652                         { \
653                                 output_row[j * 3 + 1] += chroma_offset * mask_row[j] / max; \
654                                 output_row[j * 3 + 2] += chroma_offset * mask_row[j] / max; \
655                         } \
656                 } \
657         } \
660 #define APPLY_MASK_MULTIPLY_ALPHA(type, max, components, do_yuv) \
661 { \
662         type *output_row = (type*)engine->output->get_rows()[i]; \
663         type *mask_row = (type*)engine->mask->get_rows()[i]; \
664         int chroma_offset = (max + 1) / 2; \
666         for(int j  = 0; j < mask_w; j++) \
667         { \
668                 if(components == 4) \
669                 { \
670                         output_row[j * 4 + 3] = output_row[j * 4 + 3] * mask_row[j] / max; \
671                 } \
672                 else \
673                 { \
674                         output_row[j * 3] = output_row[j * 3] * mask_row[j] / max; \
676                         output_row[j * 3 + 1] = output_row[j * 3 + 1] * mask_row[j] / max; \
677                         output_row[j * 3 + 2] = output_row[j * 3 + 2] * mask_row[j] / max; \
679                         if(do_yuv) \
680                         { \
681                                 output_row[j * 3 + 1] += chroma_offset * (max - mask_row[j]) / max; \
682                                 output_row[j * 3 + 2] += chroma_offset * (max - mask_row[j]) / max; \
683                         } \
684                 } \
685         } \
691 //printf("MaskUnit::process_package 1 %d\n", engine->mode);
692                 for(int i = ptr->row1; i < ptr->row2; i++)
693                 {
694                         switch(engine->mode)
695                         {
696                                 case MASK_MULTIPLY_ALPHA:
697                                         switch(engine->output->get_color_model())
698                                         {
699                                                 case BC_RGB888:
700                                                         APPLY_MASK_MULTIPLY_ALPHA(unsigned char, 0xff, 3, 0);
701                                                         break;
702                                                 case BC_YUV888:
703                                                         APPLY_MASK_MULTIPLY_ALPHA(unsigned char, 0xff, 3, 1);
704                                                         break;
705                                                 case BC_YUVA8888:
706                                                 case BC_RGBA8888:
707                                                         APPLY_MASK_MULTIPLY_ALPHA(unsigned char, 0xff, 4, 0);
708                                                         break;
709                                                 case BC_RGB161616:
710                                                         APPLY_MASK_MULTIPLY_ALPHA(uint16_t, 0xffff, 3, 0);
711                                                         break;
712                                                 case BC_YUV161616:
713                                                         APPLY_MASK_MULTIPLY_ALPHA(uint16_t, 0xffff, 3, 1);
714                                                         break;
715                                                 case BC_YUVA16161616:
716                                                 case BC_RGBA16161616:
717                                                         APPLY_MASK_MULTIPLY_ALPHA(uint16_t, 0xffff, 4, 0);
718                                                         break;
719                                         }
720                                         break;
722                                 case MASK_SUBTRACT_ALPHA:
723                                         switch(engine->output->get_color_model())
724                                         {
725                                                 case BC_RGB888:
726                                                         APPLY_MASK_SUBTRACT_ALPHA(unsigned char, 0xff, 3, 0);
727                                                         break;
728                                                 case BC_YUV888:
729                                                         APPLY_MASK_SUBTRACT_ALPHA(unsigned char, 0xff, 3, 1);
730                                                         break;
731                                                 case BC_YUVA8888:
732                                                 case BC_RGBA8888:
733                                                         APPLY_MASK_SUBTRACT_ALPHA(unsigned char, 0xff, 4, 0);
734                                                         break;
735                                                 case BC_RGB161616:
736                                                         APPLY_MASK_SUBTRACT_ALPHA(uint16_t, 0xffff, 3, 0);
737                                                         break;
738                                                 case BC_YUV161616:
739                                                         APPLY_MASK_SUBTRACT_ALPHA(uint16_t, 0xffff, 3, 1);
740                                                         break;
741                                                 case BC_YUVA16161616:
742                                                 case BC_RGBA16161616:
743                                                         APPLY_MASK_SUBTRACT_ALPHA(uint16_t, 0xffff, 4, 0);
744                                                         break;
745                                         }
746                                         break;
747                         }
748                 }
749         }
750 //printf("MaskUnit::process_package 4 %d\n", get_package_number());
757 MaskEngine::MaskEngine(int cpus)
758  : LoadServer(cpus, cpus * OVERSAMPLE * 2)
759 // : LoadServer(1, 2)
761         mask = 0;
764 MaskEngine::~MaskEngine()
766         if(mask) 
767         {
768                 delete mask;
769                 delete temp_mask;
770         }
771         point_sets.remove_all_objects();
774 int MaskEngine::points_equivalent(ArrayList<MaskPoint*> *new_points, 
775         ArrayList<MaskPoint*> *points)
777 //printf("MaskEngine::points_equivalent %d %d\n", new_points->total, points->total);
778         if(new_points->total != points->total) return 0;
779         
780         for(int i = 0; i < new_points->total; i++)
781         {
782                 if(!(*new_points->values[i] == *points->values[i])) return 0;
783         }
784         
785         return 1;
788 void MaskEngine::do_mask(VFrame *output, 
789         MaskAutos *keyframe_set, 
790         long position, 
791         int direction)
793 //printf("MaskEngine::do_mask 1\n");
794         Auto *current = 0;
795         MaskAuto *default_auto = (MaskAuto*)keyframe_set->default_auto;
796         MaskAuto *keyframe = (MaskAuto*)keyframe_set->get_prev_auto(position, 
797                 direction,
798                 current);
801 // Nothing to be done
802         int total_points = 0;
803         for(int i = 0; i < keyframe->masks.total; i++)
804         {
805                 SubMask *mask = keyframe->get_submask(i);
806                 int submask_points = mask->points.total;
807                 if(submask_points > 1) total_points += submask_points;
808         }
810 //printf("MaskEngine::do_mask 1 %d %d\n", total_points, keyframe->value);
811 // Ignore certain masks
812         if(total_points < 2 || 
813                 (keyframe->value == 0 && default_auto->mode == MASK_SUBTRACT_ALPHA))
814         {
815                 return;
816         }
818 // Fake certain masks
819         if(keyframe->value == 0 && default_auto->mode == MASK_MULTIPLY_ALPHA)
820         {
821                 output->clear_frame();
822                 return;
823         }
825 //printf("MaskEngine::do_mask 1\n");
827         int new_color_model = 0;
828         recalculate = 0;
829         switch(output->get_color_model())
830         {
831                 case BC_RGB888:
832                 case BC_RGBA8888:
833                 case BC_YUV888:
834                 case BC_YUVA8888:
835                         new_color_model = BC_A8;
836                         break;
838                 case BC_RGB161616:
839                 case BC_RGBA16161616:
840                 case BC_YUV161616:
841                 case BC_YUVA16161616:
842                         new_color_model = BC_A16;
843                         break;
844         }
846 // Determine if recalculation is needed
848 //printf("MaskEngine::do_mask 1 %d\n", recalculate);
849         if(mask && 
850                 (mask->get_w() != output->get_w() ||
851                 mask->get_h() != output->get_h() ||
852                 mask->get_color_model() != new_color_model))
853         {
854                 delete mask;
855                 delete temp_mask;
856                 mask = 0;
857                 recalculate = 1;
858         }
860 //printf("MaskEngine::do_mask %d\n", recalculate);
861         if(!recalculate)
862         {
863                 if(point_sets.total != keyframe_set->total_submasks(position, direction))
864                         recalculate = 1;
865         }
867 //printf("MaskEngine::do_mask %d\n", recalculate);
868         if(!recalculate)
869         {
870                 for(int i = 0; 
871                         i < keyframe_set->total_submasks(position, direction) && !recalculate; 
872                         i++)
873                 {
874                         ArrayList<MaskPoint*> *new_points = new ArrayList<MaskPoint*>;
875                         keyframe_set->get_points(new_points, i, position, direction);
876                         if(!points_equivalent(new_points, point_sets.values[i])) recalculate = 1;
877                         new_points->remove_all_objects();
878                 }
879         }
881 //printf("MaskEngine::do_mask 3 %d\n", recalculate);
882         if(recalculate ||
883                 !EQUIV(keyframe->feather, feather) ||
884                 !EQUIV(keyframe->value, value))
885         {
886                 recalculate = 1;
887                 if(!mask) 
888                 {
889                         mask = new VFrame(0, 
890                                         output->get_w(), 
891                                         output->get_h(),
892                                         new_color_model);
893                         temp_mask = new VFrame(0, 
894                                         output->get_w(), 
895                                         output->get_h(),
896                                         new_color_model);
897                 }
898                 if(keyframe->feather > 0)
899                         temp_mask->clear_frame();
900                 else
901                         mask->clear_frame();
902                 point_sets.remove_all_objects();
904                 for(int i = 0; 
905                         i < keyframe_set->total_submasks(position, direction); 
906                         i++)
907                 {
908                         ArrayList<MaskPoint*> *new_points = new ArrayList<MaskPoint*>;
909                         keyframe_set->get_points(new_points, i, position, direction);
910                         point_sets.append(new_points);
911                 }
912         }
914 //printf("MaskEngine::do_mask 4 %d\n", recalculate);
917         this->output = output;
918         this->mode = default_auto->mode;
919         this->feather = keyframe->feather;
920         this->value = keyframe->value;
921 //printf("MaskEngine::do_mask 5\n");
924 // Run units
925         process_packages();
928 //printf("MaskEngine::do_mask 6\n");
931 void MaskEngine::init_packages()
933 //printf("MaskEngine::init_packages 1\n");
934         int division = (int)((float)output->get_h() / (total_packages / 2) + 0.5);
935         if(division < 1) division = 1;
937 // Always a multiple of 2 packages exist
938         for(int i = 0; i < get_total_packages() / 2; i++)
939         {
940                 MaskPackage *part1 = (MaskPackage*)packages[i];
941                 MaskPackage *part2 = (MaskPackage*)packages[i + total_packages / 2];
942                 part2->row1 = part1->row1 = division * i;
943                 part2->row2 = part1->row2 = division * i + division;
944                 part2->row1 = part1->row1 = MIN(output->get_h(), part1->row1);
945                 part2->row2 = part1->row2 = MIN(output->get_h(), part1->row2);
946                 
947                 if(i >= (total_packages / 2) - 1) 
948                 {
949                         part2->row2 = part1->row2 = output->get_h();
950                 }
952                 part2->apply_mutex->lock();
954                 part1->part = RECALCULATE_PART;
955                 part2->part = APPLY_PART;
956         }
957 //printf("MaskEngine::init_packages 2\n");
960 LoadClient* MaskEngine::new_client()
962         return new MaskUnit(this);
965 LoadPackage* MaskEngine::new_package()
967         return new MaskPackage;