1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2014 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <math.h>
24 #include <gthumb.h>
25 #include <extensions/image_viewer/image-viewer.h>
26 #include "cairo-blur.h"
27 #include "cairo-effects.h"
28 #include "gth-curve.h"
29 #include "gth-file-tool-effects.h"
30 #include "gth-preview-tool.h"
31
32
33 #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
34 #define APPLY_DELAY 150
35 #define PREVIEW_SIZE 0.9
36
37
38 struct _GthFileToolEffectsPrivate {
39 cairo_surface_t *destination;
40 cairo_surface_t *preview;
41 GtkBuilder *builder;
42 GthTask *image_task;
43 GthImageViewerTool *preview_tool;
44 guint apply_event;
45 gboolean apply_to_original;
46 gboolean closing;
47 gboolean view_original;
48 int method;
49 int last_applied_method;
50 GtkWidget *filter_grid;
51 };
52
53
54 G_DEFINE_TYPE_WITH_CODE (GthFileToolEffects,
55 gth_file_tool_effects,
56 GTH_TYPE_IMAGE_VIEWER_PAGE_TOOL,
57 G_ADD_PRIVATE (GthFileToolEffects))
58
59
60 static void apply_changes (GthFileToolEffects *self);
61
62
63 static void
image_task_completed_cb(GthTask * task,GError * error,gpointer user_data)64 image_task_completed_cb (GthTask *task,
65 GError *error,
66 gpointer user_data)
67 {
68 GthFileToolEffects *self = user_data;
69 GthImage *destination_image;
70
71 g_signal_handlers_disconnect_matched (task, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, image_task_completed_cb, self);
72 self->priv->image_task = NULL;
73
74 if (self->priv->closing) {
75 g_object_unref (task);
76 gth_image_viewer_page_tool_reset_image (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
77 return;
78 }
79
80 if (error != NULL) {
81 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
82 apply_changes (self);
83 g_object_unref (task);
84 return;
85 }
86
87 destination_image = gth_image_task_get_destination (GTH_IMAGE_TASK (task));
88 if (destination_image == NULL) {
89 g_object_unref (task);
90 return;
91 }
92
93 cairo_surface_destroy (self->priv->destination);
94 self->priv->destination = gth_image_get_cairo_surface (destination_image);
95 self->priv->last_applied_method = self->priv->method;
96
97 if (self->priv->apply_to_original) {
98 if (self->priv->destination != NULL) {
99 GtkWidget *window;
100 GthViewerPage *viewer_page;
101
102 window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
103 viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
104 gth_image_viewer_page_set_image (GTH_IMAGE_VIEWER_PAGE (viewer_page), self->priv->destination, TRUE);
105 }
106
107 gth_file_tool_hide_options (GTH_FILE_TOOL (self));
108 }
109 else {
110 if (! self->priv->view_original)
111 gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->destination);
112 }
113
114 g_object_unref (task);
115 }
116
117
118 static gboolean
apply_cb(gpointer user_data)119 apply_cb (gpointer user_data)
120 {
121 GthFileToolEffects *self = user_data;
122 GtkWidget *window;
123
124 if (self->priv->apply_event != 0) {
125 g_source_remove (self->priv->apply_event);
126 self->priv->apply_event = 0;
127 }
128
129 if (self->priv->image_task != NULL) {
130 gth_task_cancel (self->priv->image_task);
131 return FALSE;
132 }
133
134 window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
135
136 self->priv->image_task = gth_filter_grid_get_task (GTH_FILTER_GRID (self->priv->filter_grid), self->priv->method);
137 if (self->priv->apply_to_original)
138 gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task), gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self)));
139 else
140 gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task), self->priv->preview);
141 g_signal_connect (self->priv->image_task,
142 "completed",
143 G_CALLBACK (image_task_completed_cb),
144 self);
145 gth_browser_exec_task (GTH_BROWSER (window), self->priv->image_task, GTH_TASK_FLAGS_DEFAULT);
146
147 return FALSE;
148 }
149
150
151 static void
apply_changes(GthFileToolEffects * self)152 apply_changes (GthFileToolEffects *self)
153 {
154 if (self->priv->apply_event != 0) {
155 g_source_remove (self->priv->apply_event);
156 self->priv->apply_event = 0;
157 }
158 self->priv->apply_event = g_timeout_add (APPLY_DELAY, apply_cb, self);
159 }
160
161
162 static void
gth_file_tool_effects_reset_image(GthImageViewerPageTool * base)163 gth_file_tool_effects_reset_image (GthImageViewerPageTool *base)
164 {
165 GthFileToolEffects *self = GTH_FILE_TOOL_EFFECTS (base);
166
167 if (self->priv->image_task != NULL) {
168 self->priv->closing = TRUE;
169 return;
170 }
171
172 if (self->priv->apply_event != 0) {
173 g_source_remove (self->priv->apply_event);
174 self->priv->apply_event = 0;
175 }
176
177 gth_image_viewer_page_reset (GTH_IMAGE_VIEWER_PAGE (gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self))));
178 gth_file_tool_hide_options (GTH_FILE_TOOL (self));
179 }
180
181
182 static void
filter_grid_activated_cb(GthFilterGrid * filter_grid,int filter_id,gpointer user_data)183 filter_grid_activated_cb (GthFilterGrid *filter_grid,
184 int filter_id,
185 gpointer user_data)
186 {
187 GthFileToolEffects *self = user_data;
188
189 self->priv->view_original = (filter_id == GTH_FILTER_GRID_NO_FILTER);
190 if (self->priv->view_original) {
191 gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview);
192 }
193 else if (filter_id == self->priv->last_applied_method) {
194 gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->destination);
195 }
196 else {
197 self->priv->method = filter_id;
198 apply_changes (self);
199 }
200 }
201
202
203 static GtkWidget *
gth_file_tool_effects_get_options(GthFileTool * base)204 gth_file_tool_effects_get_options (GthFileTool *base)
205 {
206 GthFileToolEffects *self;
207 GtkWidget *window;
208 GthViewerPage *viewer_page;
209 GtkWidget *viewer;
210 cairo_surface_t *source;
211 GtkWidget *options;
212 int width, height;
213 GtkAllocation allocation;
214
215 self = (GthFileToolEffects *) base;
216
217 window = gth_file_tool_get_window (base);
218 viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
219 if (! GTH_IS_IMAGE_VIEWER_PAGE (viewer_page))
220 return NULL;
221
222 cairo_surface_destroy (self->priv->destination);
223 cairo_surface_destroy (self->priv->preview);
224
225 viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
226 source = gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
227 if (source == NULL)
228 return NULL;
229
230 width = cairo_image_surface_get_width (source);
231 height = cairo_image_surface_get_height (source);
232 gtk_widget_get_allocation (GTK_WIDGET (viewer), &allocation);
233 if (scale_keeping_ratio (&width, &height, PREVIEW_SIZE * allocation.width, PREVIEW_SIZE * allocation.height, FALSE))
234 self->priv->preview = _cairo_image_surface_scale_fast (source, width, height);
235 else
236 self->priv->preview = cairo_surface_reference (source);
237
238 self->priv->destination = cairo_surface_reference (self->priv->preview);
239 self->priv->apply_to_original = FALSE;
240 self->priv->closing = FALSE;
241
242 self->priv->builder = _gtk_builder_new_from_file ("effects-options.ui", "file_tools");
243 options = _gtk_builder_get_widget (self->priv->builder, "options");
244 gtk_widget_show (options);
245
246 self->priv->filter_grid = gth_filter_grid_new ();
247 gth_hook_invoke ("add-special-effect", self->priv->filter_grid);
248 gtk_widget_show (self->priv->filter_grid);
249 gtk_box_pack_start (GTK_BOX (GET_WIDGET ("filter_grid_box")), self->priv->filter_grid, TRUE, FALSE, 0);
250
251 g_signal_connect (self->priv->filter_grid,
252 "activated",
253 G_CALLBACK (filter_grid_activated_cb),
254 self);
255
256 self->priv->preview_tool = gth_preview_tool_new ();
257 gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview);
258 gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), self->priv->preview_tool);
259 gth_filter_grid_generate_previews (GTH_FILTER_GRID (self->priv->filter_grid), source);
260
261 return options;
262 }
263
264
265 static void
gth_file_tool_effects_destroy_options(GthFileTool * base)266 gth_file_tool_effects_destroy_options (GthFileTool *base)
267 {
268 GthFileToolEffects *self;
269 GtkWidget *window;
270 GthViewerPage *viewer_page;
271
272 self = (GthFileToolEffects *) base;
273
274 if (self->priv->apply_event != 0) {
275 g_source_remove (self->priv->apply_event);
276 self->priv->apply_event = 0;
277 }
278
279 window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
280 viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
281 gth_image_viewer_page_reset_viewer_tool (GTH_IMAGE_VIEWER_PAGE (viewer_page));
282 gth_viewer_page_update_sensitivity (viewer_page);
283
284 _g_clear_object (&self->priv->builder);
285
286 _cairo_clear_surface (&self->priv->preview);
287 _cairo_clear_surface (&self->priv->destination);
288 self->priv->method = GTH_FILTER_GRID_NO_FILTER;
289 self->priv->last_applied_method = GTH_FILTER_GRID_NO_FILTER;
290 self->priv->view_original = TRUE;
291 }
292
293
294 static void
gth_file_tool_effects_apply_options(GthFileTool * base)295 gth_file_tool_effects_apply_options (GthFileTool *base)
296 {
297 GthFileToolEffects *self;
298
299 self = (GthFileToolEffects *) base;
300
301 if (! self->priv->view_original) {
302 self->priv->apply_to_original = TRUE;
303 apply_changes (self);
304 }
305 }
306
307
308 static void
gth_file_tool_effects_finalize(GObject * object)309 gth_file_tool_effects_finalize (GObject *object)
310 {
311 GthFileToolEffects *self;
312
313 g_return_if_fail (object != NULL);
314 g_return_if_fail (GTH_IS_FILE_TOOL_EFFECTS (object));
315
316 self = (GthFileToolEffects *) object;
317
318 _g_clear_object (&self->priv->builder);
319 _cairo_clear_surface (&self->priv->preview);
320 _cairo_clear_surface (&self->priv->destination);
321
322 G_OBJECT_CLASS (gth_file_tool_effects_parent_class)->finalize (object);
323 }
324
325
326 static void
gth_file_tool_effects_class_init(GthFileToolEffectsClass * klass)327 gth_file_tool_effects_class_init (GthFileToolEffectsClass *klass)
328 {
329 GObjectClass *gobject_class;
330 GthFileToolClass *file_tool_class;
331 GthImageViewerPageToolClass *image_viewer_page_tool_class;
332
333 gobject_class = (GObjectClass*) klass;
334 gobject_class->finalize = gth_file_tool_effects_finalize;
335
336 file_tool_class = GTH_FILE_TOOL_CLASS (klass);
337 file_tool_class->get_options = gth_file_tool_effects_get_options;
338 file_tool_class->destroy_options = gth_file_tool_effects_destroy_options;
339 file_tool_class->apply_options = gth_file_tool_effects_apply_options;
340
341 image_viewer_page_tool_class = (GthImageViewerPageToolClass *) klass;
342 image_viewer_page_tool_class->reset_image = gth_file_tool_effects_reset_image;
343 }
344
345
346 static void
gth_file_tool_effects_init(GthFileToolEffects * self)347 gth_file_tool_effects_init (GthFileToolEffects *self)
348 {
349 self->priv = gth_file_tool_effects_get_instance_private (self);
350 self->priv->preview = NULL;
351 self->priv->destination = NULL;
352 self->priv->builder = NULL;
353 self->priv->method = GTH_FILTER_GRID_NO_FILTER;
354 self->priv->last_applied_method = GTH_FILTER_GRID_NO_FILTER;
355 self->priv->view_original = TRUE;
356
357 gth_file_tool_construct (GTH_FILE_TOOL (self),
358 "special-effects-symbolic",
359 _("Special Effects"),
360 GTH_TOOLBOX_SECTION_COLORS);
361 }
362
363
364 /* -- Warmer -- */
365
366
367 static gpointer
warmer_exec(GthAsyncTask * task,gpointer user_data)368 warmer_exec (GthAsyncTask *task,
369 gpointer user_data)
370 {
371 cairo_surface_t *original;
372 cairo_surface_t *source;
373 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
374
375 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
376 source = _cairo_image_surface_copy (original);
377
378 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
379 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 117,136, 255,255);
380 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
381 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 136,119, 255,255);
382 if (cairo_image_surface_apply_curves (source, curve, task))
383 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
384
385 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
386 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
387 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
388 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
389 cairo_surface_destroy (source);
390 cairo_surface_destroy (original);
391
392 return NULL;
393 }
394
395
396 void
warmer_add_to_special_effects(GthFilterGrid * grid)397 warmer_add_to_special_effects (GthFilterGrid *grid)
398 {
399 gth_filter_grid_add_filter (grid,
400 GTH_FILTER_GRID_NEW_FILTER_ID,
401 gth_image_task_new (_("Applying changes"), NULL, warmer_exec, NULL, NULL, NULL),
402 /* Translators: this is the name of a filter that produces warmer colors */
403 _("Warmer"),
404 NULL);
405 }
406
407
408 /* -- Cooler -- */
409
410
411 static gpointer
cooler_exec(GthAsyncTask * task,gpointer user_data)412 cooler_exec (GthAsyncTask *task,
413 gpointer user_data)
414 {
415 cairo_surface_t *original;
416 cairo_surface_t *source;
417 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
418
419 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
420 source = _cairo_image_surface_copy (original);
421
422 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
423 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 136,119, 255,255);
424 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
425 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 117,136, 255,255);
426 if (cairo_image_surface_apply_curves (source, curve, task))
427 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
428
429 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
430 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
431 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
432 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
433 cairo_surface_destroy (source);
434 cairo_surface_destroy (original);
435
436 return NULL;
437 }
438
439
440 void
cooler_add_to_special_effects(GthFilterGrid * grid)441 cooler_add_to_special_effects (GthFilterGrid *grid)
442 {
443 gth_filter_grid_add_filter (grid,
444 GTH_FILTER_GRID_NEW_FILTER_ID,
445 gth_image_task_new (_("Applying changes"), NULL, cooler_exec, NULL, NULL, NULL),
446 /* Translators: this is the name of a filter that produces cooler colors */
447 _("Cooler"),
448 NULL);
449 }
450
451
452 /* -- Soil -- */
453
454
455 static gpointer
soil_exec(GthAsyncTask * task,gpointer user_data)456 soil_exec (GthAsyncTask *task,
457 gpointer user_data)
458 {
459 cairo_surface_t *original;
460 cairo_surface_t *source;
461 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
462
463 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
464 source = _cairo_image_surface_copy (original);
465
466 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
467 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 75,83, 198,185, 255,255);
468 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 70,63, 184,189, 255,255);
469 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 76,74, 191,176, 255,255);
470
471 if (cairo_image_surface_apply_curves (source, curve, task)
472 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
473 {
474 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
475 }
476
477 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
478 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
479 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
480 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
481 cairo_surface_destroy (source);
482 cairo_surface_destroy (original);
483
484 return NULL;
485 }
486
487
488 void
soil_add_to_special_effects(GthFilterGrid * grid)489 soil_add_to_special_effects (GthFilterGrid *grid)
490 {
491 gth_filter_grid_add_filter (grid,
492 GTH_FILTER_GRID_NEW_FILTER_ID,
493 gth_image_task_new (_("Applying changes"), NULL, soil_exec, NULL, NULL, NULL),
494 /* Translators: this is the name of an image filter */
495 _("Soil"),
496 NULL);
497 }
498
499
500 /* -- Desert -- */
501
502
503 static gpointer
desert_exec(GthAsyncTask * task,gpointer user_data)504 desert_exec (GthAsyncTask *task,
505 gpointer user_data)
506 {
507 cairo_surface_t *original;
508 cairo_surface_t *source;
509 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
510
511 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
512 source = _cairo_image_surface_copy (original);
513
514 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 115,145, 255,255);
515 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 71,66, 208,204, 255,255);
516 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 71,55, 200,206, 255,255);
517 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 232,185, 255,248);
518
519 if (cairo_image_surface_apply_curves (source, curve, task)
520 && cairo_image_surface_apply_bcs (source, 0, 0, 20.0 / 100, task)
521 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
522 {
523 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
524 }
525
526 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
527 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
528 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
529 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
530 cairo_surface_destroy (source);
531 cairo_surface_destroy (original);
532
533 return NULL;
534 }
535
536
537 void
desert_add_to_special_effects(GthFilterGrid * grid)538 desert_add_to_special_effects (GthFilterGrid *grid)
539 {
540 gth_filter_grid_add_filter (grid,
541 GTH_FILTER_GRID_NEW_FILTER_ID,
542 gth_image_task_new (_("Applying changes"), NULL, desert_exec, NULL, NULL, NULL),
543 /* Translators: this is the name of an image filter */
544 _("Desert"),
545 NULL);
546 }
547
548
549 /* -- Artic -- */
550
551
552 static gpointer
artic_exec(GthAsyncTask * task,gpointer user_data)553 artic_exec (GthAsyncTask *task,
554 gpointer user_data)
555 {
556 cairo_surface_t *original;
557 cairo_surface_t *source;
558 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
559
560 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
561 source = _cairo_image_surface_copy (original);
562
563 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
564 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 133,122, 255,255);
565 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 64,57, 176,186, 255,255);
566 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 180,207, 255,255);
567
568 if (cairo_image_surface_apply_curves (source, curve, task)
569 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
570 {
571 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
572 }
573
574 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
575 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
576 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
577 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
578 cairo_surface_destroy (source);
579 cairo_surface_destroy (original);
580
581 return NULL;
582 }
583
584
585 void
artic_add_to_special_effects(GthFilterGrid * grid)586 artic_add_to_special_effects (GthFilterGrid *grid)
587 {
588 gth_filter_grid_add_filter (grid,
589 GTH_FILTER_GRID_NEW_FILTER_ID,
590 gth_image_task_new (_("Applying changes"), NULL, artic_exec, NULL, NULL, NULL),
591 /* Translators: this is the name of an image filter */
592 _("Arctic"),
593 NULL);
594 }
595
596
597 /* -- Mangos -- */
598
599
600 static gpointer
mangos_exec(GthAsyncTask * task,gpointer user_data)601 mangos_exec (GthAsyncTask *task,
602 gpointer user_data)
603 {
604 cairo_surface_t *original;
605 cairo_surface_t *source;
606 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
607
608 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
609 source = _cairo_image_surface_copy (original);
610
611 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
612 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 5, 0,0, 75,67, 135,155, 171,193, 252,255);
613 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 84,65, 162,167, 255,255);
614 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 84,74, 160,150, 255,255);
615
616 if (cairo_image_surface_apply_curves (source, curve, task)
617 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
618 {
619 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
620 }
621
622 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
623 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
624 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
625 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
626 cairo_surface_destroy (source);
627 cairo_surface_destroy (original);
628
629 return NULL;
630 }
631
632
633 void
mangos_add_to_special_effects(GthFilterGrid * grid)634 mangos_add_to_special_effects (GthFilterGrid *grid)
635 {
636 gth_filter_grid_add_filter (grid,
637 GTH_FILTER_GRID_NEW_FILTER_ID,
638 gth_image_task_new (_("Applying changes"), NULL, mangos_exec, NULL, NULL, NULL),
639 /* Translators: this is the name of an image filter */
640 _("Mangos"),
641 NULL);
642 }
643
644
645 /* -- Fresh Blue -- */
646
647
648 static gpointer
fresh_blue_exec(GthAsyncTask * task,gpointer user_data)649 fresh_blue_exec (GthAsyncTask *task,
650 gpointer user_data)
651 {
652 cairo_surface_t *original;
653 cairo_surface_t *source;
654 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
655
656 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
657 source = _cairo_image_surface_copy (original);
658
659 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 95,61, 116,71, 255,255);
660 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 81,94, 96,125, 255,255);
661 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 75,107, 86,129, 255,255);
662 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 4, 0,0, 39,139, 58,168, 255,255);
663
664 if (cairo_image_surface_apply_curves (source, curve, task)
665 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
666 {
667 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
668 }
669
670 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
671 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
672 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
673 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
674 cairo_surface_destroy (source);
675 cairo_surface_destroy (original);
676
677 return NULL;
678 }
679
680
681 void
fresh_blue_add_to_special_effects(GthFilterGrid * grid)682 fresh_blue_add_to_special_effects (GthFilterGrid *grid)
683 {
684 gth_filter_grid_add_filter (grid,
685 GTH_FILTER_GRID_NEW_FILTER_ID,
686 gth_image_task_new (_("Applying changes"), NULL, fresh_blue_exec, NULL, NULL, NULL),
687 /* Translators: this is the name of an image filter */
688 _("Fresh Blue"),
689 NULL);
690 }
691
692
693 /* -- Cherry -- */
694
695
696 static gpointer
cherry_exec(GthAsyncTask * task,gpointer user_data)697 cherry_exec (GthAsyncTask *task,
698 gpointer user_data)
699 {
700 cairo_surface_t *original;
701 cairo_surface_t *source;
702 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
703
704 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
705 source = _cairo_image_surface_copy (original);
706
707 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
708 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 5, 0,12, 74,79, 134,156, 188,209, 239,255);
709 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 5, 12,0, 78,67, 138,140, 189,189, 252,233);
710 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 5, 0,8, 77,100, 139,140, 202,186, 255,244);
711 if (cairo_image_surface_apply_curves (source, curve, task)
712 && cairo_image_surface_apply_vignette (source, NULL, 127, task))
713 {
714 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
715 }
716
717 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
718 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
719 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
720 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
721 cairo_surface_destroy (source);
722 cairo_surface_destroy (original);
723
724 return NULL;
725 }
726
727
728 void
cherry_add_to_special_effects(GthFilterGrid * grid)729 cherry_add_to_special_effects (GthFilterGrid *grid)
730 {
731 gth_filter_grid_add_filter (grid,
732 GTH_FILTER_GRID_NEW_FILTER_ID,
733 gth_image_task_new (_("Applying changes"), NULL, cherry_exec, NULL, NULL, NULL),
734 /* Translators: this is the name of an image filter */
735 _("Cherry"),
736 NULL);
737 }
738
739
740 /* -- Vintage -- */
741
742
743 static gpointer
vintage_exec(GthAsyncTask * task,gpointer user_data)744 vintage_exec (GthAsyncTask *task,
745 gpointer user_data)
746 {
747 cairo_surface_t *original;
748 cairo_surface_t *source;
749 GthCurve *curve[GTH_HISTOGRAM_N_CHANNELS];
750
751 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
752 source = _cairo_image_surface_copy (original);
753
754 curve[GTH_HISTOGRAM_CHANNEL_VALUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 3, 0,0, 76,173, 255,255);
755 curve[GTH_HISTOGRAM_CHANNEL_RED] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
756 curve[GTH_HISTOGRAM_CHANNEL_GREEN] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
757 curve[GTH_HISTOGRAM_CHANNEL_BLUE] = gth_curve_new_for_points (GTH_TYPE_BEZIER, 0);
758
759 if (cairo_image_surface_colorize (source, 112, 66, 20, 255, task)
760 && cairo_image_surface_apply_bcs (source, 0, -20 / 100, -20 / 100, task)
761 && cairo_image_surface_apply_vignette (source, curve, 255, task))
762 {
763 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), source);
764 }
765
766 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_BLUE]);
767 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_GREEN]);
768 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_RED]);
769 g_object_unref (curve[GTH_HISTOGRAM_CHANNEL_VALUE]);
770 cairo_surface_destroy (source);
771 cairo_surface_destroy (original);
772
773 return NULL;
774 }
775
776
777 void
vintage_add_to_special_effects(GthFilterGrid * grid)778 vintage_add_to_special_effects (GthFilterGrid *grid)
779 {
780 gth_filter_grid_add_filter (grid,
781 GTH_FILTER_GRID_NEW_FILTER_ID,
782 gth_image_task_new (_("Applying changes"), NULL, vintage_exec, NULL, NULL, NULL),
783 /* Translators: this is the name of an image filter */
784 _("Vintage"),
785 NULL);
786 }
787
788
789 /* -- Blurred Edges -- */
790
791
792 static gpointer
blurred_edges_exec(GthAsyncTask * task,gpointer user_data)793 blurred_edges_exec (GthAsyncTask *task,
794 gpointer user_data)
795 {
796 cairo_surface_t *original;
797 cairo_surface_t *source;
798 cairo_surface_t *blurred;
799 int blurred_stride;
800 double center_x, center_y, min_radius, d, max_radius, max_distance;
801 int width;
802 int height;
803 cairo_format_t format;
804 int source_stride;
805 cairo_surface_t *destination;
806 int destination_stride;
807 unsigned char *p_source_line;
808 unsigned char *p_destination_line;
809 unsigned char *p_blurred_line;
810 int x, y, temp;
811 double progress;
812 unsigned char *p_source;
813 unsigned char *p_destination;
814 unsigned char *p_blurred;
815 unsigned char image_red, image_green, image_blue, image_alpha;
816 unsigned char layer_red, layer_green, layer_blue, layer_alpha;
817 gboolean cancelled = FALSE;
818
819 gimp_op_init ();
820
821 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
822 source = _cairo_image_surface_copy (original);
823
824 blurred = _cairo_image_surface_copy (source);
825 blurred_stride = cairo_image_surface_get_stride (blurred);
826 if (! _cairo_image_surface_blur (blurred, 2, task)) {
827 cairo_surface_destroy (blurred);
828 cairo_surface_destroy (source);
829 return NULL;
830 }
831
832 width = cairo_image_surface_get_width (source);
833 height = cairo_image_surface_get_height (source);
834 format = cairo_image_surface_get_format (source);
835 source_stride = cairo_image_surface_get_stride (source);
836
837 center_x = width / 2.0;
838 center_y = height / 2.0;
839 min_radius = MIN (width, height) / 2.0;
840 min_radius -= min_radius * 0.5;
841 max_radius = MAX (width, height) / 2.0 /*sqrt (SQR (center_x) + SQR (center_y))*/;
842 max_distance = max_radius - min_radius;
843
844 destination = cairo_image_surface_create (format, width, height);
845 destination_stride = cairo_image_surface_get_stride (destination);
846
847 p_source_line = _cairo_image_surface_flush_and_get_data (source);
848 p_blurred_line = _cairo_image_surface_flush_and_get_data (blurred);
849 p_destination_line = _cairo_image_surface_flush_and_get_data (destination);
850 for (y = 0; y < height; y++) {
851 gth_async_task_get_data (task, NULL, &cancelled, NULL);
852 if (cancelled)
853 break;
854
855 progress = (double) y / height;
856 gth_async_task_set_data (task, NULL, NULL, &progress);
857
858 p_source = p_source_line;
859 p_blurred = p_blurred_line;
860 p_destination = p_destination_line;
861 for (x = 0; x < width; x++) {
862 d = sqrt (SQR (x - center_x) + SQR (y - center_y));
863 d = CLAMP_PIXEL (d < min_radius ? 0 : d > max_radius ? 255 : (255.0 * ((d - min_radius) / max_distance)));
864
865 CAIRO_GET_RGBA (p_source, image_red, image_green, image_blue, image_alpha);
866
867 /* blurred image layer with a radial mask (to blur the edges) */
868
869 CAIRO_GET_RGBA (p_blurred, layer_red, layer_green, layer_blue, layer_alpha);
870 layer_alpha = (guchar) (d);
871 /*layer_red = layer_green = layer_blue = 0;*/
872 p_destination[CAIRO_RED] = GIMP_OP_NORMAL (layer_red, image_red, layer_alpha);
873 p_destination[CAIRO_GREEN] = GIMP_OP_NORMAL (layer_green, image_green, layer_alpha);
874 p_destination[CAIRO_BLUE] = GIMP_OP_NORMAL (layer_blue, image_blue, layer_alpha);
875 p_destination[CAIRO_ALPHA] = 255;
876
877 p_source += 4;
878 p_blurred += 4;
879 p_destination += 4;
880 }
881 p_source_line += source_stride;
882 p_blurred_line += blurred_stride;
883 p_destination_line += destination_stride;
884 }
885
886 if (! cancelled) {
887 cairo_surface_mark_dirty (destination);
888 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination);
889 }
890
891 cairo_surface_destroy (destination);
892 cairo_surface_destroy (blurred);
893 cairo_surface_destroy (source);
894
895 return NULL;
896 }
897
898
899 void
blurred_edges_add_to_special_effects(GthFilterGrid * grid)900 blurred_edges_add_to_special_effects (GthFilterGrid *grid)
901 {
902 gth_filter_grid_add_filter (grid,
903 GTH_FILTER_GRID_NEW_FILTER_ID,
904 gth_image_task_new (_("Applying changes"), NULL, blurred_edges_exec, NULL, NULL, NULL),
905 /* Translators: this is the name of an image filter */
906 _("Blurred Edges"),
907 NULL);
908 }
909
910
911 /* -- Vignette -- */
912
913
914 static gpointer
vignette_exec(GthAsyncTask * task,gpointer user_data)915 vignette_exec (GthAsyncTask *task,
916 gpointer user_data)
917 {
918 cairo_surface_t *original;
919 cairo_surface_t *destination;
920
921 original = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
922 destination = _cairo_image_surface_copy (original);
923
924 if (cairo_image_surface_apply_vignette (destination, NULL, 127, task))
925 gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination);
926
927 cairo_surface_destroy (destination);
928 cairo_surface_destroy (original);
929
930 return NULL;
931 }
932
933
934 void
vignette_add_to_special_effects(GthFilterGrid * grid)935 vignette_add_to_special_effects (GthFilterGrid *grid)
936 {
937 gth_filter_grid_add_filter (grid,
938 GTH_FILTER_GRID_NEW_FILTER_ID,
939 gth_image_task_new (_("Applying changes"), NULL, vignette_exec, NULL, NULL, NULL),
940 /* Translators: this is the name of an image filter that produces darker edges */
941 _("Vignette"),
942 NULL);
943 }
944