no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bctheme.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 "bcresources.h"
23 #include "bctheme.h"
24 #include "bcwindowbase.h"
25 #include "clip.h"
26 #include "language.h"
27 #include "vframe.h"
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 BC_Theme::BC_Theme()
34 {
35         last_image_data = 0;
36         last_image_set = 0;
37         image_sets_start = -1;
38 }
39
40 BC_Theme::~BC_Theme()
41 {
42         image_sets.remove_all_objects();
43         images.remove_all_objects();
44 }
45
46 void BC_Theme::dump()
47 {
48         printf("BC_Theme::dump 1 images=%d\n", images.size());
49         for( int i=0; i<images.size(); ++i ) {
50                 BC_ImageData *image_data = images[i];
51                 printf("%4d. %s %p\n", i, image_data->name, image_data->data);
52         }
53         printf("BC_Theme::dump 2 image_sets=%d\n", image_sets.size());
54         for( int i=0; i<image_sets.size(); ++i ) {
55                 BC_ThemeSet *image_set = image_sets[i];
56                 printf("%4d. %s %p\n", i, image_set->title, image_set->data[0]);
57         }
58 }
59
60 BC_Resources* BC_Theme::get_resources()
61 {
62         return BC_WindowBase::get_resources();
63 }
64
65 // These create single images for storage in the image_sets table.
66 VFrame* BC_Theme::new_image(const char *title, const char *path)
67 {
68         VFrame *existing_image = title[0] ? get_image(title, 0) : 0;
69         if( existing_image ) return existing_image;
70
71         BC_ThemeSet *result = new BC_ThemeSet(1, 0, title);
72         result->data[0] = new VFramePng(get_image_data(path));
73         add_image_set(result);
74         return result->data[0];
75 }
76
77 VFrame* BC_Theme::new_image(const char *path)
78 {
79         return new_image("", path);
80 }
81
82 VFrame* BC_Theme::new_image1(const char *title, const char *path)
83 {
84         VFrame *existing_image = title[0] ? get_image(title, 0) : 0;
85         if( existing_image ) return existing_image;
86
87         BC_ThemeSet *result = new BC_ThemeSet(1, 0, title);
88         result->data[0] = new VFramePng(get_image_data(path), 1.);
89         add_image_set(result);
90         return result->data[0];
91 }
92
93 // These create image sets which are stored in the image_sets table.
94 VFrame** BC_Theme::new_image_set(const char *title, int total, va_list *args)
95 {
96         if( !total ) {
97                 printf("BC_Theme::new_image_set %d %s zero number of images\n",
98                         __LINE__, title);
99         }
100
101         VFrame **existing_image_set = title[0] ? get_image_set(title, 0) : 0;
102         if( existing_image_set ) return existing_image_set;
103
104         BC_ThemeSet *result = new BC_ThemeSet(total, 1, title);
105         add_image_set(result);
106         for( int i=0; i<total; ++i ) {
107                 char *path = va_arg(*args, char*);
108                 result->data[i] = new_image(path);
109         }
110         return result->data;
111 }
112
113 VFrame** BC_Theme::new_image_set_images(const char *title, int total, ...)
114 {
115         va_list list;
116         va_start(list, total);
117         BC_ThemeSet *existing_image_set = title[0] ? get_image_set_object(title) : 0;
118         if( existing_image_set ) {
119                 image_sets.remove_object(existing_image_set);
120                 last_image_set = 0;
121                 last_image_data = 0;
122         }
123
124         BC_ThemeSet *result = new BC_ThemeSet(total, 0, title);
125         add_image_set(result);
126         for( int i=0; i<total; ++i ) {
127                 result->data[i] = va_arg(list, VFrame*);
128         }
129         va_end(list);
130         return result->data;
131 }
132
133 VFrame** BC_Theme::new_image_set(const char *title, int total, ...)
134 {
135         va_list list;
136         va_start(list, total);
137         VFrame **result = new_image_set(title, total, &list);
138         va_end(list);
139
140         return result;
141 }
142
143 VFrame** BC_Theme::new_image_set(int total, ...)
144 {
145         va_list list;
146         va_start(list, total);
147         VFrame **result = new_image_set("", total, &list);
148         va_end(list);
149
150         return result;
151 }
152
153 void BC_Theme::add_image_set(BC_ThemeSet *image_set)
154 {
155         image_sets.append(image_set);
156         if( image_sets_start >= 0 ) {
157                 printf("BC_Theme::add_image_set image_sets unsorted, lookups degraded\n");
158                 image_sets_start = -1;
159         }
160 }
161
162 int BC_Theme::image_set_cmpr(const void *ap, const void *bp)
163 {
164         BC_ThemeSet*a = *(BC_ThemeSet**)ap, *b = *(BC_ThemeSet**)bp;
165         return strcmp(a->title, b->title);
166 }
167
168 void BC_Theme::sort_image_sets()
169 {
170         if( image_sets_start >= 0 ) return;
171         qsort(&image_sets[0], image_sets.size(), sizeof(image_sets[0]), image_set_cmpr);
172 // skip over un-titled image sets
173         int i = 0, n = image_sets.size();
174         while( i<n && !image_sets[i]->title[0] ) ++i;
175         image_sets_start = i;
176 }
177
178 BC_ThemeSet* BC_Theme::get_image_set_object(const char *title)
179 {
180         if( last_image_set && !strcmp(title,last_image_set->title) )
181                 return last_image_set;
182
183         if( image_sets_start >= 0 ) {
184 // binary search for image set
185                 int r = image_sets.size(), l = image_sets_start-1;
186                 int m = 0, v = -1;
187                 while( r-l > 1 ) {
188                         m = (l + r) / 2;
189                         BC_ThemeSet *image_set = image_sets[m];
190                         if( !(v=strcmp(title, image_set->title)) )
191                                 return last_image_set = image_set;
192                         if( v > 0 ) l = m; else r = m;
193                 }
194         }
195         else {
196 // compare title[0],title[1] for faster prefix test
197                 const unsigned char *tp = (const unsigned char*)title;
198                 unsigned short tval = tp[0];
199                 if( tval ) tval |= (tp[1] << 8);
200
201                 for( int i=0; i<image_sets.size(); ++i ) {
202                         tp = (const unsigned char *)image_sets[i]->title;
203                         unsigned short val = tp[0];
204                         if( val ) val |= (tp[1] << 8);
205                         if( val != tval ) continue;
206                         if( !strcmp((const char *)tp, title) )
207                                 return last_image_set = image_sets[i];
208                 }
209         }
210
211         return 0;
212 }
213
214 VFrame* BC_Theme::get_image(const char *title, int use_default)
215 {
216         BC_ThemeSet* tsp = get_image_set_object(title);
217         if( tsp ) return tsp->data[0];
218
219
220 // Return the first image it can find.  This should always work.
221         if( use_default ) {
222                 printf("BC_Theme::get_image: image \"%s\" not found.\n",
223                         title);
224                 if( image_sets.size() )
225                         return image_sets[0]->data[0];
226         }
227
228 // Give up and go to a movie.
229         return 0;
230 }
231
232 VFrame** BC_Theme::get_image_set(const char *title, int use_default)
233 {
234         BC_ThemeSet* tsp = get_image_set_object(title);
235         if( tsp ) return tsp->data;
236
237 // Get the image set with the largest number of images.
238         if( use_default ) {
239                 printf("BC_Theme::get_image_set: image set \"%s\" not found.\n",
240                         title);
241                 int max_total = 0;
242                 int max_number = -1;
243                 for( int i=0; i<image_sets.size(); ++i ) {
244                         if( image_sets[i]->total > max_total ) {
245                                 max_total = image_sets[i]->total;
246                                 max_number = i;
247                         }
248                 }
249
250                 if( max_number >= 0 )
251                         return image_sets[max_number]->data;
252         }
253
254 // Give up and go to a movie
255         return 0;
256 }
257
258
259
260
261
262
263
264
265
266
267
268 VFrame** BC_Theme::new_button(const char *overlay_path,
269         const char *up_path,
270         const char *hi_path,
271         const char *dn_path,
272         const char *title)
273 {
274         VFramePng default_data(get_image_data(overlay_path));
275         BC_ThemeSet *result = new BC_ThemeSet(3, 1, title ? title : "");
276         if( title ) add_image_set(result);
277
278         result->data[0] = new_image(up_path);
279         result->data[1] = new_image(hi_path);
280         result->data[2] = new_image(dn_path);
281         for( int i=0; i<3; ++i ) {
282                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
283         }
284         return result->data;
285 }
286
287
288 VFrame** BC_Theme::new_button4(const char *overlay_path,
289         const char *up_path,
290         const char *hi_path,
291         const char *dn_path,
292         const char *disabled_path,
293         const char *title)
294 {
295         VFramePng default_data(get_image_data(overlay_path));
296         BC_ThemeSet *result = new BC_ThemeSet(4, 1, title ? title : "");
297         if( title ) add_image_set(result);
298
299         result->data[0] = new_image(up_path);
300         result->data[1] = new_image(hi_path);
301         result->data[2] = new_image(dn_path);
302         result->data[3] = new_image(disabled_path);
303         for( int i=0; i<4; ++i ) {
304                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
305         }
306         return result->data;
307 }
308
309
310 VFrame** BC_Theme::new_button(const char *overlay_path,
311         VFrame *up,
312         VFrame *hi,
313         VFrame *dn,
314         const char *title)
315 {
316         VFramePng default_data(get_image_data(overlay_path));
317         BC_ThemeSet *result = new BC_ThemeSet(3, 0, title ? title : "");
318         if( title ) add_image_set(result);
319
320         result->data[0] = new VFrame(*up);
321         result->data[1] = new VFrame(*hi);
322         result->data[2] = new VFrame(*dn);
323         for( int i=0; i<3; ++i )
324                 overlay(result->data[i], &default_data, -1, -1, (i == 2));
325         return result->data;
326 }
327
328
329 VFrame** BC_Theme::new_toggle(const char *overlay_path,
330         const char *up_path,
331         const char *hi_path,
332         const char *checked_path,
333         const char *dn_path,
334         const char *checkedhi_path,
335         const char *title)
336 {
337         VFramePng default_data(get_image_data(overlay_path));
338         BC_ThemeSet *result = new BC_ThemeSet(5, 1, title ? title : "");
339         if( title ) add_image_set(result);
340
341         result->data[0] = new_image(up_path);
342         result->data[1] = new_image(hi_path);
343         result->data[2] = new_image(checked_path);
344         result->data[3] = new_image(dn_path);
345         result->data[4] = new_image(checkedhi_path);
346         for( int i=0; i<5; ++i )
347                 overlay(result->data[i], &default_data, -1, -1, (i == 3));
348         return result->data;
349 }
350
351 VFrame** BC_Theme::new_toggle(const char *overlay_path,
352         VFrame *up,
353         VFrame *hi,
354         VFrame *checked,
355         VFrame *dn,
356         VFrame *checkedhi,
357         const char *title)
358 {
359         VFramePng default_data(get_image_data(overlay_path));
360         BC_ThemeSet *result = new BC_ThemeSet(5, 0, title ? title : "");
361         if( title ) add_image_set(result);
362
363         result->data[0] = new VFrame(*up);
364         result->data[1] = new VFrame(*hi);
365         result->data[2] = new VFrame(*checked);
366         result->data[3] = new VFrame(*dn);
367         result->data[4] = new VFrame(*checkedhi);
368         for( int i=0; i<5; ++i )
369                 overlay(result->data[i], &default_data, -1, -1, (i == 3));
370         return result->data;
371 }
372
373 void BC_Theme::overlay(VFrame *dst, VFrame *src, int in_x1, int in_x2, int shift)
374 {
375         int w;
376         int h;
377         unsigned char **in_rows;
378         unsigned char **out_rows;
379
380         if( in_x1 < 0 ) {
381                 w = MIN(src->get_w(), dst->get_w());
382                 h = MIN(dst->get_h(), src->get_h());
383                 in_x1 = 0;
384                 in_x2 = w;
385         }
386         else {
387                 w = in_x2 - in_x1;
388                 h = MIN(dst->get_h(), src->get_h());
389         }
390         in_rows = src->get_rows();
391         out_rows = dst->get_rows();
392
393         switch( src->get_color_model() )
394         {
395                 case BC_RGBA8888:
396                         switch( dst->get_color_model() )
397                         {
398                                 case BC_RGBA8888:
399                                         for( int i=shift; i<h; ++i ) {
400                                                 unsigned char *in_row = 0;
401                                                 unsigned char *out_row;
402
403                                                 if( !shift ) {
404                                                         in_row = in_rows[i] + in_x1 * 4;
405                                                         out_row = out_rows[i];
406                                                 }
407                                                 else {
408                                                         in_row = in_rows[i - 1] + in_x1 * 4;
409                                                         out_row = out_rows[i] + 4;
410                                                 }
411
412                                                 for( int j=shift; j<w; ++j ) {
413                                                         int opacity = in_row[3];
414                                                         int transparency = 0xff - opacity;
415
416                                                         out_row[0] = (in_row[0] * opacity + out_row[0] * transparency) / 0xff;
417                                                         out_row[1] = (in_row[1] * opacity + out_row[1] * transparency) / 0xff;
418                                                         out_row[2] = (in_row[2] * opacity + out_row[2] * transparency) / 0xff;
419                                                         out_row[3] = MAX(in_row[3], out_row[3]);
420                                                         out_row += 4;
421                                                         in_row += 4;
422                                                 }
423                                         }
424                                         break;
425
426                                 case BC_RGB888:
427                                         for( int i=shift; i<h; ++i ) {
428                                                 unsigned char *in_row;
429                                                 unsigned char *out_row = out_rows[i];
430
431                                                 if( !shift ) {
432                                                         in_row = in_rows[i] + in_x1 * 3;
433                                                         out_row = out_rows[i];
434                                                 }
435                                                 else {
436                                                         in_row = in_rows[i - 1] + in_x1 * 3;
437                                                         out_row = out_rows[i] + 3;
438                                                 }
439
440                                                 for( int j=shift; j<w; ++j ) {
441                                                         int opacity = in_row[3];
442                                                         int transparency = 0xff - opacity;
443                                                         out_row[0] = (in_row[0] * opacity + out_row[0] * transparency) / 0xff;
444                                                         out_row[1] = (in_row[1] * opacity + out_row[1] * transparency) / 0xff;
445                                                         out_row[2] = (in_row[2] * opacity + out_row[2] * transparency) / 0xff;
446                                                         out_row += 3;
447                                                         in_row += 4;
448                                                 }
449                                         }
450                                         break;
451                         }
452                         break;
453         }
454 }
455
456 void BC_Theme::set_data(unsigned char *ptr)
457 {
458         int hdr_sz = *(int*)ptr - sizeof(int);
459         unsigned char *cp = ptr + sizeof(int);
460         unsigned char *dp = cp + hdr_sz;
461         int start_item = images.size();
462
463         while( cp < dp ) {
464                 char *nm = (char *)cp;
465                 while( cp < dp && *cp++ );
466                 if( cp + sizeof(unsigned) > dp ) break;
467                 unsigned ofs = 0;
468                 for( int i=sizeof(unsigned); --i>=0; ofs|=cp[i] ) ofs <<= 8;
469                 images.append(new BC_ImageData(nm, dp+ofs));
470                 cp += sizeof(unsigned);
471         }
472
473         int items = images.size() - start_item;
474         data_items.append(items);
475         qsort(&images[start_item], items, sizeof(images[0]), images_cmpr);
476 }
477
478 int BC_Theme::images_cmpr(const void *ap, const void *bp)
479 {
480         BC_ImageData *a = *(BC_ImageData**)ap, *b = *(BC_ImageData**)bp;
481         return strcmp(a->name, b->name);
482 }
483
484 unsigned char* BC_Theme::get_image_data(const char *name, int log_errs)
485 {
486 // Image is the same as the last one
487         if( last_image_data && !strcmp(last_image_data->name, name) )
488                 return last_image_data->data;
489
490 // look forwards thru data sets for name
491         int start_item = 0;
492         for( int i=0,n=data_items.size(); i<n; ++i ) {
493                 int end_item = start_item + data_items[i];
494                 int r = end_item, l = start_item-1;
495 // binary search for image
496                 int m = 0, v = -1;
497                 while( r-l > 1 ) {
498                         m = (l + r) / 2;
499                         BC_ImageData *image_data = images[m];
500                         if( !(v=strcmp(name, image_data->name)) ) {
501                                 image_data->used = 1;
502                                 last_image_data = image_data;
503                                 return image_data->data;
504                         }
505                         if( v > 0 ) l = m; else r = m;
506                 }
507                 start_item = end_item;
508         }
509
510         if( log_errs )
511                 fprintf(stderr, _("Theme::get_image: %s not found.\n"), name);
512         return 0;
513 }
514
515 void BC_Theme::check_used()
516 {
517 // Can't use because some images are gotten the old fashioned way.
518 return;
519         int got_it = 0;
520         for( int i=0; i<images.size(); ++i ) {
521                 if( !images[i]->used ) {
522                         if( !got_it ) printf(_("BC_Theme::check_used: Images aren't used.\n"));
523                         printf("%s ", images[i]->name);
524                         got_it = 1;
525                 }
526         }
527         if( got_it ) printf("\n");
528 }
529
530
531 BC_ThemeSet::BC_ThemeSet(int total, int is_reference, const char *title)
532 {
533         this->total = total;
534         this->title = new char[strlen(title) + 1];
535         strcpy(this->title, title);
536         this->is_reference = is_reference;
537         data = new VFrame*[total];
538 }
539
540 BC_ThemeSet::~BC_ThemeSet()
541 {
542         if( data ) {
543                 if( !is_reference ) {
544                         for( int i = 0; i < total; i++ )
545                                 delete data[i];
546                 }
547
548                 delete [] data;
549         }
550
551         delete [] title;
552 }
553