Revision: 8035 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8035&view=rev Author: leejjoon Date: 2009-12-16 01:22:41 +0000 (Wed, 16 Dec 2009)
Log Message: ----------- support unsampled image for ps backend Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/backends/backend_mixed.py trunk/matplotlib/lib/matplotlib/backends/backend_ps.py trunk/matplotlib/lib/matplotlib/image.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-12-15 02:57:46 UTC (rev 8034) +++ trunk/matplotlib/CHANGELOG 2009-12-16 01:22:41 UTC (rev 8035) @@ -1,3 +1,5 @@ +2009-12-15 Add raw-image (unsampled) support for the ps backend. - JJL + 2009-12-14 Add patch_artist kwarg to boxplot, but keep old default. Convert boxplot_demo2.py to use the new patch_artist. - ADS Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-12-15 02:57:46 UTC (rev 8034) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-12-16 01:22:41 UTC (rev 8035) @@ -340,6 +340,13 @@ """ return False + def option_scale_image(self): + """ + override this method for renderers that support arbitrary + scaling of image (most of the vector backend). + """ + return False + def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): """ """ Modified: trunk/matplotlib/lib/matplotlib/backends/backend_mixed.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_mixed.py 2009-12-15 02:57:46 UTC (rev 8034) +++ trunk/matplotlib/lib/matplotlib/backends/backend_mixed.py 2009-12-16 01:22:41 UTC (rev 8035) @@ -59,7 +59,7 @@ get_texmanager get_text_width_height_descent new_gc open_group option_image_nocomposite points_to_pixels strip_math start_filter stop_filter draw_gouraud_triangle - draw_gouraud_triangles + draw_gouraud_triangles option_scale_image """.split() def _set_current_renderer(self, renderer): self._renderer = renderer Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-12-15 02:57:46 UTC (rev 8034) +++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-12-16 01:22:41 UTC (rev 8035) @@ -380,8 +380,14 @@ """ return self.image_magnification - def draw_image(self, gc, x, y, im): + def option_scale_image(self): """ + ps backend support arbitrary scaling of image. + """ + return True + + def draw_image(self, gc, x, y, im, sx=None, sy=None): + """ Draw the Image instance into the current axes; x is the distance in pixels from the left hand side of the canvas and y is the distance from bottom @@ -400,9 +406,13 @@ imagecmd = "false 3 colorimage" hexlines = '\n'.join(self._hex_lines(bits)) - xscale, yscale = ( - w/self.image_magnification, h/self.image_magnification) - + if sx is None: + sx = 1./self.image_magnification + if sy is None: + sy = 1./self.image_magnification + + xscale, yscale = (w*sx, h*sy) + figh = self.height*72 #print 'values', origin, flipud, figh, h, y Modified: trunk/matplotlib/lib/matplotlib/image.py =================================================================== --- trunk/matplotlib/lib/matplotlib/image.py 2009-12-15 02:57:46 UTC (rev 8034) +++ trunk/matplotlib/lib/matplotlib/image.py 2009-12-16 01:22:41 UTC (rev 8035) @@ -127,21 +127,140 @@ def make_image(self, magnification=1.0): raise RuntimeError('The make_image method must be overridden.') + + def _get_unsampled_image(self, A, image_extents, viewlim): + """ + convert numpy array A with given extents ([x1, x2, y1, y2] in + data coordinate) into the Image, given the vielim (should be a + bbox instance). Image will be clipped if the extents is + significantly larger than the viewlim. + """ + xmin, xmax, ymin, ymax = image_extents + dxintv = xmax-xmin + dyintv = ymax-ymin + + # the viewport scale factor + sx = dxintv/viewlim.width + sy = dyintv/viewlim.height + numrows, numcols = A.shape[:2] + if sx > 2: + x0 = (viewim.x0-xmin)/dxintv * numcols + ix0 = max(0, int(x0 - self._filterrad)) + x1 = (viewlim.x1-xmin)/dxintv * numcols + ix1 = min(numcols, int(x1 + self._filterrad)) + xslice = slice(ix0, ix1) + xmin_old = xmin + xmin = xmin_old + ix0*dxintv/numcols + xmax = xmin_old + ix1*dxintv/numcols + dxintv = xmax - xmin + sx = dxintv/viewlim.width + else: + xslice = slice(0, numcols) + + if sy > 2: + y0 = (viewlim.y0-ymin)/dyintv * numrows + iy0 = max(0, int(y0 - self._filterrad)) + y1 = (viewlim.y1-ymin)/dyintv * numrows + iy1 = min(numrows, int(y1 + self._filterrad)) + if self.origin == 'upper': + yslice = slice(numrows-iy1, numrows-iy0) + else: + yslice = slice(iy0, iy1) + ymin_old = ymin + ymin = ymin_old + iy0*dyintv/numrows + ymax = ymin_old + iy1*dyintv/numrows + dyintv = ymax - ymin + sy = dyintv/self.axes.viewLim.height + else: + yslice = slice(0, numrows) + + if xslice != self._oldxslice or yslice != self._oldyslice: + self._imcache = None + self._oldxslice = xslice + self._oldyslice = yslice + + if self._imcache is None: + if self._A.dtype == np.uint8 and len(self._A.shape) == 3: + im = _image.frombyte(self._A[yslice,xslice,:], 0) + im.is_grayscale = False + else: + if self._rgbacache is None: + x = self.to_rgba(self._A, self._alpha) + self._rgbacache = x + else: + x = self._rgbacache + im = _image.fromarray(x[yslice,xslice], 0) + if len(self._A.shape) == 2: + im.is_grayscale = self.cmap.is_gray() + else: + im.is_grayscale = False + self._imcache = im + + if self.origin=='upper': + im.flipud_in() + else: + im = self._imcache + + return im, xmin, ymin, dxintv, dyintv, sx, sy + + + def _draw_unsampled_image(self, renderer, gc): + """ + draw unsampled image. The renderer should support a draw_image method + with scale parameter. + """ + im, xmin, ymin, dxintv, dyintv, sx, sy = \ + self._get_unsampled_image(self._A, self.get_extent(), self.axes.viewLim) + + if im is None: return # I'm not if this check is required. -JJL + + transData = self.axes.transData + xx1, yy1 = transData.transform_point((xmin, ymin)) + xx2, yy2 = transData.transform_point((xmin+dxintv, ymin+dyintv)) + + fc = self.axes.patch.get_facecolor() + bg = mcolors.colorConverter.to_rgba(fc, 0) + im.set_bg( *bg) + + # image input dimensions + im.reset_matrix() + numrows, numcols = im.get_size() + + im.resize(numcols, numrows) # just to create im.bufOut that is required by backends. There may be better solution -JJL + + sx = (xx2-xx1)/numcols + sy = (yy2-yy1)/numrows + im._url = self.get_url() + renderer.draw_image(gc, xx1, yy1, im, sx, sy) + + + def _check_unsampled_image(self, renderer): + """ + return True if the image is better to be drawn unsampled. + The derived class needs to override it. + """ + return False + @allow_rasterization def draw(self, renderer, *args, **kwargs): if not self.get_visible(): return if (self.axes.get_xscale() != 'linear' or self.axes.get_yscale() != 'linear'): warnings.warn("Images are not supported on non-linear axes.") - im = self.make_image(renderer.get_image_magnification()) - if im is None: - return - im._url = self.get_url() + l, b, widthDisplay, heightDisplay = self.axes.bbox.bounds gc = renderer.new_gc() gc.set_clip_rectangle(self.axes.bbox.frozen()) gc.set_clip_path(self.get_clip_path()) - renderer.draw_image(gc, l, b, im) + + if self._check_unsampled_image(renderer): + self._draw_unsampled_image(renderer, gc) + else: + im = self.make_image(renderer.get_image_magnification()) + if im is None: + return + im._url = self.get_url() + renderer.draw_image(gc, l, b, im) gc.restore() def contains(self, mouseevent): @@ -338,72 +457,9 @@ if self._A is None: raise RuntimeError('You must first set the image array or the image attribute') - xmin, xmax, ymin, ymax = self.get_extent() - dxintv = xmax-xmin - dyintv = ymax-ymin + im, xmin, ymin, dxintv, dyintv, sx, sy = \ + self._get_unsampled_image(self._A, self.get_extent(), self.axes.viewLim) - # the viewport scale factor - sx = dxintv/self.axes.viewLim.width - sy = dyintv/self.axes.viewLim.height - numrows, numcols = self._A.shape[:2] - if sx > 2: - x0 = (self.axes.viewLim.x0-xmin)/dxintv * numcols - ix0 = max(0, int(x0 - self._filterrad)) - x1 = (self.axes.viewLim.x1-xmin)/dxintv * numcols - ix1 = min(numcols, int(x1 + self._filterrad)) - xslice = slice(ix0, ix1) - xmin_old = xmin - xmin = xmin_old + ix0*dxintv/numcols - xmax = xmin_old + ix1*dxintv/numcols - dxintv = xmax - xmin - sx = dxintv/self.axes.viewLim.width - else: - xslice = slice(0, numcols) - - if sy > 2: - y0 = (self.axes.viewLim.y0-ymin)/dyintv * numrows - iy0 = max(0, int(y0 - self._filterrad)) - y1 = (self.axes.viewLim.y1-ymin)/dyintv * numrows - iy1 = min(numrows, int(y1 + self._filterrad)) - if self.origin == 'upper': - yslice = slice(numrows-iy1, numrows-iy0) - else: - yslice = slice(iy0, iy1) - ymin_old = ymin - ymin = ymin_old + iy0*dyintv/numrows - ymax = ymin_old + iy1*dyintv/numrows - dyintv = ymax - ymin - sy = dyintv/self.axes.viewLim.height - else: - yslice = slice(0, numrows) - - if xslice != self._oldxslice or yslice != self._oldyslice: - self._imcache = None - self._oldxslice = xslice - self._oldyslice = yslice - - if self._imcache is None: - if self._A.dtype == np.uint8 and len(self._A.shape) == 3: - im = _image.frombyte(self._A[yslice,xslice,:], 0) - im.is_grayscale = False - else: - if self._rgbacache is None: - x = self.to_rgba(self._A, self._alpha) - self._rgbacache = x - else: - x = self._rgbacache - im = _image.fromarray(x[yslice,xslice], 0) - if len(self._A.shape) == 2: - im.is_grayscale = self.cmap.is_gray() - else: - im.is_grayscale = False - self._imcache = im - - if self.origin=='upper': - im.flipud_in() - else: - im = self._imcache - fc = self.axes.patch.get_facecolor() bg = mcolors.colorConverter.to_rgba(fc, 0) im.set_bg( *bg) @@ -435,6 +491,15 @@ return im + def _check_unsampled_image(self, renderer): + """ + return True if the image is better to be drawn unsampled. + """ + if renderer.option_scale_image() and self.get_interpolation() == "nearest": + return True + else: + return False + def set_extent(self, extent): """ extent is data axes (left, right, bottom, top) for making image plots This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ This SF.Net email is sponsored by the Verizon Developer Community Take advantage of Verizon's best-in-class app development support A streamlined, 14 day to market process makes app distribution fast and easy Join now and get one step closer to millions of Verizon customers http://p.sf.net/sfu/verizon-dev2dev _______________________________________________ Matplotlib-checkins mailing list Matplotlib-checkins@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins