It appears Solaris's cc is ignoring the signedness of bitfield types.
[xiph/unicode.git] / sushivision / plane-2d.c
blob5302d30e0624e613c0eb338ab461841e64220ce3
1 /*
3 * sushivision copyright (C) 2006-2007 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define _GNU_SOURCE
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <math.h>
28 #include <signal.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <gtk/gtk.h>
32 #include <cairo-ft.h>
33 #include "internal.h"
35 static inline swapp(void **a, void **b){
36 void *tmp=*a;
37 *a = *b;
38 *b = tmp;
41 static float slow_scale_map(sv_scalespace_t *to, sv_scalespace_t *from,
42 unsigned char *delA, unsigned char *delB,
43 int *posA, int *posB,
44 int xymul){
45 int i;
46 int dw = from->pixels;
47 int pw = to->pixels;
49 long scalenum = _sv_scalespace_scalenum(to,from);
50 long scaleden = _sv_scalespace_scaleden(to,from);
51 long del = _sv_scalespace_scaleoff(to,from);
52 int bin = del / scaleden;
53 int discscale = (scaleden>scalenum?scalenum:scaleden);
54 int total = xymul*scalenum/discscale;
55 del -= bin * scaleden;
57 for(i=0;i<pw;i++){
58 long del2 = del + scalenum;
59 int sizeceil = (del2 + scaleden - 1)/ scaleden; // ceiling
60 int sizefloor = del2 / scaleden;
62 while(bin<0 && del2>scaleden){
63 bin++;
64 del = 0;
65 del2 -= scaleden;
66 sizeceil--;
69 if(del2 > scaleden && bin>=0 && bin<dw){
70 int rem = total;
72 delA[i] = ((xymul * (scaleden - del)) + (discscale>>1)) / discscale;
73 posA[i] = bin;
74 rem -= delA[i];
75 rem -= xymul*(sizeceil-2);
77 while(bin+sizeceil>dw){
78 sizeceil--;
79 del2=0;
82 del2 %= scaleden;
83 if(rem<0){
84 delA[i] += rem;
85 delB[i] = 0;
86 }else{
87 delB[i] = rem; // don't leak
89 posB[i] = bin+sizeceil;
91 }else{
92 if(bin<0 || bin>=dw){
93 delA[i] = 0;
94 posA[i] = 0;
95 delB[i] = 0;
96 posB[i] = 0;
97 }else{
98 delA[i] = xymul;
99 posA[i] = bin;
100 delB[i] = 0;
101 posB[i] = bin+1;
102 if(del2 == scaleden)del2=0;
106 bin += sizefloor;
107 del = del2;
109 return (float)xymul/total;
112 static void slow_scale(sv_plane_t *pl,
113 sv_ucolor_t *work,
114 int dw, int dh,
115 int iw, int ih,
116 void (*mapfunc)(int,int, _sv_lcolor_t *),
117 int i){
119 sv_ucolor_t *image = pl->image;
120 sv_ccolor_t *cwork = work;
122 if(ih!=dh || iw!=dw){
123 /* resampled row computation; may involve multiple data rows */
125 float idel = pl->resample_yscalemul * pl->resample_xscalemul;
127 /* by column */
128 /* XXXXX by row should be more efficient... */
129 int ydelA=pl->resample_ydelA[i];
130 int ydelB=pl->resample_ydelB[i];
131 int ystart=pl->resample_ynumA[i];
132 int yend=pl->resample_ynumB[i];
133 int lh = yend - ystart;
134 float data[lh*dw];
135 float *in_data = pl->data+ystart*dw;
136 unsigned char *xdelA = pl->resample_xdelA;
137 unsigned char *xdelB = pl->resample_xdelB;
138 int *xnumA = pl->resample_xnumA;
139 int *xnumB = pl->resample_xnumB;
141 // although the slider map is only locked against heap changes,
142 // the calculation is well formed such that data inconsistencies
143 // are guaranteed to be cosmetic only and short lived.
144 for(j=0;j<lh*dw;j++)
145 data[j] = _sv_slidermap_to_mapdel(pl->scale, in_data[j])*65535.f;
147 /* by panel col */
148 for(j=0;j<pw;j++){
150 sv_lcolor_t out = (sv_lcolor_t){0,0,0,0};
151 int xstart = xnumA[j];
152 int xend = xnumB[j];
153 int dx = xstart;
154 int xA = xdelA[j];
155 int xB = xdelB[j];
156 int y = ystart;
158 // first line
159 if(y<yend){
160 if(dx<xend)
161 mapfunc(data[dx++], ydelA*xA, &out);
163 for(; dx < xend-1; dx++)
164 mapfunc(data[dx], ydelA*17, &out);
166 if(dx<xend)
167 mapfunc(data[dx], ydelA*xB, &out);
168 y++;
171 // mid lines
172 for(;y<yend-1;y++){
173 dx = xstart += dw;
174 xend += dw;
175 if(dx<xend)
176 mapfunc(data[dx++], 15*xA, &out);
178 for(; dx < xend-1; dx++)
179 mapfunc(data[dx], 255, &out);
181 if(dx<xend)
182 mapfunc(data[dx], 15*xB, &out);
185 // last line
186 if(y<yend){
187 dx = xstart += dw;
188 xend += dw;
189 if(dx<xend)
190 mapfunc(data[dx++], ydelB*xA, &out);
192 for(; dx < xend-1; dx++)
193 mapfunc(data[dx], ydelB*17, &out);
195 if(dx<xend)
196 mapfunc(data[dx], ydelB*xB, &out);
199 work[j].a = (u_int32_t)(out.a*idel);
200 work[j].r = (u_int32_t)(out.r*idel);
201 work[j].g = (u_int32_t)(out.g*idel);
202 work[j].b = (u_int32_t)(out.b*idel);
206 }else{
207 /* non-resampling render */
209 float data[dw];
210 float *in_data = pl->data+i*dw;
212 for(j=0;j<dw;j++)
213 data[j] = _sv_slider_val_to_mapdel(scale, in_data[j])*65535.f;
215 for(j=0;j<pw;j++){
217 sv_lcolor_t out = (sv_lcolor_t){0,0,0,0};
218 mapfunc(data[j], 255, &out);
220 work[j].a = (u_int32_t)(out.a);
221 work[j].r = (u_int32_t)(out.r);
222 work[j].g = (u_int32_t)(out.g);
223 work[j].b = (u_int32_t)(out.b);
228 static void fast_scale_map(int *map,
229 sv_scalespace_t new,
230 sv_scalespace_t old){
231 int i;
232 double old_n = old.pixels;
233 double new_n = new.pixels;
235 double old_lo = _sv_scalespace_value(&old,0);
236 double old_hi = _sv_scalespace_value(&old,old_n);
237 double new_lo = _sv_scalespace_value(&new,0);
238 double new_hi = _sv_scalespace_value(&new,new_n);
239 double newscale = (new_hi-new_lo)/new_n;
240 double oldscale = old_n/(old_hi-old_lo);
241 for(i=0;i<new_n;i++){
242 double val = i*newscale+new_lo;
243 map[i] = (int)floor((val-old_lo)*oldscale);
247 static void fast_scale_imagex(sv_ucolor_t *data,
248 sv_scalespace_t new,
249 sv_scalespace_t old,
250 int *map){
251 int i;
252 sv_ucolor_t work[new_w];
254 for(i=0;i<new.pixels;i++){
255 int base = map[i];
256 if(base<0 || base>=old.pixels){
257 work[i]=NAN;
258 }else{
259 work[i]=data[base];
262 memcpy(data,work,new.pixels*sizeof(*work));
265 static void fast_scale_imagey(sv_ucolor_t *olddata,
266 sv_ucolor_t *newdata,
267 int new_w,
268 int old_w,
269 sv_scalespace_t new,
270 sv_scalespace_t old,
271 int *map){
272 int i;
274 for(i=0;i<new.pixels;i++){
275 int base = map[i];
276 if(base<0 || base>=old.pixels){
277 *newdata=NAN;
278 }else{
279 *newdata=olddata[base*old_w];
281 newdata+=new_w;
285 static void fast_scale_datax(float *data,
286 sv_scalespace_t new,
287 sv_scalespace_t old,
288 int *map){
289 int i;
290 float work[new_w];
292 for(i=0;i<new.pixels;i++){
293 int base = map[i];
294 if(base<0 || base>=old.pixels){
295 work[i]=NAN;
296 }else{
297 work[i]=data[base];
300 memcpy(data,work,new.pixels*sizeof(*work));
303 static void fast_scale_datay(float *olddata,
304 float *newdata,
305 int new_w,
306 int old_w,
307 sv_scalespace_t new,
308 sv_scalespace_t old,
309 int *map){
310 int i;
312 for(i=0;i<new.pixels;i++){
313 int base = map[i];
314 if(base<0 || base>=old.pixels){
315 *newdata=NAN;
316 }else{
317 *newdata=olddata[base*old_w];
319 newdata+=new_w;
323 int _sv_plane_recompute_setup_common(sv_plane_t *pl){
324 sv_plane_common_t *c = &pl->c;
325 sv_panel_t *p = c->panel;
326 int w = p->bg->image_x->pixels;
327 int h = p->bg->image_y->pixels;
328 sv_scalespace_t pending_scales[pl->c.axes];
329 double pending_input[pl->o->inputs];
330 int flag=0;
332 // generate new pending scales
333 for(i=0;i<pl->c.axes;i++){
334 int axisnum = c->axis_list[i];
335 int dimnum = p->axis_dims[axisnum];
337 // check that dims are actually set
338 // should only come up if dimensions are declared to provide an
339 // axis, but there are too few dims to actually populate all
340 // declared axes.
341 if(dimnum<0){
342 fprintf(stderr,"sushivision: Panel \"%s\" has not set the %s axis\n"
343 "\tfor objective \"%s\".\n",
344 p->name,p->axis_names[axisnum],o->name);
345 pending_scales[i] = {0};
346 }else{
348 switch(axisnum){
349 case 0: // X
350 pending_scales[i] =
351 _sv_dim_datascale(p->dim_data+dimnum, p->bg->image_x,
352 w * p->oversample_n / p->oversample_d, 0);
353 break;
355 case 1: // Y
356 pending_scales[i] =
357 _sv_dim_datascale(p->dim_data+dimnum, p->bg->image_y,
358 h * p->oversample_n / p->oversample_d, 1);
359 break;
361 case 2: // Z
362 fprintf(stderr,"Z axis unimplemented!\n");
363 pending_scales[i] = {0};
364 break;
366 default: // all auxiliary scales
367 fprintf(stderr,"auxiliary axes unimplemented!\n");
368 pending_scales[i] = {0};
369 break;
374 // precompute non-iteration values in function input vector
375 for(i=0;i<pl->o->inputs;i++){
376 int dim = pl->o->input_dims[i];
377 for(j=0;j<pl->c.axes;j++)
378 if(dim == p->axis_dims[pl->c.axis_list[j]])
379 pending_input[i]=NAN;
380 else
381 pending_input[i]=p->dim_data[dim].val;
384 // Do we really need to recompute? Check dims and axes for changes.
385 // Any changes, we must recompute.
387 // check that the axes dimensions have not changed
388 //for(i=0;i<pl->c.axes;i++)
389 //if(pl->c.axis_dims[i] != p->axis_dims[pl->c.axis_list[i]]){
390 // flag=1;
391 // break;
394 // check that the axes scales have not changed
395 if(!flag)
396 for(i=0;i<pl->c.axes;i++)
397 if(_sv_scalecmp(c->data_scales+i,pending_scales+i)){
398 flag = 1;
399 break;
402 // check whether dimension values have changed.
403 // the fact that axis dim values are NAN allows this to double as a
404 // check that the axes dims have not changed.
405 if(!flag)
406 for(i=0;i<pl->o->inputs;i++)
407 if(pending_input[i] != c->dim_input[i]){
408 flag=1;
409 break;
412 if(flag){
413 memcpy(c->dim_input,pending_input,sizeof(pending_input));
414 memcpy(c->pending_data_scales,pending_scales,sizeof(pending_scales));
417 return flag;
420 int _sv_plane_resample_setup_common(sv_plane_t *in){
421 sv_plane_common_t *c = &pl->c;
422 sv_panel_t *p = c->panel;
424 if(_sv_scalecmp(&c->image_x,&p->bg->image_x) ||
425 _sv_scalecmp(&c->image_y,&p->bg->image_y)) return 1;
426 return 0;
429 // called from worker thread
430 static void recompute_setup(sv_plane_t *in){
431 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
432 sv_panel_t *p = pl->panel;
434 int flag=_sv_plane_recompute_presetup(in);
436 // Do we really need to recompute?
437 if(flag){
438 pl->data_task=0;
439 pl->data_outstanding=0;
440 pl->data_next=0;
443 // Regardless of recomputation, we check for remap (eg, resizing the
444 // plane of a fixed size data set requires a rerender, but not a
445 // recompute)
447 flag|=_sv_plane_resample_setup_common(in);
449 if(flag){
450 pl->image_serialno++;
451 pl->image_outstanding=0;
452 pl->image_task=0;
456 // called from worker thread
457 static int image_resize(sv_plane_t *in, sv_panel_t *p){
458 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
459 sv_scalespace_t newx = p->bg->image_x;
460 sv_scalespace_t newy = p->bg->image_y;
461 sv_scalespace_t oldx = pl->image_x;
462 sv_scalespace_t oldy = pl->image_y;
463 int new_w = newx.pixels;
464 int new_h = newy.pixels;
465 int old_w = oldx.pixels;
466 int old_h = oldy.pixels;
467 int serialno = p->comp_serialno;
469 if(pl->image_task==-1) return STATUS_BUSY;
471 if(pl->image_task==0){ // realloc
472 sv_ucolor_t *image = NULL;
473 int *map = NULL;
475 // drop locks while alloc()ing/free()ing
476 pl->image_task = -1;
477 pthread_mutex_unlock(pl->status_m);
478 pthread_rwlock_unlock(pl->panel_m);
480 image = calloc(new_w*new_h,sizeof(*image));
481 map = calloc(max(new_h,new_w),sizeof(*map));
482 if(new_w > old_w){
483 fast_scale_map(map,new_h,newy,oldy);
484 }else{
485 fast_scale_map(map,old_w,newx,oldx);
488 pthread_rwlock_wrlock(pl->panel_m);
489 //pthread_mutex_lock(pl->status_m); don't need two exclusive locks!
491 if(p->comp_serialno == serialno){
493 swapp(&pl->pending_image, &image);
494 swapp(&pl->map, &map);
495 pl->image_task = 1;
496 pl->image_next=0;
498 if(new_w > old_w){
499 // y then x
500 pl->image_outstanding=0;
501 }else{
502 // x then y
503 pl->image_outstanding=0;
505 image = NULL;
506 map = NULL;
509 //pthread_mutex_unlock(pl->status_m);
510 pthread_rwlock_unlock(pl->panel_m);
512 if(map)free(map);
513 if(image)free(image);
515 pthread_rwlock_rdlock(pl->panel_m);
516 pthread_mutex_lock(pl->status_m);
518 return STATUS_WORKING;
521 if(pl->image_task==1){ // scale first dim
522 if(new_w > old_w){
523 // y then x
525 int next = pl->image_next;
526 if(next >= oldx){
527 if(pl->image_outstanding) return STATUS_BUSY;
529 pl->image_task=-1;
530 pthread_mutex_unlock(pl->status_m);
531 fast_scale_map(map,new_w,newx,oldx);
532 pthread_mutex_lock(pl->status_m);
534 if(p->comp_serialno == serialno){
535 pl->image_task=2;
536 pl->image_next=0;
538 }else{
540 pl->image_next++;
541 pl->image_outstanding++;
543 pthread_mutex_unlock(pl->status_m);
544 fast_scale_imagey(olddata+next,newdata+next,new_w,old_w,newy,oldy,pl->map);
545 pthread_mutex_lock(pl->status_m);
547 if(p->comp_serialno == serialno)
548 pl->image_outstanding--;
550 }else{
551 // x then y
553 int next = pl->image_next;
554 if(next >= oldy){
555 if(pl->image_outstanding) return STATUS_BUSY;
557 pl->image_task=-1;
558 pthread_mutex_unlock(pl->status_m);
559 fast_scale_map(map,new_w,newy,oldy);
560 pthread_mutex_lock(pl->status_m);
562 if(p->comp_serialno == serialno){
563 pl->image_task=2;
564 pl->image_next=0;
566 }else{
568 pl->image_next++;
569 pl->image_outstanding++;
571 pthread_mutex_unlock(pl->status_m);
572 fast_scale_imagex(olddata+next*old_w,newx,oldx,pl->map);
573 pthread_mutex_lock(pl->status_m);
574 if(p->comp_serialno == serialno)
575 pl->image_outstanding--;
578 return STATUS_WORKING;
581 if(pl->image_task==2){ // scale second dim
582 if(new_w > old_w){
583 // now x
585 int next = pl->image_next;
586 if(next >= newy){
587 if(pl->image_outstanding) return STATUS_BUSY;
588 if(p->comp_serialno == serialno)
589 pl->image_task=3;
590 }else{
591 pthread_mutex_unlock(pl->status_m);
592 fast_scale_imagex(newdata+next*new_w,newx,oldx,pl->map);
593 pthread_mutex_lock(pl->status_m);
594 if(p->comp_serialno == serialno)
595 pl->image_outstanding--;
598 }else{
599 // now y
601 int next = pl->image_next;
602 if(next >= newx){
603 if(pl->image_outstanding) return STATUS_BUSY;
604 if(p->comp_serialno == serialno)
605 pl->image_task=3;
606 }else{
607 pthread_mutex_unlock(pl->status_m);
608 fast_scale_imagey(olddata+next,newdata+next,new_w,old_w,newy,oldy,pl->map);
609 pthread_mutex_lock(pl->status_m);
610 if(p->comp_serialno == serialno)
611 pl->image_outstanding--;
614 return STATUS_WORKING;
617 if(pl->image_task==3){ // commit new data
618 int *flags = NULL;
619 int *map = NULL;
621 unsigned char *xdelA = NULL;
622 unsigned char *xdelB = NULL;
623 int *xnumA = NULL;
624 int *xnumB = NULL;
625 float xscalemul;
626 unsigned char *ydelA = NULL;
627 unsigned char *ydelB = NULL;
628 int *ynumA = NULL;
629 int *ynumB = NULL;
630 float yscalemul;
632 pl->image_task = -1;
634 pthread_mutex_unlock(pl->status_m);
635 pthread_rwlock_unlock(pl->panel_m);
637 flags = calloc(new_h,sizeof(*newflags));
639 xdelA = calloc(pw,sizeof(*xdelA));
640 xdelB = calloc(pw,sizeof(*xdelB));
641 xnumA = calloc(pw,sizeof(*xnumA));
642 xnumB = calloc(pw,sizeof(*xnumB));
643 xscalemul = slow_scale_map(newx, pl->pending_data_x, xdelA, xdelB, xnumA, xnumB, 17);
645 ydelA = calloc(ph,sizeof(*ydelA));
646 ydelB = calloc(ph,sizeof(*ydelB));
647 ynumA = calloc(ph,sizeof(*ynumA));
648 ynumB = calloc(ph,sizeof(*ynumB));
649 yscalemul = slow_scale_map(newy, pl->pending_data_y, ydelA, ydelB, ynumA, ynumB, 15);
651 pthread_rwlock_wrlock(pl->panel_m);
652 //pthread_mutex_lock(pl->status_m);
654 if(p->comp_serialno == serialno){
655 pl->image_x = p->bg->image_x;
656 pl->image_y = p->bg->image_y;
658 swapp(&flags,&pl->imageflags);
659 swapp(&map,&pl->map);
661 swapp(&xdelA,&pl->resample_xdelA);
662 swapp(&xdelB,&pl->resample_xdelB);
663 swapp(&xnumA,&pl->resample_xnumA);
664 swapp(&xnumB,&pl->resample_xnumB);
665 swapp(&ydelA,&pl->resample_ydelA);
666 swapp(&ydelB,&pl->resample_ydelB);
667 swapp(&ynumA,&pl->resample_ynumA);
668 swapp(&ynumB,&pl->resample_ynumB);
669 pl->resample_xscalemul = xscalemul;
670 pl->resample_yscalemul = yscalemul;
671 bg_rerender_full(p);
672 pl->image_task = 4;
673 pl->image_outstanding=0;
676 //pthread_mutex_unlock(pl->status_m);
677 pthread_rwlock_unlock(pl->panel_m);
679 if(map)free(map);
680 if(flags)free(flags);
682 if(xdelA)free(xdelA);
683 if(xdelB)free(xdelB);
684 if(xnumA)free(xnumA);
685 if(xnumB)free(xnumB);
686 if(ydelA)free(ydelA);
687 if(ydelB)free(ydelB);
688 if(ynumA)free(ynumA);
689 if(ynumB)free(ynumB);
691 pthread_rwlock_rdlock(pl->panel_m);
692 pthread_mutex_lock(pl->status_m);
693 return STATUS_WORKING;
696 return STATUS_IDLE;
699 // called from worker thread
700 static int data_resize(sv_plane_t *in, sv_panel_t *p){
701 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
702 sv_scalespace_t newx = pl->pending_data_x;
703 sv_scalespace_t newy = pl->pending_data_y;
704 sv_scalespace_t oldx = pl->data_x;
705 sv_scalespace_t oldy = pl->data_y;
706 int new_w = newx.pixels;
707 int new_h = newy.pixels;
708 int old_w = oldx.pixels;
709 int old_h = oldy.pixels;
710 int serialno = p->comp_serialno;
712 if(pl->data_task==-1) return STATUS_BUSY;
714 if(pl->data_task==0){ // realloc
715 float *old_data = NULL;
716 float *data = NULL;
717 int *old_map = NULL;
718 int *map = NULL;
719 pl->data_task = -1;
721 // drop locks while alloc()ing/free()ing
723 pthread_mutex_unlock(pl->status_m);
724 pthread_rwlock_unlock(pl->panel_m);
726 data = calloc(new_w*new_h,sizeof(*data));
727 map = calloc(max(new_h,new_w),sizeof(*map));
728 if(new_w > old_w){
729 fast_scale_map(map,new_h,newy,oldy);
730 }else{
731 fast_scale_map(map,old_w,newx,oldx);
734 pthread_rwlock_wrlock(pl->panel_m);
735 //pthread_mutex_lock(pl->status_m);
737 if(p->comp_serialno == serialno){
739 old_data = pl->pending_data;
740 old_map = pl->map;
741 pl->pending_data = data;
742 pl->map = map;
743 pl->data_task = 1;
744 pl->data_next=0;
746 if(new_w > old_w){
747 // y then x
748 pl->data_waiting=old_w;
749 pl->data_incomplete=old_w;
750 }else{
751 // x then y
752 pl->data_waiting=old_h;
753 pl->data_incomplete=old_h;
755 data = NULL;
756 map = NULL;
759 //pthread_mutex_unlock(pl->status_m);
760 pthread_rwlock_unlock(pl->panel_m);
762 if(old_map)free(old_map);
763 if(map)free(map);
764 if(old_data)free(old_data);
765 if(data)free(data);
767 pthread_rwlock_rdlock(pl->panel_m);
768 pthread_mutex_lock(pl->status_m);
770 return STATUS_WORKING;
773 if(pl->data_task==1){ // scale first dim
774 if(new_w > old_w){
775 // y then x
777 int next = pl->data_next;
778 if(next >= oldx){
779 if(pl->data_outstanding)return STATUS_BUSY;
781 pl->data_task=-1;
782 pthread_mutex_unlock(pl->status_m);
783 fast_scale_map(map,new_w,newx,oldx);
784 pthread_mutex_lock(pl->status_m);
786 if(p->comp_serialno == serialno){
787 pl->data_task=2;
788 pl->data_next=0;
790 }else{
792 pl->data_next++;
793 pl->data_outstanding++;
795 pthread_mutex_unlock(pl->status_m);
796 fast_scale_datay(olddata+next,newdata+next,new_w,old_w,newy,oldy,pl->map);
797 pthread_mutex_lock(pl->status_m);
798 if(p->comp_serialno == serialno)
799 pl->data_outstanding--;
801 }else{
802 // x then y
804 int next = pl->data_next;
805 if(next >= oldy){
806 if(pl->data_outstanding)return STATUS_BUSY;
808 pl->data_task=-1;
809 pthread_mutex_unlock(pl->status_m);
810 fast_scale_map(map,new_w,newy,oldy);
811 pthread_mutex_lock(pl->status_m);
813 if(p->comp_serialno == serialno){
814 pl->data_task=2;
815 pl->data_next=0;
817 }else{
819 pl->data_next++;
820 pl->data_outstanding++;
822 pthread_mutex_unlock(pl->status_m);
823 fast_scale_datax(olddata+next*old_w,newx,oldx,pl->map);
824 pthread_mutex_lock(pl->status_m);
825 if(p->comp_serialno == serialno)
827 if(--pl->data_incomplete==0)
828 pl->data_outstanding--;
831 return STATUS_WORKING;
834 if(pl->data_task==2){ // scale second dim
835 if(new_w > old_w){
836 // now x
838 int next = pl->data_next;
839 if(next >= newy){
840 if(pl->data_outstanding)return STATUS_BUSY;
841 if(p->comp_serialno == serialno)
842 pl->data_task=3;
843 }else{
844 pl->data_next++;
845 pl->data_outstanding++;
846 pthread_mutex_unlock(pl->status_m);
847 fast_scale_datax(newdata+next*new_w,newx,oldx,pl->map);
848 pthread_mutex_lock(pl->status_m);
849 if(p->comp_serialno == serialno)
850 pl->data_outstanding--;
852 }else{
853 // now y
855 int next = pl->data_next;
856 if(next >= newx){
857 if(pl->data_outstanding)return STATUS_BUSY;
858 if(p->comp_serialno == serialno)
859 pl->data_task=3;
860 }else{
861 pl->data_next++;
862 pl->data_outstanding++;
863 pthread_mutex_unlock(pl->status_m);
864 fast_scale_datay(olddata+next,newdata+next,new_w,old_w,newy,oldy,pl->map);
865 pthread_mutex_lock(pl->status_m);
866 if(p->comp_serialno == serialno)
867 pl->data_outstanding--;
870 return STATUS_WORKING;
873 if(pl->data_task==3){ // commit new data
874 int *map = NULL;
876 pl->data_task = -1;
878 pthread_mutex_unlock(pl->status_m);
879 pthread_rwlock_unlock(pl->panel_m);
881 pthread_rwlock_wrlock(pl->panel_m);
882 //pthread_mutex_lock(pl->status_m);
884 if(p->comp_serialno == serialno){
885 pl->data_x = pl->pending_data_x;
886 pl->data_y = pl->pending_data_y;
887 pl->data_task = 4;
888 pl->data_outstanding=0;
889 map = pl->map;
890 pl->map = NULL;
893 //pthread_mutex_unlock(pl->status_m);
894 pthread_rwlock_unlock(pl->panel_m);
896 if(map)free(map);
898 pthread_rwlock_rdlock(pl->panel_m);
899 pthread_mutex_lock(pl->status_m);
900 return STATUS_WORKING;
903 return STATUS_IDLE;
906 // called from worker thread
907 static int image_work(sv_plane_t *in, sv_panel_t *p){
908 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
910 int h = pl->image_y.pixels;
911 int w = pl->image_x.pixels;
912 int last = pl->image_next;
913 int mapno = pl->image_serialno;
914 int i;
915 sv_ucolor_t work[w];
917 if(pl->image_task == 5 && pl->image_outstanding) return STATUS_BUSY;
918 if(pl->image_task != 4) return STATUS_IDLE;
921 i = pl->image_next;
922 pl->image_next++;
923 if(pl->image_next>=h)pl->image_next=0;
925 if(pl->image_flags[i]){
926 sv_scalespace_t dx = pl->data_x;
927 sv_scalespace_t dy = pl->data_y;
928 sv_scalespace_t ix = pl->image_x;
929 sv_scalespace_t iy = pl->image_y;
930 void (*mapping)(int, int, _sv_lcolor_t *)=mapfunc[pl->image_mapnum];
931 pl->image_flags[i]=0;
932 pl->image_outstanding++;
934 pthread_mutex_unlock(pl->status_m);
935 slow_scale(dx,dy,ix,iy,mapping,i);
936 pthread_mutex_lock(pl->status_m);
938 if(pl->image_serialno == mapno){
939 pl->image_outstanding--;
940 bg_rerender_line(p,i);
942 return STATUS_WORKING;
944 }while(i!=last);
946 pl->image_task = 5;
947 if(pl->image_outstanding) return STATUS_BUSY;
948 return STATUS_IDLE;
951 static int vswizzle(int y, int height){
952 int yy = height >> 5;
953 if(y < yy)
954 return (y<<5)+31;
956 y -= yy;
957 yy = (height+16) >> 5;
958 if(y < yy)
959 return (y<<5)+15;
961 y -= yy;
962 yy = (height+8) >> 4;
963 if(y < yy)
964 return (y<<4)+7;
966 y -= yy;
967 yy = (height+4) >> 3;
968 if(y < yy)
969 return (y<<3)+3;
971 y -= yy;
972 yy = (height+2) >> 2;
973 if(y < yy)
974 return (y<<2)+1;
976 y -= yy;
977 return y<<1;
980 static void data_demultiplex_2d(sv_plane_t *in,sv_panel_t *p,double *output,
981 int w, int xoff, int yoff, int n){
982 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
983 int i,on=pl->o->outputs;
984 float *data_line = pl->data+w*yoff+xoff;
985 output += pl->data_z_output;
987 for(i=0;i<n;i++){
988 *data_line++ = *output;
989 output+=on;
993 // called from worker thread
994 static int data_work(sv_plane_t *in, sv_panel_t *p){
995 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
996 int serialno = p->comp_serialno;
997 int next = pl->data_next;
998 int i,j;
1000 // each plane is associated with a single objective, however
1001 // multiple objectives may be associated with a given computation.
1002 // This is an optimization for dealing with multiple display
1003 // ojectives drawing from different output values of the exact same
1004 // input computation. The plane types sharing a computation may be
1005 // different, but the input dimension value vector and input axes
1006 // will be identical.
1008 // if this is a 'slave' plane in the computation chain, return idle;
1009 // some other plane is doing the calculation for us.
1011 if(pl->c.share_prev)return STATUS_IDLE;
1012 if(pl->data_task == 4)
1013 if(next >= pl->data_y.pixels)
1014 pl->data_task = 5;
1015 if(pl->data_task == 5 && pl->data_outstanding) return STATUS_BUSY;
1016 if(pl->data_task != 4) return STATUS_IDLE;
1017 pl->data_next++;
1019 // marshal iterators, dimension value vectors
1020 int outputs = pl->o->outputs;
1021 double input[pl->o->inputs];
1022 int xpos = -1;
1023 int yline = vswizzle(next);
1024 sv_scalespace_t dx = pl->data_x;
1025 int dw = dx.pixels;
1026 int dh = pl->data_y.pixels;
1027 int iw = pl->image_x.pixels;
1028 int ih = pl->image_y.pixels;
1030 for(i=0;i<pl->o->inputs;i++){
1031 int dim = pl->o->input_dims[i]; // dim setup in an objective is immutable
1032 if(dim == p->ydim){
1033 input[i] = _sv_scalespace_value(&pl->data_y,yline);
1034 }else
1035 input[i] = p->dim_data[dim].val;
1037 if(dim == p->xdim) xpos = i;
1040 // drop status lock and compute. Writes to the data plane are not
1041 // locked because we still hold the panel concurrent lock; changes
1042 // to the computational parameters (and heap) can't happen. Reads
1043 // from the data array may get inconsistent information, but a
1044 // completed line flushes which causes a new read to replace the
1045 // inconsistent one.
1047 pl->data_outstanding++;
1048 pthread_mutex_unlock(pl->status_m);
1050 int sofar = 0;
1051 int step = (1024+outputs-1)/outputs; // at least one
1052 sv_plane_t *slave;
1053 while(sofar < w){
1054 int this_step = (step>w-sofar?w-sofar:step);
1055 double output[outputs*step];
1056 double *outptr=output;
1057 slave = pl->c.share.next;
1059 // compute
1060 for(i=0;i<this_step;i++){
1061 // set x val
1062 input[xpos] = _sv_scalespace_value(&dx,i+sofar);
1063 pl->o->function(input,outptr); // func setup in an objective is immutable
1064 outptr+=outputs;
1067 // demultiplex
1068 data_demultiplex_2d(pl,p,output,yline,sofar,this_step);
1069 while(slave){
1070 slave->data_demultiplex_2d(slave,p,output,yline,sofar,this_step);
1071 slave = slave->c.share_next;
1073 sofar+= this_step;
1076 pthread_mutex_lock(pl->status_m);
1077 if(p->comp_serialno != serialno)return STATUS_WORKING;
1079 pl->data_outstanding--;
1081 // determine all image lines this y data line affects
1082 slave = pl;
1083 while(slave){
1084 if(ih!=dh || iw!=dw){
1085 /* resampled row computation; may involve multiple data rows */
1086 for(i=0;i<ih;i++)
1087 if(pl->resample_ynumA[i]<=yline && pl->resample_ynumB[i]>yline)
1088 pl->image_flags[i]=1;
1089 }else
1090 pl->image_flags[yline]=1;
1091 slave = slave->c.share_next;
1094 pl->image_task = 4;
1096 return STATUS_WORKING;
1100 // called from GTK/API for map scale changes
1101 static void plane_remap(sv_plane_t *in, sv_panel_t *p){
1102 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
1103 int i,flag=1;
1105 // check to see if scale vals have changed or just scale settings
1106 // scalemap is strictly read-only in the worker threads, so there's
1107 // no need to lock this comparison beyond the GDK lock.
1108 if(pl->scale.n == pl->scale_widget->labels){
1109 flag=0;
1110 for(i=0;i<pl->scale.n;i++)
1111 if(pl->scale.vals[i] != pl->scale_widget->label_vals[i]){
1112 flag=1;
1113 break;
1116 if(flag){
1117 // the actual scale changed so updating the cached information
1118 // requires complete write locking for heap work
1119 pthread_rwlock_wrlock(pl->panel_m);
1121 _sv_slidermap_clear(&pl->slider);
1122 _sv_slidermap_init(&pl->slider,&pl->slider_widget);
1124 wake_workers();
1125 }else{
1126 // no scale change
1127 pthread_mutex_lock(pl->status_m);
1128 _sv_slidermap_partial_update(&pl->slider,&pl->slider_widget);
1131 p->map_render=1;
1132 for(i=0;i<pl->image_y.pixels;i++)
1133 pl->image_flags[i]=1;
1134 pl->image_mapnum = gtk_combo_box_get_active(GTK_COMBO_BOX(pl->range_rulldown));
1135 p->image_serialno++;
1136 p->image_outstanding=0;
1138 if(flag){
1139 pthread_rwlock_unlock(pl->panel_m);
1140 }else{
1141 pthread_mutex_unlock(pl->status_m);
1146 // called from GTK/API
1147 static void plane_free(sv_plane_t *pl){
1148 sv_plane_2d_t *pl = (sv_plane_2d_t *)in;
1150 if(pl){
1151 if(pl->data)free(pl->data);
1152 if(pl->image)free(pl->image);
1153 if(pl->image_flags)free(pl->image_flags);
1155 if(pl->resample_xdelA)free(pl->resample_xdelA);
1156 if(pl->resample_xdelB)free(pl->resample_xdelB);
1157 if(pl->resample_xnumA)free(pl->resample_xnumA);
1158 if(pl->resample_xnumB)free(pl->resample_xnumB);
1159 if(pl->resample_xscalemul)free(pl->resample_xscalemul);
1161 if(pl->resample_ydelA)free(pl->resample_ydelA);
1162 if(pl->resample_ydelB)free(pl->resample_ydelB);
1163 if(pl->resample_ynumA)free(pl->resample_ynumA);
1164 if(pl->resample_ynumB)free(pl->resample_ynumB);
1165 if(pl->resample_yscalemul)free(pl->resample_yscalemul);
1167 if(pl->mapping)_sv_mapping_free(pl->mapping);
1168 if(pl->scale)_sv_slider_free(pl->scale);
1169 if(pl->range_pulldown)gtk_widget_destroy(pl->range_pulldown);
1171 _sv_slidermap_clear(&pl->slider);
1173 free(pl);
1177 GtkWidget *_sv_plane_2d_label_widget(sv_plane2d_t *pl){
1178 GtkWidget *label = gtk_label_new(o->name);
1179 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
1180 return label;
1184 GtkWidget *_sv_plane_2d_obj_widget(sv_plane2d_t *pl){
1185 GtkWidget *table = gtk_table_new
1192 sv_plane_t *sv_plane_2d_new(){
1193 sv_plane_2d_t *ret = calloc(1,sizeof(*ret));
1194 ret->recompute_setup = recompute_setup;
1195 ret->image_resize = image_resize;
1196 ret->data_resize = data_resize;
1197 ret->image_work = image_work;
1198 ret->data_work = data_work;
1199 ret->plane_remap = plane_remap;
1200 ret->plane_free = plane_free;
1202 ret->plane_label = _sv_plane_2d_label_widget;
1203 ret->plane_obj = _sv_plane_2d_obj_widget;
1205 return (sv_plane_t *)ret;
1209 static void _sv_panel2d_realize(sv_panel_t *p){
1210 _sv_panel2d_t *p2 = p->subtype->p2;
1211 int i;
1215 /* obj box */
1217 p2->obj_table = gtk_table_new(p->objectives, 5, 0);
1218 gtk_box_pack_start(GTK_BOX(p->private->topbox), p2->obj_table, 0,0,1);
1220 /* objective sliders */
1221 p2->range_scales = calloc(p->objectives,sizeof(*p2->range_scales));
1222 p2->range_pulldowns = calloc(p->objectives,sizeof(*p2->range_pulldowns));
1223 p2->alphadel = calloc(p->objectives,sizeof(*p2->alphadel));
1224 p2->mappings = calloc(p->objectives,sizeof(*p2->mappings));
1225 for(i=0;i<p->objectives;i++){
1226 GtkWidget **sl = calloc(3,sizeof(*sl));
1227 sv_obj_t *o = p->objective_list[i].o;
1228 int lo = o->scale->val_list[0];
1229 int hi = o->scale->val_list[o->scale->vals-1];
1231 /* label */
1233 gtk_table_attach(GTK_TABLE(p2->obj_table),label,0,1,i,i+1,
1234 GTK_FILL,0,8,0);
1236 /* mapping pulldown */
1238 GtkWidget *menu=_gtk_combo_box_new_markup();
1239 int j;
1240 for(j=0;j<_sv_mapping_names();j++)
1241 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), _sv_mapping_name(j));
1242 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1243 g_signal_connect (G_OBJECT (menu), "changed",
1244 G_CALLBACK (_sv_panel2d_mapchange_callback), p->objective_list+i);
1245 gtk_table_attach(GTK_TABLE(p2->obj_table),menu,4,5,i,i+1,
1246 GTK_SHRINK,GTK_SHRINK,0,0);
1247 p2->range_pulldowns[i] = menu;
1250 /* the range mapping slices/slider */
1251 sl[0] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
1252 sl[1] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
1253 sl[2] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
1255 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[0],1,2,i,i+1,
1256 GTK_EXPAND|GTK_FILL,0,0,0);
1257 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[1],2,3,i,i+1,
1258 GTK_EXPAND|GTK_FILL,0,0,0);
1259 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[2],3,4,i,i+1,
1260 GTK_EXPAND|GTK_FILL,0,0,0);
1261 p2->range_scales[i] = _sv_slider_new((_sv_slice_t **)sl,3,o->scale->label_list,o->scale->val_list,
1262 o->scale->vals,_SV_SLIDER_FLAG_INDEPENDENT_MIDDLE);
1263 gtk_table_set_col_spacing(GTK_TABLE(p2->obj_table),3,5);
1265 _sv_slice_thumb_set((_sv_slice_t *)sl[0],lo);
1266 _sv_slice_thumb_set((_sv_slice_t *)sl[1],lo);
1267 _sv_slice_thumb_set((_sv_slice_t *)sl[2],hi);
1268 _sv_mapping_setup(&p2->mappings[i],0.,1.,0);
1269 _sv_slider_set_gradient(p2->range_scales[i], &p2->mappings[i]);
1273 /* dims */
1275 p2->dim_table = gtk_table_new(p->dimensions,4,0);
1276 gtk_box_pack_start(GTK_BOX(p->private->topbox), p2->dim_table, 0,0,4);
1278 GtkWidget *first_x = NULL;
1279 GtkWidget *first_y = NULL;
1280 GtkWidget *pressed_y = NULL;
1281 p->private->dim_scales = calloc(p->dimensions,sizeof(*p->private->dim_scales));
1282 p2->dim_xb = calloc(p->dimensions,sizeof(*p2->dim_xb));
1283 p2->dim_yb = calloc(p->dimensions,sizeof(*p2->dim_yb));
1285 for(i=0;i<p->dimensions;i++){
1286 sv_dim_t *d = p->dimension_list[i].d;
1288 /* label */
1289 GtkWidget *label = gtk_label_new(d->legend);
1290 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
1291 gtk_table_attach(GTK_TABLE(p2->dim_table),label,0,1,i,i+1,
1292 GTK_FILL,0,5,0);
1294 /* x/y radio buttons */
1295 if(!(d->flags & SV_DIM_NO_X)){
1296 if(first_x)
1297 p2->dim_xb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x),"X");
1298 else{
1299 first_x = p2->dim_xb[i] = gtk_radio_button_new_with_label(NULL,"X");
1300 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]),TRUE);
1302 gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_xb[i],1,2,i,i+1,
1303 GTK_SHRINK,0,3,0);
1306 if(!(d->flags & SV_DIM_NO_Y)){
1307 if(first_y)
1308 p2->dim_yb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_y),"Y");
1309 else
1310 first_y = p2->dim_yb[i] = gtk_radio_button_new_with_label(NULL,"Y");
1311 if(!pressed_y && p2->dim_xb[i]!=first_x){
1312 pressed_y = p2->dim_yb[i];
1313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]),TRUE);
1315 gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_yb[i],2,3,i,i+1,
1316 GTK_SHRINK,0,3,0);
1319 p->private->dim_scales[i] =
1320 _sv_dim_widget_new(p->dimension_list+i,_sv_panel2d_center_callback,_sv_panel2d_bracket_callback);
1322 gtk_table_attach(GTK_TABLE(p2->dim_table),
1323 p->private->dim_scales[i]->t,
1324 3,4,i,i+1,
1325 GTK_EXPAND|GTK_FILL,0,0,0);
1328 for(i=0;i<p->dimensions;i++){
1329 if(p2->dim_xb[i])
1330 g_signal_connect (G_OBJECT (p2->dim_xb[i]), "toggled",
1331 G_CALLBACK (_sv_panel2d_dimchange_callback), p);
1332 if(p2->dim_yb[i])
1333 g_signal_connect (G_OBJECT (p2->dim_yb[i]), "toggled",
1334 G_CALLBACK (_sv_panel2d_dimchange_callback), p);
1338 _sv_panel2d_update_xysel(p);
1340 gtk_widget_realize(p->private->toplevel);
1341 gtk_widget_realize(p->private->graph);
1342 gtk_widget_realize(GTK_WIDGET(p->private->spinner));
1343 gtk_widget_show_all(p->private->toplevel);
1344 _sv_panel2d_update_xysel(p); // yes, this was already done; however,
1345 // gtk clobbered the event setup on the
1346 // insensitive buttons when it realized
1347 // them. This call will restore them.
1349 _sv_undo_resume();