code: #
  # transform.py -- coordinate transforms for Ginga
  #
  # This is open-source software licensed under a BSD license.
  # Please see the file LICENSE.txt for details.
  #
  import numpy as np
  
  from ginga import trcalc
  from ginga.misc import Bunch
  
  __all__ = ['TransformError', 'BaseTransform', 'ComposedTransform',
             'InvertedTransform', 'PassThruTransform',
             'WindowNativeTransform', 'CartesianWindowTransform',
             'CartesianNativeTransform',
             'RotationTransform', 'ScaleTransform',
             'DataCartesianTransform', 'OffsetDataTransform',
             'WCSDataTransform', 'get_catalog'
             ]
  
  
  class TransformError(Exception):
      pass
  
  
  class BaseTransform(object):
  
      def __init__(self):
          super(BaseTransform, self).__init__()
  
      def to_(self, x, y):
          raise TransformError("subclass should override this method")
  
      def from_(self, tx, ty):
          raise TransformError("subclass should override this method")
  
      def __add__(self, trans):
          return ComposedTransform(self, trans)
  
      def invert(self):
          return InvertedTransform(self)
  
  
  class ComposedTransform(BaseTransform):
      """
      A transform that composes two other transforms to make a new one.
      """
  
      def __init__(self, tform1, tform2):
          super(ComposedTransform, self).__init__()
          self.tform1 = tform1
          self.tform2 = tform2
  
      def to_(self, pts, **kwargs):
          return self.tform2.to_(self.tform1.to_(pts, **kwargs))
  
      def from_(self, pts, **kwargs):
          return self.tform1.from_(self.tform2.from_(pts), **kwargs)
  
  
  class InvertedTransform(BaseTransform):
      """
      A transform that inverts another transform.
      """
  
      def __init__(self, tform):
          super(InvertedTransform, self).__init__()
          self.tform = tform
  
      def to_(self, pts, **kwargs):
          return self.tform.from_(pts, **kwargs)
  
      def from_(self, pts, **kwargs):
          return self.tform.to_(pts, **kwargs)
  
  
  class PassThruTransform(BaseTransform):
      """
      A transform that essentially acts as a no-op.
      """
  
      def __init__(self, viewer):
          super(PassThruTransform, self).__init__()
  
      def to_(self, pts, **kwargs):
          return pts
  
      def from_(self, pts, **kwargs):
          return pts
  
  
  class WindowNativeTransform(BaseTransform):
      """
      A transform from a typical window standard coordinate space with the
      upper left at (0, 0) to the viewer back end native pixel space.
      """
  
      def __init__(self, viewer):
          super(WindowNativeTransform, self).__init__()
          self.viewer = viewer
  
      def to_(self, win_pts):
          if self.viewer.origin_upper:
              return win_pts
  
          win_pts = np.asarray(win_pts)
          has_z = (win_pts.shape[-1] > 2)
  
          # invert Y coord for backends that have the origin in the lower left
          win_wd, win_ht = self.viewer.get_window_size()
  
          # win_x, win_y = cvs_x, win_ht - cvs_y
          mpy_pt = [1.0, -1.0]
          if has_z:
              mpy_pt.append(1.0)
  
          add_pt = [0.0, win_ht]
          if has_z:
              add_pt.append(0.0)
  
          ntv_pts = np.add(np.multiply(win_pts, mpy_pt), add_pt)
  
          return ntv_pts
  
      def from_(self, ntv_pts):
          return self.to_(ntv_pts)
  
  
  class WindowPercentageTransform(BaseTransform):
      """
      A transform from standard window coordinates of a viewer
      to percentage coordinates.
      """
  
      def __init__(self, viewer, as_int=True):
          super(WindowPercentageTransform, self).__init__()
          self.viewer = viewer
          self.as_int = as_int
  
      def to_(self, win_pts):
          win_pts = np.asarray(win_pts, dtype=np.float)
          has_z = (win_pts.shape[-1] > 2)
  
          max_pt = list(self.viewer.get_window_size())
          if has_z:
              max_pt.append(0.0)
  
          pct_pts = np.divide(win_pts, max_pt)
          return pct_pts
  
      def from_(self, pct_pts):
          """Reverse of :meth:`to_`."""
          pct_pts = np.asarray(pct_pts, dtype=np.float)
          has_z = (pct_pts.shape[-1] > 2)
  
          max_pt = list(self.viewer.get_window_size())
          if has_z:
              max_pt.append(0.0)
  
          win_pts = np.multiply(pct_pts, max_pt)
  
          # round to pixel units, if asked
          if self.as_int:
              win_pts = np.rint(win_pts).astype(np.int, copy=False)
  
          return win_pts
  
  
  class CartesianWindowTransform(BaseTransform):
      """
      A transform from cartesian coordinates to standard window coordinates
      of a viewer.
      """
  
      def __init__(self, viewer, as_int=True):
          super(CartesianWindowTransform, self).__init__()
          self.viewer = viewer
          self.as_int = as_int
  
      def to_(self, off_pts):
          # add center pixel to convert from X/Y coordinate space to
          # window graphics space
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          ctr_pt = list(self.viewer.get_center())
          if has_z:
              ctr_pt.append(0.0)
  
          # win_x = off_x + ctr_x
          # win_y = ctr_y - off_y
          mpy_pt = [1.0, -1.0]
          if has_z:
              mpy_pt.append(1.0)
  
          win_pts = np.add(np.multiply(off_pts, mpy_pt), ctr_pt)
  
          # round to pixel units, if asked
          if self.as_int:
              win_pts = np.rint(win_pts).astype(np.int, copy=False)
  
          return win_pts
  
      def from_(self, win_pts):
          """Reverse of :meth:`to_`."""
          # make relative to center pixel to convert from window
          # graphics space to standard X/Y coordinate space
          win_pts = np.asarray(win_pts, dtype=np.float)
          has_z = (win_pts.shape[-1] > 2)
  
          ctr_pt = list(self.viewer.get_center())
          if has_z:
              ctr_pt.append(0.0)
  
          mpy_pt = [1.0, -1.0]
          if has_z:
              mpy_pt.append(1.0)
  
          # off_x = win_x - ctr_x
          #       = win_x + -ctr_x
          # off_y = ctr_y - win_y
          #       = -win_y + ctr_y
          ctr_pt[0] = -ctr_pt[0]
          off_pts = np.add(np.multiply(win_pts, mpy_pt), ctr_pt)
  
          return off_pts
  
  
  class CartesianNativeTransform(BaseTransform):
      """
      A transform from cartesian coordinates to the native pixel coordinates
      of a viewer.
      """
  
      def __init__(self, viewer, as_int=True):
          super(CartesianNativeTransform, self).__init__()
          self.viewer = viewer
          self.as_int = as_int
  
      def to_(self, off_pts):
          # add center pixel to convert from X/Y coordinate space to
          # back end graphics space
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          ctr_pt = list(self.viewer.get_center())
          if has_z:
              ctr_pt.append(0.0)
  
          if self.viewer.origin_upper:
              mpy_pt = [1.0, -1.0]
          else:
              mpy_pt = [1.0, 1.0]
  
          if has_z:
              mpy_pt.append(1.0)
  
          win_pts = np.add(np.multiply(off_pts, mpy_pt), ctr_pt)
  
          # round to pixel units, if asked
          if self.as_int:
              win_pts = np.rint(win_pts).astype(np.int, copy=False)
  
          return win_pts
  
      def from_(self, win_pts):
          """Reverse of :meth:`to_`."""
          # make relative to center pixel to convert from back end
          # graphics space to standard X/Y coordinate space
          win_pts = np.asarray(win_pts, dtype=np.float)
          has_z = (win_pts.shape[-1] > 2)
  
          ctr_pt = list(self.viewer.get_center())
          if has_z:
              ctr_pt.append(0.0)
  
          ctr_pt[0] = -ctr_pt[0]
          if self.viewer.origin_upper:
              mpy_pt = [1.0, -1.0]
          else:
              ctr_pt[1] = -ctr_pt[1]
              mpy_pt = [1.0, 1.0]
  
          if has_z:
              mpy_pt.append(1.0)
  
          off_pts = np.add(np.multiply(win_pts, mpy_pt), ctr_pt)
  
          return off_pts
  
  
  class RotationTransform(BaseTransform):
      """
      A transform in cartesian coordinates based on the flip/swap setting and
      rotation setting of a viewer.
      """
  
      def __init__(self, viewer):
          super(RotationTransform, self).__init__()
          self.viewer = viewer
  
      def to_(self, off_pts):
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          t_ = self.viewer.t_
  
          # flip
          flip_pt = [1.0, 1.0]
          if t_['flip_x']:
              flip_pt[0] = -1.0
          if t_['flip_y']:
              flip_pt[1] = -1.0
          if has_z:
              # no flip_z at the moment
              flip_pt.append(1.0)
  
          off_pts = np.multiply(off_pts, flip_pt)
  
          # swap
          if t_['swap_xy']:
              p = list(off_pts.T)
              off_pts = np.asarray([p[1], p[0]] + list(p[2:])).T
  
          # rotate
          if t_['rot_deg'] != 0:
              thetas = [t_['rot_deg']]
              offset = [0.0, 0.0]
              if has_z:
                  offset.append(0.0)
              off_pts = trcalc.rotate_coord(off_pts, thetas, offset)
  
          return off_pts
  
      def from_(self, off_pts):
          """Reverse of :meth:`to_`."""
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          t_ = self.viewer.t_
  
          # rotate
          if t_['rot_deg'] != 0:
              thetas = [- t_['rot_deg']]
              offset = [0.0, 0.0]
              if has_z:
                  offset.append(0.0)
              off_pts = trcalc.rotate_coord(off_pts, thetas, offset)
  
          # swap
          if t_['swap_xy']:
              p = list(off_pts.T)
              off_pts = np.asarray([p[1], p[0]] + list(p[2:])).T
  
          # flip
          flip_pt = [1.0, 1.0]
          if t_['flip_x']:
              flip_pt[0] = -1.0
          if t_['flip_y']:
              flip_pt[1] = -1.0
          if has_z:
              # no flip_z at the moment
              flip_pt.append(1.0)
  
          off_pts = np.multiply(off_pts, flip_pt)
  
          return off_pts
  
  
  class ScaleTransform(BaseTransform):
      """
      A transform in cartesian coordinates based on the scale of a viewer.
      """
  
      def __init__(self, viewer):
          super(ScaleTransform, self).__init__()
          self.viewer = viewer
  
      def to_(self, off_pts):
          """Reverse of :meth:`from_`."""
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          # scale according to current settings
          scale_pt = [self.viewer._org_scale_x, self.viewer._org_scale_y]
          if has_z:
              scale_pt.append(self.viewer._org_scale_z)
  
          off_pts = np.multiply(off_pts, scale_pt)
          return off_pts
  
      def from_(self, off_pts):
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          scale_pt = [1.0 / self.viewer._org_scale_x,
                      1.0 / self.viewer._org_scale_y]
          if has_z:
              scale_pt.append(1.0 / self.viewer._org_scale_z)
  
          # Reverse scaling
          off_pts = np.multiply(off_pts, scale_pt)
          return off_pts
  
  
  class DataCartesianTransform(BaseTransform):
      """
      A transform from data coordinates to cartesian coordinates based on
      a viewer's pan position.
      """
  
      def __init__(self, viewer, use_center=True):
          super(DataCartesianTransform, self).__init__()
          self.viewer = viewer
          # If use_center is True, then the coordinates are mapped such that the
          # pixel is centered on the square when the image is zoomed in past
          # 1X. This is the specification of the FITS image standard,
          # that the pixel is centered on the integer row/column.
          self.use_center = use_center
  
      def to_(self, data_pts):
          """Reverse of :meth:`from_`."""
          data_pts = np.asarray(data_pts, dtype=np.float)
          has_z = (data_pts.shape[-1] > 2)
  
          if self.use_center:
              data_pts = data_pts - self.viewer.data_off
  
          # subtract data indexes at center reference pixel
          ref_pt = [self.viewer._org_x, self.viewer._org_y]
          if has_z:
              ref_pt.append(self.viewer._org_z)
  
          off_pts = np.subtract(data_pts, ref_pt)
          return off_pts
  
      def from_(self, off_pts):
          off_pts = np.asarray(off_pts, dtype=np.float)
          has_z = (off_pts.shape[-1] > 2)
  
          # Add data index at center to offset
          # subtract data indexes at center reference pixel
          ref_pt = [self.viewer._org_x, self.viewer._org_y]
          if has_z:
              ref_pt.append(self.viewer._org_z)
  
          data_pts = np.add(off_pts, ref_pt)
  
          if self.use_center:
              data_pts = data_pts + self.viewer.data_off
  
          return data_pts
  
  
  class OffsetDataTransform(BaseTransform):
      """
      A transform whose coordinate space is offsets from a point in
      data space.
      """
  
      def __init__(self, pt):
          super(OffsetDataTransform, self).__init__()
          self.pt = pt
  
      def to_(self, delta_pts):
          delta_x, delta_y = np.asarray(delta_pts, dtype=np.float).T
          ref_x, ref_y = self.pt[:2]
          res_x, res_y = ref_x + delta_x, ref_y + delta_y
          return np.asarray((res_x, res_y)).T
  
      def from_(self, data_pts):
          data_x, data_y = np.asarray(data_pts, dtype=np.float).T
          ref_x, ref_y = self.pt[:2]
          res_x, res_y = data_x - ref_x, data_y - ref_y
          return np.asarray((res_x, res_y)).T
  
  
  class WCSDataTransform(BaseTransform):
      """
      A transform whose coordinate space is based on the WCS of the primary
      image loaded in a viewer.
      """
  
      def __init__(self, viewer):
          super(WCSDataTransform, self).__init__()
          self.viewer = viewer
  
      def to_(self, wcs_pts):
          wcs_pts = np.asarray(wcs_pts)
  
          # hack to work around passing singleton pt vs. array of pts
          unpack = False
          if len(wcs_pts.shape) < 2:
              # passed a single coordinate
              wcs_pts = np.asarray([wcs_pts])
              unpack = True
  
          image = self.viewer.get_image()
          if image is None:
              raise TransformError("No image, no WCS")
          wcs = image.wcs
          if wcs is None:
              raise TransformError("No valid WCS found in image")
  
          naxispath = image.naxispath
  
          res = wcs.wcspt_to_datapt(wcs_pts, naxispath=naxispath)
          if unpack:
              return res[0]
          return res
  
      def from_(self, data_pts):
          data_pts = np.asarray(data_pts)
  
          # hack to work around passing singleton pt vs. array of pts
          unpack = False
          if len(data_pts.shape) < 2:
              # passed a single coordinate
              data_pts = np.asarray([data_pts])
              unpack = True
  
          image = self.viewer.get_image()
          if image is None:
              raise TransformError("No image, no WCS")
          wcs = image.wcs
          if wcs is None:
              raise TransformError("No valid WCS found in image")
  
          naxispath = image.naxispath
  
          res = wcs.datapt_to_wcspt(data_pts, naxispath=naxispath)
          if unpack:
              return res[0]
          return res
  
  
  def get_catalog():
      """Returns a catalog of available transforms.  These are used to
      build chains for rendering with different back ends.
      """
      tforms = {}
      for name, value in list(globals().items()):
          if name.endswith('Transform'):
              tforms[name] = value
  
      return Bunch.Bunch(tforms, caseless=True)
  
  #END
   #
  # CanvasRenderQt.py -- for rendering into a ImageViewQt widget
  #
  # This is open-source software licensed under a BSD license.
  # Please see the file LICENSE.txt for details.
  #
  import numpy as np
  
  from ginga.qtw.QtHelp import (QtCore, QPen, QPolygon, QColor,
                                QPainterPath, QImage, QPixmap, get_font,
                                get_painter)
  
  from ginga import colors
  from ginga.canvas import render
  # force registration of all canvas types
  import ginga.canvas.types.all  # noqa
  
  
  class RenderContext(render.RenderContextBase):
  
      def __init__(self, renderer, viewer, surface):
          render.RenderContextBase.__init__(self, renderer, viewer)
  
          self.cr = get_painter(surface)
  
      def __get_color(self, color, alpha):
          clr = QColor()
          if isinstance(color, tuple):
              clr.setRgbF(color[0], color[1], color[2], alpha)
          else:
              r, g, b = colors.lookup_color(color)
              clr.setRgbF(r, g, b, alpha)
          return clr
  
      def set_line_from_shape(self, shape):
          pen = QPen()
          pen.setWidthF(getattr(shape, 'linewidth', 1.0))
  
          if hasattr(shape, 'linestyle'):
              if shape.linestyle == 'dash':
                  pen.setDashPattern([3.0, 4.0, 6.0, 4.0])
                  pen.setDashOffset(5.0)
  
          alpha = getattr(shape, 'alpha', 1.0)
          color = self.__get_color(shape.color, alpha)
          pen.setColor(color)
          self.cr.setPen(pen)
  
      def set_fill_from_shape(self, shape):
          fill = getattr(shape, 'fill', False)
          if fill:
              if hasattr(shape, 'fillcolor') and shape.fillcolor:
                  color = shape.fillcolor
              else:
                  color = shape.color
  
              if color is None:
                  self.cr.setBrush(QtCore.Qt.NoBrush)
              else:
                  alpha = getattr(shape, 'alpha', None)
                  fillalpha = getattr(shape, 'fillalpha', alpha)
                  color = self.__get_color(color, fillalpha)
                  self.cr.setBrush(color)
          else:
              self.cr.setBrush(QtCore.Qt.NoBrush)
  
      def set_font_from_shape(self, shape):
          if hasattr(shape, 'font'):
              if (hasattr(shape, 'fontsize') and shape.fontsize is not None and
                  not getattr(shape, 'fontscale', False)):
                  fontsize = shape.fontsize
              else:
                  fontsize = shape.scale_font(self.viewer)
              fontsize = self.scale_fontsize(fontsize)
              font = get_font(shape.font, fontsize)
              self.cr.setFont(font)
  
      def initialize_from_shape(self, shape, line=True, fill=True, font=True):
          if line:
              self.set_line_from_shape(shape)
          if fill:
              self.set_fill_from_shape(shape)
          if font:
              self.set_font_from_shape(shape)
  
      def set_line(self, color, alpha=1.0, linewidth=1, style='solid'):
          clr = self.__get_color(color, alpha)
          pen = self.cr.pen()
          pen.setColor(clr)
          pen.setWidthF(float(linewidth))
          if style == 'dash':
              pen.setDashPattern([3.0, 4.0, 6.0, 4.0])
              pen.setDashOffset(5.0)
          self.cr.setPen(pen)
  
      def set_fill(self, color, alpha=1.0):
          if color is None:
              self.cr.setBrush(QtCore.Qt.NoBrush)
          else:
              color = self.__get_color(color, alpha)
              self.cr.setBrush(color)
  
      def set_font(self, fontname, fontsize, color='black', alpha=1.0):
          self.set_line(color, alpha=alpha)
          fontsize = self.scale_fontsize(fontsize)
          font = get_font(fontname, fontsize)
          self.cr.setFont(font)
  
      def text_extents(self, text):
          fm = self.cr.fontMetrics()
          width = fm.width(text)
          height = fm.height()
          return width, height
  
      ##### DRAWING OPERATIONS #####
  
      def draw_text(self, cx, cy, text, rot_deg=0.0):
          self.cr.save()
          self.cr.translate(cx, cy)
          self.cr.rotate(-rot_deg)
  
          self.cr.drawText(0, 0, text)
  
          self.cr.restore()
  
      def draw_polygon(self, cpoints):
          qpoints = [QtCore.QPoint(p[0], p[1]) for p in cpoints]
          p = cpoints[0]
          qpoints.append(QtCore.QPoint(p[0], p[1]))
          qpoly = QPolygon(qpoints)
  
          self.cr.drawPolygon(qpoly)
  
      def draw_circle(self, cx, cy, cradius):
          # this is necessary to work around a bug in Qt--radius of 0
          # causes a crash
          cradius = max(cradius, 0.000001)
          pt = QtCore.QPointF(cx, cy)
          self.cr.drawEllipse(pt, float(cradius), float(cradius))
  
      def draw_bezier_curve(self, cp):
          path = QPainterPath()
          path.moveTo(cp[0][0], cp[0][1])
          path.cubicTo(cp[1][0], cp[1][1], cp[2][0], cp[2][1], cp[3][0], cp[3][1])
          self.cr.drawPath(path)
  
      def draw_ellipse_bezier(self, cp):
          # draw 4 bezier curves to make the ellipse
          path = QPainterPath()
          path.moveTo(cp[0][0], cp[0][1])
          path.cubicTo(cp[1][0], cp[1][1], cp[2][0], cp[2][1], cp[3][0], cp[3][1])
          path.cubicTo(cp[4][0], cp[4][1], cp[5][0], cp[5][1], cp[6][0], cp[6][1])
          path.cubicTo(cp[7][0], cp[7][1], cp[8][0], cp[8][1], cp[9][0], cp[9][1])
          path.cubicTo(cp[10][0], cp[10][1], cp[11][0], cp[11][1], cp[12][0], cp[12][1])
          self.cr.drawPath(path)
  
      def draw_line(self, cx1, cy1, cx2, cy2):
          self.cr.pen().setCapStyle(QtCore.Qt.RoundCap)
          self.cr.drawLine(cx1, cy1, cx2, cy2)
  
      def draw_path(self, cp):
          self.cr.pen().setCapStyle(QtCore.Qt.RoundCap)
          pts = [QtCore.QLineF(QtCore.QPointF(cp[i][0], cp[i][1]),
                               QtCore.QPointF(cp[i + 1][0], cp[i + 1][1]))
                 for i in range(len(cp) - 1)]
          self.cr.drawLines(pts)
  
  
  class CanvasRenderer(render.RendererBase):
  
      def __init__(self, viewer, surface_type='qimage'):
          render.RendererBase.__init__(self, viewer)
  
          self.kind = 'qt'
          # Qt needs this to be in BGRA
          self.rgb_order = 'BGRA'
          self.qimg_fmt = QImage.Format_RGB32
          self.surface_type = surface_type
          # the offscreen drawing surface
          self.surface = None
  
      def resize(self, dims):
          """Resize our drawing area to encompass a space defined by the
          given dimensions.
          """
          width, height = dims[:2]
          self.logger.debug("renderer reconfigured to %dx%d" % (
              width, height))
          if self.surface_type == 'qpixmap':
              self.surface = QPixmap(width, height)
          else:
              self.surface = QImage(width, height, self.qimg_fmt)
  
          # fill surface with background color;
          # this reduces unwanted garbage in the resizing window
          painter = get_painter(self.surface)
          size = self.surface.size()
          sf_wd, sf_ht = size.width(), size.height()
          bg = self.viewer.img_bg
          bgclr = self._get_color(*bg)
          painter.fillRect(QtCore.QRect(0, 0, sf_wd, sf_ht), bgclr)
  
      def _get_qimage(self, rgb_data):
          ht, wd, channels = rgb_data.shape
  
          result = QImage(rgb_data.data, wd, ht, self.qimg_fmt)
          # Need to hang on to a reference to the array
          result.ndarray = rgb_data
          return result
  
      def _get_color(self, r, g, b):
          # TODO: combine with the method from the RenderContext?
          n = 255.0
          clr = QColor(int(r * n), int(g * n), int(b * n))
          return clr
  
      def render_image(self, rgbobj, dst_x, dst_y):
          """Render the image represented by (rgbobj) at dst_x, dst_y
          in the pixel space.
          *** internal method-- do not use ***
          """
          self.logger.debug("redraw surface=%s" % (self.surface))
          if self.surface is None:
              return
          self.logger.debug("drawing to surface")
  
          # Prepare array for rendering
          # TODO: what are options for high bit depth under Qt?
          data = rgbobj.get_array(self.rgb_order, dtype=np.uint8)
          (height, width) = data.shape[:2]
  
          daht, dawd, depth = data.shape
          self.logger.debug("data shape is %dx%dx%d" % (dawd, daht, depth))
  
          # Get qimage for copying pixel data
          qimage = self._get_qimage(data)
          drawable = self.surface
  
          painter = get_painter(drawable)
          #painter.setWorldMatrixEnabled(True)
  
          # fill surface with background color
          size = drawable.size()
          sf_wd, sf_ht = size.width(), size.height()
          bg = self.viewer.img_bg
          bgclr = self._get_color(*bg)
          painter.fillRect(QtCore.QRect(0, 0, sf_wd, sf_ht), bgclr)
  
          # draw image data from buffer to offscreen pixmap
          painter.drawImage(QtCore.QRect(dst_x, dst_y, width, height),
                            qimage,
                            QtCore.QRect(0, 0, width, height))
  
      def get_surface_as_array(self, order=None):
          if self.surface_type == 'qpixmap':
              qimg = self.surface.toImage()
          else:
              qimg = self.surface
          #qimg = qimg.convertToFormat(QImage.Format_RGBA32)
  
          width, height = qimg.width(), qimg.height()
  
          if hasattr(qimg, 'bits'):
              # PyQt
              ptr = qimg.bits()
              ptr.setsize(qimg.byteCount())
          else:
              # PySide
              ptr = qimg.constBits()
  
          arr = np.array(ptr).reshape(height, width, 4)
  
          # adjust according to viewer's needed order
          return self.reorder(order, arr)
  
      def setup_cr(self, shape):
          cr = RenderContext(self, self.viewer, self.surface)
          cr.initialize_from_shape(shape, font=False)
          return cr
  
      def get_dimensions(self, shape):
          cr = self.setup_cr(shape)
          cr.set_font_from_shape(shape)
          return cr.text_extents(shape.text)
  
  
  #END
  
apis: numpy.rint numpy.multiply numpy.divide numpy.subtract numpy.asarray numpy.add
apis: numpy.array