Commits

Lenard Lindstrom committed ef526f5

freetype.Face.render: Split out Face.render_to method to blit to an existing surface

  • Participants
  • Parent commits a1c860d

Comments (0)

Files changed (6)

docs/reST/ref/freetype.rst

    .. method:: render
 
       | :sl:`Renders text on a surface`
-      | :sg:`render(dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)`
+      | :sg:`render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)`
 
-      Renders the string 'text' to a :mod:`pygame.Surface`, using the color
-      'fgcolor'.
+      Renturns a new :mod:`pygame.Surface`, with the text rendered to it
+      in the color given by 'fgcolor'. If ``bgcolor`` is given, the surface
+      will be filled with this color. If no background color is given,
+      the surface is filled with zero alpha opacity. Normally the returned
+      surface has a 32 bit pixel size. However, if ``bgcolor`` is ``None``
+      and antialiasing is disabled a two color 8 bit surface with colorkey
+      set for the background color is returned.
 
-      The 'dest' parameter is supposed to be a sequence containing the surface
-      and the coordinates at which the text will be rendered, in that order.
-      The sequence may be either (surf, posn) or (surf, x, y), where x and y
-      are numbers. posn can be any sequence, including Rect, for which the
-      first two elements are positions x and y. If x and y are not integers
-      they will be cast to int: ``int(x)``, ``int(y)``.
+      The return value is a tuple: the new surface and the bounding
+      rectangle giving the size and origin of the rendered text.
+
+      If an empty string is passed for text then the returned Rect is zero
+      width and the height of the face. If dest is None the returned surface is
+      the same dimensions as the boundary rect. The rect will test False.
+
+      The rendering is done using the face's default size in points and its
+      default style, without any rotation, and taking into account faces which
+      are set to be drawn vertically via the :meth:`Face.vertical` attribute.
+      Optionally you may specify another point size to use via the 'ptsize'
+      argument, a text rotation via the 'rotation' argument, or a new text
+      style via the 'style' argument.
+
+      If text is a char (byte) string, then its encoding is assumed to be
+      ``LATIN1``.
+
+   .. method:: render_to
+
+      | :sl:`Renders text to an existing surface`
+      | :sg:`render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> Rect`
+
+      Renders the string 'text' to a :mod:`pygame.Surface` 'surf',
+      using the color 'fgcolor'.
+
+      Argument 'dest' is an (x, y) surface coordinate pair. If either x
+      or y is not an integer it is converted to one if possible.
+      Any sequence, including Rect, for which the first two elements are
+      positions x and y is accepted.
 
       If such a sequence exists, and the destination surface is a valid
       :mod:`pygame.Surface` (independently of its bit depth), the text will be
       'bgcolor', if available. The alpha values for both colors are always
       taken into account.
 
-      If 'None' is passed instead of a destination sequence, a new 
-      :mod:`pygame.Surface` will be created with the required size to contain
-      the drawn text, and using ``bgcolor`` as its background color. If a
-      background color is not available, the surface will be filled with zero
-      alpha opacity. Normally the returned surface has a 32 bit pixel size.
-      However, if ``bgcolor`` is ``None`` and antialiasing is disabled
-      a two color 8 bit surface with colorkey set for the background color
-      is returned.
-
-      The return value is a tuple: the target surface and the bounding
-      rectangle giving the size and position of the rendered text within the
-      surface.
+      The return value is a rectangle giving the size and position of the
+      rendered text within the surface.
 
       If an empty string is passed for text then the returned Rect is zero
-      width and the height of the face. If dest is None the returned surface is
-      the same dimensions as the boundary rect. The rect will test False.
+      width and the height of the face. The rect will test False.
 
       The rendering is done using the face's default size in points and its
       default style, without any rotation, and taking into account faces which

docs/ref/freetype.html

 <td>—</td>
 <td>Renders text on a surface</td>
 </tr>
+<tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.render_to">pygame.freetype.Face.render_to</a></td>
+<td>—</td>
+<td>Renders text to an existing surface</td>
+</tr>
 <tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.render_raw">pygame.freetype.Face.render_raw</a></td>
 <td>—</td>
 <td>Renders text as a string of bytes</td>
 <tt class="descname">render</tt><big>(</big><big>)</big><a class="headerlink" href="#pygame.freetype.Face.render" title="Permalink to this definition">¶</a></dt>
 <dd><div class="line-block">
 <div class="line"><span class="summaryline">Renders text on a surface</span></div>
