def do_scroll_paints(self, scrolls, flush=0, callbacks=[]): log("do_scroll_paints%s", (scrolls, flush)) context = self.gl_context() if not context: log.warn("Warning: cannot paint scroll, no OpenGL context!") return def fail(msg): log.error("Error: %s", msg) fire_paint_callbacks(callbacks, False, msg) with context: bw, bh = self.size self.set_rgb_paint_state() #paste from offscreen to tmp with delta offset: glBindFramebuffer(GL_READ_FRAMEBUFFER, self.offscreen_fbo) glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_FBO]) glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_FBO], 0) glReadBuffer(GL_COLOR_ATTACHMENT0) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self.tmp_fbo) glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_TMP_FBO]) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_TMP_FBO], 0) glDrawBuffer(GL_COLOR_ATTACHMENT1) #copy current fbo: glBlitFramebuffer(0, 0, bw, bh, 0, 0, bw, bh, GL_COLOR_BUFFER_BIT, GL_NEAREST) for x,y,w,h,xdelta,ydelta in scrolls: if abs(xdelta)>=bw: fail("invalid xdelta value: %i" % xdelta) continue if abs(ydelta)>=bh: fail("invalid ydelta value: %i" % ydelta) continue if ydelta==0 and xdelta==0: fail("scroll has no delta!") continue if w<=0 or h<=0: fail("invalid scroll area size: %ix%i" % (w, h)) continue #these should be errors, #but desktop-scaling can cause a mismatch between the backing size #and the real window size server-side.. so we clamp the dimensions instead if x+w>bw: w = bw-x if y+h>bh: h = bh-y if x+w+xdelta>bw: w = bw-x-xdelta if w<=0: continue #nothing left! if y+h+ydelta>bh: h = bh-y-ydelta if h<=0: continue #nothing left! if x+xdelta<0: fail("horizontal scroll by %i: rectangle %s overflows the backing buffer size %s" % (xdelta, (x, y, w, h), self.size)) continue if y+ydelta<0: fail("vertical scroll by %i: rectangle %s overflows the backing buffer size %s" % (ydelta, (x, y, w, h), self.size)) continue #opengl buffer is upside down, so we must invert Y coordinates: bh-(..) glBlitFramebuffer(x, bh-y, x+w, bh-(y+h), x+xdelta, bh-(y+ydelta), x+w+xdelta, bh-(y+h+ydelta), GL_COLOR_BUFFER_BIT, GL_NEAREST) glFlush() #now swap references to tmp and offscreen so tmp becomes the new offscreen: tmp = self.offscreen_fbo self.offscreen_fbo = self.tmp_fbo self.tmp_fbo = tmp tmp = self.textures[TEX_FBO] self.textures[TEX_FBO] = self.textures[TEX_TMP_FBO] self.textures[TEX_TMP_FBO] = tmp #restore normal paint state: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, self.textures[TEX_FBO], 0) glBindFramebuffer(GL_READ_FRAMEBUFFER, self.offscreen_fbo) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self.offscreen_fbo) glBindFramebuffer(GL_FRAMEBUFFER, self.offscreen_fbo) self.unset_rgb_paint_state() self.paint_box("scroll", True, x+xdelta, y+ydelta, x+w+xdelta, y+h+ydelta) self.present_fbo(0, 0, bw, bh, flush) fire_paint_callbacks(callbacks, True)