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