-<div class="line"><span class="signature">render(dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -&gt; (Surface, Rect)</span></div>
+<div class="line"><span class="signature">render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -&gt; (Surface, Rect)</span></div>
 </div>
-<p>Renders the string &#8216;text&#8217; to a <a class="tooltip reference internal" href="surface.html#pygame.Surface" title=""><tt class="xref py py-mod docutils literal"><span class="pre">pygame.Surface</span></tt><span class="tooltip-content">pygame object for representing images</span></a>, using the color
-&#8216;fgcolor&#8217;.</p>
-<p>The &#8216;dest&#8217; parameter is supposed to be a sequence containing the surface
-and the coordinates at which the text will be rendered, in that order.
-The sequence may be either (surf, posn) or (surf, x, y), where x and y
-are numbers. posn can be any sequence, including Rect, for which the
-first two elements are positions x and y. If x and y are not integers
-they will be cast to int: <tt class="docutils literal"><span class="pre">int(x)</span></tt>, <tt class="docutils literal"><span class="pre">int(y)</span></tt>.</p>
+<p>Renturns a new <a class="tooltip reference internal" href="surface.html#pygame.Surface" title=""><tt class="xref py py-mod docutils literal"><span class="pre">pygame.Surface</span></tt><span class="tooltip-content">pygame object for representing images</span></a>, with the text rendered to it
+in the color given by &#8216;fgcolor&#8217;. If <tt class="docutils literal"><span class="pre">bgcolor</span></tt> is given, the surface
+will be filled with this color. If no background color is given,
+the surface is filled with zero alpha opacity. Normally the returned
+surface has a 32 bit pixel size. However, if <tt class="docutils literal"><span class="pre">bgcolor</span></tt> is <tt class="xref docutils literal"><span class="pre">None</span></tt>
+and antialiasing is disabled a two color 8 bit surface with colorkey
+set for the background color is returned.</p>
+<p>The return value is a tuple: the new surface and the bounding
+rectangle giving the size and origin of the rendered text.</p>
+<p>If an empty string is passed for text then the returned Rect is zero
+width and the height of the face. If dest is None the returned surface is
+the same dimensions as the boundary rect. The rect will test False.</p>
+<p>The rendering is done using the face&#8217;s default size in points and its
+default style, without any rotation, and taking into account faces which
+are set to be drawn vertically via the <a class="reference internal" href="#pygame.freetype.Face.vertical" title="pygame.freetype.Face.vertical"><tt class="xref py py-meth docutils literal"><span class="pre">Face.vertical()</span></tt></a> attribute.
+Optionally you may specify another point size to use via the &#8216;ptsize&#8217;
+argument, a text rotation via the &#8216;rotation&#8217; argument, or a new text
+style via the &#8216;style&#8217; argument.</p>
+<p>If text is a char (byte) string, then its encoding is assumed to be
+<tt class="docutils literal"><span class="pre">LATIN1</span></tt>.</p>
+</dd></dl>
+
+<dl class="definition method">
+<dt class="title" id="pygame.freetype.Face.render_to">
+<tt class="descname">render_to</tt><big>(</big><big>)</big><a class="headerlink" href="#pygame.freetype.Face.render_to" title="Permalink to this definition">¶</a></dt>
+<dd><div class="line-block">
+<div class="line"><span class="summaryline">Renders text to an existing surface</span></div>
+<div class="line"><span class="signature">render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -&gt; Rect</span></div>
+</div>
+<p>Renders the string &#8216;text&#8217; to a <a class="tooltip reference internal" href="surface.html#pygame.Surface" title=""><tt class="xref py py-mod docutils literal"><span class="pre">pygame.Surface</span></tt><span class="tooltip-content">pygame object for representing images</span></a> &#8216;surf&#8217;,
+using the color &#8216;fgcolor&#8217;.</p>
+<p>Argument &#8216;dest&#8217; is an (x, y) surface coordinate pair. If either x
+or y is not an integer it is converted to one if possible.
+Any sequence, including Rect, for which the first two elements are
+positions x and y is accepted.</p>
 <p>If such a sequence exists, and the destination surface is a valid
 <a class="tooltip reference internal" href="surface.html#pygame.Surface" title=""><tt class="xref py py-mod docutils literal"><span class="pre">pygame.Surface</span></tt><span class="tooltip-content">pygame object for representing images</span></a> (independently of its bit depth), the text will be
 rendered directly on top of it at the passed coordinates, using the given
 &#8216;fgcolor&#8217;, and painting the background of the text with the given
 &#8216;bgcolor&#8217;, if available. The alpha values for both colors are always
 taken into account.</p>
-<p>If &#8216;None&#8217; is passed instead of a destination sequence, a new
-<a class="tooltip reference internal" href="surface.html#pygame.Surface" title=""><tt class="xref py py-mod docutils literal"><span class="pre">pygame.Surface</span></tt><span class="tooltip-content">pygame object for representing images</span></a> will be created with the required size to contain
-the drawn text, and using <tt class="docutils literal"><span class="pre">bgcolor</span></tt> as its background color. If a
-background color is not available, the surface will be filled with zero
-alpha opacity. Normally the returned surface has a 32 bit pixel size.
-However, if <tt class="docutils literal"><span class="pre">bgcolor</span></tt> is <tt class="xref docutils literal"><span class="pre">None</span></tt> and antialiasing is disabled
-a two color 8 bit surface with colorkey set for the background color
-is returned.</p>
-<p>The return value is a tuple: the target surface and the bounding
-rectangle giving the size and position of the rendered text within the
-surface.</p>
+<p>The return value is a rectangle giving the size and position of the
+rendered text within the surface.</p>
 <p>If an empty string is passed for text then the returned Rect is zero
-width and the height of the face. If dest is None the returned surface is
-the same dimensions as the boundary rect. The rect will test False.</p>
+width and the height of the face. The rect will test False.</p>
 <p>The rendering is done using the face&#8217;s default size in points and its
 default style, without any rotation, and taking into account faces which
 are set to be drawn vertically via the <a class="reference internal" href="#pygame.freetype.Face.vertical" title="pygame.freetype.Face.vertical"><tt class="xref py py-meth docutils literal"><span class="pre">Face.vertical()</span></tt></a> attribute.

examples/freetype_misc.py

 
     face.underline_adjustment = 0.5
     face.pad = True
-    face.render((screen, 32, 32), "Hello World", colors["red"], colors['grey_dark'],
-            ptsize=64, style=freetype.STYLE_UNDERLINE|freetype.STYLE_OBLIQUE)
+    face.render_to(screen, (32, 32), "Hello World", colors["red"],
+                   colors['grey_dark'], ptsize=64,
+                   style=freetype.STYLE_UNDERLINE|freetype.STYLE_OBLIQUE)
     face.pad = False
 
-    face.render((screen, 32, 128), "abcdefghijklm", colors["grey_dark"], colors["green"],
-            ptsize=64)
+    face.render_to(screen, (32, 128), "abcdefghijklm", colors["grey_dark"],
+                   colors["green"], ptsize=64)
 
     face.vertical = True
-    face.render((screen, 32, 200), "Vertical?", colors["blue"], None, ptsize=32)
+    face.render_to(screen, (32, 200), "Vertical?", colors["blue"],
+                   None, ptsize=32)
     face.vertical = False
 
-    face.render((screen, 64, 190), "Let's spin!", colors["red"], None,
-            ptsize=48, rotation=55)
+    face.render_to(screen, (64, 190), "Let's spin!", colors["red"],
+                   None, ptsize=48, rotation=55)
 
-    face.render((screen, 160, 290), "All around!", colors["green"], None,
-            ptsize=48, rotation=-55)
+    face.render_to(screen, (160, 290), "All around!", colors["green"],
+                   None, ptsize=48, rotation=-55)
 
-    face.render((screen, 250, 220), "and BLEND", pygame.Color(255, 0, 0, 128), None,
-            ptsize=64)
+    face.render_to(screen, (250, 220), "and BLEND",
+                   pygame.Color(255, 0, 0, 128), None, ptsize=64)
 
-    face.render((screen, 265, 237), "or BLAND!", pygame.Color(0, 0xCC, 28, 128), None,
-            ptsize=64)
+    face.render_to(screen, (265, 237), "or BLAND!",
+                   pygame.Color(0, 0xCC, 28, 128), None, ptsize=64)
 
+    # Some pinwheels
     face.origin = True
     for angle in range(0, 360, 45):
-        face.render((screen, 200, 500), ")", pygame.Color('black'),
-                    ptsize=48, rotation=angle)
+        face.render_to(screen, (200, 500), ")", pygame.Color('black'),
+                       ptsize=48, rotation=angle)
     face.vertical = True
     for angle in range(15, 375, 30):
-        face.render((screen, 600, 400), "|^*", pygame.Color('orange'),
-                    ptsize=48, rotation=angle)
+        face.render_to(screen, (600, 400), "|^*", pygame.Color('orange'),
+                       ptsize=48, rotation=angle)
     face.vertical = False
     face.origin = False
 
     utext = pygame.compat.as_unicode(r"I \u2665 Unicode")
-    face.render((screen, 298, 320), utext, pygame.Color(0, 0xCC, 0xDD), None,
-            ptsize=64)
+    face.render_to(screen, (298, 320), utext, pygame.Color(0, 0xCC, 0xDD),
+                   None, ptsize=64)
 
     utext = pygame.compat.as_unicode(r"\u2665")
-    face.render((screen, 480, 32), utext, colors["grey_light"], colors["red"],
-            ptsize=148)
+    face.render_to(screen, (480, 32), utext, colors["grey_light"],
+                   colors["red"], ptsize=148)
 
-    face.render((screen, 380, 380), "...yes, this is an SDL surface", pygame.Color(0, 0, 0), None,
-            ptsize=24, style=freetype.STYLE_STRONG)
+    face.render_to(screen, (380, 380), "...yes, this is an SDL surface",
+                   pygame.Color(0, 0, 0),
+                   None, ptsize=24, style=freetype.STYLE_STRONG)
 
     pygame.display.flip()
 

src/doc/freetype_doc.h

 
 #define DOC_FACEGETSIZEDGLYPHHEIGHT "get_sized_glyph_height() -> int\nGets the scaled height of the face in pixels"
 
-#define DOC_FACERENDER "render(dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)\nRenders text on a surface"
+#define DOC_FACERENDER "render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)\nRenders text on a surface"
+
+#define DOC_FACERENDERTO "render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> Rect\nRenders text to an existing surface"
 
 #define DOC_FACERENDERRAW "render_raw(text, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (bytes, (int, int))\nRenders text as a string of bytes"
 
 Gets the scaled height of the face in pixels
 
 pygame.freetype.Face.render
- render(dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)
+ render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (Surface, Rect)
 Renders text on a surface
 
+pygame.freetype.Face.render_to
+ render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> Rect
+Renders text to an existing surface
+
 pygame.freetype.Face.render_raw
  render_raw(text, style=STYLE_DEFAULT, rotation=0, ptsize=default) -> (bytes, (int, int))
 Renders text as a string of bytes
 static PyObject *_ftface_getrect(PgFaceObject *, PyObject *, PyObject *);
 static PyObject *_ftface_getmetrics(PgFaceObject *, PyObject *, PyObject *);
 static PyObject *_ftface_render(PgFaceObject *, PyObject *, PyObject *);
+static PyObject *_ftface_render_to(PgFaceObject *, PyObject *, PyObject *);
 static PyObject *_ftface_render_raw(PgFaceObject *, PyObject *, PyObject *);
 static PyObject *_ftface_getsizedascender(PgFaceObject *, PyObject *);
 static PyObject *_ftface_getsizeddescender(PgFaceObject *, PyObject *);
 static PyObject *get_metrics(FreeTypeInstance *, FaceRenderMode *,
                              PgFaceObject *, PGFT_String *);
 static PyObject *load_font_res(const char *);
-static int parse_dest(PyObject *, PyObject **, int *, int *);
+static int parse_dest(PyObject *, int *, int *);
 
 
 /*
 }
 
 static int
-parse_dest(PyObject *dest, PyObject **surf, int *x, int *y)
+parse_dest(PyObject *dest, int *x, int *y)
 {
-    PyObject *s = PySequence_GetItem(dest, 0);
-    int len = PySequence_Length(dest);
     PyObject *oi;
     PyObject *oj;
     int i, j;
 
-    if (!PySurface_Check(s)) {
+    if (!PySequence_Check(dest) ||  /* conditional and */
+        !PySequence_Size(dest) > 1) {
         PyErr_Format(PyExc_TypeError,
-                     "expected a Surface as element 0 of dest:"
+                     "Expected length 2 sequence for dest argument:"
                      " got type %.1024s",
-                     Py_TYPE(s)->tp_name);
-        Py_DECREF(s);
+                     Py_TYPE(dest)->tp_name);
         return -1;
     }
