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)
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.
30 #include <sys/types.h>
35 static inline swapp(void **a
, void **b
){
41 static float slow_scale_map(sv_scalespace_t
*to
, sv_scalespace_t
*from
,
42 unsigned char *delA
, unsigned char *delB
,
46 int dw
= from
->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
;
58 long del2
= del
+ scalenum
;
59 int sizeceil
= (del2
+ scaleden
- 1)/ scaleden
; // ceiling
60 int sizefloor
= del2
/ scaleden
;
62 while(bin
<0 && del2
>scaleden
){
69 if(del2
> scaleden
&& bin
>=0 && bin
<dw
){
72 delA
[i
] = ((xymul
* (scaleden
- del
)) + (discscale
>>1)) / discscale
;
75 rem
-= xymul
*(sizeceil
-2);
77 while(bin
+sizeceil
>dw
){
87 delB
[i
] = rem
; // don't leak
89 posB
[i
] = bin
+sizeceil
;
102 if(del2
== scaleden
)del2
=0;
109 return (float)xymul
/total
;
112 static void slow_scale(sv_plane_t
*pl
,
116 void (*mapfunc
)(int,int, _sv_lcolor_t
*),
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
;
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
;
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.
145 data
[j
] = _sv_slidermap_to_mapdel(pl
->scale
, in_data
[j
])*65535.f
;
150 sv_lcolor_t out
= (sv_lcolor_t
){0,0,0,0};
151 int xstart
= xnumA
[j
];
161 mapfunc(data
[dx
++], ydelA
*xA
, &out
);
163 for(; dx
< xend
-1; dx
++)
164 mapfunc(data
[dx
], ydelA
*17, &out
);
167 mapfunc(data
[dx
], ydelA
*xB
, &out
);
176 mapfunc(data
[dx
++], 15*xA
, &out
);
178 for(; dx
< xend
-1; dx
++)
179 mapfunc(data
[dx
], 255, &out
);
182 mapfunc(data
[dx
], 15*xB
, &out
);
190 mapfunc(data
[dx
++], ydelB
*xA
, &out
);
192 for(; dx
< xend
-1; dx
++)
193 mapfunc(data
[dx
], ydelB
*17, &out
);
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
);
207 /* non-resampling render */
210 float *in_data
= pl
->data
+i
*dw
;
213 data
[j
] = _sv_slider_val_to_mapdel(scale
, in_data
[j
])*65535.f
;
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
,
230 sv_scalespace_t old
){
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
,
252 sv_ucolor_t work
[new_w
];
254 for(i
=0;i
<new.pixels
;i
++){
256 if(base
<0 || base
>=old
.pixels
){
262 memcpy(data
,work
,new.pixels
*sizeof(*work
));
265 static void fast_scale_imagey(sv_ucolor_t
*olddata
,
266 sv_ucolor_t
*newdata
,
274 for(i
=0;i
<new.pixels
;i
++){
276 if(base
<0 || base
>=old
.pixels
){
279 *newdata
=olddata
[base
*old_w
];
285 static void fast_scale_datax(float *data
,
292 for(i
=0;i
<new.pixels
;i
++){
294 if(base
<0 || base
>=old
.pixels
){
300 memcpy(data
,work
,new.pixels
*sizeof(*work
));
303 static void fast_scale_datay(float *olddata
,
312 for(i
=0;i
<new.pixels
;i
++){
314 if(base
<0 || base
>=old
.pixels
){
317 *newdata
=olddata
[base
*old_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
];
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
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};
351 _sv_dim_datascale(p
->dim_data
+dimnum
, p
->bg
->image_x
,
352 w
* p
->oversample_n
/ p
->oversample_d
, 0);
357 _sv_dim_datascale(p
->dim_data
+dimnum
, p
->bg
->image_y
,
358 h
* p
->oversample_n
/ p
->oversample_d
, 1);
362 fprintf(stderr
,"Z axis unimplemented!\n");
363 pending_scales
[i
] = {0};
366 default: // all auxiliary scales
367 fprintf(stderr
,"auxiliary axes unimplemented!\n");
368 pending_scales
[i
] = {0};
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
;
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]]){
394 // check that the axes scales have not changed
396 for(i
=0;i
<pl
->c
.axes
;i
++)
397 if(_sv_scalecmp(c
->data_scales
+i
,pending_scales
+i
)){
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.
406 for(i
=0;i
<pl
->o
->inputs
;i
++)
407 if(pending_input
[i
] != c
->dim_input
[i
]){
413 memcpy(c
->dim_input
,pending_input
,sizeof(pending_input
));
414 memcpy(c
->pending_data_scales
,pending_scales
,sizeof(pending_scales
));
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;
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?
439 pl
->data_outstanding
=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
447 flag
|=_sv_plane_resample_setup_common(in
);
450 pl
->image_serialno
++;
451 pl
->image_outstanding
=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
;
475 // drop locks while alloc()ing/free()ing
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
));
483 fast_scale_map(map
,new_h
,newy
,oldy
);
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
);
500 pl
->image_outstanding
=0;
503 pl
->image_outstanding
=0;
509 //pthread_mutex_unlock(pl->status_m);
510 pthread_rwlock_unlock(pl
->panel_m
);
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
525 int next
= pl
->image_next
;
527 if(pl
->image_outstanding
) return STATUS_BUSY
;
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
){
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
--;
553 int next
= pl
->image_next
;
555 if(pl
->image_outstanding
) return STATUS_BUSY
;
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
){
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
585 int next
= pl
->image_next
;
587 if(pl
->image_outstanding
) return STATUS_BUSY
;
588 if(p
->comp_serialno
== serialno
)
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
--;
601 int next
= pl
->image_next
;
603 if(pl
->image_outstanding
) return STATUS_BUSY
;
604 if(p
->comp_serialno
== serialno
)
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
621 unsigned char *xdelA
= NULL
;
622 unsigned char *xdelB
= NULL
;
626 unsigned char *ydelA
= NULL
;
627 unsigned char *ydelB
= NULL
;
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
;
673 pl
->image_outstanding
=0;
676 //pthread_mutex_unlock(pl->status_m);
677 pthread_rwlock_unlock(pl
->panel_m
);
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
;
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
;
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
));
729 fast_scale_map(map
,new_h
,newy
,oldy
);
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
;
741 pl
->pending_data
= data
;
748 pl
->data_waiting
=old_w
;
749 pl
->data_incomplete
=old_w
;
752 pl
->data_waiting
=old_h
;
753 pl
->data_incomplete
=old_h
;
759 //pthread_mutex_unlock(pl->status_m);
760 pthread_rwlock_unlock(pl
->panel_m
);
762 if(old_map
)free(old_map
);
764 if(old_data
)free(old_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
777 int next
= pl
->data_next
;
779 if(pl
->data_outstanding
)return STATUS_BUSY
;
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
){
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
--;
804 int next
= pl
->data_next
;
806 if(pl
->data_outstanding
)return STATUS_BUSY
;
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
){
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
838 int next
= pl
->data_next
;
840 if(pl
->data_outstanding
)return STATUS_BUSY
;
841 if(p
->comp_serialno
== serialno
)
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
--;
855 int next
= pl
->data_next
;
857 if(pl
->data_outstanding
)return STATUS_BUSY
;
858 if(p
->comp_serialno
== serialno
)
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
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
;
888 pl
->data_outstanding
=0;
893 //pthread_mutex_unlock(pl->status_m);
894 pthread_rwlock_unlock(pl
->panel_m
);
898 pthread_rwlock_rdlock(pl
->panel_m
);
899 pthread_mutex_lock(pl
->status_m
);
900 return STATUS_WORKING
;
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
;
917 if(pl
->image_task
== 5 && pl
->image_outstanding
) return STATUS_BUSY
;
918 if(pl
->image_task
!= 4) return STATUS_IDLE
;
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
;
947 if(pl
->image_outstanding
) return STATUS_BUSY
;
951 static int vswizzle(int y
, int height
){
952 int yy
= height
>> 5;
957 yy
= (height
+16) >> 5;
962 yy
= (height
+8) >> 4;
967 yy
= (height
+4) >> 3;
972 yy
= (height
+2) >> 2;
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
;
988 *data_line
++ = *output
;
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
;
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
)
1015 if(pl
->data_task
== 5 && pl
->data_outstanding
) return STATUS_BUSY
;
1016 if(pl
->data_task
!= 4) return STATUS_IDLE
;
1019 // marshal iterators, dimension value vectors
1020 int outputs
= pl
->o
->outputs
;
1021 double input
[pl
->o
->inputs
];
1023 int yline
= vswizzle(next
);
1024 sv_scalespace_t dx
= pl
->data_x
;
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
1033 input
[i
] = _sv_scalespace_value(&pl
->data_y
,yline
);
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
);
1051 int step
= (1024+outputs
-1)/outputs
; // at least one
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
;
1060 for(i
=0;i
<this_step
;i
++){
1062 input
[xpos
] = _sv_scalespace_value(&dx
,i
+sofar
);
1063 pl
->o
->function(input
,outptr
); // func setup in an objective is immutable
1068 data_demultiplex_2d(pl
,p
,output
,yline
,sofar
,this_step
);
1070 slave
->data_demultiplex_2d(slave
,p
,output
,yline
,sofar
,this_step
);
1071 slave
= slave
->c
.share_next
;
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
1084 if(ih
!=dh
|| iw
!=dw
){
1085 /* resampled row computation; may involve multiple data rows */
1087 if(pl
->resample_ynumA
[i
]<=yline
&& pl
->resample_ynumB
[i
]>yline
)
1088 pl
->image_flags
[i
]=1;
1090 pl
->image_flags
[yline
]=1;
1091 slave
= slave
->c
.share_next
;
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
;
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
){
1110 for(i
=0;i
<pl
->scale
.n
;i
++)
1111 if(pl
->scale
.vals
[i
] != pl
->scale_widget
->label_vals
[i
]){
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
);
1127 pthread_mutex_lock(pl
->status_m
);
1128 _sv_slidermap_partial_update(&pl
->slider
,&pl
->slider_widget
);
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;
1139 pthread_rwlock_unlock(pl
->panel_m
);
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
;
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
);
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);
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
;
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];
1233 gtk_table_attach(GTK_TABLE(p2
->obj_table
),label
,0,1,i
,i
+1,
1236 /* mapping pulldown */
1238 GtkWidget
*menu
=_gtk_combo_box_new_markup();
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
]);
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
;
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,
1294 /* x/y radio buttons */
1295 if(!(d
->flags
& SV_DIM_NO_X
)){
1297 p2
->dim_xb
[i
] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x
),"X");
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,
1306 if(!(d
->flags
& SV_DIM_NO_Y
)){
1308 p2
->dim_yb
[i
] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_y
),"Y");
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,
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
,
1325 GTK_EXPAND
|GTK_FILL
,0,0,0);
1328 for(i
=0;i
<p
->dimensions
;i
++){
1330 g_signal_connect (G_OBJECT (p2
->dim_xb
[i
]), "toggled",
1331 G_CALLBACK (_sv_panel2d_dimchange_callback
), p
);
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.