Merge branch 'ct' of git.pipapo.org:cinelerra-ct into ct
[cinelerra_cv/ct.git] / guicast / vframe.C
blobba31a88216da40404b2e3a1b3a5fb8f53e50cc76
1 #include <png.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdint.h>
6 #include "bchash.h"
7 #include "bcpbuffer.h"
8 #include "bcsignals.h"
9 #include "bcsynchronous.h"
10 #include "bctexture.h"
11 #include "bcwindowbase.h"
12 #include "clip.h"
13 #include "colormodels.h"
14 #include "vframe.h"
16 class PngReadFunction
18 public:
19         static void png_read_function(png_structp png_ptr,
20                    png_bytep data, 
21                                    png_size_t length)
22         {
23                 VFrame *frame = (VFrame*)png_get_io_ptr(png_ptr);
24                 if(frame->image_size - frame->image_offset < length) 
25                         length = frame->image_size - frame->image_offset;
27                 memcpy(data, &frame->image[frame->image_offset], length);
28                 frame->image_offset += length;
29         };
38 //static BCCounter counter;
41 VFrame::VFrame(unsigned char *png_data)
43         reset_parameters(1);
44         params = new BC_Hash;
45         read_png(png_data);
48 VFrame::VFrame(VFrame &frame)
50         reset_parameters(1);
51         params = new BC_Hash;
52         allocate_data(0, 0, 0, 0, frame.w, frame.h, frame.color_model, frame.bytes_per_line);
53         memcpy(data, frame.data, bytes_per_line * h);
54         copy_stacks(&frame);
57 VFrame::VFrame(unsigned char *data, 
58         int w, 
59         int h, 
60         int color_model, 
61         long bytes_per_line)
63         reset_parameters(1);
64         params = new BC_Hash;
65         allocate_data(data, 0, 0, 0, w, h, color_model, bytes_per_line);
68 VFrame::VFrame(unsigned char *data, 
69                 long y_offset,
70                 long u_offset,
71                 long v_offset, 
72                 int w, 
73                 int h, 
74                 int color_model, 
75                 long bytes_per_line)
77         reset_parameters(1);
78         params = new BC_Hash;
79         allocate_data(data, 
80                 y_offset, 
81                 u_offset, 
82                 v_offset, 
83                 w, 
84                 h, 
85                 color_model, 
86                 bytes_per_line);
89 VFrame::VFrame()
91         reset_parameters(1);
92         params = new BC_Hash;
93         this->color_model = BC_COMPRESSED;
106 VFrame::~VFrame()
108         clear_objects(1);
109 // Delete effect stack
110         prev_effects.remove_all_objects();
111         next_effects.remove_all_objects();
112         delete params;
115 int VFrame::equivalent(VFrame *src, int test_stacks)
117         return (src->get_color_model() == get_color_model() &&
118                 src->get_w() == get_w() &&
119                 src->get_h() == get_h() &&
120                 src->bytes_per_line == bytes_per_line &&
121                 (!test_stacks || equal_stacks(src)));
124 long VFrame::set_shm_offset(long offset)
126         shm_offset = offset;
127         return 0;
130 long VFrame::get_shm_offset()
132         return shm_offset;
135 int VFrame::get_shared()
137         return shared;
140 int VFrame::params_match(int w, int h, int color_model)
142         return (this->w == w &&
143                 this->h == h &&
144                 this->color_model == color_model);
148 int VFrame::reset_parameters(int do_opengl)
150         field2_offset = -1;
151         shared = 0;
152         shm_offset = 0;
153         bytes_per_line = 0;
154         data = 0;
155         rows = 0;
156         color_model = 0;
157         compressed_allocated = 0;
158         compressed_size = 0;   // Size of current image
159         w = 0;
160         h = 0;
161         y = u = v = 0;
162         y_offset = 0;
163         u_offset = 0;
164         v_offset = 0;
165         sequence_number = -1;
166         is_keyframe = 0;
168         if(do_opengl)
169         {
170 // By default, anything is going to be done in RAM
171                 opengl_state = VFrame::RAM;
172                 pbuffer = 0;
173                 texture = 0;
174         }
176         prev_effects.set_array_delete();
177         next_effects.set_array_delete();
178         return 0;
181 int VFrame::clear_objects(int do_opengl)
183 // Remove texture
184         if(do_opengl)
185         {
186                 delete texture;
187                 texture = 0;
189                 delete pbuffer;
190                 pbuffer = 0;
191         }
193 // Delete data
194         if(!shared)
195         {
197 // Memory check
198 //int size = calculate_data_size(this->w, this->h, this->bytes_per_line, this->color_model);
199 //if(size > 2560 * 1920)
200 UNBUFFER(data);
201                 if(data) delete [] data;
202                 data = 0;
203         }
205 // Delete row pointers
206         switch(color_model)
207         {
208                 case BC_COMPRESSED:
209                 case BC_YUV420P:
210                         break;
212                 default:
213                         delete [] rows;
214                         break;
215         }
218         return 0;
221 int VFrame::get_field2_offset()
223         return field2_offset;
226 int VFrame::set_field2_offset(int value)
228         this->field2_offset = value;
229         return 0;
232 void VFrame::set_keyframe(int value)
234         this->is_keyframe = value;
237 int VFrame::get_keyframe()
239         return is_keyframe;
243 int VFrame::calculate_bytes_per_pixel(int color_model)
245         return cmodel_calculate_pixelsize(color_model);
248 long VFrame::get_bytes_per_line()
250         return bytes_per_line;
253 long VFrame::get_data_size()
255         return calculate_data_size(w, h, bytes_per_line, color_model) - 4;
256 //      return h * bytes_per_line;
259 long VFrame::calculate_data_size(int w, int h, int bytes_per_line, int color_model)
261         return cmodel_calculate_datasize(w, h, bytes_per_line, color_model);
262         return 0;
265 void VFrame::create_row_pointers()
267         switch(color_model)
268         {
269                 case BC_YUV420P:
270                 case BC_YUV411P:
271                         if(!this->v_offset)
272                         {
273                                 this->y_offset = 0;
274                                 this->u_offset = w * h;
275                                 this->v_offset = w * h + w * h / 4;
276                         }
277                         y = this->data + this->y_offset;
278                         u = this->data + this->u_offset;
279                         v = this->data + this->v_offset;
280                         break;
282                 case BC_YUV422P:
283                         if(!this->v_offset)
284                         {
285                                 this->y_offset = 0;
286                                 this->u_offset = w * h;
287                                 this->v_offset = w * h + w * h / 2;
288                         }
289                         y = this->data + this->y_offset;
290                         u = this->data + this->u_offset;
291                         v = this->data + this->v_offset;
292                         break;
294                 default:
295                         rows = new unsigned char*[h];
296                         for(int i = 0; i < h; i++)
297                         {
298                                 rows[i] = &this->data[i * this->bytes_per_line];
299                         }
300                         break;
301         }
304 int VFrame::allocate_data(unsigned char *data, 
305         long y_offset,
306         long u_offset,
307         long v_offset,
308         int w, 
309         int h, 
310         int color_model, 
311         long bytes_per_line)
313         this->w = w;
314         this->h = h;
315         this->color_model = color_model;
316         this->bytes_per_pixel = calculate_bytes_per_pixel(color_model);
317         this->y_offset = this->u_offset = this->v_offset = 0;
319         if(bytes_per_line >= 0)
320         {
321                 this->bytes_per_line = bytes_per_line;
322         }
323         else
324                 this->bytes_per_line = this->bytes_per_pixel * w;
326 // Allocate data + padding for MMX
327         if(data)
328         {
329                 shared = 1;
330                 this->data = data;
331                 this->y_offset = y_offset;
332                 this->u_offset = u_offset;
333                 this->v_offset = v_offset;
334         }
335         else
336         {
337                 shared = 0;
338                 int size = calculate_data_size(this->w, 
339                         this->h, 
340                         this->bytes_per_line, 
341                         this->color_model);
342                 this->data = new unsigned char[size];
344 // Memory check
345 //if(size >= 720 * 480 * 3)
346 //BUFFER2(this->data, "VFrame::allocate_data");
348 if(!this->data)
349 printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
351 //printf("VFrame::allocate_data %p %d %d\n", this, this->w, this->h);
352 //if(size > 1000000) printf("VFrame::allocate_data %d\n", size);
353         }
355 // Create row pointers
356         create_row_pointers();
357         return 0;
360 void VFrame::set_memory(unsigned char *data, 
361                 long y_offset,
362                 long u_offset,
363                 long v_offset)
365         shared = 1;
366         this->data = data;
367         this->y_offset = y_offset;
368         this->u_offset = u_offset;
369         this->v_offset = v_offset;
370         y = this->data + this->y_offset;
371         u = this->data + this->u_offset;
372         v = this->data + this->v_offset;
373         create_row_pointers();
376 void VFrame::set_compressed_memory(unsigned char *data,
377         int data_size,
378         int data_allocated)
380         clear_objects(0);
381         shared = 1;
382         this->data = data;
383         this->compressed_allocated = data_allocated;
384         this->compressed_size = data_size;
388 // Reallocate uncompressed buffer with or without alpha
389 int VFrame::reallocate(unsigned char *data, 
390                 long y_offset,
391                 long u_offset,
392                 long v_offset,
393                 int w, 
394                 int h, 
395                 int color_model, 
396                 long bytes_per_line)
398         clear_objects(0);
399         reset_parameters(0);
400         allocate_data(data, 
401                 y_offset, 
402                 u_offset, 
403                 v_offset, 
404                 w, 
405                 h, 
406                 color_model, 
407                 bytes_per_line);
408         return 0;
411 int VFrame::allocate_compressed_data(long bytes)
413         if(bytes < 1) return 1;
415 // Want to preserve original contents
416         if(data && compressed_allocated < bytes)
417         {
418                 unsigned char *new_data = new unsigned char[bytes];
419                 bcopy(data, new_data, compressed_allocated);
420 UNBUFFER(data);
421                 delete [] data;
422                 data = new_data;
423                 compressed_allocated = bytes;
424         }
425         else
426         if(!data)
427         {
428                 data = new unsigned char[bytes];
429                 compressed_allocated = bytes;
430                 compressed_size = 0;
431         }
433         return 0;
436 int VFrame::read_png(unsigned char *data)
438         png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
439         png_infop info_ptr = png_create_info_struct(png_ptr);
440         int new_color_model;
442         image_offset = 0;
443         image = data + 4;
444         image_size = (((unsigned long)data[0]) << 24) | 
445                 (((unsigned long)data[1]) << 16) | 
446                 (((unsigned long)data[2]) << 8) | 
447                 (unsigned char)data[3];
448         png_set_read_fn(png_ptr, this, PngReadFunction::png_read_function);
449         png_read_info(png_ptr, info_ptr);
451         w = png_get_image_width(png_ptr, info_ptr);
452         h = png_get_image_height(png_ptr, info_ptr);
454         int src_color_model = png_get_color_type(png_ptr, info_ptr);
455         switch(src_color_model)
456         {
457                 case PNG_COLOR_TYPE_RGB:
458                         new_color_model = BC_RGB888;
459                         break;
462                 case PNG_COLOR_TYPE_GRAY_ALPHA:
463                 case PNG_COLOR_TYPE_RGB_ALPHA:
464                 default:
465                         new_color_model = BC_RGBA8888;
466                         break;
467         }
469         reallocate(NULL, 
470                 0, 
471                 0, 
472                 0, 
473                 w, 
474                 h, 
475                 new_color_model,
476                 -1);
478         png_read_image(png_ptr, get_rows());
482         if(src_color_model == PNG_COLOR_TYPE_GRAY_ALPHA)
483         {
484                 for(int i = 0; i < get_h(); i++)
485                 {
486                         unsigned char *row = get_rows()[i];
487                         unsigned char *out_ptr = row + get_w() * 4 - 4;
488                         unsigned char *in_ptr = row + get_w() * 2 - 2;
490                         for(int j = get_w() - 1; j >= 0; j--)
491                         {
492                                 out_ptr[0] = in_ptr[0];
493                                 out_ptr[1] = in_ptr[0];
494                                 out_ptr[2] = in_ptr[0];
495                                 out_ptr[3] = in_ptr[1];
496                                 out_ptr -= 4;
497                                 in_ptr -= 2;
498                         }
499                 }
500         }
502         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
503         return 0;
506 unsigned char* VFrame::get_data()
508         return data;
511 long VFrame::get_compressed_allocated()
513         return compressed_allocated;
516 long VFrame::get_compressed_size()
518         return compressed_size;
521 long VFrame::set_compressed_size(long size)
523         compressed_size = size;
524         return 0;
527 int VFrame::get_color_model()
529         return color_model;
533 int VFrame::equals(VFrame *frame)
535         if(frame->data == data) 
536                 return 1;
537         else
538                 return 0;
541 #define ZERO_YUV(components, type, max) \
542 { \
543         for(int i = 0; i < h; i++) \
544         { \
545                 type *row = (type*)get_rows()[i]; \
546                 for(int j = 0; j < w; j++) \
547                 { \
548                         row[j * components] = 0; \
549                         row[j * components + 1] = (max + 1) / 2; \
550                         row[j * components + 2] = (max + 1) / 2; \
551                         if(components == 4) row[j * components + 3] = 0; \
552                 } \
553         } \
556 int VFrame::clear_frame()
558         switch(color_model)
559         {
560                 case BC_COMPRESSED:
561                         break;
563                 case BC_YUV420P:
564                         bzero(data, h * w * 2);
565                         break;
567                 case BC_YUV888:
568                         ZERO_YUV(3, unsigned char, 0xff);
569                         break;
570                 
571                 case BC_YUVA8888:
572                         ZERO_YUV(4, unsigned char, 0xff);
573                         break;
575                 case BC_YUV161616:
576                         ZERO_YUV(3, uint16_t, 0xffff);
577                         break;
578                 
579                 case BC_YUVA16161616:
580                         ZERO_YUV(4, uint16_t, 0xffff);
581                         break;
582                 
583                 default:
584                         bzero(data, h * bytes_per_line);
585                         break;
586         }
587         return 0;
590 void VFrame::rotate90()
592 // Allocate new frame
593         int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
594         unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
595         unsigned char **new_rows = new unsigned char*[new_h];
596         for(int i = 0; i < new_h; i++)
597                 new_rows[i] = &new_data[new_bytes_per_line * i];
599 // Copy data
600         for(int in_y = 0, out_x = new_w - 1; in_y < h; in_y++, out_x--)
601         {
602                 for(int in_x = 0, out_y = 0; in_x < w; in_x++, out_y++)
603                 {
604                         for(int k = 0; k < bytes_per_pixel; k++)
605                         {
606                                 new_rows[out_y][out_x * bytes_per_pixel + k] = 
607                                         rows[in_y][in_x * bytes_per_pixel + k];
608                         }
609                 }
610         }
612 // Swap frames
613         clear_objects(0);
614         data = new_data;
615         rows = new_rows;
616         bytes_per_line = new_bytes_per_line;
617         w = new_w;
618         h = new_h;
621 void VFrame::rotate270()
623 // Allocate new frame
624         int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
625         unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
626         unsigned char **new_rows = new unsigned char*[new_h];
627         for(int i = 0; i < new_h; i++)
628                 new_rows[i] = &new_data[new_bytes_per_line * i];
630 // Copy data
631         for(int in_y = 0, out_x = 0; in_y < h; in_y++, out_x++)
632         {
633                 for(int in_x = 0, out_y = new_h - 1; in_x < w; in_x++, out_y--)
634                 {
635                         for(int k = 0; k < bytes_per_pixel; k++)
636                         {
637                                 new_rows[out_y][out_x * bytes_per_pixel + k] = 
638                                         rows[in_y][in_x * bytes_per_pixel + k];
639                         }
640                 }
641         }
643 // Swap frames
644         clear_objects(0);
645         data = new_data;
646         rows = new_rows;
647         bytes_per_line = new_bytes_per_line;
648         w = new_w;
649         h = new_h;
652 void VFrame::flip_vert()
654         unsigned char *temp = new unsigned char[bytes_per_line];
655         for(int i = 0, j = h - 1; i < j; i++, j--)
656         {
657                 memcpy(temp, rows[j], bytes_per_line);
658                 memcpy(rows[j], rows[i], bytes_per_line);
659                 memcpy(rows[i], temp, bytes_per_line);
660         }
661         delete [] temp;
666 int VFrame::copy_from(VFrame *frame)
668         int w = MIN(this->w, frame->get_w());
669         int h = MIN(this->h, frame->get_h());
670         
672         switch(frame->color_model)
673         {
674                 case BC_COMPRESSED:
675                         allocate_compressed_data(frame->compressed_size);
676                         memcpy(data, frame->data, frame->compressed_size);
677                         this->compressed_size = frame->compressed_size;
678                         break;
680                 case BC_YUV420P:
681 //printf("%d %d %p %p %p %p %p %p\n", w, h, get_y(), get_u(), get_v(), frame->get_y(), frame->get_u(), frame->get_v());
682                         memcpy(get_y(), frame->get_y(), w * h);
683                         memcpy(get_u(), frame->get_u(), w * h / 4);
684                         memcpy(get_v(), frame->get_v(), w * h / 4);
685                         break;
687                 case BC_YUV422P:
688 //printf("%d %d %p %p %p %p %p %p\n", w, h, get_y(), get_u(), get_v(), frame->get_y(), frame->get_u(), frame->get_v());
689                         memcpy(get_y(), frame->get_y(), w * h);
690                         memcpy(get_u(), frame->get_u(), w * h / 2);
691                         memcpy(get_v(), frame->get_v(), w * h / 2);
692                         break;
694                 default:
695 // printf("VFrame::copy_from %d\n", calculate_data_size(w, 
696 //                              h, 
697 //                              -1, 
698 //                              frame->color_model));
699                         memcpy(data, frame->data, calculate_data_size(w, 
700                                 h, 
701                                 -1, 
702                                 frame->color_model));
703                         break;
704         }
706         return 0;
710 #define OVERLAY(type, max, components) \
711 { \
712         type **in_rows = (type**)src->get_rows(); \
713         type **out_rows = (type**)get_rows(); \
714         int in_w = src->get_w(); \
715         int in_h = src->get_h(); \
717         for(int i = 0; i < in_h; i++) \
718         { \
719                 if(i + out_y1 >= 0 && i + out_y1 < h) \
720                 { \
721                         type *src_row = in_rows[i]; \
722                         type *dst_row = out_rows[i + out_y1] + out_x1 * components; \
724                         for(int j = 0; j < in_w; j++) \
725                         { \
726                                 if(j + out_x1 >= 0 && j + out_x1 < w) \
727                                 { \
728                                         int opacity = src_row[3]; \
729                                         int transparency = max - src_row[3]; \
730                                         dst_row[0] = (transparency * dst_row[0] + opacity * src_row[0]) / max; \
731                                         dst_row[1] = (transparency * dst_row[1] + opacity * src_row[1]) / max; \
732                                         dst_row[2] = (transparency * dst_row[2] + opacity * src_row[2]) / max; \
733                                         dst_row[3] = MAX(dst_row[3], src_row[3]); \
734                                 } \
736                                 dst_row += components; \
737                                 src_row += components; \
738                         } \
739                 } \
740         } \
744 void VFrame::overlay(VFrame *src, 
745                 int out_x1, 
746                 int out_y1)
748         switch(get_color_model())
749         {
750                 case BC_RGBA8888:
751                         OVERLAY(unsigned char, 0xff, 4);
752                         break;
753         }
758 int VFrame::get_scale_tables(int *column_table, int *row_table, 
759                         int in_x1, int in_y1, int in_x2, int in_y2,
760                         int out_x1, int out_y1, int out_x2, int out_y2)
762         int y_out, i;
763         float w_in = in_x2 - in_x1;
764         float h_in = in_y2 - in_y1;
765         int w_out = out_x2 - out_x1;
766         int h_out = out_y2 - out_y1;
768         float hscale = w_in / w_out;
769         float vscale = h_in / h_out;
771         for(i = 0; i < w_out; i++)
772         {
773                 column_table[i] = (int)(hscale * i);
774         }
776         for(i = 0; i < h_out; i++)
777         {
778                 row_table[i] = (int)(vscale * i) + in_y1;
779         }
780         return 0;
783 int VFrame::get_bytes_per_pixel()
785         return bytes_per_pixel;
788 unsigned char** VFrame::get_rows()
790         if(rows)
791         {
792                 return rows;
793         }
794         return 0;
797 int VFrame::get_w()
799         return w;
802 int VFrame::get_h()
804         return h;
807 int VFrame::get_w_fixed()
809         return w - 1;
812 int VFrame::get_h_fixed()
814         return h - 1;
817 unsigned char* VFrame::get_y()
819         return y;
822 unsigned char* VFrame::get_u()
824         return u;
827 unsigned char* VFrame::get_v()
829         return v;
832 void VFrame::set_number(long number)
834         sequence_number = number;
837 long VFrame::get_number()
839         return sequence_number;
842 void VFrame::push_prev_effect(char *name)
844         char *ptr;
845         prev_effects.append(ptr = new char[strlen(name) + 1]);
846         strcpy(ptr, name);
847         if(prev_effects.total > MAX_STACK_ELEMENTS) prev_effects.remove_object(0);
850 void VFrame::pop_prev_effect()
852         if(prev_effects.total)
853                 prev_effects.remove_object(prev_effects.last());
856 void VFrame::push_next_effect(char *name)
858         char *ptr;
859         next_effects.append(ptr = new char[strlen(name) + 1]);
860         strcpy(ptr, name);
861         if(next_effects.total > MAX_STACK_ELEMENTS) next_effects.remove_object(0);
864 void VFrame::pop_next_effect()
866         if(next_effects.total)
867                 next_effects.remove_object(next_effects.last());
870 char* VFrame::get_next_effect(int number)
872         if(!next_effects.total) return "";
873         else
874         if(number > next_effects.total - 1) number = next_effects.total - 1;
876         return next_effects.values[next_effects.total - number - 1];
879 char* VFrame::get_prev_effect(int number)
881         if(!prev_effects.total) return "";
882         else
883         if(number > prev_effects.total - 1) number = prev_effects.total - 1;
885         return prev_effects.values[prev_effects.total - number - 1];
888 BC_Hash* VFrame::get_params()
890         return params;
893 void VFrame::clear_stacks()
895         next_effects.remove_all_objects();
896         prev_effects.remove_all_objects();
897         delete params;
898         params = new BC_Hash;
901 void VFrame::copy_params(VFrame *src)
903         params->copy_from(src->params);
906 void VFrame::copy_stacks(VFrame *src)
908         clear_stacks();
910         for(int i = 0; i < src->next_effects.total; i++)
911         {
912                 char *ptr;
913                 next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]);
914                 strcpy(ptr, src->next_effects.values[i]);
915         }
916         for(int i = 0; i < src->prev_effects.total; i++)
917         {
918                 char *ptr;
919                 prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]);
920                 strcpy(ptr, src->prev_effects.values[i]);
921         }
923         params->copy_from(src->params);
926 int VFrame::equal_stacks(VFrame *src)
928         for(int i = 0; i < src->next_effects.total && i < next_effects.total; i++)
929         {
930                 if(strcmp(src->next_effects.values[i], next_effects.values[i])) return 0;
931         }
932         for(int i = 0; i < src->prev_effects.total && i < prev_effects.total; i++)
933         {
934                 if(strcmp(src->prev_effects.values[i], prev_effects.values[i])) return 0;
935         }
936         if(!params->equivalent(src->params)) return 0;
937         return 1;
940 void VFrame::dump_stacks()
942         printf("VFrame::dump_stacks\n");
943         printf("        next_effects:\n");
944         for(int i = next_effects.total - 1; i >= 0; i--)
945                 printf("                %s\n", next_effects.values[i]);
946         printf("        prev_effects:\n");
947         for(int i = prev_effects.total - 1; i >= 0; i--)
948                 printf("                %s\n", prev_effects.values[i]);
951 void VFrame::dump_params()
953         params->dump();
959 //      Local Variables:
960 //      mode: C++
961 //      c-file-style: "linux"
962 //      End: