2a2029edf138fd548dd811d91d5e19e9b8a50bf3
[goodguy/history.git] / cinelerra-5.1 / guicast / rotateframe.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include "condition.h"
23 #include "rotateframe.h"
24 #include "vframe.h"
25
26 #include <math.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #define SQR(x) ((x) * (x))
32
33 RotateFrame::RotateFrame(int cpus, int width, int height)
34 {
35         int y1, y2, y_increment;
36         y_increment = height / cpus;
37         y1 = 0;
38         this->cpus = cpus;
39
40         engine = new RotateEngine*[cpus];
41         for(int i = 0; i < cpus; i++)
42         {
43                 y2 = y1 + y_increment;
44                 if(i == cpus - 1 && y2 < height - 1) y2 = height - 1;
45                 engine[i] = new RotateEngine(this, y1, y2);
46                 engine[i]->start();
47                 y1 += y_increment;
48         }
49
50         float_matrix = 0;
51         int_matrix = 0;
52         int_rows = 0;
53         float_rows = 0;
54         last_angle = 0;
55         last_interpolate = 0;
56 }
57
58 RotateFrame::~RotateFrame()
59 {
60         for(int i = 0; i < cpus; i++)
61         {
62                 delete engine[i];
63         }
64         delete [] engine;
65         if(float_matrix) delete [] float_matrix;
66         if(int_matrix) delete [] int_matrix;
67         if(float_rows) delete [] float_rows;
68         if(int_rows) delete [] int_rows;
69 }
70
71
72 void RotateFrame::rotate(VFrame *output, 
73         VFrame *input, 
74         double angle, 
75         int interpolate)
76 {
77         this->angle = angle;
78         this->output = output;
79         this->input = input;
80         this->interpolate = interpolate;
81
82         if(angle != 0)
83         {
84         if(angle == 90 || angle == 180 || angle == 270)
85             rotate_rightangle(input, 
86                                 output, 
87                                 (int)angle);
88                 else
89                 {
90                         rotate_obliqueangle(input, 
91                                 output, 
92                                 angle,
93                                 interpolate);
94                 }
95         }
96         else
97 // Data never processed so copy if necessary
98         {
99                 output->copy_from(input);
100         }
101         this->last_angle = angle;
102 }
103
104 int RotateFrame::get_rightdimensions(VFrame *frame, 
105         int &diameter, 
106         int &in_x1, 
107         int &in_y1, 
108         int &in_x2, 
109         int &in_y2, 
110         int &out_x1, 
111         int &out_y1, 
112         int &out_x2, 
113         int &out_y2)
114 {
115     diameter = frame->get_w() < frame->get_h() ? frame->get_w() : frame->get_h();
116     out_x1 = in_x1 = frame->get_w() / 2 - diameter / 2;
117     out_x2 = in_x2 = in_x1 + diameter - 1;
118     out_y1 = in_y1 = frame->get_h() / 2 - diameter / 2;
119     out_y2 = in_y2 = in_y1 + diameter - 1;
120         return 0;
121 }
122
123
124
125 #define ROTATE_RIGHTANGLE(type, components) \
126 { \
127         type **input_rows = (type**)input->get_rows(); \
128         type **output_rows = (type**)output->get_rows(); \
129         int height = output->get_h(); \
130         int width = output->get_w(); \
131  \
132         switch(angle) \
133         { \
134                 case 90: \
135                         get_rightdimensions(input, \
136                                 diameter,  \
137                                 in_x1,  \
138                                 in_y1,  \
139                                 in_x2,  \
140                                 in_y2,  \
141                                 out_x1,  \
142                                 out_y1,  \
143                                 out_x2,  \
144                                 out_y2); \
145             while(in_x2 > in_x1) \
146             { \
147                 diameter = in_x2 - in_x1; \
148                 for(int i = 0; i < diameter; i++) \
149                 { \
150                     type temp_pixel[components]; \
151                                         for(int j = 0; j < components; j++) \
152                                         { \
153                                                 temp_pixel[j] = input_rows[in_y1 + i][in_x2 * components + j]; \
154  \
155                         output_rows[in_y1 + i][in_x2 * components + j]   = input_rows[in_y1][(in_x1 + i) * components + j]; \
156                         output_rows[in_y1][(in_x1 + i) * components + j] = input_rows[in_y2 - i][in_x1 * components + j]; \
157                         output_rows[in_y2 - i][in_x1 * components + j]   = input_rows[in_y2][(in_x2 - i) * components + j]; \
158                         output_rows[in_y2][(in_x2 - i) * components + j] = temp_pixel[j]; \
159                                         } \
160                 } \
161  \
162                 in_x2--; \
163                 in_x1++; \
164                 in_y2--; \
165                 in_y1++; \
166             } \
167                         break; \
168  \
169         case 180: \
170                 for(int i = 0, j = height - 1; i < j; i++, j--) \
171             { \
172                 for(int k = 0, l = width - 1; k < width; k++, l--) \
173                 { \
174                     type temp_pixel[components]; \
175                                         for(int m = 0; m < components; m++) \
176                                         { \
177                                 temp_pixel[m] = input_rows[j][k * components + m]; \
178                         output_rows[j][k * components + m] = input_rows[i][l * components + m]; \
179                         output_rows[i][l * components + m] = temp_pixel[m]; \
180                                         } \
181                 } \
182             } \
183                         break; \
184  \
185                 case 270: \
186                         get_rightdimensions(input, \
187                                 diameter,  \
188                                 in_x1,  \
189                                 in_y1,  \
190                                 in_x2,  \
191                                 in_y2,  \
192                                 out_x1,  \
193                                 out_y1,  \
194                                 out_x2,  \
195                                 out_y2); \
196  \
197             while(in_x2 > in_x1) \
198             { \
199                 diameter = in_x2 - in_x1; \
200                 for(int i = 0; i < diameter; i++) \
201                 { \
202                     type temp_pixel[components]; \
203                                         for(int j = 0; j < components; j++) \
204                                         { \
205                         temp_pixel[j] = input_rows[in_y1 + i][in_x1 * components + j]; \
206                         output_rows[in_y1 + i][in_x1 * components + j]   = input_rows[in_y1][(in_x2 - i) * components + j]; \
207                         output_rows[in_y1][(in_x2 - i) * components + j] = input_rows[in_y2 - i][in_x2 * components + j]; \
208                         output_rows[in_y2 - i][in_x2 * components + j]   = input_rows[in_y2][(in_x1 + i) * components + j]; \
209                         output_rows[in_y2][(in_x1 + i) * components + j] = temp_pixel[j]; \
210                                         } \
211                 } \
212  \
213                 in_x2--; \
214                 in_x1++; \
215                 in_y2--; \
216                 in_y1++; \
217             } \
218                         break; \
219         } \
220 }
221
222
223 int RotateFrame::rotate_rightangle(VFrame *input, 
224         VFrame *output, 
225         int angle)
226 {
227         int in_x1 = 0;
228     int in_y1 = 0;
229     int in_x2 = input->get_w();
230     int in_y2 = input->get_h();
231         int out_x1, out_y1, out_x2, out_y2;
232     int diameter;
233
234         output->clear_frame();
235         switch(output->get_color_model())
236         {
237                 case BC_RGB888:
238                         ROTATE_RIGHTANGLE(unsigned char, 3);
239                         break;
240                 case BC_RGBA8888:
241                         ROTATE_RIGHTANGLE(unsigned char, 4);
242                         break;
243                 case BC_RGB_FLOAT:
244                         ROTATE_RIGHTANGLE(float, 3);
245                         break;
246                 case BC_RGBA_FLOAT:
247                         ROTATE_RIGHTANGLE(float, 4);
248                         break;
249                 case BC_YUV888:
250                         ROTATE_RIGHTANGLE(unsigned char, 3);
251                         break;
252                 case BC_YUVA8888:
253                         ROTATE_RIGHTANGLE(unsigned char, 4);
254                         break;
255                 case BC_RGB161616:
256                         ROTATE_RIGHTANGLE(uint16_t, 3);
257                         break;
258                 case BC_RGBA16161616:
259                         ROTATE_RIGHTANGLE(uint16_t, 4);
260                         break;
261                 case BC_YUV161616:
262                         ROTATE_RIGHTANGLE(uint16_t, 3);
263                         break;
264                 case BC_YUVA16161616:
265                         ROTATE_RIGHTANGLE(uint16_t, 4);
266                         break;
267         }
268         return 0;
269 }
270
271 int RotateFrame::rotate_obliqueangle(VFrame *input, 
272         VFrame *output, 
273         double angle,
274         int interpolate)
275 {
276         int i;
277         int center_x, center_y;
278         int need_matrix = 0;
279
280         center_x = input->get_w() / 2;
281         center_y = input->get_h() / 2;
282
283         if(last_angle != angle || 
284                 (interpolate && !float_matrix) || 
285                 (!interpolate && !int_matrix))
286         {
287                 if(interpolate && !float_matrix)
288                 {
289                         float_matrix = new SourceCoord[input->get_w() * input->get_h()];
290                         float_rows = new SourceCoord*[input->get_h()];
291                         for(i = 0; i < input->get_h(); i++)
292                         {
293                                 float_rows[i] = &float_matrix[i * input->get_w()];
294                         }
295                 }
296                 else
297                 if(!interpolate && !int_matrix)
298                 {
299                         int_matrix = new int[input->get_w() * input->get_h()];
300                         int_rows = new int*[input->get_h()];
301                         for(i = 0; i < input->get_h(); i++)
302                         {
303                                 int_rows[i] = &int_matrix[i * input->get_w()];
304                         }
305                 }
306
307                 need_matrix = 1;
308         }
309
310         if(last_angle != angle) need_matrix = 1;
311         if(last_interpolate != interpolate) need_matrix = 1;
312
313         if(need_matrix)
314         {
315 // Last angle != angle implied by first buffer needing to be allocated
316                 for(i = 0; i < cpus; i++)
317                 {
318                         engine[i]->generate_matrix(interpolate);
319                 }
320
321                 for(i = 0; i < cpus; i++)
322                 {
323                         engine[i]->wait_completion();
324                 }
325         }
326
327         last_angle = angle;
328         last_interpolate = interpolate;
329
330 // Perform the rotation
331         for(i = 0; i < cpus; i++)
332         {
333                 engine[i]->perform_rotation(input, output, interpolate);
334         }
335
336         for(i = 0; i < cpus; i++)
337         {
338                 engine[i]->wait_completion();
339         }
340
341
342
343 #define FILL_CENTER(type, components) \
344 { \
345         type *out_pixel = ((type**)output->get_rows())[center_y] + center_x * components; \
346         type *in_pixel = ((type**)input->get_rows())[center_y] + center_x * components; \
347  \
348         out_pixel[0] = in_pixel[0]; \
349         out_pixel[1] = in_pixel[1]; \
350         out_pixel[2] = in_pixel[2]; \
351         if(components == 4) out_pixel[3] = in_pixel[3]; \
352 }
353
354
355
356
357
358 // Fill center pixel
359         switch(input->get_color_model())
360         {
361                 case BC_RGB_FLOAT:
362                         FILL_CENTER(float, 3)
363                         break;
364                 case BC_RGBA_FLOAT:
365                         FILL_CENTER(float, 4)
366                         break;
367                 case BC_RGB888:
368                 case BC_YUV888:
369                         FILL_CENTER(unsigned char, 3)
370                         break;
371                 case BC_RGBA8888:
372                 case BC_YUVA8888:
373                         FILL_CENTER(unsigned char, 4)
374                         break;
375                 case BC_RGB161616:
376                 case BC_YUV161616:
377                         FILL_CENTER(uint16_t, 3)
378                         break;
379                 case BC_RGBA16161616:
380                 case BC_YUVA16161616:
381                         FILL_CENTER(uint16_t, 4)
382                         break;
383         }
384         return 0;
385 }
386
387
388
389
390
391
392
393
394 RotateEngine::RotateEngine(RotateFrame *plugin, int row1, int row2)
395  : Thread(1, 0, 0)
396 {
397         this->plugin = plugin;
398         do_matrix = do_rotation = 0;
399         done = 0;
400         this->row1 = row1;
401         this->row2 = row2;
402         input_lock = new Condition(0, "RotateEngine::input_lock");
403         output_lock = new Condition(0, "RotateEngine::output_lock");
404 }
405 RotateEngine::~RotateEngine()
406 {
407         if(!done)
408         {
409                 done = 1;
410                 input_lock->unlock();
411                 join();
412         }
413         delete input_lock;
414         delete output_lock;
415 }
416
417 int RotateEngine::generate_matrix(int interpolate)
418 {
419         this->do_matrix = 1;
420         this->interpolate = interpolate;
421         input_lock->unlock();
422         return 0;
423 }
424
425 int RotateEngine::perform_rotation(VFrame *input, 
426         VFrame *output, 
427         int interpolate)
428 {
429         this->input = input;
430         this->output = output;
431         this->do_rotation = 1;
432         this->interpolate = interpolate;
433         input_lock->unlock();
434         return 0;
435 }
436
437
438 int RotateEngine::wait_completion()
439 {
440         output_lock->lock("RotateEngine::wait_completion");
441         return 0;
442 }
443
444 int RotateEngine::coords_to_pixel(int &input_y, int &input_x)
445 {
446         if(input_y < 0) return -1;
447         else
448         if(input_y >= plugin->input->get_h()) return -1;
449         else
450         if(input_x < 0) return -1;
451         else
452         if(input_x >= plugin->input->get_w()) return -1;
453         else
454         return input_y * plugin->input->get_w() + input_x;
455 }
456
457 int RotateEngine::coords_to_pixel(SourceCoord &float_pixel, float &input_y, float &input_x)
458 {
459         if(input_y < 0) float_pixel.y = -1;
460         else
461         if(input_y >= plugin->input->get_h()) float_pixel.y = -1;
462         else
463         float_pixel.y = input_y;
464
465         if(input_x < 0) float_pixel.x = -1;
466         else
467         if(input_x >= plugin->input->get_w()) float_pixel.x = -1;
468         else
469         float_pixel.x = input_x;
470         return 0;
471 }
472
473
474 int RotateEngine::create_matrix()
475 {
476 // Polar coords of pixel
477         register double k, l, magnitude, angle, offset_angle, offset_angle2;
478         register double x_offset, y_offset;
479         register int i, j;
480         int *int_row = 0;
481         SourceCoord *float_row = 0;
482         int input_x_i, input_y_i;
483         float input_x_f, input_y_f;
484
485 //printf("RotateEngine::create_matrix 1\n");
486 // The following is the result of pure trial and error.
487 // Fix the angles
488 // The source pixels are seen as if they were rotated counterclockwise so the sign is OK.
489         offset_angle = -(plugin->angle - 90) / 360 * 2 * M_PI;
490         offset_angle2 = -(plugin->angle - 270) / 360 * 2 * M_PI;
491
492 // Calculate an offset to add to all the pixels to compensate for the quadrant
493         y_offset = plugin->input->get_h() / 2;
494         x_offset = plugin->input->get_w() / 2;
495
496         for(i = row1, l = row1 - plugin->input->get_h() / 2; i < row2; i++, l++)
497         {
498                 int l_suare = (int)(l * l);
499                 if(!interpolate)
500                         int_row = plugin->int_rows[i];
501                 else
502                         float_row = plugin->float_rows[i];
503
504 //printf("RotateEngine::create_matrix 2 %d %f\n", i, l);
505                 for(j = 0, k = -plugin->input->get_w() / 2; 
506                         j < plugin->input->get_w(); 
507                         j++, k++)
508                 {
509 // Add offsets to input
510 // Convert to polar coords
511                         magnitude = sqrt(SQR(k) + l_suare);
512 //printf("RotateEngine::create_matrix 3.2 %f %f\n", k, l);
513                         if(l != 0)
514                                 angle = atan(-k / l);
515                         else
516                         if(k < 0)
517                                 angle = M_PI / 2;
518                         else
519                                 angle = M_PI * 1.5;
520 //printf("RotateEngine::create_matrix 3.3\n");
521 // Rotate
522                         angle += (l < 0) ? offset_angle2 : offset_angle;
523
524 // Convert back to cartesian coords
525                         if(!interpolate)
526                         {
527                                 input_y_i = (int)(y_offset + magnitude * sin(angle));
528                                 input_x_i = (int)(x_offset + magnitude * cos(angle));
529                                 int_row[j] = coords_to_pixel(input_y_i, input_x_i);
530                         }
531                         else
532                         {
533                                 input_y_f = y_offset + magnitude * sin(angle);
534                                 input_x_f = x_offset + magnitude * cos(angle);
535                                 coords_to_pixel(float_row[j], input_y_f, input_x_f);
536                         }
537                 }
538 //printf("RotateEngine::create_matrix 3\n");
539         }
540 //printf("RotateEngine::create_matrix 2\n");
541         return 0;
542 }
543
544 #define ROTATE_NEAREST(type, components, black_chroma) \
545 { \
546         type **input_rows = (type**)input->get_rows(); \
547         type **output_rows = (type**)output->get_rows(); \
548  \
549         for(int i = row1; i < row2; i++) \
550         { \
551                 int *int_row = plugin->int_rows[i]; \
552                 for(int j = 0; j < width; j++) \
553                 { \
554                         if(int_row[j] < 0) \
555                         {  \
556                                 for(int k = 0; k < components; k++) \
557                                         output_rows[i][j * components + k] = 0; \
558                         } \
559                         else \
560                         { \
561                                 for(int k = 0; k < components; k++) \
562                                         output_rows[i][j * components + k] = *(input_rows[0] + int_row[j] * components + k); \
563                         } \
564                 } \
565         } \
566 }
567
568 #define ROTATE_INTERPOLATE(type, components, black_chroma) \
569 { \
570         type zero_pixel[] = { 0, black_chroma, black_chroma, 0 }; \
571         int i, j; \
572         float k, l; \
573         type **input_rows = (type**)input->get_rows(); \
574         type **output_rows = (type**)output->get_rows(); \
575         float x_fraction1, x_fraction2, y_fraction1, y_fraction2; \
576         float fraction1, fraction2, fraction3, fraction4; \
577         int x_pixel1, x_pixel2, y_pixel1, y_pixel2; \
578         type *pixel1, *pixel2, *pixel3, *pixel4; \
579  \
580         for(i = row1, k = row1; i < row2; i++, k++) \
581         { \
582                 SourceCoord *float_row = plugin->float_rows[i]; \
583                 for(j = 0, l = 0; j < width; j++, l++) \
584                 { \
585                         if(float_row[j].x < 0 || float_row[j].y < 0) \
586                         { \
587                                 output_rows[i][j * components + 0] = 0; \
588                                 output_rows[i][j * components + 1] = black_chroma; \
589                                 output_rows[i][j * components + 2] = black_chroma; \
590                                 if(components == 4) output_rows[i][j * components + 3] = 0; \
591                         } \
592                         else \
593                         { \
594 /* Interpolate input pixels */ \
595                                 x_pixel1 = (int)float_row[j].x; \
596                                 x_pixel2 = (int)(float_row[j].x + 1); \
597                                 y_pixel1 = (int)(float_row[j].y); \
598                                 y_pixel2 = (int)(float_row[j].y + 1); \
599                                 x_fraction1 = float_row[j].x - x_pixel1; \
600                                 x_fraction2 = (float)x_pixel2 - float_row[j].x; \
601                                 y_fraction1 = float_row[j].y - y_pixel1; \
602                                 y_fraction2 = (float)y_pixel2 - float_row[j].y; \
603 /* By trial and error this fraction order seems to work. */ \
604                                 fraction4 = x_fraction1 * y_fraction1; \
605                                 fraction3 = x_fraction2 * y_fraction1; \
606                                 fraction2 = x_fraction1 * y_fraction2; \
607                                 fraction1 = x_fraction2 * y_fraction2; \
608                                 pixel1 =                                                          &input_rows[y_pixel1][x_pixel1 * components]; \
609                                 pixel2 = (x_pixel2 >= width)                       ? zero_pixel : &input_rows[y_pixel1][x_pixel2 * components]; \
610                                 pixel3 = (y_pixel2 >= height)                      ? zero_pixel : &input_rows[y_pixel2][x_pixel1 * components]; \
611                                 pixel4 = (x_pixel2 >= width || y_pixel2 >= height) ? zero_pixel : &input_rows[y_pixel2][x_pixel2 * components]; \
612  \
613                                 for(int m = 0; m < components; m++) \
614                                 { \
615                                         output_rows[i][j * components + m] =  \
616                                                 (type)((pixel1[m] * fraction1) +  \
617                                                         (pixel2[m] * fraction2) +  \
618                                                         (pixel3[m] * fraction3) +  \
619                                                         (pixel4[m] * fraction4)); \
620                                 } \
621                         } \
622                 } \
623         } \
624 }
625
626 int RotateEngine::perform_rotation()
627 {
628         int width = input->get_w();
629         int height = input->get_h();
630
631         if(!interpolate)
632         {
633                 switch(input->get_color_model())
634                 {
635                         case BC_RGB888:
636                                 ROTATE_NEAREST(unsigned char, 3, 0x0);
637                                 break;
638                         case BC_RGB_FLOAT:
639                                 ROTATE_NEAREST(float, 3, 0x0);
640                                 break;
641                         case BC_YUV888:
642                                 ROTATE_NEAREST(unsigned char, 3, 0x80);
643                                 break;
644                         case BC_RGBA8888:
645                                 ROTATE_NEAREST(unsigned char, 4, 0x0);
646                                 break;
647                         case BC_RGBA_FLOAT:
648                                 ROTATE_NEAREST(float, 4, 0x0);
649                                 break;
650                         case BC_YUVA8888:
651                                 ROTATE_NEAREST(unsigned char, 4, 0x80);
652                                 break;
653
654                         case BC_RGB161616:
655                                 ROTATE_NEAREST(uint16_t, 3, 0x0);
656                                 break;
657                         case BC_YUV161616:
658                                 ROTATE_NEAREST(uint16_t, 3, 0x8000);
659                                 break;
660
661                         case BC_RGBA16161616:
662                                 ROTATE_NEAREST(uint16_t, 4, 0x0);
663                                 break;
664                         case BC_YUVA16161616:
665                                 ROTATE_NEAREST(uint16_t, 4, 0x8000);
666                                 break;
667                 }
668         }
669         else
670         {
671                 switch(input->get_color_model())
672                 {
673                         case BC_RGB888:
674                                 ROTATE_INTERPOLATE(unsigned char, 3, 0x0);
675                                 break;
676                         case BC_RGB_FLOAT:
677                                 ROTATE_INTERPOLATE(float, 3, 0x0);
678                                 break;
679                         case BC_YUV888:
680                                 ROTATE_INTERPOLATE(unsigned char, 3, 0x80);
681                                 break;
682                         case BC_RGBA8888:
683                                 ROTATE_INTERPOLATE(unsigned char, 4, 0x0);
684                                 break;
685                         case BC_RGBA_FLOAT:
686                                 ROTATE_INTERPOLATE(float, 4, 0x0);
687                                 break;
688                         case BC_YUVA8888:
689                                 ROTATE_INTERPOLATE(unsigned char, 4, 0x80);
690                                 break;
691
692                         case BC_RGB161616:
693                                 ROTATE_INTERPOLATE(uint16_t, 3, 0x0);
694                                 break;
695                         case BC_YUV161616:
696                                 ROTATE_INTERPOLATE(uint16_t, 3, 0x8000);
697                                 break;
698
699                         case BC_RGBA16161616:
700                                 ROTATE_INTERPOLATE(uint16_t, 4, 0x0);
701                                 break;
702                         case BC_YUVA16161616:
703                                 ROTATE_INTERPOLATE(uint16_t, 4, 0x8000);
704                                 break;
705                 }
706         }
707         return 0;
708 }
709
710 void RotateEngine::run()
711 {
712         while(!done)
713         {
714                 input_lock->lock("RotateEngine::run");
715                 if(done) return;
716
717                 if(do_matrix)
718                 {
719                         create_matrix();
720                 }
721                 else
722                 if(do_rotation)
723                 {
724                         perform_rotation();
725                 }
726
727                 do_matrix = 0;
728                 do_rotation = 0;
729                 output_lock->unlock();
730         }
731 }