-    if (len == 2) {
-        PyObject *size = PySequence_GetItem(dest, 1);
-
-        if (!size) {
-            Py_DECREF(s);
-            return -1;
-        }
-        if (!PySequence_Check(size)) {
-            PyErr_Format(PyExc_TypeError,
-                         "expected an (x,y) position for element 1"
-                         " of dest: got type %.1024s",
-                         Py_TYPE(size)->tp_name);
-            Py_DECREF(s);
-            Py_DECREF(size);
-            return -1;
-        }
-        len = PySequence_Length(size);
-        if (len < 2) {
-            PyErr_Format(PyExc_TypeError,
-                         "expected at least a length 2 sequence for element 1"
-                         " of dest: not length %d", len);
-            Py_DECREF(s);
-            Py_DECREF(size);
-            return -1;
-        }
-        oi = PySequence_GetItem(size, 0);
-        if (!oi) {
-            Py_DECREF(s);
-            Py_DECREF(size);
-            return -1;
-        }
-        oj = PySequence_GetItem(size, 1);
-        Py_DECREF(size);
-        if (!oj) {
-            Py_DECREF(s);
-            Py_DECREF(oi);
-            return -1;
-        }
-        if (!PyNumber_Check(oi) || !PyNumber_Check(oj)) {
-            Py_DECREF(s);
-            Py_DECREF(oi);
-            Py_DECREF(oj);
-            PyErr_Format(PyExc_TypeError,
-                         "expected a pair of numbers for element 1 of dest:"
-                         " got types %.1024s and %.1024s",
-                         Py_TYPE(oi)->tp_name, Py_TYPE(oj)->tp_name);
-            return -1;
-        }
+    oi = PySequence_GetItem(dest, 0);
+    if (!oi) {
+        return -1;
     }
-    else if (len == 3) {
-        oi = PySequence_GetItem(dest, 1);
-        if (!oi) {
-            Py_DECREF(s);
-            return -1;
-        }
-        oj = PySequence_GetItem(dest, 2);
-        if (!oj) {
-            Py_DECREF(oi);
-            Py_DECREF(s);
-            return -1;
-        }
-        if (!PyNumber_Check(oi) || !PyNumber_Check(oj)) {
-            Py_DECREF(s);
-            PyErr_Format(PyExc_TypeError,
-                         "for dest expected a pair of numbers"
-                         "for elements 1 and 2: got types %.1024s and %1024s",
-                         Py_TYPE(oi)->tp_name, Py_TYPE(oj)->tp_name);
-            Py_DECREF(oi);
-            Py_DECREF(oj);
-            return -1;
-        }
+    oj = PySequence_GetItem(dest, 1);
+    if (!oj) {
+        Py_DECREF(oi);
+        return -1;
     }
-    else {
-        Py_DECREF(s);
+    if (!PyNumber_Check(oi) || !PyNumber_Check(oj)) {
         PyErr_Format(PyExc_TypeError,
-                     "for dest expected a sequence of either 2 or 3:"
-                     " not length %d", len);
+                     "for dest expected a pair of numbers"
+                     "for elements 1 and 2: got types %.1024s and %1024s",
+                     Py_TYPE(oi)->tp_name, Py_TYPE(oj)->tp_name);
+        Py_DECREF(oi);
+        Py_DECREF(oj);
         return -1;
     }
     i = PyInt_AsLong(oi);
     Py_DECREF(oi);
     if (i == -1 && PyErr_Occurred()) {
-        Py_DECREF(s);
         Py_DECREF(oj);
         return -1;
     }
     j = PyInt_AsLong(oj);
     Py_DECREF(oj);
     if (j == -1 && PyErr_Occurred()) {
-        Py_DECREF(s);
         return -1;
     }
-    *surf = s;
     *x = i;
     *y = j;
     return 0;
         DOC_FACERENDER
     },
     {
+        "render_to",
+        (PyCFunction)_ftface_render_to,
+        METH_VARARGS | METH_KEYWORDS,
+        DOC_FACERENDERTO
+    },
+    {
         "render_raw",
         (PyCFunction)_ftface_render_raw,
         METH_VARARGS | METH_KEYWORDS,
 #else
     /* keyword list */
     static char *kwlist[] =  {
-        "dest", "text", "fgcolor", "bgcolor",
-        "style", "rotation", "ptsize", 0
+        "text", "fgcolor", "bgcolor", "style", "rotation", "ptsize", 0
     };
 
     /* input arguments */
     PyObject *textobj = 0;
     PGFT_String *text;
     int ptsize = -1;
-    PyObject *dest = 0;
-    PyObject *surface_obj = 0;
-    int xpos = 0;
-    int ypos = 0;
     PyObject *fg_color_obj = 0;
     PyObject *bg_color_obj = 0;
     int rotation = 0;
     int style = FT_STYLE_DEFAULT;
 
     /* output arguments */
+    SDL_Surface *surface;
+    PyObject *surface_obj = 0;
     PyObject *rtuple = 0;
     SDL_Rect r;
     PyObject *rect_obj;
     FreeTypeInstance *ft;
     ASSERT_GRAB_FREETYPE(ft, 0);
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO|Oiii", kwlist,
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oiii", kwlist,
                                      /* required */
-                                     &dest, &textobj, &fg_color_obj,
+                                     &textobj, &fg_color_obj,
                                      /* optional */
                                      &bg_color_obj, &style,
                                      &rotation, &ptsize)) {
         PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
         return 0;
     }
