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