1. pygame
  2. Untitled project
  3. pygame

Commits

Lenard Lindstrom  committed e352941

add a fgcolor property to freetype.Font (towards Issue #75)

To make the freetype.Font properties complete, fgcolor is added. The
fgcolor argument of methods render() and render_to() becomes optional.
A default background color property is absent because it is incompatible
with the render_to() bgcolor argument.

This commit finalizes the freetype.Font Python api for Pygame 1.9.2. Consider
it stable.

  • Participants
  • Parent commits e0a9058
  • Branches default

Comments (0)

Files changed (5)

File docs/reST/ref/freetype.rst

View file
  • Ignore whitespace
    .. method:: render
 
       | :sl:`Return rendered text as a surface`
-      | :sg:`render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)`
+      | :sg:`render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)`
 
       Returns a new :mod:`pygame.Surface`, with the text rendered to it
-      in the color given by 'fgcolor'. If ``bgcolor`` is given, the surface
+      in the color given by 'fgcolor'. If no foreground color is given,
+      the default foreground color, :attr:`fgcolor` is used.
+      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 :const:`None`
    .. method:: render_to
 
       | :sl:`Render text onto an existing surface`
-      | :sg:`render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect`
+      | :sg:`render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect`
 
       Renders the string 'text' to a :mod:`pygame.Surface` 'surf',
-      using the color 'fgcolor'.
+      using the color 'fgcolor', if given, or the default foreground
+      color, :attr:`fgcolor`, otherwise.
 
       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.
       An attempt to change the rotation of an inactive font objects, as
       returned by Font.__new__(), raises a RuntimeError.
 
+   .. attribute:: fgcolor
+
+      | :sl:`default foreground color`
+      | :sg:`fgcolor -> Color`
+
+      Get or set the default glyph rendering color. It is initially opaque
+      black ― (0, 0, 0, 255). Applies to :meth:`render` and :meth:`render_to`.
+
    .. attribute:: origin
 
       | :sl:`Font render to text origin mode`

File src/_freetype.c

View file
  • Ignore whitespace
 static int _ftfont_setunderlineadjustment(PgFontObject *, PyObject *, void *);
 static PyObject *_ftfont_getrotation(PgFontObject *, void *);
 static int _ftfont_setrotation(PgFontObject *, PyObject *, void *);
+static PyObject *_ftfont_getfgcolor(PgFontObject *, void *);
+static int _ftfont_setfgcolor(PgFontObject *, PyObject *, void *);
 
 static PyObject *_ftfont_getresolution(PgFontObject *, void *);
 
         0
     },
     {
+        "fgcolor",
+        (getter)_ftfont_getfgcolor,
+        (setter)_ftfont_setfgcolor,
+        DOC_FONTFGCOLOR,
+        0
+    },
+    {
         "origin",
         (getter)_ftfont_getrender_flag,
         (setter)_ftfont_setrender_flag,
         obj->transform.xy = 0;
         obj->transform.yx = 0;
         obj->transform.yy = FX16_ONE;
+        obj->fgcolor[0] = 0;  /* rgba opaque black */
+        obj->fgcolor[1] = 0;
+        obj->fgcolor[2] = 0;
+        obj->fgcolor[3] = 255;
     }
     return (PyObject *)obj;
 }
     return obj_to_rotation(value, &self->rotation) ? 0 : -1;
 }
 
+/** default glyph color */
+static PyObject *
+_ftfont_getfgcolor(PgFontObject *self, void *closure)
+{
+    return PyColor_New(self->fgcolor);
+}
+
+static int
+_ftfont_setfgcolor(PgFontObject *self, PyObject *value, void *closure)
+{
+    if (!RGBAFromObj(value, self->fgcolor)) {
+        PyErr_Format(PyExc_AttributeError,
+                     "unable to convert %128s object to a color",
+                     Py_TYPE(value)->tp_name);
+        return -1;
+    }
+    return 0;
+}
+
 /** testing and debugging */
 #if defined(PGFT_DEBUG_CACHE)
 static PyObject *
     ASSERT_SELF_IS_ALIVE(self);
     ASSERT_GRAB_FREETYPE(ft, 0);
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OiO&O&", kwlist,
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOiO&O&", kwlist,
                                      /* required */
-                                     &textobj, &fg_color_obj,
+                                     &textobj,
                                      /* optional */
-                                     &bg_color_obj, &style,
+                                     &fg_color_obj, &bg_color_obj, &style,
                                      obj_to_rotation, (void *)&rotation,
                                      obj_to_scale, (void *)&face_size))
         goto error;
 