-
     if (bg_color_obj) {
         if (bg_color_obj == Py_None) {
             bg_color_obj = 0;
         return 0;
     }
 
-    if (dest == Py_None) {
-        SDL_Surface *r_surface = 0;
+    surface = _PGFT_Render_NewSurface(ft, self, &render, text, &fg_color,
+                                      bg_color_obj ? &bg_color : 0, &r);
+    _PGFT_FreeString(text);
+    if (!surface) {
+        return 0;
+    }
+    surface_obj = PySurface_New(surface);
+    if (!surface_obj) {
+        SDL_FreeSurface(surface);
+        return 0;
+    }
 
-        r_surface = _PGFT_Render_NewSurface(ft, self, &render, text, &fg_color,
-                                            bg_color_obj ? &bg_color : 0,
-                                            &r);
-        _PGFT_FreeString(text);
-
-        if (!r_surface) {
-            return 0;
-        }
-
-        surface_obj = PySurface_New(r_surface);
-        if (!surface_obj) {
-            return 0;
-        }
-    }
-    else if (PySequence_Check(dest) &&  /* conditional and */
-             PySequence_Size(dest) > 1) {
-        SDL_Surface *surface = 0;
-        int rcode;
-
-        if (parse_dest(dest, &surface_obj, &xpos, &ypos)) {
-            _PGFT_FreeString(text);
-            return 0;
-        }
-
-        surface = PySurface_AsSurface(surface_obj);
-
-        rcode = _PGFT_Render_ExistingSurface(ft, self, &render, text, surface,
-                                             xpos, ypos, &fg_color,
-                                             bg_color_obj ? &bg_color : 0,
-                                             &r);
-        _PGFT_FreeString(text);
-        if (rcode) {
-            Py_DECREF(surface_obj);
-            return 0;
-        }
-    }
-    else {
-        _PGFT_FreeString(text);
-        return PyErr_Format(PyExc_TypeError,
-                            "Expected a (surface, posn) or None for"
-			    " dest argument:"
-                            " got type %.1024s",
-                            Py_TYPE(dest)->tp_name);
-    }
     rect_obj = PyRect_New(&r);
     if (rect_obj) {
         rtuple = PyTuple_Pack(2, surface_obj, rect_obj);
 }
 
 static PyObject *
+_ftface_render_to(PgFaceObject *self, PyObject *args, PyObject *kwds)
+{
+#ifndef HAVE_PYGAME_SDL_VIDEO
+
+    PyErr_SetString(PyExc_RuntimeError,
+		    "SDL support is missing. Cannot render on surfaces");
+    return 0;
+
+#else
+    /* keyword list */
+    static char *kwlist[] =  {
+        "surf", "dest", "text", "fgcolor", "bgcolor",
+        "style", "rotation", "ptsize", 0
+    };
+
+    /* input arguments */
+    PyObject *surface_obj = 0;
+    PyObject *textobj = 0;
+    PGFT_String *text;
+    int ptsize = -1;
+    PyObject *dest = 0;
+    int xpos = 0;
+    int ypos = 0;
+    PyObject *fg_color_obj = 0;
+    PyObject *bg_color_obj = 0;
+    int rotation = 0;
+    int style = FT_STYLE_DEFAULT;
+    SDL_Surface *surface = 0;
+
+    /* output arguments */
+    SDL_Rect r;
+    int rcode;
+
+    FaceColor fg_color;
+    FaceColor bg_color;
+    FaceRenderMode render;
+
+    FreeTypeInstance *ft;
+    ASSERT_GRAB_FREETYPE(ft, 0);
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!OOO|Oiii", kwlist,
+                                     /* required */
+                                     &PySurface_Type, &surface_obj, &dest,
+                                     &textobj, &fg_color_obj,
+                                     /* optional */
+                                     &bg_color_obj, &style,
+                                     &rotation, &ptsize)) {
+        return 0;
+    }
+
+    if (parse_dest(dest, &xpos, &ypos)) {
+        return 0;
+    }
+    if (!RGBAFromColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
+        PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
+        return 0;
+    }
+    if (bg_color_obj) {
+        if (bg_color_obj == Py_None) {
+            bg_color_obj = 0;
+        }
+        else if (!RGBAFromColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
+            PyErr_SetString(PyExc_TypeError, "bgcolor must be a Color");
+            return 0;
+        }
+    }
+
+    ASSERT_SELF_IS_ALIVE(self);
+
+    /* Encode text */
+    text = _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
+    if (!text) {
+        return 0;
+    }
+
+    if (_PGFT_BuildRenderMode(ft, self, &render, ptsize, style, rotation)) {
+        _PGFT_FreeString(text);
+        return 0;
+    }
+
+    surface = PySurface_AsSurface(surface_obj);
+    rcode = _PGFT_Render_ExistingSurface(ft, self, &render, text, surface,
+                                         xpos, ypos, &fg_color,
+                                         bg_color_obj ? &bg_color : 0, &r);
+    _PGFT_FreeString(text);
+    if (rcode) {
+        return 0;
+    }
+
+    return PyRect_New(&r);
+
+#endif // HAVE_PYGAME_SDL_VIDEO
+}
+
+static PyObject *
 _ftface_gettransform(PgFaceObject *self)
 {
     if (!(self->render_flags & FT_RFLAG_TRANSFORM)) {

test/freetype_test.py

         nf = nullface()
         self.assertEqual(nf.name, repr(nf))
 
+    def test_freetype_Face_render_to(self):
+        # Rendering to an existing target surface is equivalent to
+        # blitting a surface returned by Face.render with the target.
+        face = self._TEST_FONTS['sans']
+
+        surf = pygame.Surface((800, 600))
+        color = pygame.Color(0, 0, 0)
+
+        rrect = face.render_to(surf, (32, 32),
+                               'FoobarBaz', color, None, ptsize=24)
+        self.assertTrue(isinstance(rrect, pygame.Rect))
+        self.assertEqual(rrect.top, rrect.height)
+##        self.assertEqual(rrect.left, something or other)
+        rcopy = rrect.copy()
+        rcopy.topleft = (32, 32)
+        self.assertTrue(surf.get_rect().contains(rcopy))
+        
+        rect = pygame.Rect(20, 20, 2, 2)
+        rrect = face.render_to(surf, rect, 'FoobarBax', color, None, ptsize=24)
+        self.assertEqual(rrect.top, rrect.height)
+        self.assertNotEqual(rrect.size, rect.size)
+        rrect = face.render_to(surf, (20.1, 18.9), 'FoobarBax',
+                               color, None, ptsize=24)
+##        self.assertEqual(tuple(rend[1].topleft), (20, 18))
+
+        rrect = face.render_to(surf, rect, '', color, None, ptsize=24)
+        self.assertFalse(rrect)
+        self.assertEqual(rrect.height, face.get_sized_height(24))
+
+        # invalid surf test
+        self.assertRaises(TypeError, face.render_to,
+                          "not a surface", "text", color)
+        self.assertRaises(TypeError, face.render_to,
+                          pygame.Surface, "text", color)
+                          
+        # invalid dest test
+        for dest in [None, 0, 'a', 'ab',
+                     (), (1,), ('a', 2), (1, 'a'), (1+2j, 2), (1, 1+2j),
+                     (1, int), (int, 1)]: 
+            self.assertRaises(TypeError, face.render,
+                              surf, dest, 'foobar', color, ptsize=24)
+
+        # misc parameter test
+        self.assertRaises(ValueError, face.render_to, surf, (0, 0),
+                          'foobar', color)
+        self.assertRaises(TypeError, face.render_to, surf, (0, 0),
+                          'foobar', color, "", ptsize=24)
+        self.assertRaises(ValueError, face.render_to, surf, (0, 0),
+                          'foobar', color, None, style=42, ptsize=24)
+        self.assertRaises(TypeError, face.render_to, surf, (0, 0),
+                          'foobar', color, None, style=None, ptsize=24)
+        self.assertRaises(ValueError, face.render_to, surf, (0, 0),
+                          'foobar', color, None, style=97, ptsize=24)
+
+
     def test_freetype_Face_render(self):
 
         face = self._TEST_FONTS['sans']
         color = pygame.Color(0, 0, 0)
 
         # make sure we always have a valid fg color
-        self.assertRaises(TypeError, face.render, None, 'FoobarBaz')
-        self.assertRaises(TypeError, face.render, None, 'FoobarBaz', None)
+        self.assertRaises(TypeError, face.render, 'FoobarBaz')
+        self.assertRaises(TypeError, face.render, 'FoobarBaz', None)
 
-        # render to new surface
-        rend = face.render(None, 'FoobarBaz', pygame.Color(0, 0, 0), None, ptsize=24)
+        rend = face.render('FoobarBaz', pygame.Color(0, 0, 0), None, ptsize=24)
         self.assertTrue(isinstance(rend, tuple))
         self.assertEqual(len(rend), 2)
         self.assertTrue(isinstance(rend[0], pygame.Surface))
         self.assertTrue(isinstance(rend[1], pygame.Rect))
         self.assertEqual(rend[0].get_rect().size, rend[1].size)
-        s, r = face.render(None, '', pygame.Color(0, 0, 0), None, ptsize=24)
+        s, r = face.render('', pygame.Color(0, 0, 0), None, ptsize=24)
         self.assertEqual(r.width, 1)
         self.assertEqual(r.height, face.get_sized_height(24))
         self.assertEqual(s.get_size(), r.size)
         self.assertEqual(s.get_bitsize(), 32)
 
-        # render to existing surface
-        refcount = sys.getrefcount(surf);
-        rend = face.render((surf, 32, 32), 'FoobarBaz', color, None, ptsize=24)
-        self.assertEqual(sys.getrefcount(surf), refcount + 1)
-        self.assertTrue(isinstance(rend, tuple))
-        self.assertEqual(len(rend), 2)
-        rsurf, rrect = rend
-        self.assertTrue(rsurf is surf)
-        self.assertTrue(isinstance(rrect, pygame.Rect))
-        self.assertEqual(rrect.top, rrect.height)
-##        self.assertEqual(rrect.left, something or other)
-        rcopy = rrect.copy()
-        rcopy.topleft = (32, 32)
-        self.assertTrue(rsurf.get_rect().contains(rcopy))
-        
-        rect = pygame.Rect(20, 20, 2, 2)
-        rend = face.render((surf, rect), 'FoobarBax', color, None, ptsize=24)
-        self.assertEqual(rend[1].top, rend[1].height)
-        self.assertNotEqual(rend[1].size, rect.size)
-        rend = face.render((surf, 20.1, 18.9), 'FoobarBax',
-                           color, None, ptsize=24)
-##        self.assertEqual(tuple(rend[1].topleft), (20, 18))
-
-        s, r = face.render((surf, rect), '', color, None, ptsize=24)
-        self.assertFalse(r)
-        self.assertEqual(r.height, face.get_sized_height(24))
-        self.assertTrue(s is surf)
-
-        # invalid dest test
-        for dest in [0, (), (surf,), (surf, 'a'), (surf, ()),
-                     (surf, (1,)), (surf, ('a', 2)), (surf, (1, 'a')),
-                     (surf, (1+2j, 2)), (surf, (1, 1+2j)),
-                     (surf, 'a', 2), (surf, 1, 'a'), (surf, 1+2j, 2),
-                     (surf, 1, 1+2j), (surf, 1, 2, 3)]: 
-            self.assertRaises(TypeError, face.render,
-                              dest, 'foobar', color, ptsize=24)
-
         # misc parameter test
-        self.assertRaises(ValueError, face.render, None, 'foobar', color)
-        self.assertRaises(TypeError, face.render, None, 'foobar', color, "",
-                ptsize=24)
-        self.assertRaises(ValueError, face.render, None, 'foobar', color, None,
-                style=42, ptsize=24)
-        self.assertRaises(TypeError, face.render, None, 'foobar', color, None,
-                style=None, ptsize=24)
-        self.assertRaises(ValueError, face.render, None, 'foobar', color, None,
-                style=97, ptsize=24)
+        self.assertRaises(ValueError, face.render, 'foobar', color)
+        self.assertRaises(TypeError, face.render, 'foobar', color, "",
+                          ptsize=24)
+        self.assertRaises(ValueError, face.render, 'foobar', color, None,
+                          style=42, ptsize=24)
+        self.assertRaises(TypeError, face.render, 'foobar', color, None,
+                          style=None, ptsize=24)
+        self.assertRaises(ValueError, face.render, 'foobar', color, None,
+                          style=97, ptsize=24)
 
         # valid surrogate pairs
 #        rend1 = face.render(None, as_unicode(r'\uD800\uDC00'), color, ptsize=24)
             
         # malformed surrogate pairs
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uD80C'), color, ptsize=24)
+                          as_unicode(r'\uD80C'), color, ptsize=24)
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uDCA7'), color, ptsize=24)
+                          as_unicode(r'\uDCA7'), color, ptsize=24)
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uD7FF\uDCA7'), color, ptsize=24)
+                          as_unicode(r'\uD7FF\uDCA7'), color, ptsize=24)
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uDC00\uDCA7'), color, ptsize=24)
+                          as_unicode(r'\uDC00\uDCA7'), color, ptsize=24)
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uD80C\uDBFF'), color, ptsize=24)
+                          as_unicode(r'\uD80C\uDBFF'), color, ptsize=24)
         self.assertRaises(UnicodeEncodeError, face.render,
-                          None, as_unicode(r'\uD80C\uE000'), color, ptsize=24)
+                          as_unicode(r'\uD80C\uE000'), color, ptsize=24)
 
         # raises exception when uninitalized
         self.assertRaises(RuntimeError, nullface().render,
-                          None, 'a', (0, 0, 0), ptsize=24)
+                          'a', (0, 0, 0), ptsize=24)
 
         # *** need more unicode testing to ensure the proper glyphs are rendered
 
         save_antialiased = face.antialiased
         face.antialiased = False
         try:
