fix up file renaming code a little bit
[ArdourMidi.git] / gtk2_ardour / canvas-waveview.c
blobc1383f087be12cca31fc81d4c55f6974eff43bdc
1 /*
2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 $Id$
21 #include <stdio.h>
22 #include <math.h>
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <cairo.h>
25 #include <string.h>
26 #include <limits.h>
28 #include "ardour/dB.h"
30 #include "logmeter.h"
31 #include "canvas-waveview.h"
32 #include "rgb_macros.h"
35 extern void c_stacktrace();
37 enum {
38 PROP_0,
39 PROP_DATA_SRC,
40 PROP_CHANNEL,
41 PROP_LENGTH_FUNCTION,
42 PROP_SOURCEFILE_LENGTH_FUNCTION,
43 PROP_PEAK_FUNCTION,
44 PROP_GAIN_FUNCTION,
45 PROP_GAIN_SRC,
46 PROP_CACHE,
47 PROP_CACHE_UPDATER,
48 PROP_SAMPLES_PER_UNIT,
49 PROP_AMPLITUDE_ABOVE_AXIS,
50 PROP_X,
51 PROP_Y,
52 PROP_HEIGHT,
53 PROP_WAVE_COLOR,
54 PROP_CLIP_COLOR,
55 PROP_ZERO_COLOR,
56 PROP_FILL_COLOR,
57 PROP_FILLED,
58 PROP_RECTIFIED,
59 PROP_ZERO_LINE,
60 PROP_REGION_START,
61 PROP_LOGSCALED,
64 static void gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class);
66 static void gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview);
68 static void gnome_canvas_waveview_destroy (GtkObject *object);
70 static void gnome_canvas_waveview_set_property (GObject *object,
71 guint prop_id,
72 const GValue *value,
73 GParamSpec *pspec);
74 static void gnome_canvas_waveview_get_property (GObject *object,
75 guint prop_id,
76 GValue *value,
77 GParamSpec *pspec);
79 static void gnome_canvas_waveview_update (GnomeCanvasItem *item,
80 double *affine,
81 ArtSVP *clip_path,
82 int flags);
84 static void gnome_canvas_waveview_bounds (GnomeCanvasItem *item,
85 double *x1,
86 double *y1,
87 double *x2,
88 double *y2);
90 static double gnome_canvas_waveview_point (GnomeCanvasItem *item,
91 double x,
92 double y,
93 int cx,
94 int cy,
95 GnomeCanvasItem **actual_item);
97 static void gnome_canvas_waveview_render (GnomeCanvasItem *item,
98 GnomeCanvasBuf *buf);
100 static void gnome_canvas_waveview_draw (GnomeCanvasItem *item,
101 GdkDrawable *drawable,
102 int x,
103 int y,
104 int w,
105 int h);
107 static void gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *,
108 void *);
110 static void gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *,
111 guint32);
113 static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview,
114 gulong start_sample,
115 gulong end_sample);
117 static GnomeCanvasItemClass *parent_class;
119 GType
120 gnome_canvas_waveview_get_type (void)
122 static GType waveview_type;
124 if (!waveview_type) {
125 static const GTypeInfo object_info = {
126 sizeof (GnomeCanvasWaveViewClass),
127 (GBaseInitFunc) NULL,
128 (GBaseFinalizeFunc) NULL,
129 (GClassInitFunc) gnome_canvas_waveview_class_init,
130 (GClassFinalizeFunc) NULL,
131 NULL, /* class_data */
132 sizeof (GnomeCanvasWaveView),
133 0, /* n_preallocs */
134 (GInstanceInitFunc) gnome_canvas_waveview_init,
135 NULL /* value_table */
138 waveview_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWaveView",
139 &object_info, 0);
142 return waveview_type;
145 static void
146 gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class)
148 GObjectClass *gobject_class;
149 GtkObjectClass *object_class;
150 GnomeCanvasItemClass *item_class;
152 gobject_class = (GObjectClass *) class;
153 object_class = (GtkObjectClass *) class;
154 item_class = (GnomeCanvasItemClass *) class;
156 parent_class = g_type_class_peek_parent (class);
158 gobject_class->set_property = gnome_canvas_waveview_set_property;
159 gobject_class->get_property = gnome_canvas_waveview_get_property;
161 g_object_class_install_property
162 (gobject_class,
163 PROP_DATA_SRC,
164 g_param_spec_pointer ("data_src", NULL, NULL,
165 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
167 g_object_class_install_property
168 (gobject_class,
169 PROP_CHANNEL,
170 g_param_spec_uint ("channel", NULL, NULL,
171 0, G_MAXUINT, 0,
172 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
175 (gobject_class,
176 PROP_LENGTH_FUNCTION,
177 g_param_spec_pointer ("length_function", NULL, NULL,
178 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180 g_object_class_install_property
181 (gobject_class,
182 PROP_SOURCEFILE_LENGTH_FUNCTION,
183 g_param_spec_pointer ("sourcefile_length_function", NULL, NULL,
184 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186 g_object_class_install_property
187 (gobject_class,
188 PROP_PEAK_FUNCTION,
189 g_param_spec_pointer ("peak_function", NULL, NULL,
190 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192 g_object_class_install_property
193 (gobject_class,
194 PROP_GAIN_FUNCTION,
195 g_param_spec_pointer ("gain_function", NULL, NULL,
196 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198 g_object_class_install_property
199 (gobject_class,
200 PROP_GAIN_SRC,
201 g_param_spec_pointer ("gain_src", NULL, NULL,
202 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
204 g_object_class_install_property
205 (gobject_class,
206 PROP_CACHE,
207 g_param_spec_pointer ("cache", NULL, NULL,
208 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
210 g_object_class_install_property
211 (gobject_class,
212 PROP_CACHE_UPDATER,
213 g_param_spec_boolean ("cache_updater", NULL, NULL,
214 FALSE,
215 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
217 g_object_class_install_property
218 (gobject_class,
219 PROP_SAMPLES_PER_UNIT,
220 g_param_spec_double ("samples_per_unit", NULL, NULL,
221 0.0, G_MAXDOUBLE, 0.0,
222 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
224 g_object_class_install_property
225 (gobject_class,
226 PROP_AMPLITUDE_ABOVE_AXIS,
227 g_param_spec_double ("amplitude_above_axis", NULL, NULL,
228 0.0, G_MAXDOUBLE, 0.0,
229 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231 g_object_class_install_property
232 (gobject_class,
233 PROP_X,
234 g_param_spec_double ("x", NULL, NULL,
235 0.0, G_MAXDOUBLE, 0.0,
236 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
238 g_object_class_install_property
239 (gobject_class,
240 PROP_Y,
241 g_param_spec_double ("y", NULL, NULL,
242 0.0, G_MAXDOUBLE, 0.0,
243 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
245 g_object_class_install_property
246 (gobject_class,
247 PROP_HEIGHT,
248 g_param_spec_double ("height", NULL, NULL,
249 0.0, G_MAXDOUBLE, 0.0,
250 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
252 g_object_class_install_property
253 (gobject_class,
254 PROP_WAVE_COLOR,
255 g_param_spec_uint ("wave_color", NULL, NULL,
256 0, G_MAXUINT, 0,
257 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
259 g_object_class_install_property
260 (gobject_class,
261 PROP_CLIP_COLOR,
262 g_param_spec_uint ("clip_color", NULL, NULL,
263 0, G_MAXUINT, 0,
264 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
266 g_object_class_install_property
267 (gobject_class,
268 PROP_ZERO_COLOR,
269 g_param_spec_uint ("zero_color", NULL, NULL,
270 0, G_MAXUINT, 0,
271 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
273 g_object_class_install_property
274 (gobject_class,
275 PROP_FILL_COLOR,
276 g_param_spec_uint ("fill_color", NULL, NULL,
277 0, G_MAXUINT, 0,
278 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
280 g_object_class_install_property
281 (gobject_class,
282 PROP_FILLED,
283 g_param_spec_boolean ("filled", NULL, NULL,
284 FALSE,
285 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
287 g_object_class_install_property
288 (gobject_class,
289 PROP_RECTIFIED,
290 g_param_spec_boolean ("rectified", NULL, NULL,
291 FALSE,
292 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
294 g_object_class_install_property
295 (gobject_class,
296 PROP_ZERO_LINE,
297 g_param_spec_boolean ("zero_line", NULL, NULL,
298 FALSE,
299 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
301 g_object_class_install_property
302 (gobject_class,
303 PROP_LOGSCALED,
304 g_param_spec_boolean ("logscaled", NULL, NULL,
305 FALSE,
306 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
308 g_object_class_install_property
309 (gobject_class,
310 PROP_REGION_START,
311 g_param_spec_uint ("region_start", NULL, NULL,
312 0, G_MAXUINT, 0,
313 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
315 object_class->destroy = gnome_canvas_waveview_destroy;
317 item_class->update = gnome_canvas_waveview_update;
318 item_class->bounds = gnome_canvas_waveview_bounds;
319 item_class->point = gnome_canvas_waveview_point;
320 item_class->render = gnome_canvas_waveview_render;
321 item_class->draw = gnome_canvas_waveview_draw;
324 GnomeCanvasWaveViewCache*
325 gnome_canvas_waveview_cache_new ()
327 GnomeCanvasWaveViewCache *c;
329 c = g_malloc (sizeof (GnomeCanvasWaveViewCache));
331 c->allocated = 2048;
332 c->data = g_malloc (sizeof (GnomeCanvasWaveViewCacheEntry) * c->allocated);
333 c->data_size = 0;
334 c->start = 0;
335 c->end = 0;
337 return c;
340 void
341 gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache* cache)
343 g_free (cache->data);
344 g_free (cache);
347 static void
348 gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
350 waveview->x = 0.0;
351 waveview->y = 0.0;
352 waveview->cache = 0;
353 waveview->cache_updater = FALSE;
354 waveview->data_src = NULL;
355 waveview->channel = 0;
356 waveview->peak_function = NULL;
357 waveview->length_function = NULL;
358 waveview->sourcefile_length_function = NULL;
359 waveview->gain_curve_function = NULL;
360 waveview->gain_src = NULL;
361 waveview->rectified = FALSE;
362 waveview->logscaled = FALSE;
363 waveview->filled = TRUE;
364 waveview->zero_line = FALSE;
365 waveview->region_start = 0;
366 waveview->samples_per_unit = 1.0;
367 waveview->amplitude_above_axis = 1.0;
368 waveview->height = 100.0;
369 waveview->screen_width = gdk_screen_width ();
370 waveview->reload_cache_in_render = FALSE;
372 waveview->wave_color = 0;
373 waveview->clip_color = 0;
374 waveview->zero_color = 0;
375 waveview->fill_color = 0;
378 static void
379 gnome_canvas_waveview_destroy (GtkObject *object)
381 GnomeCanvasWaveView *waveview;
383 g_return_if_fail (object != NULL);
384 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
386 waveview = GNOME_CANVAS_WAVEVIEW (object);
388 if (GTK_OBJECT_CLASS (parent_class)->destroy)
389 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
392 #define DEBUG_CACHE 0
393 #undef CACHE_MEMMOVE_OPTIMIZATION
395 /** @return cache index of start_sample within the cache */
396 static guint32
397 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview, gulong start_sample, gulong end_sample)
399 gulong required_cache_entries;
400 gulong rf1, rf2,rf3, required_frames;
401 gulong new_cache_start, new_cache_end;
402 gulong half_width;
403 gulong npeaks;
404 gulong offset;
405 gulong ostart;
406 gulong copied;
407 GnomeCanvasWaveViewCache *cache;
408 float* gain;
409 #ifdef CACHE_MEMMOVE_OPTIMIZATION
410 gulong present_frames;
411 gulong present_entries;
412 #endif
414 cache = waveview->cache;
416 start_sample = start_sample + waveview->region_start;
417 end_sample = end_sample + waveview->region_start;
418 #if DEBUG_CACHE
419 // printf("waveview->region_start == %lu\n",waveview->region_start);
420 // c_stacktrace ();
421 printf ("\n\n=> 0x%x cache @ 0x%x range: %lu - %lu request: %lu - %lu (%lu frames)\n",
422 waveview, cache,
423 cache->start, cache->end,
424 start_sample, end_sample, end_sample - start_sample);
425 #endif
427 if (cache->start <= start_sample && cache->end >= end_sample) {
428 #if DEBUG_CACHE
429 // printf ("0x%x: cache hit for %lu-%lu (cache holds: %lu-%lu\n",
430 // waveview, start_sample, end_sample, cache->start, cache->end);
431 #endif
432 goto out;
435 /* make sure the cache is at least twice as wide as the screen width, and put the start sample
436 in the middle, ensuring that we cover the end_sample.
439 /* Note the assumption that we have a 1:1 units:pixel ratio for the canvas. Its everywhere ... */
441 half_width = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit)/2.0 + 0.5);
443 if (start_sample < half_width) {
444 new_cache_start = 0;
445 } else {
446 new_cache_start = start_sample - half_width;
449 /* figure out how many frames we want */
451 rf1 = end_sample - start_sample + 1;
452 rf2 = (gulong) floor ((waveview->screen_width * waveview->samples_per_unit * 2.0f));
453 required_frames = MAX(rf1,rf2);
455 /* but make sure it doesn't extend beyond the end of the source material */
457 rf3 = (gulong) (waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit)) + 1;
458 if (rf3 < new_cache_start) {
459 rf3 = 0;
460 } else {
461 rf3 -= new_cache_start;
464 #if DEBUG_CACHE
465 fprintf (stderr, "AVAILABLE FRAMES = %lu of %lu, start = %lu, sstart = %lu, cstart = %lu\n",
466 rf3, waveview->sourcefile_length_function (waveview->data_src, waveview->samples_per_unit),
467 waveview->region_start, start_sample, new_cache_start);
468 #endif
470 required_frames = MIN(required_frames,rf3);
472 new_cache_end = new_cache_start + required_frames - 1;
474 required_cache_entries = (gulong) floor (required_frames / waveview->samples_per_unit );
476 #if DEBUG_CACHE
477 fprintf (stderr, "new cache = %lu - %lu\n", new_cache_start, new_cache_end);
478 fprintf(stderr,"required_cach_entries = %lu, samples_per_unit = %f req frames = %lu\n",
479 required_cache_entries,waveview->samples_per_unit, required_frames);
480 #endif
482 if (required_cache_entries > cache->allocated) {
483 cache->data = g_realloc (cache->data, sizeof (GnomeCanvasWaveViewCacheEntry) * required_cache_entries);
484 cache->allocated = required_cache_entries;
485 // cache->start = 0;
486 // cache->end = 0;
489 ostart = new_cache_start;
491 #ifdef CACHE_MEMMOVE_OPTIMIZATION
493 /* data is not entirely in the cache, so go fetch it, making sure to fill the cache */
495 /* some of the required cache entries are in the cache, but in the wrong
496 locations. use memmove to fix this.
499 if (cache->start < new_cache_start && new_cache_start < cache->end) {
501 /* case one: the common area is at the end of the existing cache. move it
502 to the beginning of the cache, and set up to refill whatever remains.
505 wv->cache_start wv->cache_end
506 |-------------------------------------------------------| cache
507 |--------------------------------| requested
508 <------------------->
509 "present"
510 new_cache_start new_cache_end
514 present_frames = cache->end - new_cache_start;
515 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
517 #if DEBUG_CACHE
518 fprintf (stderr, "existing material at end of current cache, move to start of new cache\n"
519 "\tcopy from %lu to start\n", cache->data_size - present_entries);
520 #endif
522 memmove (&cache->data[0],
523 &cache->data[cache->data_size - present_entries],
524 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
526 #if DEBUG_CACHE
527 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
528 present_frames, required_frames, present_entries, new_cache_start + present_entries,
529 cache->data + present_entries);
530 #endif
532 copied = present_entries;
533 offset = present_entries;
534 new_cache_start += present_frames;
535 required_frames -= present_frames;
537 } else if (new_cache_end > cache->start && new_cache_end < cache->end) {
539 /* case two: the common area lives at the beginning of the existing cache.
541 wv->cache_start wv->cache_end
542 |-----------------------------------------------------|
543 |--------------------------------|
544 <----------------->
545 "present"
547 new_cache_start new_cache_end
550 present_frames = new_cache_end - cache->start;
551 present_entries = (gulong) floor (present_frames / waveview->samples_per_unit);
553 memmove (&cache->data[cache->data_size - present_entries],
554 &cache->data[0],
555 present_entries * sizeof (GnomeCanvasWaveViewCacheEntry));
557 #if DEBUG_CACHE
558 fprintf (stderr, "existing material at start of current cache, move to start of end cache\n");
559 #endif
561 #if DEBUG_CACHE
562 fprintf (stderr, "satisfied %lu of %lu frames, offset = %lu, will start at %lu (ptr = 0x%x)\n",
563 present_entries, required_frames, present_entries, new_cache_start + present_entries,
564 cache->data + present_entries);
565 #endif
567 copied = present_entries;
568 offset = 0;
569 required_frames -= present_frames;
572 } else {
573 copied = 0;
574 offset = 0;
578 #else
579 copied = 0;
580 offset = 0;
582 #endif /* CACHE_MEMMOVE_OPTIMIZATION */
584 // fprintf(stderr,"length == %lu\n",waveview->length_function (waveview->data_src));
585 // required_frames = MIN (waveview->length_function (waveview->data_src) - new_cache_start, required_frames);
587 npeaks = (gulong) floor (required_frames / waveview->samples_per_unit);
588 required_frames = npeaks * waveview->samples_per_unit;
590 #if DEBUG_CACHE
593 printf ("requesting %lu/%f to cover %lu-%lu at %f spu (request was %lu-%lu) into cache + %lu\n",
594 required_frames, required_frames/waveview->samples_per_unit, new_cache_start, new_cache_end,
595 waveview->samples_per_unit, start_sample, end_sample, offset);
596 #endif
598 #if DEBUG_CACHE
599 // printf ("cache holds %lu entries, requesting %lu to cover %lu-%lu (request was %lu-%lu)\n",
600 // cache->data_size, npeaks, new_cache_start, new_cache_end,
601 // start_sample, end_sample);
602 #endif
604 if (required_frames) {
605 waveview->peak_function (waveview->data_src, npeaks, new_cache_start, required_frames, cache->data + offset, waveview->channel,waveview->samples_per_unit);
607 /* take into account any copied peaks */
609 npeaks += copied;
610 } else {
611 npeaks = copied;
614 if (npeaks < cache->allocated) {
615 #if DEBUG_CACHE
616 fprintf (stderr, "zero fill cache for %lu at %lu\n", cache->allocated - npeaks, npeaks);
617 #endif
618 memset (&cache->data[npeaks], 0, sizeof (GnomeCanvasWaveViewCacheEntry) * (cache->allocated - npeaks));
619 cache->data_size = npeaks;
620 } else {
621 cache->data_size = cache->allocated;
624 if (waveview->gain_curve_function) {
625 guint32 n;
627 gain = (float*) malloc (sizeof (float) * cache->data_size);
629 waveview->gain_curve_function (waveview->gain_src, new_cache_start, new_cache_end, gain, cache->data_size);
631 for (n = 0; n < cache->data_size; ++n) {
632 cache->data[n].min *= gain[n];
633 cache->data[n].max *= gain[n];
636 free (gain);
640 /* do optional log scaling. this implementation is not particularly efficient */
642 if (waveview->logscaled) {
643 guint32 n;
644 GnomeCanvasWaveViewCacheEntry* buf = cache->data;
646 for (n = 0; n < cache->data_size; ++n) {
648 if (buf[n].max > 0.0f) {
649 buf[n].max = alt_log_meter(fast_coefficient_to_dB(buf[n].max));
650 } else if (buf[n].max < 0.0f) {
651 buf[n].max = -alt_log_meter(fast_coefficient_to_dB(-buf[n].max));
654 if (buf[n].min > 0.0f) {
655 buf[n].min = alt_log_meter(fast_coefficient_to_dB(buf[n].min));
656 } else if (buf[n].min < 0.0f) {
657 buf[n].min = -alt_log_meter(fast_coefficient_to_dB(-buf[n].min));
662 cache->start = ostart;
663 cache->end = new_cache_end;
665 out:
666 #if DEBUG_CACHE
667 fprintf (stderr, "return cache index = %d\n",
668 (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5));
669 #endif
670 return (guint32) floor ((((double) (start_sample - cache->start)) / waveview->samples_per_unit) + 0.5);
674 void
675 gnome_canvas_waveview_set_data_src (GnomeCanvasWaveView *waveview, void *data_src)
678 if (waveview->cache_updater) {
679 if (waveview->data_src == data_src) {
680 waveview->reload_cache_in_render = TRUE;
681 return;
684 waveview->cache->start = 0;
685 waveview->cache->end = 0;
688 waveview->data_src = data_src;
691 void
692 gnome_canvas_waveview_set_channel (GnomeCanvasWaveView *waveview, guint32 chan)
694 if (waveview->channel == chan) {
695 return;
698 waveview->channel = chan;
701 static void
702 gnome_canvas_waveview_reset_bounds (GnomeCanvasItem *item)
705 double x1, x2, y1, y2;
706 ArtPoint i1, i2;
707 ArtPoint w1, w2;
708 int Ix1, Ix2, Iy1, Iy2;
709 double i2w[6];
711 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
713 i1.x = x1;
714 i1.y = y1;
715 i2.x = x2;
716 i2.y = y2;
718 gnome_canvas_item_i2w_affine (item, i2w);
719 art_affine_point (&w1, &i1, i2w);
720 art_affine_point (&w2, &i2, i2w);
722 Ix1 = (int) rint(w1.x);
723 Ix2 = (int) rint(w2.x);
724 Iy1 = (int) rint(w1.y);
725 Iy2 = (int) rint(w2.y);
727 gnome_canvas_update_bbox (item, Ix1, Iy1, Ix2, Iy2);
731 * CANVAS CALLBACKS
734 static void
735 gnome_canvas_waveview_set_property (GObject *object,
736 guint prop_id,
737 const GValue *value,
738 GParamSpec *pspec)
741 (void) pspec;
743 GnomeCanvasItem *item;
744 GnomeCanvasWaveView *waveview;
745 int redraw = FALSE;
746 int calc_bounds = FALSE;
748 g_return_if_fail (object != NULL);
749 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
751 item = GNOME_CANVAS_ITEM (object);
752 waveview = GNOME_CANVAS_WAVEVIEW (object);
754 switch (prop_id) {
755 case PROP_DATA_SRC:
756 gnome_canvas_waveview_set_data_src (waveview, g_value_get_pointer(value));
757 redraw = TRUE;
758 break;
760 case PROP_CHANNEL:
761 gnome_canvas_waveview_set_channel (waveview, g_value_get_uint(value));
762 redraw = TRUE;
763 break;
765 case PROP_LENGTH_FUNCTION:
766 waveview->length_function = (waveview_length_function_t) g_value_get_pointer(value);
767 redraw = TRUE;
768 break;
770 case PROP_SOURCEFILE_LENGTH_FUNCTION:
771 waveview->sourcefile_length_function = (waveview_sourcefile_length_function_t) g_value_get_pointer(value);
772 redraw = TRUE;
773 break;
775 case PROP_PEAK_FUNCTION:
776 waveview->peak_function = (waveview_peak_function_t) g_value_get_pointer(value);
777 redraw = TRUE;
778 break;
780 case PROP_GAIN_FUNCTION:
781 waveview->gain_curve_function = (waveview_gain_curve_function_t) g_value_get_pointer(value);
782 redraw = TRUE;
783 break;
785 case PROP_GAIN_SRC:
786 waveview->gain_src = g_value_get_pointer(value);
787 if (waveview->cache_updater) {
788 waveview->cache->start = 0;
789 waveview->cache->end = 0;
791 redraw = TRUE;
792 calc_bounds = TRUE;
793 break;
795 case PROP_CACHE:
796 waveview->cache = g_value_get_pointer(value);
797 redraw = TRUE;
798 break;
801 case PROP_CACHE_UPDATER:
802 waveview->cache_updater = g_value_get_boolean(value);
803 redraw = TRUE;
804 break;
806 case PROP_SAMPLES_PER_UNIT:
807 if ((waveview->samples_per_unit = g_value_get_double(value)) < 1.0) {
808 waveview->samples_per_unit = 1.0;
810 if (waveview->cache_updater) {
811 waveview->cache->start = 0;
812 waveview->cache->end = 0;
814 redraw = TRUE;
815 calc_bounds = TRUE;
816 break;
818 case PROP_AMPLITUDE_ABOVE_AXIS:
819 waveview->amplitude_above_axis = g_value_get_double(value);
820 redraw = TRUE;
821 break;
823 case PROP_X:
824 if (waveview->x != g_value_get_double (value)) {
825 waveview->x = g_value_get_double (value);
826 calc_bounds = TRUE;
828 break;
830 case PROP_Y:
831 if (waveview->y != g_value_get_double (value)) {
832 waveview->y = g_value_get_double (value);
833 calc_bounds = TRUE;
835 break;
837 case PROP_HEIGHT:
838 if (waveview->height != fabs (g_value_get_double (value))) {
839 waveview->height = fabs (g_value_get_double (value));
840 redraw = TRUE;
842 break;
844 case PROP_WAVE_COLOR:
845 if (waveview->wave_color != g_value_get_uint(value)) {
846 waveview->wave_color = g_value_get_uint(value);
847 redraw = TRUE;
849 break;
851 case PROP_CLIP_COLOR:
852 if (waveview->clip_color != g_value_get_uint(value)) {
853 waveview->clip_color = g_value_get_uint(value);
854 redraw = TRUE;
856 break;
858 case PROP_ZERO_COLOR:
859 if (waveview->zero_color != g_value_get_uint(value)) {
860 waveview->zero_color = g_value_get_uint(value);
861 redraw = TRUE;
863 break;
865 case PROP_FILL_COLOR:
866 if (waveview->fill_color != g_value_get_uint(value)) {
867 waveview->fill_color = g_value_get_uint(value);
868 redraw = TRUE;
870 break;
872 case PROP_FILLED:
873 if (waveview->filled != g_value_get_boolean(value)) {
874 waveview->filled = g_value_get_boolean(value);
875 redraw = TRUE;
877 break;
879 case PROP_RECTIFIED:
880 if (waveview->rectified != g_value_get_boolean(value)) {
881 waveview->rectified = g_value_get_boolean(value);
882 redraw = TRUE;
884 break;
886 case PROP_ZERO_LINE:
887 if (waveview->zero_line != g_value_get_boolean(value)) {
888 waveview->zero_line = g_value_get_boolean(value);
889 redraw = TRUE;
891 break;
893 case PROP_LOGSCALED:
894 if (waveview->logscaled != g_value_get_boolean(value)) {
895 waveview->logscaled = g_value_get_boolean(value);
896 if (waveview->cache_updater) {
897 waveview->cache->start = 0;
898 waveview->cache->end = 0;
900 redraw = TRUE;
901 calc_bounds = TRUE;
903 break;
904 case PROP_REGION_START:
905 waveview->region_start = g_value_get_uint(value);
906 redraw = TRUE;
907 calc_bounds = TRUE;
908 break;
911 default:
912 break;
915 if (calc_bounds) {
916 gnome_canvas_waveview_reset_bounds (item);
919 if (redraw) {
920 gnome_canvas_item_request_update (item);
925 static void
926 gnome_canvas_waveview_get_property (
927 GObject *object,
928 guint prop_id,
929 GValue *value,
930 GParamSpec *pspec)
934 g_return_if_fail (object != NULL);
935 g_return_if_fail (GNOME_IS_CANVAS_WAVEVIEW (object));
937 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (object);
939 switch (prop_id) {
940 case PROP_DATA_SRC:
941 g_value_set_pointer(value, waveview->data_src);
942 break;
944 case PROP_CHANNEL:
945 g_value_set_uint(value, waveview->channel);
946 break;
948 case PROP_LENGTH_FUNCTION:
949 g_value_set_pointer(value, (void*) waveview->length_function);
950 break;
952 case PROP_SOURCEFILE_LENGTH_FUNCTION:
953 g_value_set_pointer(value, (void*) waveview->sourcefile_length_function);
954 break;
956 case PROP_PEAK_FUNCTION:
957 g_value_set_pointer(value, (void*) waveview->peak_function);
958 break;
960 case PROP_GAIN_FUNCTION:
961 g_value_set_pointer(value, (void*) waveview->gain_curve_function);
962 break;
964 case PROP_GAIN_SRC:
965 g_value_set_pointer(value, waveview->gain_src);
966 break;
968 case PROP_CACHE:
969 g_value_set_pointer(value, waveview->cache);
970 break;
972 case PROP_CACHE_UPDATER:
973 g_value_set_boolean(value, waveview->cache_updater);
974 break;
976 case PROP_SAMPLES_PER_UNIT:
977 g_value_set_double(value, waveview->samples_per_unit);
978 break;
980 case PROP_AMPLITUDE_ABOVE_AXIS:
981 g_value_set_double(value, waveview->amplitude_above_axis);
982 break;
984 case PROP_X:
985 g_value_set_double (value, waveview->x);
986 break;
988 case PROP_Y:
989 g_value_set_double (value, waveview->y);
990 break;
992 case PROP_HEIGHT:
993 g_value_set_double (value, waveview->height);
994 break;
996 case PROP_WAVE_COLOR:
997 g_value_set_uint (value, waveview->wave_color);
998 break;
1000 case PROP_CLIP_COLOR:
1001 g_value_set_uint (value, waveview->clip_color);
1002 break;
1004 case PROP_ZERO_COLOR:
1005 g_value_set_uint (value, waveview->zero_color);
1006 break;
1008 case PROP_FILL_COLOR:
1009 g_value_set_uint (value, waveview->fill_color);
1010 break;
1012 case PROP_FILLED:
1013 g_value_set_boolean (value, waveview->filled);
1014 break;
1016 case PROP_RECTIFIED:
1017 g_value_set_boolean (value, waveview->rectified);
1018 break;
1020 case PROP_ZERO_LINE:
1021 g_value_set_boolean (value, waveview->zero_line);
1022 break;
1024 case PROP_LOGSCALED:
1025 g_value_set_boolean (value, waveview->logscaled);
1026 break;
1028 case PROP_REGION_START:
1029 g_value_set_uint (value, waveview->region_start);
1030 break;
1032 default:
1033 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1034 break;
1038 static void
1039 gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1041 GnomeCanvasWaveView *waveview;
1042 double x, y;
1044 waveview = GNOME_CANVAS_WAVEVIEW (item);
1046 // check_cache (waveview, "start of update");
1048 if (parent_class->update)
1049 (* parent_class->update) (item, affine, clip_path, flags);
1051 gnome_canvas_waveview_reset_bounds (item);
1053 /* get the canvas coordinates of the view. Do NOT use affines
1054 for this, because they do not round to the integer units used
1055 by the canvas, resulting in subtle pixel-level errors later.
1058 x = waveview->x;
1059 y = waveview->y;
1061 gnome_canvas_item_i2w (item, &x, &y);
1062 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_ulx, &waveview->bbox_uly);
1064 waveview->samples = waveview->length_function (waveview->data_src);
1066 x = waveview->x + (waveview->samples / waveview->samples_per_unit);
1067 y = waveview->y + waveview->height;
1069 gnome_canvas_item_i2w (item, &x, &y);
1070 gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x, y, &waveview->bbox_lrx, &waveview->bbox_lry);
1072 /* cache the half-height and the end point in canvas units */
1074 waveview->half_height = waveview->height / 2.0;
1076 /* parse the color */
1078 UINT_TO_RGBA (waveview->wave_color, &waveview->wave_r, &waveview->wave_g, &waveview->wave_b,
1079 &waveview->wave_a);
1080 UINT_TO_RGBA (waveview->clip_color, &waveview->clip_r, &waveview->clip_g, &waveview->clip_b,
1081 &waveview->clip_a);
1082 UINT_TO_RGBA (waveview->fill_color, &waveview->fill_r, &waveview->fill_g, &waveview->fill_b,
1083 &waveview->fill_a);
1085 // check_cache (waveview, "end of update");
1088 static void
1089 gnome_canvas_waveview_render (GnomeCanvasItem *item,
1090 GnomeCanvasBuf *buf)
1092 GnomeCanvasWaveView *waveview;
1093 gulong s1, s2;
1094 int clip_length = 0;
1095 int pymin, pymax;
1096 guint cache_index;
1097 double half_height;
1098 int x;
1099 char rectify;
1101 waveview = GNOME_CANVAS_WAVEVIEW (item);
1103 // check_cache (waveview, "start of render");
1105 if (parent_class->render) {
1106 (*parent_class->render) (item, buf);
1109 if (buf->is_bg) {
1110 gnome_canvas_buf_ensure_buf (buf);
1111 buf->is_bg = FALSE;
1114 /* a "unit" means a pixel */
1116 /* begin: render start x (units) */
1117 int const begin = MAX (waveview->bbox_ulx, buf->rect.x0);
1119 /* zbegin: start x for zero line (units) */
1120 int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin;
1122 /* end: render end x (units) */
1123 int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1;
1125 /* zend: end x for zero-line (units) */
1126 int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end;
1128 if (begin == end) {
1129 return;
1132 /* s1: start sample
1133 s2: end sample
1136 s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit);
1138 // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit);
1140 if (end == waveview->bbox_lrx) {
1141 /* This avoids minor rounding errors when we have the
1142 entire region visible.
1144 s2 = waveview->samples;
1145 } else {
1146 s2 = s1 + floor ((end - begin) * waveview->samples_per_unit);
1149 #if 0
1150 printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)"
1151 " b/e %d..%d s= %lu..%lu @ %f\n",
1152 waveview,
1153 buf->rect.x0,
1154 buf->rect.x1,
1155 buf->rect.y0,
1156 buf->rect.y1,
1157 waveview->bbox_ulx,
1158 waveview->bbox_lrx,
1159 waveview->bbox_uly,
1160 waveview->bbox_lry,
1161 begin, end, s1, s2,
1162 waveview->samples_per_unit);
1163 #endif
1165 /* now ensure that the cache is full and properly
1166 positioned.
1169 // check_cache (waveview, "pre-ensure");
1171 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1172 waveview->cache->start = 0;
1173 waveview->cache->end = 0;
1174 waveview->reload_cache_in_render = FALSE;
1177 // check_cache (waveview, "post-ensure");
1179 /* don't rectify at single-sample zoom */
1180 if (waveview->rectified && waveview->samples_per_unit > 1) {
1181 rectify = TRUE;
1183 else {
1184 rectify = FALSE;
1187 clip_length = MIN(5,(waveview->height/4));
1190 Now draw each line, clipping it appropriately. The clipping
1191 is done by the macros PAINT_FOO().
1194 half_height = waveview->half_height;
1196 /* this makes it slightly easier to comprehend whats going on */
1197 #define origin half_height
1199 if (waveview->filled && !rectify) {
1200 int prev_pymin = 1;
1201 int prev_pymax = 0;
1202 int last_pymin = 1;
1203 int last_pymax = 0;
1204 int next_pymin, next_pymax;
1205 double max, min;
1206 int next_clip_max = 0;
1207 int next_clip_min = 0;
1209 if (s1 < waveview->samples_per_unit) {
1210 /* we haven't got a prev vars to compare with, so outline the whole line here */
1211 prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1212 prev_pymin = prev_pymax;
1214 else {
1215 s1 -= waveview->samples_per_unit;
1218 if(end == waveview->bbox_lrx) {
1219 /* we don't have the NEXT vars for the last sample */
1220 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1221 last_pymin = last_pymax;
1223 else {
1224 s2 += waveview->samples_per_unit;
1227 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1230 * Compute the variables outside the rendering rect
1232 if(prev_pymax != prev_pymin) {
1234 prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1235 prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1236 ++cache_index;
1238 if(last_pymax != last_pymin) {
1239 /* take the index of one sample right of what we render */
1240 guint index = cache_index + (end - begin);
1242 if (index >= waveview->cache->data_size) {
1244 /* the data we want is off the end of the cache, which must mean its beyond
1245 the end of the region's source; hence the peak values are 0 */
1246 last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1247 last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1249 } else {
1251 last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit);
1252 last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit);
1259 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1261 max = waveview->cache->data[cache_index].max;
1262 min = waveview->cache->data[cache_index].min;
1264 if (max >= 1.0) {
1265 max = 1.0;
1266 next_clip_max = 1;
1269 if (min <= -1.0) {
1270 min = -1.0;
1271 next_clip_min = 1;
1274 max *= half_height;
1275 min *= half_height;
1277 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1278 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1281 * And now the loop
1283 for(x = begin; x < end; ++x) {
1284 int clip_max = next_clip_max;
1285 int clip_min = next_clip_min;
1286 int fill_max, fill_min;
1288 pymax = next_pymax;
1289 pymin = next_pymin;
1291 /* compute next */
1292 if(x == end - 1) {
1293 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1294 next_pymax = last_pymax;
1295 next_pymin = last_pymin;
1297 else {
1298 ++cache_index;
1300 if (cache_index < waveview->cache->data_size) {
1301 max = waveview->cache->data[cache_index].max;
1302 min = waveview->cache->data[cache_index].min;
1303 } else {
1304 max = min = 0;
1307 next_clip_max = 0;
1308 next_clip_min = 0;
1310 if (max >= 1.0) {
1311 max = 1.0;
1312 next_clip_max = 1;
1315 if (min <= -1.0) {
1316 min = -1.0;
1317 next_clip_min = 1;
1320 max *= half_height;
1321 min *= half_height;
1323 next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1324 next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1327 /* render */
1328 if (pymax == pymin) {
1329 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1330 } else {
1331 if((prev_pymax < pymax && next_pymax < pymax) ||
1332 (prev_pymax == pymax && next_pymax == pymax)) {
1333 fill_max = pymax + 1;
1334 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1336 else {
1337 fill_max = MAX(prev_pymax, next_pymax);
1338 if(pymax == fill_max) {
1339 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1340 ++fill_max;
1342 else {
1343 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1347 if((prev_pymin > pymin && next_pymin > pymin) ||
1348 (prev_pymin == pymin && next_pymin == pymin)) {
1349 fill_min = pymin - 1;
1350 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1);
1352 else {
1353 fill_min = MIN(prev_pymin, next_pymin);
1354 if(pymin == fill_min) {
1355 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1357 else {
1358 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin);
1362 if(fill_max < fill_min) {
1363 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min);
1365 else if(fill_max == fill_min) {
1366 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max);
1370 if (clip_max) {
1371 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1374 if (clip_min) {
1375 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1378 prev_pymax = pymax;
1379 prev_pymin = pymin;
1382 } else if (waveview->filled && rectify) {
1384 int prev_pymax = -1;
1385 int last_pymax = -1;
1386 int next_pymax;
1387 double max, min;
1388 int next_clip_max = 0;
1389 int next_clip_min = 0;
1391 // for rectified, this stays constant throughout the loop
1392 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1394 if(s1 < waveview->samples_per_unit) {
1395 /* we haven't got a prev vars to compare with, so outline the whole line here */
1396 prev_pymax = pymin;
1398 else {
1399 s1 -= waveview->samples_per_unit;
1402 if(end == waveview->bbox_lrx) {
1403 /* we don't have the NEXT vars for the last sample */
1404 last_pymax = pymin;
1406 else {
1407 s2 += waveview->samples_per_unit;
1410 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1413 * Compute the variables outside the rendering rect
1415 if(prev_pymax < 0) {
1416 max = MIN(waveview->cache->data[cache_index].max, 1.0);
1417 min = MAX(waveview->cache->data[cache_index].min, -1.0);
1419 if (fabs (min) > fabs (max)) {
1420 max = fabs (min);
1423 prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1424 ++cache_index;
1426 if(last_pymax < 0) {
1427 /* take the index of one sample right of what we render */
1428 int index = cache_index + (end - begin);
1430 max = MIN(waveview->cache->data[index].max, 1.0);
1431 min = MAX(waveview->cache->data[index].min, -1.0);
1433 if (fabs (min) > fabs (max)) {
1434 max = fabs (min);
1437 last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1441 * initialize NEXT* variables for the first run, duplicated in the loop for speed
1443 max = waveview->cache->data[cache_index].max;
1444 min = waveview->cache->data[cache_index].min;
1446 if (max >= 1.0) {
1447 max = 1.0;
1448 next_clip_max = 1;
1451 if (min <= -1.0) {
1452 min = -1.0;
1453 next_clip_min = 1;
1456 if (fabs (min) > fabs (max)) {
1457 max = fabs (min);
1460 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1463 * And now the loop
1465 for(x = begin; x < end; ++x) {
1466 int clip_max = next_clip_max;
1467 int clip_min = next_clip_min;
1468 int fill_max;
1470 pymax = next_pymax;
1472 /* compute next */
1473 if(x == end - 1) {
1474 /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/
1475 next_pymax = last_pymax;
1477 else {
1478 ++cache_index;
1480 max = waveview->cache->data[cache_index].max;
1481 min = waveview->cache->data[cache_index].min;
1483 if (max >= 1.0) {
1484 max = 1.0;
1485 next_clip_max = 1;
1488 if (min <= -1.0) {
1489 min = -1.0;
1490 next_clip_min = 1;
1493 if (fabs (min) > fabs (max)) {
1494 max = fabs (min);
1497 next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit);
1500 /* render */
1501 if (pymax == pymin) {
1502 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1503 } else {
1504 if((prev_pymax < pymax && next_pymax < pymax) ||
1505 (prev_pymax == pymax && next_pymax == pymax)) {
1506 fill_max = pymax + 1;
1507 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1509 else {
1510 fill_max = MAX(prev_pymax, next_pymax);
1511 if(pymax == fill_max) {
1512 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax);
1513 ++fill_max;
1515 else {
1516 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max);
1520 if(fill_max < pymin) {
1521 PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin);
1523 else if(fill_max == pymin) {
1524 PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin);
1528 if (clip_max) {
1529 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1532 if (clip_min) {
1533 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1536 prev_pymax = pymax;
1539 else {
1540 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1542 for (x = begin; x < end; x++) {
1544 double max, min;
1545 int clip_max, clip_min;
1547 clip_max = 0;
1548 clip_min = 0;
1550 max = waveview->cache->data[cache_index].max;
1551 min = waveview->cache->data[cache_index].min;
1553 if (max >= 1.0) {
1554 max = 1.0;
1555 clip_max = 1;
1558 if (min <= -1.0) {
1559 min = -1.0;
1560 clip_min = 1;
1563 if (rectify) {
1565 if (fabs (min) > fabs (max)) {
1566 max = fabs (min);
1569 max = max * waveview->height;
1571 pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit);
1572 pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit);
1574 } else {
1576 max = max * half_height;
1577 min = min * half_height;
1579 pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit);
1580 pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit);
1583 /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax,
1584 or, if samples_per_unit == 1, then a dot at each location.
1587 if (pymax == pymin) {
1588 PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin);
1589 } else {
1590 PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin);
1593 /* show clipped waveforms with small red lines */
1595 if (clip_max) {
1596 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length);
1599 if (clip_min) {
1600 PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin);
1603 /* presto, we're done */
1605 cache_index++;
1609 if (!waveview->rectified && waveview->zero_line) {
1610 // Paint zeroline.
1611 //PAINT_HORIZA(buf, waveview->zero_r, waveview->zero_g, waveview->zero_b, waveview->zero_a, begin, endi-1, origin );
1613 unsigned char zero_r, zero_g, zero_b, zero_a;
1614 UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a );
1615 int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit);
1616 PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y);
1618 #undef origin
1622 static void
1623 gnome_canvas_waveview_draw (GnomeCanvasItem *item,
1624 GdkDrawable *drawable,
1625 int x, int y,
1626 int width, int height)
1628 GnomeCanvasWaveView *waveview;
1629 cairo_t* cr;
1630 gulong s1, s2;
1631 int cache_index;
1632 gboolean rectify;
1633 double origin;
1634 double clip_length;
1635 double xoff;
1636 double yoff = 0.0;
1637 double ulx;
1638 double uly;
1639 double lrx;
1640 double lry;
1642 waveview = GNOME_CANVAS_WAVEVIEW (item);
1644 /* compute intersection of Drawable area and waveview,
1645 in canvas coordinate space
1648 if (x > waveview->bbox_ulx) {
1649 ulx = x;
1650 } else {
1651 ulx = waveview->bbox_ulx;
1654 if (y > waveview->bbox_uly) {
1655 uly = y;
1656 } else {
1657 uly = waveview->bbox_uly;
1660 if (x + width > waveview->bbox_lrx) {
1661 lrx = waveview->bbox_lrx;
1662 } else {
1663 lrx = x + width;
1666 if (y + height > waveview->bbox_lry) {
1667 lry = waveview->bbox_lry;
1668 } else {
1669 lry = y + height;
1672 /* figure out which samples we need for the resulting intersection */
1674 s1 = floor ((ulx - waveview->bbox_ulx) * waveview->samples_per_unit) ;
1676 if (lrx == waveview->bbox_lrx) {
1677 /* This avoids minor rounding errors when we have the
1678 entire region visible.
1680 s2 = waveview->samples;
1681 } else {
1682 s2 = s1 + floor ((lrx - ulx) * waveview->samples_per_unit);
1685 /* translate back to buffer coordinate space */
1687 ulx -= x;
1688 uly -= y;
1689 lrx -= x;
1690 lry -= y;
1692 /* don't rectify at single-sample zoom */
1693 if(waveview->rectified && waveview->samples_per_unit > 1.0) {
1694 rectify = TRUE;
1695 } else {
1696 rectify = FALSE;
1699 clip_length = MIN(5,(waveview->height/4));
1701 cr = gdk_cairo_create (drawable);
1702 cairo_set_line_width (cr, 0.5);
1704 origin = waveview->bbox_uly - y + waveview->half_height;
1706 cairo_rectangle (cr, ulx, uly, lrx - ulx, lry - uly);
1707 cairo_clip (cr);
1709 if (waveview->cache_updater && waveview->reload_cache_in_render) {
1710 waveview->cache->start = 0;
1711 waveview->cache->end = 0;
1712 waveview->reload_cache_in_render = FALSE;
1715 cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2);
1717 #if 0
1718 printf ("%p r (%d,%d)(%d,%d)[%d x %d] bbox (%d,%d)(%d,%d)[%d x %d]"
1719 " draw (%.1f,%.1f)(%.1f,%.1f)[%.1f x %.1f] s= %lu..%lu\n",
1720 waveview,
1721 x, y,
1722 x + width,
1723 y + height,
1724 width,
1725 height,
1726 waveview->bbox_ulx,
1727 waveview->bbox_uly,
1728 waveview->bbox_lrx,
1729 waveview->bbox_lry,
1730 waveview->bbox_lrx - waveview->bbox_ulx,
1731 waveview->bbox_lry - waveview->bbox_uly,
1732 ulx, uly,
1733 lrx, lry,
1734 lrx - ulx,
1735 lry - uly,
1736 s1, s2);
1737 #endif
1739 /* draw the top half */
1741 for (xoff = ulx; xoff < lrx; xoff++) {
1742 double max, min;
1744 max = waveview->cache->data[cache_index].max;
1745 min = waveview->cache->data[cache_index].min;
1747 if (min <= -1.0) {
1748 min = -1.0;
1751 if (max >= 1.0) {
1752 max = 1.0;
1755 if (rectify) {
1756 if (fabs (min) > fabs (max)) {
1757 max = fabs (min);
1761 yoff = origin - (waveview->half_height * max) + 0.5;
1763 if (xoff == ulx) {
1764 /* first point */
1765 cairo_move_to (cr, xoff+0.5, yoff);
1766 } else {
1767 cairo_line_to (cr, xoff+0.5, yoff);
1770 cache_index++;
1773 /* from the final top point, move out of the clip zone */
1775 cairo_line_to (cr, xoff + 10, yoff);
1777 /* now draw the bottom half */
1779 for (--xoff, --cache_index; xoff >= ulx; --xoff) {
1780 double min;
1782 min = waveview->cache->data[cache_index].min;
1784 if (min <= -1.0) {
1785 min = -1.0;
1788 yoff = origin - (waveview->half_height * min) + 0.5;
1790 cairo_line_to (cr, xoff+0.5, yoff);
1791 cache_index--;
1794 /* from the final lower point, move out of the clip zone */
1796 cairo_line_to (cr, xoff - 10, yoff);
1798 /* close path to fill */
1800 cairo_close_path (cr);
1802 /* fill and stroke */
1804 cairo_set_source_rgba (cr,
1805 (waveview->fill_r/255.0),
1806 (waveview->fill_g/255.0),
1807 (waveview->fill_b/255.0),
1808 (waveview->fill_a/255.0));
1809 cairo_fill_preserve (cr);
1810 cairo_set_source_rgba (cr,
1811 (waveview->wave_r/255.0),
1812 (waveview->wave_g/255.0),
1813 (waveview->wave_b/255.0),
1814 (waveview->wave_a/255.0));
1815 cairo_stroke (cr);
1817 cairo_destroy (cr);
1820 #if 0
1821 if (clip_max || clip_min) {
1822 cairo_set_source_rgba (cr, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a);
1825 if (clip_max) {
1826 cairo_move_to (cr, xoff, yoff1);
1827 cairo_line_to (cr, xoff, yoff1 + clip_length);
1828 cairo_stroke (cr);
1831 if (clip_min) {
1832 cairo_move_to (cr, xoff, yoff2);
1833 cairo_line_to (cr, xoff, yoff2 - clip_length);
1834 cairo_stroke (cr);
1837 #endif
1839 static void
1840 gnome_canvas_waveview_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1842 GnomeCanvasWaveView *waveview = GNOME_CANVAS_WAVEVIEW (item);
1844 *x1 = waveview->x;
1845 *y1 = waveview->y;
1847 *x2 = ceil (*x1 + (waveview->length_function (waveview->data_src) / waveview->samples_per_unit));
1848 *y2 = *y1 + waveview->height;
1850 #if 0
1851 x = 0; y = 0;
1852 gnome_canvas_item_i2w (item, &x, &y);
1853 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &a, &b);
1854 x = *x2;
1855 y = *y2;
1856 gnome_canvas_item_i2w (item, &x, &y);
1857 gnome_canvas_w2c_d (GNOME_CANVAS(item->canvas), x, y, &c, &d);
1858 printf ("item bounds now (%g,%g),(%g,%g)\n", a, b, c, d);
1859 #endif
1863 static double
1864 gnome_canvas_waveview_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
1866 (void) item;
1867 (void) x;
1868 (void) y;
1869 (void) cx;
1870 (void) cy;
1871 (void) actual_item;
1873 /* XXX for now, point is never inside the wave
1874 GnomeCanvasWaveView *waveview;
1875 double x1, y1, x2, y2;
1876 double dx, dy;
1879 return DBL_MAX;
1881 #if 0
1882 waveview = GNOME_CANVAS_WAVEVIEW (item);
1884 *actual_item = item;
1886 /* Find the bounds for the rectangle plus its outline width */
1888 gnome_canvas_waveview_bounds (item, &x1, &y1, &x2, &y2);
1890 /* Is point inside rectangle */
1892 if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
1893 return 0.0;
1896 /* Point is outside rectangle */
1898 if (x < x1)
1899 dx = x1 - x;
1900 else if (x > x2)
1901 dx = x - x2;
1902 else
1903 dx = 0.0;
1905 if (y < y1)
1906 dy = y1 - y;
1907 else if (y > y2)
1908 dy = y - y2;
1909 else
1910 dy = 0.0;
1912 return sqrt (dx * dx + dy * dy);
1913 #endif