1 /* wavbreaker - A tool to split a wave file up into multiple wave.
2 * Copyright (C) 2002-2005 Timothy Robinson
3 * Copyright (C) 2007-2019 Thomas Perl
4 *
5 * This program 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 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <gtk/gtk.h>
21 #include <math.h>
22
23 #include "draw.h"
24
25 static void draw_sample_surface(struct WaveformSurface *self, struct WaveformSurfaceDrawContext *ctx);
26 static void draw_summary_surface(struct WaveformSurface *self, struct WaveformSurfaceDrawContext *ctx);
27
28 /**
29 * Generated using the following Python 3 snippet (with some editing):
30 *
31 * import colorsys
32 * for i in range(6):
33 * print(' { %3d, %3d, %3d, },' % tuple(int(x*255)
34 * for x in colorsys.hsv_to_rgb(i/8., 0.8, 0.8)))
35 **/
36 const unsigned char SAMPLE_COLORS_VALUES[][3] = {
37 { 204, 40, 40, },
38 { 204, 163, 40, },
39 { 122, 204, 40, },
40 { 40, 81, 204, },
41 { 40, 204, 204, },
42 };
43
44 #define SAMPLE_COLORS G_N_ELEMENTS(SAMPLE_COLORS_VALUES)
45
46 #define SAMPLE_SHADES 3
47
48 GdkRGBA sample_colors[SAMPLE_COLORS][SAMPLE_SHADES];
49 GdkRGBA bg_color;
50 GdkRGBA nowrite_color;
51
52 static inline void
set_cairo_source(cairo_t * cr,GdkRGBA color)53 set_cairo_source(cairo_t *cr, GdkRGBA color)
54 {
55 cairo_set_source_rgb(cr, color.red, color.green, color.blue);
56 }
57
58 static inline void
fill_cairo_rectangle(cairo_t * cr,GdkRGBA * color,int width,int height)59 fill_cairo_rectangle(cairo_t *cr, GdkRGBA *color, int width, int height)
60 {
61 set_cairo_source(cr, *color);
62 cairo_rectangle(cr, 0.f, 0.f, (float)width, (float)height);
63 cairo_fill(cr);
64 }
65
66 static inline void
draw_cairo_line(cairo_t * cr,float x,float y0,float y1)67 draw_cairo_line(cairo_t *cr, float x, float y0, float y1)
68 {
69 cairo_move_to(cr, x-0.5f, y0);
70 cairo_line_to(cr, x-0.5f, y1);
71 }
72
73 static void
waveform_surface_static_init()74 waveform_surface_static_init()
75 {
76 static gboolean inited = FALSE;
77 if (inited) {
78 return;
79 }
80
81 bg_color = (GdkRGBA){ .red = 1.f, .green = 1.f, .blue = 1.f };
82 nowrite_color = (GdkRGBA){ .red = 0.86f, .green = 0.86f, .blue = 0.86f };
83
84 for (int i=0; i<SAMPLE_COLORS; i++) {
85 for (int x=0; x<SAMPLE_SHADES; x++) {
86 float factor_white = 0.5f*((float)x/(float)SAMPLE_SHADES);
87 float factor_color = 1.f-factor_white;
88 sample_colors[i][x].red = SAMPLE_COLORS_VALUES[i][0]/255.f*factor_color+factor_white;
89 sample_colors[i][x].green = SAMPLE_COLORS_VALUES[i][1]/255.f*factor_color+factor_white;
90 sample_colors[i][x].blue = SAMPLE_COLORS_VALUES[i][2]/255.f*factor_color+factor_white;
91 }
92 }
93
94 inited = TRUE;
95 }
96
waveform_surface_create_sample()97 struct WaveformSurface *waveform_surface_create_sample()
98 {
99 waveform_surface_static_init();
100
101 struct WaveformSurface *surface = calloc(sizeof(struct WaveformSurface), 1);
102
103 surface->draw = draw_sample_surface;
104
105 return surface;
106 }
107
waveform_surface_create_summary()108 struct WaveformSurface *waveform_surface_create_summary()
109 {
110 waveform_surface_static_init();
111
112 struct WaveformSurface *surface = calloc(sizeof(struct WaveformSurface), 1);
113
114 surface->draw = draw_summary_surface;
115
116 return surface;
117 }
118
waveform_surface_draw(struct WaveformSurface * surface,struct WaveformSurfaceDrawContext * ctx)119 void waveform_surface_draw(struct WaveformSurface *surface, struct WaveformSurfaceDrawContext *ctx)
120 {
121 surface->draw(surface, ctx);
122 }
123
waveform_surface_invalidate(struct WaveformSurface * surface)124 void waveform_surface_invalidate(struct WaveformSurface *surface)
125 {
126 if (surface->surface) {
127 cairo_surface_destroy(surface->surface);
128 surface->surface = NULL;
129 }
130
131 surface->width = 0;
132 surface->height = 0;
133 }
134
waveform_surface_free(struct WaveformSurface * surface)135 void waveform_surface_free(struct WaveformSurface *surface)
136 {
137 if (surface->surface) {
138 cairo_surface_destroy(surface->surface);
139 }
140
141 free(surface);
142 }
143
moodbar_sample_color(MoodbarData * moodbar,float position)144 static GdkRGBA moodbar_sample_color(MoodbarData *moodbar, float position)
145 {
146 float index = position * moodbar->numFrames;
147 unsigned long iindex = index;
148 float fractional = index - iindex;
149 if (fractional == 0.f || iindex >= moodbar->numFrames - 1) {
150 return moodbar->frames[iindex];
151 } else {
152 GdkRGBA a = moodbar->frames[iindex];
153 GdkRGBA b = moodbar->frames[iindex+1];
154 return (GdkRGBA){
155 .red = (1.f - fractional) * a.red + fractional * b.red,
156 .green = (1.f - fractional) * a.green + fractional * b.green,
157 .blue = (1.f - fractional) * a.blue + fractional * b.blue,
158 .alpha = (1.f - fractional) * a.alpha + fractional * b.alpha,
159 };
160 }
161 }
162
163 static void
draw_sample_surface(struct WaveformSurface * self,struct WaveformSurfaceDrawContext * ctx)164 draw_sample_surface(struct WaveformSurface *self, struct WaveformSurfaceDrawContext *ctx)
165 {
166 int xaxis;
167 int width, height;
168 int y_min, y_max;
169 int scale;
170 long i;
171
172 int shade;
173
174 GdkRGBA new_color;
175
176 {
177 GtkAllocation allocation;
178 gtk_widget_get_allocation(ctx->widget, &allocation);
179
180 width = allocation.width;
181 height = allocation.height;
182 }
183
184 if (self->surface != NULL && self->width == width && self->height == height && self->offset == ctx->pixmap_offset &&
185 (ctx->moodbarData && ctx->moodbarData->numFrames) == self->moodbar) {
186 return;
187 }
188
189 if (self->surface) {
190 cairo_surface_destroy(self->surface);
191 }
192
193 self->surface = gdk_window_create_similar_surface(gtk_widget_get_window(ctx->widget),
194 CAIRO_CONTENT_COLOR, width, height);
195
196 if (!self->surface) {
197 printf("surface is NULL\n");
198 return;
199 }
200
201 cairo_t *cr = cairo_create(self->surface);
202 cairo_set_line_width(cr, 1.f);
203
204 /* clear sample_surface before drawing */
205 fill_cairo_rectangle(cr, &bg_color, width, height);
206
207 if (ctx->graphData->data == NULL) {
208 cairo_destroy(cr);
209 return;
210 }
211
212 xaxis = height / 2;
213 if (xaxis != 0) {
214 scale = ctx->graphData->maxSampleValue / xaxis;
215 if (scale == 0) {
216 scale = 1;
217 }
218 } else {
219 scale = 1;
220 }
221
222 /* draw sample graph */
223 int tb_index = 0;
224 GList *tbl = ctx->track_break_list;
225 for (i = 0; i < width && i < ctx->graphData->numSamples; i++) {
226 y_min = ctx->graphData->data[i + ctx->pixmap_offset].min;
227 y_max = ctx->graphData->data[i + ctx->pixmap_offset].max;
228
229 y_min = xaxis + fabs((double)y_min) / scale;
230 y_max = xaxis - y_max / scale;
231
232 /* find the track break we are drawing now */
233 while (tbl->next && (i + ctx->pixmap_offset) > ((TrackBreak *)(tbl->next->data))->offset) {
234 tbl = tbl->next;
235 ++tb_index;
236 }
237
238 if (ctx->moodbarData && ctx->moodbarData->numFrames) {
239 set_cairo_source(cr, moodbar_sample_color(ctx->moodbarData, (float)(i+ctx->pixmap_offset) / (float)ctx->graphData->numSamples));
240 draw_cairo_line(cr, i, 0.f, height);
241 cairo_stroke(cr);
242 }
243
244 for( shade=0; shade<SAMPLE_SHADES; shade++) {
245 TrackBreak *tb = tbl->data;
246 if (tb->write) {
247 new_color = sample_colors[tb_index % SAMPLE_COLORS][shade];
248 } else {
249 new_color = nowrite_color;
250 }
251
252 set_cairo_source(cr, new_color);
253 draw_cairo_line(cr, i, y_min+(xaxis-y_min)*shade/SAMPLE_SHADES, y_min+(xaxis-y_min)*(shade+1)/SAMPLE_SHADES);
254 draw_cairo_line(cr, i, y_max-(y_max-xaxis)*shade/SAMPLE_SHADES, y_max-(y_max-xaxis)*(shade+1)/SAMPLE_SHADES);
255 cairo_stroke(cr);
256 }
257 }
258
259 cairo_destroy(cr);
260
261 self->width = width;
262 self->height = height;
263 self->offset = ctx->pixmap_offset;
264 self->moodbar = ctx->moodbarData && ctx->moodbarData->numFrames;
265 }
266
267 static void
draw_summary_surface(struct WaveformSurface * self,struct WaveformSurfaceDrawContext * ctx)268 draw_summary_surface(struct WaveformSurface *self, struct WaveformSurfaceDrawContext *ctx)
269 {
270 int xaxis;
271 int width, height;
272 int y_min, y_max;
273 int min, max;
274 int scale;
275 int i, k;
276 int loop_end, array_offset;
277 int shade;
278
279 float x_scale;
280
281 GdkRGBA new_color;
282
283 {
284 GtkAllocation allocation;
285 gtk_widget_get_allocation(ctx->widget, &allocation);
286 width = allocation.width;
287 height = allocation.height;
288 }
289
290 if (self->surface != NULL && self->width == width && self->height == height &&
291 (ctx->moodbarData && ctx->moodbarData->numFrames) == self->moodbar) {
292 return;
293 }
294
295 if (self->surface) {
296 cairo_surface_destroy(self->surface);
297 }
298
299 self->surface = gdk_window_create_similar_surface(gtk_widget_get_window(ctx->widget),
300 CAIRO_CONTENT_COLOR, width, height);
301
302 if (!self->surface) {
303 printf("summary_surface is NULL\n");
304 return;
305 }
306
307 cairo_t *cr = cairo_create(self->surface);
308 cairo_set_line_width(cr, 1.f);
309
310 /* clear sample_surface before drawing */
311 fill_cairo_rectangle(cr, &bg_color, width, height);
312
313 if (ctx->graphData->data == NULL) {
314 cairo_destroy(cr);
315 return;
316 }
317
318 xaxis = height / 2;
319 if (xaxis != 0) {
320 scale = ctx->graphData->maxSampleValue / xaxis;
321 if (scale == 0) {
322 scale = 1;
323 }
324 } else {
325 scale = 1;
326 }
327
328 /* draw sample graph */
329
330 x_scale = (float)(ctx->graphData->numSamples) / (float)(width);
331 if (x_scale == 0) {
332 x_scale = 1;
333 }
334
335 int tb_index = 0;
336 GList *tbl = ctx->track_break_list;
337 for (i = 0; i < width && i < ctx->graphData->numSamples; i++) {
338 min = max = 0;
339 array_offset = (int)(i * x_scale);
340
341 if (x_scale != 1) {
342 loop_end = (int)x_scale;
343
344 for (k = 0; k < loop_end; k++) {
345 if (ctx->graphData->data[array_offset + k].max > max) {
346 max = ctx->graphData->data[array_offset + k].max;
347 } else if (ctx->graphData->data[array_offset + k].min < min) {
348 min = ctx->graphData->data[array_offset + k].min;
349 }
350 }
351 } else {
352 min = ctx->graphData->data[i].min;
353 max = ctx->graphData->data[i].max;
354 }
355
356 y_min = min;
357 y_max = max;
358
359 y_min = xaxis + fabs((double)y_min) / scale;
360 y_max = xaxis - y_max / scale;
361
362 /* find the track break we are drawing now */
363 while (tbl->next && array_offset > ((TrackBreak *)(tbl->next->data))->offset) {
364 tbl = tbl->next;
365 ++tb_index;
366 }
367
368 if (ctx->moodbarData && ctx->moodbarData->numFrames) {
369 set_cairo_source(cr, moodbar_sample_color(ctx->moodbarData, (float)(array_offset) / (float)(ctx->graphData->numSamples)));
370 draw_cairo_line(cr, i, 0.f, height);
371 cairo_stroke(cr);
372 }
373
374 for( shade=0; shade<SAMPLE_SHADES; shade++) {
375 TrackBreak *tb = tbl->data;
376 if (tb->write) {
377 new_color = sample_colors[tb_index % SAMPLE_COLORS][shade];
378 } else {
379 new_color = nowrite_color;
380 }
381
382 cairo_set_source_rgb(cr, new_color.red, new_color.green, new_color.blue);
383 draw_cairo_line(cr, i, y_min+(xaxis-y_min)*shade/SAMPLE_SHADES, y_min+(xaxis-y_min)*(shade+1)/SAMPLE_SHADES);
384 draw_cairo_line(cr, i, y_max-(y_max-xaxis)*shade/SAMPLE_SHADES, y_max-(y_max-xaxis)*(shade+1)/SAMPLE_SHADES);
385 cairo_stroke(cr);
386 }
387 }
388
389 cairo_destroy(cr);
390
391 self->width = width;
392 self->height = height;
393 self->moodbar = ctx->moodbarData && ctx->moodbarData->numFrames;
394 }
395
396