def repaint(self, device_bbox=None, model_bbox=None):
# FIXME: ...we do not fill tile-free background white in this function...
- cr = self.window.cairo_create()
-
if device_bbox is None:
w, h = self.window.get_size()
device_bbox = (0, 0, w, h)
-
#print 'device bbox', tuple(device_bbox)
+ gdk_clip_region = self.window.get_clip_region()
+ x, y, w, h = device_bbox
+ sparse = not gdk_clip_region.point_in(x+w/2, y+h/2)
+
+ cr = self.window.cairo_create()
+
# actually this is only neccessary if we are not answering an expose event
cr.rectangle(*device_bbox)
cr.clip()
y1 = y1/N*N
x2 = (x2/N*N) + N
y2 = (y2/N*N) + N
- w, h = x2-x1+1, y2-y1+1
+ w, h = x2-x1, y2-y1
model_bbox = x1, y1, w, h
assert w >= 0 and h >= 0
cr.rectangle(*model_bbox)
cr.clip()
- #print 'rendering pixbuf', w, h
+ layers = self.doc.layers
+ if self.show_layers_above:
+ layers = self.doc.layers[0:self.doc.layer_idx+1]
+ # OPTIMIZE: if we are scrolling, we are creating an oversized pixbuf
pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, w, h)
if self.visualize_rendering:
# green
arr = pixbuf.get_pixels_array()
arr = mypaintlib.gdkpixbuf2numpy(arr)
- layers = None
- if self.show_layers_above:
- layers = self.doc.layers[0:self.doc.layer_idx+1]
- self.doc.render(arr, -x1, -y1, layers)
-
+ for tx, ty in self.doc.get_tiles():
+ x = tx*N
+ y = ty*N
+ pixbuf_x = x - x1
+ pixbuf_y = y - y1
+ if pixbuf_x < 0 or pixbuf_x+N > w: continue
+ if pixbuf_y < 0 or pixbuf_y+N > h: continue
+
+ if sparse:
+ # it is worth checking whether this tile really will be visible
+ # (to speed up the L-shaped expose event after scrolling)
+ # (speedup clearly visible; slowdown in default case measured)
+ corners = [(x, y), (x+N-1, y), (x, y+N-1), (x+N-1, y+N-1)]
+ corners = [cr.user_to_device(x_, y_) for (x_, y_) in corners]
+ bbox = gdk.Rectangle(*helpers.rotated_rectangle_bbox(corners))
+ if gdk_clip_region.rect_in(bbox) == gdk.OVERLAP_RECTANGLE_OUT:
+ continue
+
+ dst = arr[pixbuf_y:pixbuf_y+N,pixbuf_x:pixbuf_x+N]
+ self.doc.composite_tile(dst, tx, ty, layers)
+
+ #self.doc.render(arr, -x1, -y1, layers)
#widget.window.draw_pixbuf(None, pixbuf, 0, 0, 0, 0)
cr.set_source_pixbuf(pixbuf, x1, y1)
def scroll(self, dx, dy):
if self.viewport_locked:
return
+ assert int(dx) == dx and int(dy) == dy
self.translation_x -= dx
self.translation_y -= dy
- #OPTIMIZE: fast scrolling without so much rerendering
- # but not if combined with rotation/zoom change
- self.queue_draw()
+ self.window.scroll(int(-dx), int(-dy))
def rotozoom_with_center(self, function):
if self.viewport_locked:
res.expandToIncludeRect(bbox)
return res
- def render(self, arr, px, py, layers=None):
- assert arr.shape[2] == 3 # RGB only for now
+ def get_tiles(self):
+ # OPTIMIZE: this is used for rendering, so, only visible tiles?
+ # on the other hand, visibility can be checked later too
+ tiles = set()
+ for l in self.layers:
+ tiles.update(l.get_tiles())
+ return tiles
+
+ def composite_tile(self, dst, tx, ty, layers=None):
+ # OPTIMIZE: should use some caching strategy for the results somewhere, probably not here
if layers is None:
layers = self.layers
-
+ for layer in layers:
+ surface = layer.surface
+ surface.composite_tile(dst, tx, ty)
+
+ def render(self, arr, px, py, layers=None):
+ # FIXME: remove this function or use render_tiles() internally
+ assert dst.shape[2] == 3 # RGB only for now
+ for tx, ty in self.get_tiles():
+ self.render_tile
for layer in layers:
surface = layer.surface
surface.composite_over_RGB8(arr, px, py)
rgb, alpha = self.get_tile_memory(xx, yy, readonly)
yield xx*N, yy*N, (rgb[y_start:y_end,x_start:y_end], alpha[y_start:y_end,x_start:y_end])
+ def composite_tile(self, dst, tx, ty):
+ tile = self.tiledict.get((tx, ty))
+ if tile is None:
+ return
+ tile.composite_over_RGB8(dst)
+
def composite_over_RGB8(self, dst, px, py):
h, w, channels = dst.shape
assert channels == 3