-            surf, r = face.render(None, text, color, ptsize=24)
+            surf, r = face.render(text, color, ptsize=24)
             self.assertEqual(surf.get_bitsize(), 8)
             flags = surf.get_flags()
             self.assertTrue(flags & pygame.SRCCOLORKEY)
 
             translucent_color = pygame.Color(*color)
             translucent_color.a = 55
-            surf, r = face.render(None, text, translucent_color, ptsize=24)
+            surf, r = face.render(text, translucent_color, ptsize=24)
             self.assertEqual(surf.get_bitsize(), 8)
             flags = surf.get_flags()
             self.assertTrue(flags & (pygame.SRCCOLORKEY | pygame.SRCALPHA))
             self.assertEqual(surf.get_colorkey(), colorkey)
             self.assertEqual(surf.get_alpha(), translucent_color.a)
 
-            surf, r = face.render(None, text, color, colorkey, ptsize=24)
+            surf, r = face.render(text, color, colorkey, ptsize=24)
             self.assertEqual(surf.get_bitsize(), 32)
         finally:
             face.antialiased = save_antialiased
 
+    def test_freetype_Face_render_to_mono(self):
+        # Rendering to an existing target surface is equivalent to
+        # blitting a surface returned by Face.render with the target.
+        face = self._TEST_FONTS['sans']
+        text = " ."
+        rect = face.get_rect(text, ptsize=24)
+        size = rect.size
+        fg = pygame.Surface((1, 1), pygame.SRCALPHA, 32)
+        bg = pygame.Surface((1, 1), pygame.SRCALPHA, 32)
+        surfaces = [pygame.Surface(size, 0, 8),
+                    pygame.Surface(size, 0, 16),
+                    pygame.Surface(size, pygame.SRCALPHA, 16),
+                    pygame.Surface(size, 0, 24),
+                    pygame.Surface(size, 0, 32),
+                    pygame.Surface(size, pygame.SRCALPHA, 32)]
+        fg_colors = [
+            surfaces[0].get_palette_at(2),
+            surfaces[1].unmap_rgb(surfaces[1].map_rgb((128, 64, 200))),
+            surfaces[2].unmap_rgb(surfaces[2].map_rgb((99, 0, 100, 64))),
+            (128, 97, 213),
+            (128, 97, 213),
+            (128, 97, 213, 60)]
+        fg_colors = [pygame.Color(*c) for c in fg_colors]
+        self.assertEqual(len(surfaces), len(fg_colors))  # safety check
+        bg_colors = [
+            surfaces[0].get_palette_at(4),
+            surfaces[1].unmap_rgb(surfaces[1].map_rgb((220, 20, 99))),
+            surfaces[2].unmap_rgb(surfaces[2].map_rgb((55, 200, 0, 86))),
+            (255, 120, 13),
+            (255, 120, 13),
+            (255, 120, 13, 180)]
+        bg_colors = [pygame.Color(*c) for c in bg_colors]
+        self.assertEqual(len(surfaces), len(bg_colors))  # safety check
+
+        save_antialiased = face.antialiased
+        face.antialiased = False
+        try:
+            fill_color = pygame.Color('black')
+            for i in range(len(surfaces)):
+                surf = surfaces[i]
+                surf.fill(fill_color)
+                fg_color = fg_colors[i]
+                fg.set_at((0, 0), fg_color)
+                surf.blit(fg, (0, 0))
+                r_fg_color = surf.get_at((0, 0))
+                surf.set_at((0, 0), fill_color)
+                rrect = face.render_to(surf, (0, 0), text, fg_color,
+                                       ptsize=24)
+                bottomleft = 0, rrect.height - 1
+                self.assertEqual(surf.get_at(bottomleft), fill_color)
+                bottomright = rrect.width - 1, rrect.height - 1
+                self.assertEqual(surf.get_at(bottomright), r_fg_color)
+            for i in range(len(surfaces)):
+                surf = surfaces[i]
+                surf.fill(fill_color)
+                fg_color = fg_colors[i]
+                bg_color = bg_colors[i]
+                bg.set_at((0, 0), bg_color)
+                fg.set_at((0, 0), fg_color)
+                bg.blit(fg, (0, 0))
+                surf.blit(bg, (0, 0))
+                r_fg_color = surf.get_at((0, 0))
+                surf.set_at((0, 0), fill_color)
+                rrect = face.render_to(surf, (0, 0), text, fg_color,
+                                       bg_color, ptsize=24)
+                bottomleft = 0, rrect.height - 1
+                self.assertEqual(surf.get_at(bottomleft), bg_color)
+                bottomleft = rrect.width - 1, rrect.height - 1
+                self.assertEqual(surf.get_at(bottomright), r_fg_color)
+        finally:
+            face.antialiased = save_antialiased
+
     def test_freetype_Face_render_raw(self):
     
         face = self._TEST_FONTS['sans']
         # of None.
         face = self._TEST_FONTS['sans']
 
-        img, size1 = face.render(None, unichr_(1), (0, 0, 0), ptsize=24)
-        img, size0 = face.render(None, "", (0, 0, 0), ptsize=24)
+        img, size1 = face.render(unichr_(1), (0, 0, 0), ptsize=24)
+        img, size0 = face.render("", (0, 0, 0), ptsize=24)
         self.assertTrue(size1.width > size0.width )
 
         metrics = face.get_metrics(unichr_(1) + unichr_(48), ptsize=24)