From 16c5b2406cac683eb7009ae84f0c6a4db36cf7c7 Mon Sep 17 00:00:00 2001 From: Gonzalo Odiard Date: Thu, 28 Jun 2012 14:39:08 +0000 Subject: First stage of port of Paint to cairo Brushes and shapes are working. NOTES: * The pixmaps used as canvas has been replaced by ImageSurface and cairo context. * All the gc with tool properties were removed. * The variables needed by the freeform and rainbow were moved from Area to Desenha * The freeform code was simplified. * The heart is draw using cairo curves and is a little different than before. * I am using cairo_stroke_color and cairo_fill_color values in the tool dict to enable compatibility while the port is done, later will be unified. I am setting this color with a fixed alpha of 0.3 to display artifacts if we want enable alpha in the future. It's only for test at this stage. * The code used to display the polygons (triangle, trapezoid, arrow, paralellogram, star, polygon_regular) was simplified calling a method _draw_polygon witha array of points. PENDING: * All the clipboard operations. * All the efects. * Text * Bucket * Stamps --- diff --git a/Area.py b/Area.py index a81cda3..f5b9511 100644 --- a/Area.py +++ b/Area.py @@ -69,6 +69,7 @@ import os import tempfile import math import pango +import cairo from Desenho import Desenho from urlparse import urlparse @@ -130,7 +131,7 @@ class Area(gtk.DrawingArea): gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) self.connect('drag_data_received', self.drag_data_received) - self.set_flags(gtk.CAN_FOCUS) + self.set_can_focus(True) self.grab_focus() self.set_extension_events(gtk.gdk.EXTENSION_EVENTS_CURSOR) @@ -147,13 +148,15 @@ class Area(gtk.DrawingArea): ## - 'vertices' : a integer ## All values migth be None, execept in 'name' key. self.tool = { - 'name': 'pencil', + 'name': 'brush', 'line size': 4, 'stamp size': self._get_stamp_size(), 'fill color': None, 'stroke color': None, 'line shape': 'circle', 'fill': True, + 'cairo_stroke_color': (0.0, 0.0, 0.0, 0.3), + 'cairo_fill_color': (0.0, 0.0, 0.0, 0.3), 'vertices': 6.0} self.desenha = False @@ -161,24 +164,12 @@ class Area(gtk.DrawingArea): self.sel_get_out = False self.oldx = 0 self.oldy = 0 - self.polygon_start = True - self.points = [] - self.gc = None - self.gc_rainbow = None - self.gc_line = None - self.gc_eraser = None - self.gc_brush = None - self.gc_selection = None - self.pixmap = None - self.pixmap_temp = None - self.pixmap_sel = None - self.desenho = [] + self.drawing_canvas = None self.textos = [] self.text_in_progress = False self.activity = activity self.d = Desenho(self) self.last = [] - self.rainbow_counter = 0 self.keep_aspect_ratio = False self.keep_shape_ratio = False @@ -186,7 +177,7 @@ class Area(gtk.DrawingArea): self.font_description.set_family('Sans') self.font_description.set_size(12) - self._set_selection_bounds(0, 0, 0, 0) + self.set_selection_bounds(0, 0, 0, 0) # List of pixmaps for the Undo function: self._undo_list = [] @@ -201,57 +192,32 @@ class Area(gtk.DrawingArea): """Set the stamp initial size, based on the display DPI.""" return zoom(44) + def load_from_file(self, file_path): + self.drawing_canvas = cairo.ImageSurface.create_from_png(file_path) + def setup(self, width, height): """Configure the Area object.""" - if self.pixmap: - # Already set up - return - logging.debug('Area.setup: w=%s h=%s' % (width, height)) win = self.window self.set_size_request(width, height) - ##It is the main pixmap, who is display most of the time. - self.pixmap = gtk.gdk.Pixmap(win, width, height, -1) - self.pixmap.draw_rectangle(self.get_style().white_gc, True, - 0, 0, width, height) - ##This pixmap is showed when we need show something and not draw it. - self.pixmap_temp = gtk.gdk.Pixmap(win, width, height, -1) - self.pixmap_temp.draw_rectangle(self.get_style().white_gc, True, - 0, 0, width, height) - - ##When something is selected this pixmap draw and rectangular box - # out of the selection - #self.pixmap_sel = gtk.gdk.Pixmap(win, width, height, -1) - #self.pixmap_sel.draw_rectangle(self.get_style().white_gc, True, - # 0, 0, width, height) - - self.gc = win.new_gc() - self.gc_eraser = win.new_gc() - colormap = self.get_colormap() - self.white = colormap.alloc_color('#ffffff', True, True) # white - self.black = colormap.alloc_color('#000000', True, True) # black - - self.gc_eraser.set_foreground(self.white) - self.gc_rainbow = win.new_gc() - - self.gc_brush = win.new_gc() - self.gc_brush.set_foreground(self.black) - - self.gc_line = win.new_gc() - - self.gc_selection = win.new_gc() - self.gc_selection.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_selection.set_foreground(self.black) - - #this make another white line out of the black line - self.gc_selection1 = win.new_gc() - self.gc_selection1.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_selection1.set_foreground(self.white) + ##It is the main canvas, who is display most of the time + # if is not None was read from a file + if self.drawing_canvas is None: + self.drawing_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + self.drawing_ctx = cairo.Context(self.drawing_canvas) + # paint background white + self.drawing_ctx.rectangle(0, 0, width, height) + self.drawing_ctx.set_source_rgb(1.0, 1.0, 1.0) + self.drawing_ctx.fill() + else: + self.drawing_ctx = cairo.Context(self.drawing_canvas) + + ##This canvas is showed when we need show something and not draw it. + self._init_temp_canvas(width, height) self.enableUndo(self, size=(width, height)) @@ -260,6 +226,31 @@ class Area(gtk.DrawingArea): return True + def _init_temp_canvas(self, width, height): + self.temp_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + self.temp_ctx = cairo.Context(self.temp_canvas) + self.temp_ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0) + self.temp_ctx.rectangle(0, 0, width, height) + self.temp_ctx.fill() + self.temp_ctx.set_source_surface(self.drawing_canvas) + self.temp_ctx.paint() + + def display_selection_border(self, ctx): + x, y, x2, y2 = self.get_selection_bounds() + if (x, y, x2, y2) == (0, 0, 0, 0): + return + + ctx.save() + ctx.set_line_width(1) + ctx.set_line_cap(cairo.LINE_CAP_ROUND) + ctx.set_line_join(cairo.LINE_JOIN_ROUND) + ctx.set_dash([5, 5], 0) + ctx.set_source_rgba(0., 0., 0., 1.) + ctx.rectangle(x, y, x2, y2) + ctx.stroke() + ctx.restore() + def configure_line(self, size): """Configure the new line's size. @@ -267,11 +258,12 @@ class Area(gtk.DrawingArea): @param size -- the size of the new line """ - self.gc_line.set_line_attributes(size, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) + self.drawing_ctx.set_line_width(size) + self.drawing_ctx.set_line_cap(cairo.LINE_CAP_ROUND) + self.drawing_ctx.set_line_join(cairo.LINE_JOIN_ROUND) def expose(self, widget, event): - """ This function define which pixmap will be showed to the user. + """ This function define which canvas will be showed to the user. Show up the Area object (GtkDrawingArea). @param self -- the Area object (GtkDrawingArea) @@ -280,41 +272,58 @@ class Area(gtk.DrawingArea): """ area = event.area + #logging.error('expose area %s', area) + width, height = self.window.get_size() + + context = self.window.cairo_create() + if self.desenha or self.selmove: - widget.window.draw_drawable(self.gc, self.pixmap_temp, - area[0], area[1], area[0], area[1], area[2], area[3]) + # Paint the canvas in the widget: + # TODO: clipping + context.set_source_surface(self.temp_canvas) + context.paint() else: - widget.window.draw_drawable(self.gc, self.pixmap, - area[0], area[1], area[0], area[1], area[2], area[3]) - self.show_tool_shape(widget) - return False - - def show_tool_shape(self, widget): + # TODO: clipping + context.set_source_surface(self.drawing_canvas) + context.paint() + self.show_tool_shape(context) + self._init_temp_canvas(width, height) + self.display_selection_border(context) + + def show_tool_shape(self, context): """ Show the shape of the tool selected for pencil, brush, rainbow and eraser """ if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow', 'stamp']: + context.set_source_rgba(*self.tool['cairo_stroke_color']) + context.set_line_width(1) if not self.drawing: # draw stamp border in widget.window if self.tool['name'] == 'stamp': wr, hr = self.stamp_dimentions - widget.window.draw_rectangle(self.gc_brush, False, - self.x_cursor - wr / 2, self.y_cursor - hr / 2, - wr, hr) + context.rectangle(self.x_cursor - wr / 2, + self.y_cursor - hr / 2, wr, hr) + # TODO: stroke style + context.stroke() # draw shape of the brush, square or circle elif self.tool['line shape'] == 'circle': size = self.tool['line size'] - widget.window.draw_arc(self.gc_brush, False, - self.x_cursor - size / 2, self.y_cursor - size / 2, - size, size, 0, 360 * 64) + context.move_to(self.x_cursor, + self.y_cursor) + context.arc(self.x_cursor, + self.y_cursor, size / 2, 0., + 2 * math.pi) + context.stroke() else: size = self.tool['line size'] - widget.window.draw_rectangle(self.gc_brush, False, - self.x_cursor - size / 2, self.y_cursor - size / 2, - size, size) + context.move_to(self.x_cursor - size / 2, + self.y_cursor - size / 2) + context.rectangle(self.x_cursor - size / 2, + self.y_cursor - size / 2, size, size) + context.stroke() def mousedown(self, widget, event): """Make the Area object (GtkDrawingArea) recognize @@ -350,9 +359,6 @@ class Area(gtk.DrawingArea): self.oldx, self.oldy = coords - if self.polygon_start is False and self.tool['name'] is not 'freeform': - self.d.polygon(widget, coords, False, self.tool['fill'], "bug") - x, y, state = event.window.get_pointer() if self.tool['name'] == 'picker': @@ -366,33 +372,29 @@ class Area(gtk.DrawingArea): #Handle with the left button click event. if self.tool['name'] == 'eraser': self.last = [] - self.d.eraser(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + self.d.eraser(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'brush': self.last = [] - self.d.brush(widget, coords, self.last, self.tool['line size'], - self.tool['line shape']) + self.d.brush(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'stamp': self.last = [] - self.d.stamp(widget, coords, self.last, - self.tool['stamp size']) + self.d.stamp(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'rainbow': self.last = [] - self.d.rainbow(widget, coords, self.last, - self.rainbow_counter, self.tool['line size'], - self.tool['line shape']) + self.d.rainbow(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'freeform': + logging.error('mousedown') self.configure_line(self.tool['line size']) - if self.polygon_start == False: - self.desenha = True + self.d.freeform(widget, coords, True, + self.tool['fill'], "motion") if self.selmove: #get out of the func selection if self.tool['name'] != 'marquee-rectangular': @@ -408,8 +410,10 @@ class Area(gtk.DrawingArea): (coords[1] < yi) or (coords[1] > yf): self.sel_get_out = True else: - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, - 0, 0, 0, 0, width, height) + self.temp_ctx.set_source_surface(self.drawing_canvas, 0, 0) + self.temp_ctx.rectangle(0, 0, width, height) + self.temp_ctx.paint() + self.desenha = True widget.queue_draw() @@ -436,20 +440,13 @@ class Area(gtk.DrawingArea): elif self.tool['name'] == 'line': coords = self._keep_line_ratio(coords) - if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: - if self.tool['name'] == 'pencil': - self.d.brush(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) - self.last = coords - - elif self.tool['name'] == 'eraser': - self.d.eraser(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + if state & gtk.gdk.BUTTON1_MASK: + if self.tool['name'] == 'eraser': + self.d.eraser(widget, coords, self.last) self.last = coords elif self.tool['name'] == 'brush': - self.d.brush(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + self.d.brush(widget, coords, self.last) self.last = coords elif self.tool['name'] == 'stamp': @@ -458,25 +455,17 @@ class Area(gtk.DrawingArea): self.last = coords elif self.tool['name'] == 'rainbow': - self.d.rainbow(widget, coords, self.last, - self.rainbow_counter, self.tool['line size'], - self.tool['line shape']) - self.rainbow_counter += 1 - if self.rainbow_counter > 11: - self.rainbow_counter = 0 + self.d.rainbow(widget, coords, self.last) self.last = coords if self.desenha: if self.tool['name'] == 'line': - self.configure_line(self.tool['line size']) - self.d.line(widget, coords) + self.d.line(widget, coords, True) elif self.tool['name'] == 'ellipse': - self.configure_line(self.tool['line size']) self.d.circle(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'rectangle': - self.configure_line(self.tool['line size']) self.d.square(widget, event, coords, True, self.tool['fill']) @@ -493,52 +482,44 @@ class Area(gtk.DrawingArea): self.d.moveSelection(widget, coords) elif self.tool['name'] == 'freeform': + logging.error('mousemove') self.configure_line(self.tool['line size']) - self.d.polygon(widget, coords, True, + self.d.freeform(widget, coords, True, self.tool['fill'], "motion") elif self.tool['name'] == 'triangle': - self.configure_line(self.tool['line size']) self.d.triangle(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'trapezoid': - self.configure_line(self.tool['line size']) self.d.trapezoid(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'arrow': - self.configure_line(self.tool['line size']) self.d.arrow(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'parallelogram': - self.configure_line(self.tool['line size']) self.d.parallelogram(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'star': - self.configure_line(self.tool['line size']) self.d.star(widget, coords, self.tool['vertices'], True, self.tool['fill']) elif self.tool['name'] == 'polygon_regular': - self.configure_line(self.tool['line size']) self.d.polygon_regular(widget, coords, self.tool['vertices'], True, self.tool['fill']) elif self.tool['name'] == 'heart': - self.configure_line(self.tool['line size']) self.d.heart(widget, coords, True, self.tool['fill']) else: if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil', 'stamp']: widget.queue_draw() if self.tool['name'] == 'marquee-rectangular' and self.selmove: - size = self.pixmap_sel.get_size() - xi = self.orig_x - yi = self.orig_y - xf = xi + size[0] - yf = yi + size[1] - if (coords[0] < xi) or (coords[0] > xf) or \ - (coords[1] < yi) or (coords[1] > yf): + sel_x, sel_y, sel_width, sel_height = \ + self.get_selection_bounds() + + if (coords[0] < sel_x) or (coords[0] > sel_x + sel_width) or \ + (coords[1] < sel_y) or (coords[1] > sel_y + sel_height): self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) else: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) @@ -546,7 +527,7 @@ class Area(gtk.DrawingArea): elif self.tool['name'] == 'freeform' and not self.selmove: self.desenha = True self.configure_line(self.tool['line size']) - self.d.polygon(widget, coords, True, + self.d.freeform(widget, coords, True, self.tool['fill'], "moving") gtk.gdk.event_request_motions(event) @@ -573,9 +554,7 @@ class Area(gtk.DrawingArea): private_undo = False if self.desenha or self.sel_get_out: if self.tool['name'] == 'line': - self.pixmap.draw_line(self.gc_line, self.oldx, self.oldy, - coords[0], coords[1]) - widget.queue_draw() + self.d.line(widget, coords, False) elif self.tool['name'] == 'ellipse': self.d.circle(widget, coords, False, self.tool['fill']) @@ -588,7 +567,7 @@ class Area(gtk.DrawingArea): if (event.state & gtk.gdk.CONTROL_MASK) or \ self.keep_aspect_ratio: coords = self._keep_selection_ratio(coords) - self.d.selection(widget, coords, False) + self.d.selection(widget, coords) self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) self.selmove = True self.sel_get_out = False @@ -609,7 +588,7 @@ class Area(gtk.DrawingArea): private_undo = True elif self.tool['name'] == 'freeform': - self.d.polygon(widget, coords, False, + self.d.freeform(widget, coords, False, self.tool['fill'], 'release') private_undo = True @@ -819,10 +798,6 @@ class Area(gtk.DrawingArea): self.pixmap.draw_drawable(self.gc, undo_pix, 0, 0, 0, 0, -1, -1) self.queue_draw() - #special case for func polygon - if self.tool['name'] == 'freeform': - self.polygon_start = True # start the polygon again - self.emit('undo') def redo(self): @@ -852,6 +827,9 @@ class Area(gtk.DrawingArea): @param widget -- the Area object (GtkDrawingArea) """ + #TODO + return + #logging.debug('Area.enableUndo(self,widget)') width, height = self.window.get_size() @@ -980,10 +958,8 @@ class Area(gtk.DrawingArea): width, height) self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, 0, 0, 0, 0, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, 0, 0, - size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, -1, -1, - size[0] + 2, size[1] + 2) + + self.set_selection_bounds(0, 0, size[0], size[1]) self.tool['name'] = 'marquee-rectangular' self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) @@ -1007,7 +983,9 @@ class Area(gtk.DrawingArea): @param color -- a gdk.Color object """ - self.gc.set_foreground(color) + logging.error("TODO: Area.set_stroke_color %s", color) + self.tool['cairo_fill_color'] = (color.red_float, + color.green_float, color.blue_float, 0.3) def set_stroke_color(self, color): """Set stroke color. @@ -1016,10 +994,10 @@ class Area(gtk.DrawingArea): @param color -- a gdk.Color object """ - self.gc_line.set_foreground(color) - self.gc_line.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_brush.set_foreground(color) + logging.error("TODO: Area.set_stroke_color %s", color) + self.tool['cairo_stroke_color'] = (color.red_float, + color.green_float, color.blue_float, 0.3) + return self.activity.textview.modify_text(gtk.STATE_NORMAL, color) def grayscale(self, widget): @@ -1126,10 +1104,13 @@ class Area(gtk.DrawingArea): width, height) self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, 0, 0, self.orig_x, self.orig_y, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - self.orig_x, self.orig_y, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - self.orig_x - 1, self.orig_y - 1, size[0] + 2, size[1] + 2) + self.set_selection_bounds(self.orig_x, self.orig_y, size[0], + size[1]) + +# self.pixmap_temp.draw_rectangle(self.gc_selection, False, +# self.orig_x, self.orig_y, size[0], size[1]) +# self.pixmap_temp.draw_rectangle(self.gc_selection1, False, +# self.orig_x - 1, self.orig_y - 1, size[0] + 2, size[1] + 2) else: self.pixmap.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, @@ -1147,37 +1128,6 @@ class Area(gtk.DrawingArea): while gtk.events_pending(): gtk.mainiteration(block) - def _pixbuf2Image(self, pb): - """change a pixbuf to RGB image - - @param self -- the Area object (GtkDrawingArea) - @param pb -- the pixbuf object (gtk.gdk.Pixbuf) - - @return RGB Image - - """ - width, height = pb.get_width(), pb.get_height() - return Image.fromstring("RGB", (width, height), pb.get_pixels()) - - def _image2pixbuf(self, im): - """change a RGB image to a pixbuf - - @param self -- the Area object (GtkDrawingArea) - @param im -- a RGB image - - @return pixbuf - - """ - file1 = StringIO.StringIO() - im.save(file1, "ppm") - contents = file1.getvalue() - file1.close() - loader = gtk.gdk.PixbufLoader("pnm") - loader.write(contents, len(contents)) - pixbuf = loader.get_pixbuf() - loader.close() - return pixbuf - def rotate_left(self, widget): """Rotate the image. @@ -1241,10 +1191,14 @@ class Area(gtk.DrawingArea): width, height) self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, 0, 0, self.orig_x, self.orig_y, size[1], size[0]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - self.orig_x, self.orig_y, size[1], size[0]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - self.orig_x - 1, self.orig_y - 1, size[1] + 2, size[0] + 2) + + self.set_selection_bounds(self.orig_x, self.orig_y, size[0], + size[1]) + +# self.pixmap_temp.draw_rectangle(self.gc_selection, False, +# self.orig_x, self.orig_y, size[1], size[0]) +# self.pixmap_temp.draw_rectangle(self.gc_selection1, False, +# self.orig_x - 1, self.orig_y - 1, size[1] + 2, size[0] + 2) else: win = self.window @@ -1292,14 +1246,14 @@ class Area(gtk.DrawingArea): else: return False - def _set_selection_bounds(self, x1, y1, x2, y2): + def set_selection_bounds(self, x1, y1, width, height): """ Set selection bounds @param self -- the Area object (GtkDrawingArea) @param x1,y1,x2,y2 -- the coords of limit points """ - self._selection_corners = (x1, y1, x2, y2) + self._selection_corners = (x1, y1, width, height) def get_selection_bounds(self): """ @@ -1313,15 +1267,6 @@ class Area(gtk.DrawingArea): return self._selection_corners[0], self._selection_corners[1], \ self._selection_corners[2], self._selection_corners[3] - def loadImageFromJournal(self, pixbuf): - width, height = pixbuf.get_width(), pixbuf.get_height() - - self.pixmap.draw_pixbuf(self.gc, pixbuf, 0, 0, 0, 0, - width, height, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - self.enableUndo(self, size=(width, height)) - self.queue_draw() - def loadImage(self, name, widget=None): """Load an image. @@ -1353,10 +1298,13 @@ class Area(gtk.DrawingArea): width, height) self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, 0, 0, 0, 0, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - 0, 0, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - -1, -1, size[0] + 2, size[1] + 2) + + self.set_selection_bounds(0, 0, size[0], size[1]) + +# self.pixmap_temp.draw_rectangle(self.gc_selection, False, +# 0, 0, size[0], size[1]) +# self.pixmap_temp.draw_rectangle(self.gc_selection1, False, +# -1, -1, size[0] + 2, size[1] + 2) self.tool['name'] = 'marquee-rectangular' self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) @@ -1498,8 +1446,10 @@ class Area(gtk.DrawingArea): width, height) self.orig_x = 0 self.orig_y = 0 - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - 0, 0, width - 1, height - 1) + + self.set_selection_bounds(0, 0, width - 1, height - 1) +# self.pixmap_temp.draw_rectangle(self.gc_selection, False, +# 0, 0, width - 1, height - 1) self.selmove = True self.sel_get_out = False self.emit('select') diff --git a/Desenho.py b/Desenho.py index d4572c9..da5196a 100644 --- a/Desenho.py +++ b/Desenho.py @@ -69,6 +69,7 @@ import logging import math import gc import gobject +import cairo RESIZE_DELAY = 500 # The time to wait for the resize operation to be # executed, after the resize controls are pressed. @@ -85,9 +86,23 @@ class Desenho: """ self._resize_timer = None - #self.d = widget - - def line(self, widget, coords): + self._rainbow_color_list = ['#ff0000', # red + '#ff8000', # orange + '#ffff00', # yellow + '#80ff00', # lime + '#00ff00', # green + '#00ff80', # green water + '#00ffff', # light blue + '#007fff', # almost blue + '#0000ff', # blue + '#8000ff', # indigo + '#ff00ff', # pink violet + '#ff0080'] # violet + self._rainbow_counter = 0 + + self.points = [] + + def line(self, widget, coords, temp): """Draw line. @param self -- Desenho.Desenho instance @@ -96,15 +111,21 @@ class Desenho: """ width, height = widget.window.get_size() - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - widget.pixmap_temp.draw_line(widget.gc_line, widget.oldx, widget.oldy, - coords[0], coords[1]) - #widget.newx = coords[0] - #widget.newy = coords[1] + + if temp == True: + ctx = widget.temp_ctx + else: + ctx = widget.drawing_ctx + + ctx.set_line_width(widget.tool['line size']) + ctx.set_line_cap(cairo.LINE_CAP_ROUND) + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.move_to(widget.oldx, widget.oldy) + ctx.line_to(coords[0], coords[1]) + ctx.stroke() widget.queue_draw() - def eraser(self, widget, coords, last, size=30, shape='circle'): + def eraser(self, widget, coords, last): """Erase part of the drawing. @param self -- Desenho.Desenho instance @@ -115,11 +136,10 @@ class Desenho: @param shape -- string (default 'circle') """ - widget.desenha = False - self._trace(widget, widget.gc_eraser, coords, last, size, shape) - #widget.queue_draw() + widget.drawing_ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0) + self._trace(widget, coords, last) - def brush(self, widget, coords, last, size=5, shape='circle'): + def brush(self, widget, coords, last): """Paint with brush. @param self -- Desenho.Desenho instance @@ -130,8 +150,9 @@ class Desenho: @param shape -- string (default 'circle') """ - widget.desenha = False - self._trace(widget, widget.gc_brush, coords, last, size, shape) + widget.drawing_ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + logging.error('brush') + self._trace(widget, coords, last) def stamp(self, widget, coords, last, stamp_size=20): """Paint with stamp. @@ -155,7 +176,7 @@ class Desenho: widget.queue_draw() - def rainbow(self, widget, coords, last, color, size=5, shape='circle'): + def rainbow(self, widget, coords, last): """Paint with rainbow. @param self -- Desenho.Desenho instance @@ -167,52 +188,61 @@ class Desenho: @param shape -- string (default 'circle') """ - colormap = widget.get_colormap() - rainbow_colors = [ - colormap.alloc_color('#ff0000', True, True), # vermelho - colormap.alloc_color('#ff8000', True, True), # laranja - colormap.alloc_color('#ffff00', True, True), # amarelo - colormap.alloc_color('#80ff00', True, True), # verde lima - colormap.alloc_color('#00ff00', True, True), # verde - colormap.alloc_color('#00ff80', True, True), # verde agua - colormap.alloc_color('#00ffff', True, True), # azul claro - colormap.alloc_color('#007fff', True, True), # quase azul - colormap.alloc_color('#0000ff', True, True), # azul - colormap.alloc_color('#8000ff', True, True), # anil - colormap.alloc_color('#ff00ff', True, True), # rosa violeta - colormap.alloc_color('#ff0080', True, True), # violeta - ] - - widget.gc_rainbow.set_foreground(rainbow_colors[color]) - widget.desenha = False - self._trace(widget, widget.gc_rainbow, coords, last, size, shape) + _color_str = self._rainbow_color_list[self._rainbow_counter] + _color = gtk.gdk.color_parse(_color_str) + self._rainbow_counter += 1 + if self._rainbow_counter > 11: + self._rainbow_counter = 0 - def _trace(self, widget, gc, coords, last, size, shape): + widget.drawing_ctx.set_source_rgba(_color.red, _color.green, + _color.blue, 0.3) + self._trace(widget, coords, last) + + def _trace(self, widget, coords, last): + widget.desenha = False + size = widget.tool['line size'] + shape = widget.tool['line shape'] if shape == 'circle': - widget.pixmap.draw_arc(gc, True, - coords[0] - size / 2, coords[1] - size / 2, - size, size, 0, 360 * 64) + logging.error('trace circle') if last: - gc.set_line_attributes(size, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - widget.pixmap.draw_line(gc, - last[0], last[1], coords[0], coords[1]) - gc.set_line_attributes(0, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) + logging.error('trace circle last') + widget.drawing_ctx.set_line_width(size) + + widget.drawing_ctx.set_line_cap(cairo.LINE_CAP_ROUND) + widget.drawing_ctx.set_line_join(cairo.LINE_JOIN_ROUND) + widget.drawing_ctx.move_to(last[0], last[1]) + widget.drawing_ctx.line_to(coords[0], coords[1]) + widget.drawing_ctx.stroke() + else: + widget.drawing_ctx.move_to(coords[0], + coords[1]) + widget.drawing_ctx.arc(coords[0], coords[1], + size / 2, 0., 2 * math.pi) + widget.drawing_ctx.fill() + elif shape == 'square': - widget.pixmap.draw_rectangle(gc, True, - coords[0] - size / 2, coords[1] - size / 2, size, size) if last: points = [(last[0] - size / 2, last[1] - size / 2), (coords[0] - size / 2, coords[1] - size / 2), (coords[0] + size / 2, coords[1] + size / 2), (last[0] + size / 2, last[1] + size / 2)] - widget.pixmap.draw_polygon(gc, True, points) + for point in points: + widget.drawing_ctx.line_to(*point) + widget.drawing_ctx.fill() points = [(last[0] + size / 2, last[1] - size / 2), (coords[0] + size / 2, coords[1] - size / 2), (coords[0] - size / 2, coords[1] + size / 2), (last[0] - size / 2, last[1] + size / 2)] - widget.pixmap.draw_polygon(gc, True, points) + for point in points: + widget.drawing_ctx.line_to(*point) + widget.drawing_ctx.fill() + else: + widget.drawing_ctx.move_to(coords[0] - size / 2, + coords[1] - size / 2) + widget.drawing_ctx.rectangle(coords[0] - size / 2, + coords[1] - size / 2, size, size) + widget.drawing_ctx.fill() + if last: x = min(coords[0], last[0]) width = max(coords[0], last[0]) - x @@ -230,94 +260,86 @@ class Desenho: @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object """ if temp == True: - pixmap = widget.pixmap_temp + ctx = widget.temp_ctx else: - pixmap = widget.pixmap + ctx = widget.drawing_ctx width, height = widget.window.get_size() x, y, dx, dy, = self.adjust(widget, coords) - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) + ctx.rectangle(x, y, dx, dy) if fill == True: - pixmap.draw_rectangle(widget.gc, True, x, y, dx, dy) - pixmap.draw_rectangle(widget.gc_line, False, x, y, dx, dy) + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + widget.queue_draw_area(x, y, dx, dy) + + def _draw_polygon(self, widget, temp, fill, points, closed=True): + if not points: + return + if temp == True: + ctx = widget.temp_ctx + else: + ctx = widget.drawing_ctx + width, height = widget.window.get_size() + + ctx.save() + ctx.move_to(*points[0]) + for point in points: + ctx.line_to(*point) + if closed: + ctx.close_path() + ctx.set_line_join(cairo.LINE_JOIN_MITER) + ctx.set_line_width(widget.tool['line size']) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + ctx.restore() widget.queue_draw() def triangle(self, widget, coords, temp, fill): """Draw a triangle. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - points = [(widget.oldx, widget.oldy), (widget.oldx + int((coords[0] - widget.oldx) / 2), coords[1]), (coords[0], widget.oldy)] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def trapezoid(self, widget, coords, temp, fill): """Draw a trapezoid. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - dif = int((coords[0] - widget.oldx) / 4) points = [(widget.oldx, widget.oldy), (widget.oldx + dif, coords[1]), (coords[0] - dif, coords[1]), (coords[0], widget.oldy)] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def arrow(self, widget, coords, temp, fill): """Draw a arrow. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) @@ -339,56 +361,31 @@ class Desenho: widget.oldy + int(0.74 * r * math.sin(A + dA / 6 + dA / 6)))) p.append((widget.oldx + int(r * math.cos(A + dA / 2)),\ widget.oldy + int(r * math.sin(A + dA / 2)))) - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - tp = tuple(p) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + + self._draw_polygon(widget, temp, fill, p) def parallelogram(self, widget, coords, temp, fill): """Draw a parallelogram. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = int((coords[0] - widget.oldx) / 4) points = [(widget.oldx, widget.oldy), (coords[0] - x, widget.oldy), (coords[0], coords[1]), (widget.oldx + x, coords[1])] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def star(self, widget, coords, n, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple @param n -- number of sides - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) @@ -404,32 +401,17 @@ class Desenho: widget.oldy + int(r * math.sin(A)))) p.append((widget.oldx + int(0.4 * r * math.cos(A + dA / 2)), \ widget.oldy + int(0.4 * r * math.sin(A + dA / 2)))) - tp = tuple(p) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, p) def polygon_regular(self, widget, coords, n, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple @param n -- number of sides - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) @@ -441,29 +423,21 @@ class Desenho: A = A + dA p.append((widget.oldx + int(r * math.cos(A)), \ widget.oldy + int(r * math.sin(A)))) - tp = tuple(p) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, p) def heart(self, widget, coords, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ if temp == True: - pixmap = widget.pixmap_temp + ctx = widget.temp_ctx else: - pixmap = widget.pixmap + ctx = widget.drawing_ctx + width, height = widget.window.get_size() if coords[0] < widget.oldx: @@ -477,118 +451,95 @@ class Desenho: dx = math.fabs(coords[0] - widget.oldx) dy = math.fabs(coords[1] - widget.oldy) - - w = int(4 * dx) - e = int(4 * dx / math.sqrt(3)) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx), - int(widget.oldy - e / 2), w, e, 180 * 64, 60 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - 3 * dx), - int(widget.oldy - e / 2), w, e, 300 * 64, 60 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx * 0.2), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 180 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 180 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx), - int(widget.oldy - e / 2), w, e, 180 * 64, 60 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx - w / 2), - int(widget.oldy - e / 2), w, e, 300 * 64, 60 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx * 0.2), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 132 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 48 * 64, 132 * 64) + r = math.hypot(dy, dy) + w = r / 10.0 + + if w == 0: + # non invertible cairo matrix + return + + ctx.set_line_width(widget.tool['line size']) + line_width = ctx.get_line_width() + + ctx.save() + ctx.translate(widget.oldx, widget.oldy) + ctx.scale(w, w) + ctx.move_to(0, 0) + ctx.curve_to(0, -30, -50, -30, -50, 0) + ctx.curve_to(-50, 30, 0, 35, 0, 60) + ctx.curve_to (0, 35, 50, 30, 50, 0) + ctx.curve_to (50, -30, 0, -30, 0, 0) + + ctx.set_line_width(line_width / w) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + ctx.restore() widget.queue_draw() def circle(self, widget, coords, temp, fill): """Draw a circle. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp + ctx = widget.temp_ctx else: - pixmap = widget.pixmap + ctx = widget.drawing_ctx width, height = widget.window.get_size() x, y, dx, dy = self.adjust(widget, coords) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_arc(widget.gc, True, x, y, dx, dy, 0, 360 * 64) - pixmap.draw_arc(widget.gc_line, False, x, y, dx, dy, 0, 360 * 64) + if dx == 0 or dy == 0: + # scale by 0 gives error + return + ctx.set_line_width(widget.tool['line size']) + line_width = ctx.get_line_width() + ctx.save() + ctx.translate(x, y) + ctx.scale(dx, dy) + ctx.arc(0., 0., 1., 0., 2 * math.pi) + ctx.set_line_width(line_width / float(min(dx, dy))) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + ctx.restore() widget.queue_draw() def clear(self, widget): """Clear the drawing. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) """ logging.debug('Desenho.clear') + widget.desenha = False - widget.desenho = [] widget.textos = [] - + x, y = 0, 0 + width, height = widget.window.get_size() # try to clear a selected area first if widget.is_selected(): - try: - width, height = widget.pixmap_sel.get_size() - - # Clear the selected area - widget.pixmap_sel.draw_rectangle(widget.get_style().white_gc, - True, 0, 0, width, height) - # Draw the selected area in the displayed pixmap - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, - 0, 0, widget.orig_x, widget.orig_y, width, height) - # Draw the selection rectangle - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x, widget.orig_y, width, height) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x - 1, widget.orig_y - 1, - width + 2, height + 2) - - except NameError, message: - logging.debug(message) - except Exception, message: - logging.debug('Unexpected error: %s', message) - else: - width, height = widget.window.get_size() - widget.pixmap.draw_rectangle(widget.get_style().white_gc, True, - 0, 0, width, height) - widget.pixmap_temp.draw_rectangle(widget.get_style().white_gc, - True, 0, 0, width, height) + x, y, width, height = widget.get_selection_bounds() + logging.error('clear %s', (x, y, width, height)) + + widget.drawing_ctx.rectangle(x, y, width, height) + widget.drawing_ctx.set_source_rgb(1.0, 1.0, 1.0) + widget.drawing_ctx.fill() + widget.queue_draw() def text(self, widget, event): """Display and draw text in the drawing area. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param event -- GdkEvent - """ if not widget.text_in_progress: @@ -628,47 +579,18 @@ class Desenho: widget.queue_draw() - def selection(self, widget, coords, temp=True): + def selection(self, widget, coords): """Make a selection. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp @param fill -- Fill object - - @return (x0,y0,x1,y1) -- coords of corners """ width, height = widget.window.get_size() x, y, dx, dy = self.adjust(widget, coords, True) - - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, - 0, 0, 0, 0, width, height) - if temp: - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - x, y, dx, dy) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - x - 1, y - 1, dx + 2, dy + 2) - else: - try: - del (widget.pixmap_sel) - except: - pass - - widget.pixmap_sel = gtk.gdk.Pixmap(widget.window, dx, dy, -1) - widget.pixmap_sel.draw_drawable(widget.gc, widget.pixmap, - x, y, 0, 0, dx, dy) - widget.pixmap.draw_rectangle(widget.get_style().white_gc, True, - x, y, dx, dy) - widget.orig_x = x - widget.orig_y = y - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - x, y, dx, dy) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - x - 1, y - 1, dx + 2, dy + 2) - + widget.set_selection_bounds(x, y, dx, dy) widget.queue_draw() def moveSelection(self, widget, coords): @@ -695,11 +617,14 @@ class Desenho: widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, 0, 0, widget.orig_x + dx, widget.orig_y + dy, size[0], size[1]) - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x + dx, widget.orig_y + dy, size[0], size[1]) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x + dx - 1, widget.orig_y + dy - 1, - size[0] + 2, size[1] + 2) + widget.set_selection_bounds(widget.orig_x + dx, widget.orig_y + dy, + size[0], size[1]) + +# widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, +# widget.orig_x + dx, widget.orig_y + dy, size[0], size[1]) +# widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, +# widget.orig_x + dx - 1, widget.orig_y + dy - 1, +# size[0] + 2, size[1] + 2) widget.queue_draw() @@ -772,108 +697,69 @@ class Desenho: else: widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, 0, 0, widget.orig_x, widget.orig_y, wr, hr) - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x, widget.orig_y, wr, hr) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x - 1, widget.orig_y - 1, wr + 2, hr + 2) + + widget.set_selection_bounds(widget.orig_x, widget.orig_y, wr, hr) +# widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, +# widget.orig_x, widget.orig_y, wr, hr) +# widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, +# widget.orig_x - 1, widget.orig_y - 1, wr + 2, hr + 2) widget.queue_draw() gc.collect() - def polygon(self, widget, coords, temp, fill, param=None): + def freeform(self, widget, coords, temp, fill, param=None): """Draw polygon. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap width, height = widget.window.get_size() - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - + logging.error('freeform param %s', param) if param == "moving": # mouse not pressed moving - if not widget.polygon_start: - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - elif widget.polygon_start == True: # Starting a new polygon ? - if param == "motion": - # first press - try: - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.points.append(coords) - except: - pixmap.draw_line(widget.gc_line, widget.oldx, widget.oldy, - coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.oldx, widget.oldy, coords[0], coords[1]) - widget.first = widget.oldx, widget.oldy - widget.points = [widget.first, coords] - widget.enableUndo(widget, overrite=True) - widget.last = coords - else: # param == "release" - # first release - try: - widget.first - widget.points.append(coords) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - except: - widget.first = widget.oldx, widget.oldy - widget.points = [widget.first, coords] - widget.pixmap.draw_line(widget.gc_line, - widget.oldx, widget.oldy, coords[0], coords[1]) - widget.enableUndo(widget, overrite=True) - widget.last = coords - widget.polygon_start = False - else: - if param == "motion": - # print "press" - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.enableUndo(widget, overrite=True) - widget.last = coords - widget.points.append(coords) - elif param == "release": - # print "release" - x = coords[0] - widget.first[0] - y = coords[1] - widget.first[1] - d = math.hypot(x, y) - if d > 20: # close the polygon ? - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) + if self.points: + if widget.last: + self.points.append((coords[0], coords[1])) + widget.last = [] + else: + self.points[-1] = (coords[0], coords[1]) + elif param == "motion": + # when mousepress or mousemove + if widget.last: + self.points.append((widget.last[0], widget.last[1])) + self.points.append((coords[0], coords[1])) + else: + self.points.append((widget.oldx, widget.oldy)) + self.points.append((coords[0], coords[1])) + widget.enableUndo(widget, overrite=True) + widget.last = coords + elif param == "release": + if len(self.points) > 2: + first = self.points[0] + dx = coords[0] - first[0] + dy = coords[1] - first[1] + d = math.hypot(dx, dy) + if d > 20: widget.last = coords - widget.points.append(coords) + self.points.append(coords) else: - tp = tuple(widget.points) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) + # close the polygon + logging.error('freeform ending') + self._draw_polygon(widget, False, fill, self.points) widget.last = [] - widget.polygon_start = True + self.points = [] widget.enableUndo(widget, overrite=True) - elif param == "bug": - tp = tuple(widget.points) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.last = [] - widget.polygon_start = True - widget.enableUndo(widget, overrite=True) + widget.queue_draw() + return + + logging.error('freeform points (2) %s', self.points) + widget.desenha = True + # Display the polygon open in the temp canvas + self._draw_polygon(widget, True, False, self.points, closed=False) widget.queue_draw() def adjust(self, widget, coords, locked=False): diff --git a/OficinaActivity.py b/OficinaActivity.py index 64dace4..16484b6 100644 --- a/OficinaActivity.py +++ b/OficinaActivity.py @@ -140,21 +140,23 @@ class OficinaActivity(activity.Activity): logging.debug('reading file %s, mimetype: %s, title: %s', file_path, self.metadata['mime_type'], self.metadata['title']) - pixbuf = gtk.gdk.pixbuf_new_from_file(file_path) + self.area.load_from_file(file_path) def size_allocate_cb(widget, allocation): + logging.error('read file size allocate') self.fixed.disconnect(self._setup_handle) - self.area.setup(pixbuf.get_width(), pixbuf.get_height()) + width = self.area.drawing_canvas.get_width() + height = self.area.drawing_canvas.get_height() + self.area.setup(width, height) # The scrolled window is confused with a image of the same size # of the canvas when the toolbars popup and the scrolls # keep visible. - if pixbuf.get_height() > allocation.height or \ - pixbuf.get_width() > allocation.width: - self.canvas.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + if height > allocation.height or width > allocation.width: + self.canvas.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) else: self.canvas.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) - self.area.loadImageFromJournal(pixbuf) self.center_area() self.canvas.add_with_viewport(self.fixed) @@ -186,12 +188,8 @@ class OficinaActivity(activity.Activity): self.area.d.text(self.area, event=None) self.area.getout() - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - width, height) - pixbuf.get_from_drawable(self.area.pixmap, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, -1, -1) + self.area.drawing_canvas.write_to_png(file_path) self.metadata['mime_type'] = 'image/png' - pixbuf.save(file_path, 'png', {}) def _get_area_displacement(self): """Return the point to use as top left corner in order to move diff --git a/toolbox.py b/toolbox.py index 8d58306..b5d1e1c 100644 --- a/toolbox.py +++ b/toolbox.py @@ -336,6 +336,12 @@ class ToolsToolbarBuilder(): self._activity.area.set_stroke_color(new_color) self.properties['stroke color'] = new_color + if isinstance(new_color, unicode): + new_color = gtk.gdk.Color(new_color) + self.properties['cairo_stroke_color'] = (new_color.red_float, + new_color.green_float, + new_color.blue_float, 0.3) + def _on_signal_undo_cb(self, widget, data=None): self._verify_sensitive_buttons() @@ -378,6 +384,12 @@ class ButtonFillColor(ColorToolButton): self._activity.area.set_fill_color(new_color) self.properties['fill color'] = new_color + if isinstance(new_color, unicode): + new_color = gtk.gdk.Color(new_color) + self.properties['cairo_fill_color'] = (new_color.red_float, + new_color.green_float, + new_color.blue_float, 0.3) + def create_palette(self): self._palette = self.get_child().create_palette() color_palette_hbox = self._palette._picker_hbox -- cgit v0.9.1