def __rotate_mask(self, px, py): # Rotates the entire mask around the grey axis during drags. cx, cy = self.get_center() x0, y0 = self.__drag_start_pos theta0 = math.atan2(x0 - cx, y0 - cy) theta = math.atan2(px - cx, py - cy) dntheta = (theta0 - theta) / (2 * math.pi) while dntheta <= 0: dntheta += 1.0 if self.__mask_predrag is None: self.__mask_predrag = [] for shape in self.get_mask(): shape_hcy = [HCYColor(color=c) for c in shape] self.__mask_predrag.append(shape_hcy) mgr = self.get_color_manager() newmask = [] for shape in self.__mask_predrag: shape_rot = [] for col in shape: col_r = HCYColor(color=col) h = mgr.distort_hue(col_r.h) h += dntheta h %= 1.0 col_r.h = mgr.undistort_hue(h) shape_rot.append(col_r) newmask.append(shape_rot) self.set_mask(newmask)
def get_managed_color(self): """Override, with a limited range or returned luma. """ col = super(HCYMaskEditorWheel, self).get_managed_color() col = HCYColor(color=col) col.y = clamp(col.y, self.__MIN_LUMA, self.__MAX_LUMA) return col
def __rotate_mask(self, px, py): # Rotates the entire mask around the grey axis during drags. cx, cy = self.get_center() x0, y0 = self.__drag_start_pos theta0 = math.atan2(x0-cx, y0-cy) theta = math.atan2(px-cx, py-cy) dntheta = (theta0 - theta) / (2*math.pi) while dntheta <= 0: dntheta += 1.0 if self.__mask_predrag is None: self.__mask_predrag = [] for shape in self.get_mask(): shape_hcy = [HCYColor(color=c) for c in shape] self.__mask_predrag.append(shape_hcy) mgr = self.get_color_manager() newmask = [] for shape in self.__mask_predrag: shape_rot = [] for col in shape: col_r = HCYColor(color=col) h = mgr.distort_hue(col_r.h) h += dntheta h %= 1.0 col_r.h = mgr.undistort_hue(h) shape_rot.append(col_r) newmask.append(shape_rot) self.set_mask(newmask)
def _get_paint_chip_shadow(color): """Paint chip shadow edge color""" shadow = HCYColor(color=color) ky = gui.style.PAINT_CHIP_SHADOW_HCY_Y_MULT kc = gui.style.PAINT_CHIP_SHADOW_HCY_C_MULT shadow.y = clamp(shadow.y * ky, 0, 1) shadow.c = clamp(shadow.c * kc, 0, 1) return shadow
def _get_paint_chip_highlight(color): """Paint chip highlight edge color""" highlight = HCYColor(color=color) ky = gui.style.PAINT_CHIP_HIGHLIGHT_HCY_Y_MULT kc = gui.style.PAINT_CHIP_HIGHLIGHT_HCY_C_MULT highlight.y = clamp(highlight.y * ky, 0, 1) highlight.c = clamp(highlight.c * kc, 0, 1) return highlight
def __scroll_cb(self, widget, event): # Scrolling controls luma. d = self.SCROLL_DELTA directions = (Gdk.ScrollDirection.DOWN, Gdk.ScrollDirection.LEFT) if event.direction in directions: d *= -1 col = HCYColor(color=self.get_managed_color()) y = clamp(col.y + d, 0.0, 1.0) if col.y != y: col.y = y self.set_managed_color(col) return True
def __scroll_cb(self, widget, event): # Scrolling controls luma. d = self.SCROLL_DELTA directions = (Gdk.ScrollDirection.DOWN, Gdk.ScrollDirection.LEFT) if event.direction in directions: d *= -1 col = HCYColor(color=self.get_managed_color()) y = clamp(col.y+d, 0.0, 1.0) if col.y != y: col.y = y self.set_managed_color(col) return True
def __init__(self, parent, target): super(HCYMaskTemplateDialog, self).__init__( title=C_( u"HCY Gamut Mask new-from-template dialog: window title", "New Gamut Mask from Template", ), transient_for=parent, modal=True, destroy_with_parent=True, window_position=Gtk.WindowPosition.MOUSE, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT), ) target_mgr = target.get_color_manager() prefs_ro = deepcopy(target_mgr.get_prefs()) datapath = target_mgr.get_data_path() mgr = ColorManager(prefs=prefs_ro, datapath=datapath) mgr.set_wheel_type(target_mgr.get_wheel_type()) self.target = target for name, desc, mask_shapes_float in self.__templates: mask = [] for mask_shape_float in mask_shapes_float: shape = [] for h, c, y in mask_shape_float: h = mgr.undistort_hue(h) shape.append(HCYColor(h, c, y)) mask.append(shape) label = Gtk.Label() label.set_markup("<b>%s</b>\n\n%s" % (name, desc)) label.set_size_request(375, -1) label.set_line_wrap(True) label.set_alignment(0, 0.5) preview = HCYMaskPreview() preview.set_color_manager(mgr) preview.set_mask(mask) preview_frame = Gtk.AspectFrame(obey_child=True) preview_frame.add(preview) preview_frame.set_shadow_type(Gtk.ShadowType.NONE) hbox = Gtk.HBox() hbox.set_spacing(6) hbox.pack_start(preview_frame, False, False, 0) hbox.pack_start(label, True, True, 0) button = Gtk.Button() button.add(hbox) button.set_relief(Gtk.ReliefStyle.NONE) button.connect("clicked", self.__button_clicked_cb, mask) self.vbox.pack_start(button, True, True, 0) self.connect("response", self.__response_cb) self.connect("show", self.__show_cb) for w in self.vbox: w.show_all() ref_color = target.get_managed_color() mgr.set_color(ref_color)
def __show_cb(self, widget, *a): # When the dialog is shown, clone the target adjuster's mask for # editing. Assume the user wants to turn on the mask if there # is no mask on the target already (reduce the number of mouse clicks) active = True if self.target.get_mask(): active = self.target.mask_toggle.get_active() self.mask_toggle_ctrl.set_active(active) mask = deepcopy(self.target.get_mask()) self.editor.set_mask(mask) # The wheel type may have changed elsewhere editor_mgr = self.editor.get_color_manager() wheel_type = self.target.get_color_manager().get_wheel_type() editor_mgr.set_wheel_type(wheel_type) # Clone the target's luma too, # but not too bright, not too dark col = HCYColor(color=self.target.get_managed_color()) self.editor.set_managed_color(col) # Necessary for the content to be displayed self.vbox.show_all()
def __motion_notify_cb(self, widget, event): """Button1 motion handler.""" pos = event.x, event.y if self.__button_down == 1: if self.IS_DRAG_SOURCE: if not self.__drag_start_pos: return False start_pos = self.__drag_start_pos dx = pos[0] - start_pos[0] dy = pos[1] - start_pos[1] dist = math.hypot(dx, dy) if (dist > self._drag_threshold) and self.__drag_start_color: logger.debug( "Start drag (dist=%0.3f) with colour %r", dist, self.__drag_start_color, ) self.__drag_start_color = None self.drag_begin_with_coordinates( targets = Gtk.TargetList.new([ Gtk.TargetEntry.new(*e) for e in self._DRAG_TARGETS ]), actions = Gdk.DragAction.MOVE | Gdk.DragAction.COPY, button = 1, event = event, x = event.x, y = event.y, ) return True # a drag was just started else: # Non-drag-source widgets update the color continuously while # the mouse button is held down and the pointer moved. color = self.get_color_at_position(event.x, event.y) self.set_managed_color(color) # Relative chroma/luma/hue bending with other buttons. elif self.ALLOW_HCY_TWEAKING and self.__button_down > 1: if self.__drag_start_color is None: return False col = HCYColor(color=self.__drag_start_color) alloc = self.get_allocation() w, h = alloc.width, alloc.height size = max(w, h) ex, ey = event.x, event.y sx, sy = self.__drag_start_pos dx, dy = sx - ex, sy - ey # Pick a dimension to tweak if event.state & Gdk.ModifierType.SHIFT_MASK: bend = "chroma" dy = -dy elif event.state & Gdk.ModifierType.CONTROL_MASK: bend = "hue" else: bend = "luma" dy = -dy # Interpretation of dx depends on text direction if widget.get_direction() == Gtk.TextDirection.RTL: dx = -dx # Use the delta with the largest absolute value # FIXME: this has some jarring discontinities dd = dx if abs(dx) > abs(dy) else dy if bend == "chroma": c0 = clamp(col.c, 0., 1.) p = (c0 * size) - dd col.c = clamp(p / size, 0., 1.) elif bend == "hue": h0 = clamp(col.h, 0., 1.) p = (h0 * size) - dd h = p / size while h < 0: h += 1.0 col.h = h % 1.0 else: # luma y0 = clamp(col.y, 0., 1.) p = (y0 * size) - dd col.y = clamp(p / size, 0., 1.) self.set_managed_color(col) # Let other registered handlers run, normally. return False
def set_managed_color(self, color): """Override, limiting the luma range. """ col = HCYColor(color=color) col.y = clamp(col.y, self.__MIN_LUMA, self.__MAX_LUMA) super(HCYMaskEditorWheel, self).set_managed_color(col)
def _palette_render(palette, cr, rows, columns, swatch_size, bg_color, offset_x=0, offset_y=0, rtl=False): """Renders a Palette according to a precalculated grid. :param cr: a Cairo context :param int rows: number of rows in the layout :param int columns: number of columns in the layout :param int swatch_size: size of each swatch, in pixels :param lib.color.UIColor bg_color: color used when rendering the patterned placeholder for an empty palette slot. :param bool rtl: layout direction: set to True to render right to left, instead of left to right. Currently ignored. """ HIGHLIGHT_DLUMA = 0.05 if len(palette) == 0: return if rows is None or columns is None: return cr.save() cr.translate(offset_x, offset_y) # Sizes and colors swatch_w = swatch_h = swatch_size light_col = HCYColor(color=bg_color) light_col.y += HIGHLIGHT_DLUMA dark_col = HCYColor(color=bg_color) dark_col.y -= HIGHLIGHT_DLUMA if light_col.y >= 1: light_col.y = 1.0 dark_col.y = 1.0 - (2*HIGHLIGHT_DLUMA) if dark_col.y <= 0: dark_col.y = 0.0 light_col.y = 0.0 + (2*HIGHLIGHT_DLUMA) # Upper left outline (bottom right is covered below by the # individual chips' shadows) ul_col = HCYColor(color=bg_color) ul_col.y *= 0.75 ul_col.c *= 0.5 cr.set_line_join(cairo.LINE_JOIN_ROUND) cr.set_line_cap(cairo.LINE_CAP_ROUND) cr.set_source_rgb(*ul_col.get_rgb()) cr.move_to(0.5, rows*swatch_h - 1) cr.line_to(0.5, 0.5) row1cells = min(columns, len(palette)) # needed? cr.line_to(row1cells*swatch_w - 1, 0.5) cr.set_line_width(2) cr.stroke() # Draw into the predefined grid r = c = 0 cr.set_line_width(1.0) cr.set_line_cap(cairo.LINE_CAP_SQUARE) for col in palette.iter_colors(): s_x = c*swatch_w s_y = r*swatch_h s_w = swatch_w s_h = swatch_h # Select fill bg and pattern fg colors, Tango-style edge highlight # and lower-right shadow. if col is None: # Empty slot, fill with a pattern hi_rgb = light_col.get_rgb() fill_bg_rgb = dark_col.get_rgb() fill_fg_rgb = light_col.get_rgb() sh_col = HCYColor(color=bg_color) sh_col.y *= 0.75 sh_col.c *= 0.5 sh_rgb = sh_col.get_rgb() else: # Color swatch hi_col = HCYColor(color=col) hi_col.y = min(hi_col.y * 1.1, 1) hi_col.c = min(hi_col.c * 1.1, 1) sh_col = HCYColor(color=col) sh_col.y *= 0.666 sh_col.c *= 0.5 hi_rgb = hi_col.get_rgb() fill_bg_rgb = col.get_rgb() fill_fg_rgb = None sh_rgb = sh_col.get_rgb() # Draw the swatch / color chip cr.set_source_rgb(*sh_rgb) cr.rectangle(s_x, s_y, s_w, s_h) cr.fill() cr.set_source_rgb(*fill_bg_rgb) cr.rectangle(s_x, s_y, s_w-1, s_h-1) cr.fill() if fill_fg_rgb is not None: s_w2 = int((s_w-1) / 2) s_h2 = int((s_h-1) / 2) cr.set_source_rgb(*fill_fg_rgb) cr.rectangle(s_x, s_y, s_w2, s_h2) cr.fill() cr.rectangle(s_x+s_w2, s_y+s_h2, s_w2, s_h2) cr.fill() cr.set_source_rgb(*hi_rgb) cr.rectangle(s_x+0.5, s_y+0.5, s_w-2, s_h-2) cr.stroke() c += 1 if c >= columns: c = 0 r += 1 cr.restore()
def color_at_normalized_polar_pos(self, r, theta): col = HCYColor(color=self.get_managed_color()) col.h = theta col.c = r return col
def get_background_validity(self): col = HCYColor(color=self.get_managed_color()) return int(col.h * 1000), int(col.c * 1000)
def get_new_color(self, pick_color, brush_color): new_col_hcy = self.get_new_hcy_color(HCYColor(color=pick_color), HCYColor(color=brush_color)) new_col_hcy.c = max(_C_MIN, new_col_hcy.c) new_col_hcy.y = min(_Y_MAX, max(_Y_MIN, new_col_hcy.y)) return new_col_hcy
def get_normalized_polar_pos_for_color(self, col): col = HCYColor(color=col) return col.c, col.h
def get_color_for_bar_amount(self, amt): col = HCYColor(color=self.get_managed_color()) col.y = amt return col
def get_bar_amount_for_color(self, col): col = HCYColor(color=col) return col.y
def __motion_notify_cb(self, widget, event): """Button1 motion handler.""" pos = event.x, event.y if (self.__button_down is not None) and self.__button_down == 1: if self.IS_DRAG_SOURCE: if not self.__drag_start_color: return False start_pos = self.__drag_start_pos dx = pos[0] - start_pos[0] dy = pos[1] - start_pos[1] dist = math.hypot(dx, dy) if (dist > self._drag_threshold) and self.__drag_start_color: logger.debug( "Start drag (dist=%0.3f) with colour %r", dist, self.__drag_start_color, ) self.__drag_start_color = None self.drag_begin_with_coordinates( targets = Gtk.TargetList.new([ Gtk.TargetEntry.new(*e) for e in self._DRAG_TARGETS ]), actions = Gdk.DragAction.MOVE | Gdk.DragAction.COPY, button = 1, event = event, x = event.x, y = event.y, ) return True # a drag was just started else: # Non-drag-source widgets update the color continuously while # the mouse button is held down and the pointer moved. color = self.get_color_at_position(event.x, event.y) self.set_managed_color(color) # Relative chroma/luma/hue bending with other buttons. elif ((self.__button_down is not None) and self.ALLOW_HCY_TWEAKING and self.__button_down > 1): if self.__drag_start_color is None: return False col = HCYColor(color=self.__drag_start_color) alloc = self.get_allocation() w, h = alloc.width, alloc.height size = max(w, h) ex, ey = event.x, event.y sx, sy = self.__drag_start_pos dx, dy = sx - ex, sy - ey # Pick a dimension to tweak if event.state & Gdk.ModifierType.SHIFT_MASK: bend = "chroma" dy = -dy elif event.state & Gdk.ModifierType.CONTROL_MASK: bend = "hue" else: bend = "luma" dy = -dy # Interpretation of dx depends on text direction if widget.get_direction() == Gtk.TextDirection.RTL: dx = -dx # Use the delta with the largest absolute value # FIXME: this has some jarring discontinities dd = dx if abs(dx) > abs(dy) else dy if bend == "chroma": c0 = clamp(col.c, 0., 1.) p = (c0 * size) - dd col.c = clamp(p / size, 0., 1.) elif bend == "hue": h0 = clamp(col.h, 0., 1.) p = (h0 * size) - dd h = p / size while h < 0: h += 1.0 col.h = h % 1.0 else: # luma y0 = clamp(col.y, 0., 1.) p = (y0 * size) - dd col.y = clamp(p / size, 0., 1.) self.set_managed_color(col) # Let other registered handlers run, normally. return False