-    if (!RGBAFromColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
-        PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
-        goto error;
+    if (fg_color_obj == Py_None) {
+        fg_color_obj = 0;
+    }
+    if (bg_color_obj == Py_None) {
+        bg_color_obj = 0;
+    }
+
+    if (fg_color_obj) {
+        if (!RGBAFromColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
+            PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
+            goto error;
+        }
+    }
+    else {
+        fg_color.r = self->fgcolor[0];
+        fg_color.g = self->fgcolor[1];
+        fg_color.b = self->fgcolor[2];
+        fg_color.a = self->fgcolor[3];
     }
     if (bg_color_obj) {
-        if (bg_color_obj == Py_None) {
-            bg_color_obj = 0;
-        }
-        else if (!RGBAFromColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
+        if (!RGBAFromColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
             PyErr_SetString(PyExc_TypeError, "bgcolor must be a Color");
             goto error;
         }
     FreeTypeInstance *ft;
     ASSERT_GRAB_FREETYPE(ft, 0);
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!OOO|OiO&O&", kwlist,
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!OO|OOiO&O&", kwlist,
                                      /* required */
                                      &PySurface_Type, &surface_obj, &dest,
                                      &textobj, &fg_color_obj,
                                      obj_to_scale, (void *)&face_size))
         goto error;
 
+    if (fg_color_obj == Py_None) {
+        fg_color_obj = 0;
+    }
+    if (bg_color_obj == Py_None) {
+        bg_color_obj = 0;
+    }
+
     if (parse_dest(dest, &xpos, &ypos)) goto error;
-    if (!RGBAFromColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
-        PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
-        goto error;
+    if (fg_color_obj) {
+        if (!RGBAFromColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
+            PyErr_SetString(PyExc_TypeError, "fgcolor must be a Color");
+            goto error;
+        }
+    }
+    else {
+        fg_color.r = self->fgcolor[0];
+        fg_color.g = self->fgcolor[1];
+        fg_color.b = self->fgcolor[2];
+        fg_color.a = self->fgcolor[3];
     }
     if (bg_color_obj) {
-        if (bg_color_obj == Py_None) {
-            bg_color_obj = 0;
-        }
-        else if (!RGBAFromColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
+        if (!RGBAFromColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
             PyErr_SetString(PyExc_TypeError, "bgcolor must be a Color");
             goto error;
         }

File src/doc/freetype_doc.h

View file
  • Ignore whitespace
 
 #define DOC_FONTGETSIZES "get_sizes() -> [(int, int, int, float, float), ...]\nget_sizes() -> []\nreturn the available sizes of embedded bitmaps"
 
-#define DOC_FONTRENDER "render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)\nReturn rendered text as a surface"
+#define DOC_FONTRENDER "render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)\nReturn rendered text as a surface"
 
-#define DOC_FONTRENDERTO "render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect\nRender text onto an existing surface"
+#define DOC_FONTRENDERTO "render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect\nRender text onto an existing surface"
 
 #define DOC_FONTRENDERRAW "render_raw(text, style=STYLE_DEFAULT, rotation=0, size=0, invert=False) -> (bytes, (int, int))\nReturn rendered text as a string of bytes"
 
 
 #define DOC_FONTROTATION "rotation -> int\ntext rotation in degrees counterclockwise"
 
+#define DOC_FONTFGCOLOR "fgcolor -> Color\ndefault foreground color"
+
 #define DOC_FONTORIGIN "origin -> bool\nFont render to text origin mode"
 
 #define DOC_FONTPAD "pad -> bool\npadded boundary mode"
 return the available sizes of embedded bitmaps
 
 pygame.freetype.Font.render
- render(text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)
+ render(text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> (Surface, Rect)
 Return rendered text as a surface
 
 pygame.freetype.Font.render_to
- render(surf, dest, text, fgcolor, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect
+ render_to(surf, dest, text, fgcolor=None, bgcolor=None, style=STYLE_DEFAULT, rotation=0, size=0) -> Rect
 Render text onto an existing surface
 
 pygame.freetype.Font.render_raw
  rotation -> int
 text rotation in degrees counterclockwise
 
+pygame.freetype.Font.fgcolor
+ fgcolor -> Color
+default foreground color
+
 pygame.freetype.Font.origin
  origin -> bool
 Font render to text origin mode

File src/freetype.h

View file
  • Ignore whitespace
     FT_UInt resolution;
     Angle_t rotation;
     FT_Matrix transform;
+    FT_Byte fgcolor[4];
 
     struct fontinternals_ *_internals;
 } PgFontObject;

File test/freetype_test.py

View file
  • Ignore whitespace
         surf = pygame.Surface((800, 600))
         color = pygame.Color(0, 0, 0)
 
-        # make sure we always have a valid fg color
-        self.assertRaises(TypeError, font.render, 'FoobarBaz')
-        self.assertRaises(TypeError, font.render, 'FoobarBaz', None)
-
         rend = font.render('FoobarBaz', pygame.Color(0, 0, 0), None, size=24)
         self.assertTrue(isinstance(rend, tuple))
         self.assertEqual(len(rend), 2)
         # this method will not support None text
         self.assertRaises(TypeError, f.get_metrics, None)
 
+    def test_freetype_Font_fgcolor(self):
+        f = ft.Font(self._bmp_8_75dpi_path)
+        notdef = '\0'  # the PyGameMono .notdef glyph has a pixel at (0, 0)
+        f.origin = False
+        f.pad = False
+        black = pygame.Color('black')  # initial color
+        green = pygame.Color('green')
+        alpha128 = pygame.Color(10, 20, 30, 128)
+
+        c = f.fgcolor
+        self.assertTrue(isinstance(c, pygame.Color))
+        self.assertEqual(c, black)
+        s, r = f.render(notdef)
+        self.assertEqual(s.get_at((0, 0)), black)
+        f.fgcolor = green
+        self.assertEqual(f.fgcolor, green)
+        s, r = f.render(notdef)
+        self.assertEqual(s.get_at((0, 0)), green)
+        f.fgcolor = alpha128
+        s, r = f.render(notdef)
+        self.assertEqual(s.get_at((0, 0)), alpha128)
+
+        surf = pygame.Surface(f.get_rect(notdef).size, pygame.SRCALPHA, 32)
+        f.render_to(surf, (0, 0), None)
+        self.assertEqual(surf.get_at((0, 0)), alpha128)
+
+        self.assertRaises(AttributeError, setattr, f, 'fgcolor', None)
+
     if pygame.HAVE_NEWBUF:
         def test_newbuf(self):
             self.NEWBUF_test_newbuf()