1# tool_filters.py
2#
3# Copyright 2018-2021 Romain F. T.
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 3 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, see <http://www.gnu.org/licenses/>.
17
18import cairo
19from gi.repository import Gdk, GdkPixbuf, Gio
20from .abstract_transform_tool import AbstractCanvasTool
21from .filter_blur import FilterBlur
22from .filter_colors import FilterColors
23from .filter_contrast import FilterContrast
24from .filter_emboss import FilterEmboss
25from .filter_saturation import FilterSaturation
26from .filter_transparency import FilterTransparency
27from .filter_veil import FilterVeil
28from .optionsbar_filters import OptionsBarFilters
29from .utilities_blur import utilities_blur_surface, BlurType, BlurDirection
30
31class ToolFilters(AbstractCanvasTool):
32	__gtype_name__ = 'ToolFilters'
33
34	def __init__(self, window):
35		super().__init__('filters', _("Filters"), 'tool-filters-symbolic', window)
36		self.cursor_name = 'pointer'
37		self.add_tool_action_simple('filters_preview', self.on_filter_preview)
38
39		self.add_tool_action_enum('filters_type', 'saturation')
40		self.type_label = _("Change saturation")
41		self._active_filter = 'saturation'
42
43		# Options specific to filters, but which are here for no good reason
44		self.add_tool_action_enum('filters_blur_dir', 'none')
45		self.blur_algo = BlurType.INVALID
46
47		# Initialisation of the filters
48		self._all_filters = {
49			'blur': FilterBlur('blur', self),
50			'colors': FilterColors('colors', self),
51			'contrast': FilterContrast('contrast', self),
52			'emboss': FilterEmboss('emboss', self),
53			'saturation': FilterSaturation('saturation', self),
54			'transparency': FilterTransparency('transparency', self),
55			'veil': FilterVeil('veil', self),
56		}
57
58	def try_build_pane(self):
59		self.pane_id = 'filters'
60		self.window.options_manager.try_add_bottom_pane(self.pane_id, self)
61
62	def build_bottom_pane(self):
63		self.bar = OptionsBarFilters(self.window, self)
64		self.bar.menu_btn.connect('notify::active', self._set_active_type)
65		self.bar.menu_btn.connect('notify::active', self._set_blur_direction)
66		return self.bar
67
68	def get_max_filter_width(self):
69		width = 0
70		for f in self._all_filters.values():
71			width = max(f.get_preferred_minimum_width(), width)
72		return width
73
74	def set_filters_compact(self, is_compact):
75		for f_id, f in self._all_filters.items():
76			f.set_filter_compact(f_id == self._active_filter, is_compact)
77
78	def get_edition_status(self):
79		tip_label = _("Click on the image to preview the selected filter")
80		return self.type_label + ' - ' + tip_label
81
82	def get_filters_menu(self):
83		"""Returns the Gio.MenuModel with the list of filters and some of their
84		options, for the optionsbar."""
85		model = self.get_options_model().get_item_link(0, Gio.MENU_LINK_SECTION)
86		return model.get_item_link(0, Gio.MENU_LINK_SUBMENU)
87
88	############################################################################
89
90	def _set_blur_direction(self, *args):
91		self._all_filters['blur'].set_attributes_values()
92
93	def _set_active_type(self, *args):
94		state_as_string = self.get_option_value('filters_type')
95
96		self.blur_algo = BlurType.INVALID
97		if state_as_string == 'blur_fast':
98			self.blur_algo = BlurType.CAIRO_REPAINTS
99			self.type_label =  _("Fast blur")
100			self._active_filter = 'blur'
101		elif state_as_string == 'blur_slow':
102			self.blur_algo = BlurType.PX_BOX
103			self.type_label = _("Slow blur")
104			self._active_filter = 'blur'
105		elif state_as_string == 'tiles':
106			self.blur_algo = BlurType.TILES
107			self.type_label = _("Pixelization")
108			self._active_filter = 'blur'
109
110		elif state_as_string == 'saturation':
111			self.type_label = _("Change saturation")
112			self._active_filter = 'saturation'
113		elif state_as_string == 'veil':
114			self.type_label = _("Veil")
115			self._active_filter = 'veil'
116
117		elif state_as_string == 'contrast':
118			self.type_label = _("Increase contrast")
119			self._active_filter = 'contrast'
120		# TODO changer la luminosity tant qu'à faire
121		elif state_as_string == 'emboss':
122			# Context: a filter. See "image embossing" on wikipedia
123			self.type_label = _("Emboss")
124			self._active_filter = 'emboss'
125
126		elif state_as_string == 'invert':
127			self.type_label = _("Invert colors")
128			self._active_filter = 'colors'
129
130		elif state_as_string == 'transparency':
131			self.type_label = _("Add transparency")
132			self._active_filter = 'transparency'
133		else:
134			self.type_label = _("Select a filter…")
135		self.bar.on_filter_changed()
136
137	############################################################################
138
139	def on_tool_selected(self, *args):
140		super().on_tool_selected()
141		self._set_active_type()
142		self._set_blur_direction()
143		self.bar.menu_btn.set_active(True)
144		if self.blur_algo == BlurType.INVALID:
145			self.on_filter_preview()
146			# XXX great optimization but it displays shit
147
148	def on_press_on_area(self, event, surface, event_x, event_y):
149		self.on_filter_preview()
150
151	def on_filter_preview(self, *args):
152		self._set_active_type()
153		self._set_blur_direction()
154		self.build_and_do_op()
155
156	############################################################################
157
158	def build_operation(self):
159		operation = {
160			'tool_id': self.id,
161			'is_selection': self.apply_to_selection,
162			'is_preview': True,
163			'local_dx': 0,
164			'local_dy': 0,
165			'filter_id': self._active_filter
166		}
167		options = self._all_filters[self._active_filter].build_filter_op()
168		return {**operation, **options}
169
170	def do_tool_operation(self, operation):
171		self.start_tool_operation(operation)
172		if operation['is_selection']:
173			source_pixbuf = self.get_selection_pixbuf()
174		else:
175			source_pixbuf = self.get_main_pixbuf()
176
177		active_filter = self._all_filters[operation['filter_id']]
178		active_filter.do_filter_operation(source_pixbuf, operation)
179
180		self.common_end_operation(operation)
181
182	############################################################################
183################################################################################
184
185