From: <md...@us...> - 2007-08-31 19:25:20
|
Revision: 3764 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3764&view=rev Author: mdboom Date: 2007-08-31 12:25:17 -0700 (Fri, 31 Aug 2007) Log Message: ----------- Render all the fonts in each mathtext expression to a single image buffer (memory and time savings). Add support for getting raw image data for mathtext expressions. Add mathtext_wx.py example showing how to put mathtext expressions into controls. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py trunk/matplotlib/lib/matplotlib/backends/backend_gdk.py trunk/matplotlib/lib/matplotlib/mathtext.py trunk/matplotlib/src/_backend_agg.cpp trunk/matplotlib/src/_backend_agg.h trunk/matplotlib/src/ft2font.cpp trunk/matplotlib/src/ft2font.h trunk/matplotlib/unit/agg_memleak.py Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-08-31 19:25:17 UTC (rev 3764) @@ -172,7 +172,7 @@ """ if __debug__: verbose.report('RendererAgg.draw_mathtext', 'debug-annoying') - ox, oy, width, height, descent, fonts, used_characters = \ + ox, oy, width, height, descent, font_image, used_characters = \ self.mathtext_parser.parse(s, self.dpi.get(), prop) if angle == 90: @@ -180,13 +180,11 @@ ox, oy = oy, ox x = int(x) - width + ox y = int(y) - height + oy + font_image.rotate() else: x = int(x) + ox y = int(y) - height + oy - for font in fonts: - if angle == 90: - font.horiz_image_to_vert_image() # <-- Rotate - self._renderer.draw_text( font, x, y + 1, gc) + self._renderer.draw_text_image(font_image, x, y + 1, gc) if 0: self._renderer.draw_rectangle(gc, None, int(x), @@ -212,7 +210,7 @@ #print x, y, int(x), int(y) - self._renderer.draw_text(font, int(x), int(y) + 1, gc) + self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, gc) def get_text_width_height_descent(self, s, prop, ismath, rgb=(0,0,0)): Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gdk.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gdk.py 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gdk.py 2007-08-31 19:25:17 UTC (rev 3764) @@ -215,7 +215,7 @@ for i, font in enumerate(fonts): if angle == 90: - font.horiz_image_to_vert_image() # <-- Rotate + font.get_image().rotate() # <-- Rotate imw, imh, image_str = font.image_as_str() Xall[:,i] = npy.fromstring(image_str, npy.uint8) Modified: trunk/matplotlib/lib/matplotlib/mathtext.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mathtext.py 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/lib/matplotlib/mathtext.py 2007-08-31 19:25:17 UTC (rev 3764) @@ -143,7 +143,7 @@ from matplotlib.afm import AFM from matplotlib.cbook import enumerate, iterable, Bunch, get_realpath_and_stat, \ is_string_like -from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_DEFAULT, LOAD_NO_HINTING +from matplotlib.ft2font import FT2Font, FT2Image, KERNING_DEFAULT, LOAD_DEFAULT, LOAD_NO_HINTING from matplotlib.font_manager import findfont, FontProperties from matplotlib._mathtext_data import latex_to_bakoma, \ latex_to_standard, tex2uni, type12uni, tex2type1, uni2type1 @@ -288,20 +288,19 @@ def __init__(self): self.ox = 0 self.oy = 0 + self.image = None MathtextBackend.__init__(self) def set_canvas_size(self, w, h, d): MathtextBackend.set_canvas_size(self, w, h, d) - for font in self.fonts_object.get_fonts(): - font.set_bitmap_size(int(w), int(h) + int(d)) + self.image = FT2Image(ceil(w), ceil(h + d)) def render_glyph(self, ox, oy, info): info.font.draw_glyph_to_bitmap( - ox, oy - info.metrics.ymax, info.glyph) + self.image, ox, oy - info.metrics.ymax, info.glyph) def render_rect_filled(self, x1, y1, x2, y2): - font = self.fonts_object.get_fonts()[0] - font.draw_rect_filled(x1, y1, x2, max(y2 - 1, y1)) + self.image.draw_rect_filled(x1, y1, x2, max(y2 - 1, y1)) def get_results(self, box): return (self.ox, @@ -309,7 +308,7 @@ self.width, self.height + self.depth, self.depth, - self.fonts_object.get_fonts(), + self.image, self.fonts_object.get_used_characters()) def get_hinting_type(self): @@ -318,6 +317,13 @@ def MathtextBackendAgg(): return MathtextBackendBbox(MathtextBackendAggRender()) +class MathtextBackendBitmapRender(MathtextBackendAggRender): + def get_results(self, box): + return self.image + +def MathtextBackendBitmap(): + return MathtextBackendBbox(MathtextBackendBitmapRender()) + class MathtextBackendPs(MathtextBackend): def __init__(self): self.pswriter = StringIO() @@ -2443,6 +2449,7 @@ _parser = None _backend_mapping = { + 'Bitmap': MathtextBackendBitmap, 'Agg' : MathtextBackendAgg, 'PS' : MathtextBackendPs, 'Pdf' : MathtextBackendPdf, @@ -2454,7 +2461,10 @@ self._output = output self._cache = {} - def parse(self, s, dpi, prop): + def parse(self, s, dpi = 72, prop = None): + if prop is None: + prop = FontProperties() + cacheKey = (s, dpi, hash(prop)) result = self._cache.get(cacheKey) if result is not None: Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/src/_backend_agg.cpp 2007-08-31 19:25:17 UTC (rev 3764) @@ -2106,14 +2106,15 @@ Py::Object -RendererAgg::draw_text(const Py::Tuple& args) { +RendererAgg::draw_text_image(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_text"); args.verify_length(4); + FT2Image *image = static_cast<FT2Image*>(args[0].ptr()); + if (!image->get_buffer()) + return Py::Object(); - FT2Font *font = static_cast<FT2Font*>(args[0].ptr()); - int x(0),y(0); try { x = Py::Int( args[1] ); @@ -2151,15 +2152,16 @@ t = b+h; } + const unsigned char* const buffer = image->get_buffer(); - for (size_t i=0; i<font->image.width; i++) { - for (size_t j=0; j<font->image.height; j++) { - thisx = i+x+font->image.offsetx; - thisy = j+y+font->image.offsety; + for (size_t i=0; i< image->get_width(); i++) { + for (size_t j=0; j< image->get_height(); j++) { + thisx = i+x+image->offsetx; + thisy = j+y+image->offsety; if (thisx<l || thisx>=r) continue; if (thisy<height-t || thisy>=height-b) continue; pixFmt->blend_pixel - (thisx, thisy, p, font->image.buffer[i + j*font->image.width]); + (thisx, thisy, p, buffer[i + j*image->get_width()]); } } @@ -2568,8 +2570,8 @@ "draw_markers(gc, path, x, y)\n"); add_varargs_method("draw_path", &RendererAgg::draw_path, "draw_path(gc, rgbFace, path, transform)\n"); - add_varargs_method("draw_text", &RendererAgg::draw_text, - "draw_text(font, x, y, r, g, b, a)\n"); + add_varargs_method("draw_text_image", &RendererAgg::draw_text_image, + "draw_text_image(font_image, x, y, r, g, b, a)\n"); add_varargs_method("draw_image", &RendererAgg::draw_image, "draw_image(x, y, im)"); add_varargs_method("write_rgba", &RendererAgg::write_rgba, Modified: trunk/matplotlib/src/_backend_agg.h =================================================================== --- trunk/matplotlib/src/_backend_agg.h 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/src/_backend_agg.h 2007-08-31 19:25:17 UTC (rev 3764) @@ -166,7 +166,7 @@ //Py::Object _draw_markers_nocache(const Py::Tuple & args); //Py::Object _draw_markers_cache(const Py::Tuple & args); Py::Object draw_markers(const Py::Tuple & args); - Py::Object draw_text(const Py::Tuple & args); + Py::Object draw_text_image(const Py::Tuple & args); Py::Object draw_image(const Py::Tuple & args); Py::Object write_rgba(const Py::Tuple & args); Modified: trunk/matplotlib/src/ft2font.cpp =================================================================== --- trunk/matplotlib/src/ft2font.cpp 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/src/ft2font.cpp 2007-08-31 19:25:17 UTC (rev 3764) @@ -42,9 +42,414 @@ FT_Library _ft2Library; -FT2Image::FT2Image() : bRotated(false), buffer(NULL) {} -FT2Image::~FT2Image() {delete [] buffer; buffer=NULL;} +FT2Image::FT2Image() : + offsetx(0), offsety(0), + _bRotated(false), + _isDirty(true), + _buffer(NULL), + _width(0), _height(0), + _rgbCopy(NULL), + _rgbaCopy(NULL) { + _VERBOSE("FT2Image::FT2Image"); +} +FT2Image::FT2Image(unsigned long width, unsigned long height) : + offsetx(0), offsety(0), + _bRotated(false), + _isDirty(true), + _buffer(NULL), + _width(0), _height(0), + _rgbCopy(NULL), + _rgbaCopy(NULL) { + _VERBOSE("FT2Image::FT2Image"); + resize(width, height); +} + +FT2Image::~FT2Image() { + _VERBOSE("FT2Image::~FT2Image"); + delete [] _buffer; + _buffer=NULL; +} + +void FT2Image::resize(unsigned long width, unsigned long height) { + size_t numBytes = width*height; + + if (_width != width || _height != height) { + _width = width; + _height = height; + + delete [] _buffer; + _buffer = new unsigned char [numBytes]; + } + + for (size_t n=0; n<numBytes; n++) + _buffer[n] = 0; + + _bRotated = false; + _isDirty = true; +} + +char FT2Image::resize__doc__[] = +"resize(width, height)\n" +"\n" +"Resize the dimensions of the image (it is cleared in the process).\n" +; +Py::Object +FT2Image::py_resize(const Py::Tuple & args) { + _VERBOSE("FT2Image::resize"); + + args.verify_length(2); + + long x0 = Py::Int(args[0]); + long y0 = Py::Int(args[1]); + + resize(x0, y0); + + return Py::Object(); +} + +void FT2Image::clear() { + _VERBOSE("FT2Image::clear"); + + _width = 0; + _height = 0; + offsetx = 0; + offsety = 0; + _isDirty = true; + _bRotated = false; + delete [] _buffer; + _buffer = NULL; + if (_rgbCopy) { + delete _rgbCopy; + _rgbCopy = NULL; + } + if (_rgbaCopy) { + delete _rgbaCopy; + _rgbaCopy = NULL; + } +} +char FT2Image::clear__doc__[] = +"clear()\n" +"\n" +"Clear the contents of the image.\n" +; +Py::Object +FT2Image::py_clear(const Py::Tuple & args) { + args.verify_length(0); + + clear(); + + return Py::Object(); +} + +void FT2Image::rotate() { + // If we have already rotated, just return. + if (_bRotated) + return; + + unsigned long width = _width; + unsigned long height = _height; + + unsigned long newWidth = _height; + unsigned long newHeight = _width; + + unsigned long numBytes = _width * _height; + + unsigned char * buffer = new unsigned char [numBytes]; + + unsigned long i, j, k, offset, nhMinusOne; + + nhMinusOne = newHeight - 1; + + unsigned char * read_it = _buffer; + + for (i=0; i<height; i++) { + offset = i*width; + for (j=0; j<width; j++) { + k = nhMinusOne - j; + buffer[i + k*newWidth] = *(read_it++); + } + } + + delete [] _buffer; + _buffer = buffer; + _width = newWidth; + _height = newHeight; + _bRotated = true; + _isDirty = true; +} +char FT2Image::rotate__doc__[] = +"rotate()\n" +"\n" +"Rotates the image 90 degrees.\n" +; +Py::Object +FT2Image::py_rotate(const Py::Tuple & args) { + _VERBOSE("FT2Image::rotate"); + + args.verify_length(0); + + rotate(); + + return Py::Object(); +} + +void +FT2Image::draw_bitmap( FT_Bitmap* bitmap, + FT_Int x, + FT_Int y) { + _VERBOSE("FT2Image::draw_bitmap"); + FT_Int image_width = (FT_Int)_width; + FT_Int image_height = (FT_Int)_height; + FT_Int char_width = bitmap->width; + FT_Int char_height = bitmap->rows; + + FT_Int x1 = CLAMP(x, 0, image_width); + FT_Int y1 = CLAMP(y, 0, image_height); + FT_Int x2 = CLAMP(x + char_width, 0, image_width); + FT_Int y2 = CLAMP(y + char_height, 0, image_height); + + FT_Int x_start = MAX(0, -x); + FT_Int y_offset = y1 - MAX(0, -y); + + for ( FT_Int i = y1; i < y2; ++i ) { + unsigned char* dst = _buffer + (i * image_width + x1); + unsigned char* src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start); + for ( FT_Int j = x1; j < x2; ++j, ++dst, ++src ) + *dst |= *src; + } + + _isDirty = true; +} + +void FT2Image::write_bitmap(const char* filename) const { + FILE *fh = fopen(filename, "w"); + + for ( size_t i = 0; i< _height; i++) { + for ( size_t j = 0; j < _width; ++j) { + if (_buffer[j + i*_width]) + fputc('#', fh); + else + fputc(' ', fh); + } + fputc('\n', fh); + } + + fclose(fh); +} + +char FT2Image::write_bitmap__doc__[] = +"write_bitmap(fname)\n" +"\n" +"Write the bitmap to file fname\n" +; +Py::Object +FT2Image::py_write_bitmap(const Py::Tuple & args) { + _VERBOSE("FT2Image::write_bitmap"); + + args.verify_length(1); + + std::string filename = Py::String(args[0]); + + write_bitmap(filename.c_str()); + + return Py::Object(); +} + +void +FT2Image::draw_rect(unsigned long x0, unsigned long y0, + unsigned long x1, unsigned long y1) { + if ( x0<0 || y0<0 || x1<0 || y1<0 || + x0>_width || x1>_width || + y0>_height || y1>_height ) + throw Py::ValueError("Rect coords outside image bounds"); + + size_t top = y0*_width; + size_t bottom = y1*_width; + for (size_t i=x0; i<x1+1; ++i) { + _buffer[i + top] = 255; + _buffer[i + bottom] = 255; + } + + for (size_t j=y0+1; j<y1; ++j) { + _buffer[x0 + j*_width] = 255; + _buffer[x1 + j*_width] = 255; + } + + _isDirty = true; +} + +char FT2Image::draw_rect__doc__[] = +"draw_rect(x0, y0, x1, y1)\n" +"\n" +"Draw a rect to the image.\n" +"\n" +; +Py::Object +FT2Image::py_draw_rect(const Py::Tuple & args) { + _VERBOSE("FT2Image::draw_rect"); + + args.verify_length(4); + + long x0 = Py::Int(args[0]); + long y0 = Py::Int(args[1]); + long x1 = Py::Int(args[2]); + long y1 = Py::Int(args[3]); + + draw_rect(x0, y0, x1, y1); + + return Py::Object(); +} + +void FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, + unsigned long x1, unsigned long y1) { + x0 = CLAMP(x0, 0, _width); + y0 = CLAMP(y0, 0, _height); + x1 = CLAMP(x1, 0, _width); + y1 = CLAMP(y1, 0, _height); + + for (size_t j=y0; j<y1+1; j++) { + for (size_t i=x0; i<x1+1; i++) { + _buffer[i + j*_width] = 255; + } + } + + _isDirty = true; +} + +char FT2Image::draw_rect_filled__doc__[] = +"draw_rect_filled(x0, y0, x1, y1)\n" +"\n" +"Draw a filled rect to the image.\n" +"\n" +; +Py::Object +FT2Image::py_draw_rect_filled(const Py::Tuple & args) { + _VERBOSE("FT2Image::draw_rect_filled"); + + args.verify_length(4); + + long x0 = Py::Int(args[0]); + long y0 = Py::Int(args[1]); + long x1 = Py::Int(args[2]); + long y1 = Py::Int(args[3]); + + draw_rect_filled(x0, y0, x1, y1); + + return Py::Object(); +} + +char FT2Image::as_str__doc__[] = +"width, height, s = image_as_str()\n" +"\n" +"Return the image buffer as a string\n" +"\n" +; +Py::Object +FT2Image::py_as_str(const Py::Tuple & args) { + _VERBOSE("FT2Image::as_str"); + args.verify_length(0); + + return Py::asObject(PyString_FromStringAndSize((const char *)_buffer, _width*_height)); +} + +void FT2Image::makeRgbCopy() { + if (!_isDirty) + return; + + if (!_rgbCopy) { + _rgbCopy = new FT2Image(_width * 3, _height); + } else { + _rgbCopy->resize(_width * 3, _height); + } + unsigned char *src = _buffer; + unsigned char *src_end = src + (_width * _height); + unsigned char *dst = _rgbCopy->_buffer; + + unsigned char tmp; + while (src != src_end) { + tmp = 255 - *src++; + *dst++ = tmp; + *dst++ = tmp; + *dst++ = tmp; + } +} + +char FT2Image::as_rgb_str__doc__[] = +"width, height, s = image_as_rgb_str()\n" +"\n" +"Return the image buffer as a 24-bit RGB string.\n" +"\n" +; +Py::Object +FT2Image::py_as_rgb_str(const Py::Tuple & args) { + _VERBOSE("FT2Image::as_str_rgb"); + args.verify_length(0); + + makeRgbCopy(); + + return _rgbCopy->py_as_str(args); +} + +void FT2Image::makeRgbaCopy() { + if (!_isDirty) + return; + + if (!_rgbaCopy) { + _rgbaCopy = new FT2Image(_width * 4, _height); + } else { + _rgbaCopy->resize(_width * 4, _height); + } + unsigned char *src = _buffer; + unsigned char *src_end = src + (_width * _height); + unsigned char *dst = _rgbaCopy->_buffer; + + // This pre-multiplies the alpha, which apparently shouldn't + // be necessary for wxGTK, but it sure as heck seems to be. + unsigned int c; + unsigned int tmp; + while (src != src_end) { + c = *src++; + tmp = ((255 - c) * c) >> 8; + *dst++ = tmp; + *dst++ = tmp; + *dst++ = tmp; + *dst++ = c; + } +} + +char FT2Image::as_rgba_str__doc__[] = +"width, height, s = image_as_rgb_str()\n" +"\n" +"Return the image buffer as a 32-bit RGBA string.\n" +"\n" +; +Py::Object +FT2Image::py_as_rgba_str(const Py::Tuple & args) { + _VERBOSE("FT2Image::as_str_rgba"); + args.verify_length(0); + + makeRgbaCopy(); + + return _rgbaCopy->py_as_str(args); +} + +Py::Object +FT2Image::py_get_width(const Py::Tuple & args) { + _VERBOSE("FT2Image::get_width"); + args.verify_length(0); + + return Py::Int((long)get_width()); +} + +Py::Object +FT2Image::py_get_height(const Py::Tuple & args) { + _VERBOSE("FT2Image::get_height"); + args.verify_length(0); + + return Py::Int((long)get_height()); +} + Glyph::Glyph( const FT_Face& face, const FT_Glyph& glyph, size_t ind) : glyphInd(ind) { _VERBOSE("Glyph::Glyph"); @@ -105,7 +510,6 @@ //get the glyph as a path, a list of (COMMAND, *args) as desribed in matplotlib.path // this code is from agg's decompose_ft_outline with minor modifications - enum {MOVETO, LINETO, CURVE3, CURVE4, ENDPOLY}; FT_Outline& outline = face->glyph->outline; Py::List path; @@ -345,12 +749,12 @@ } -FT2Font::FT2Font(std::string facefile) +FT2Font::FT2Font(std::string facefile) : + image(NULL) { _VERBOSE(Printf("FT2Font::FT2Font %s", facefile.c_str()).str()); clear(Py::Tuple(0)); - int error = FT_New_Face( _ft2Library, facefile.c_str(), 0, &face ); @@ -447,75 +851,20 @@ FT2Font::~FT2Font() { _VERBOSE("FT2Font::~FT2Font"); + + if(image) + Py::_XDECREF(image); FT_Done_Face ( face ); - delete [] image.buffer ; - image.buffer = NULL; - for (size_t i=0; i<glyphs.size(); i++) { FT_Done_Glyph( glyphs[i] ); } + for (size_t i=0; i<gms.size(); i++) { Py_DECREF(gms[i]); } } - -char FT2Font::horiz_image_to_vert_image__doc__[] = -"horiz_image_to_vert_image()\n" -"\n" -"Copies the horizontal image (w, h) into a\n" -"new image of size (h,w)\n" -"This is equivalent to rotating the original image\n" -"by 90 degrees ccw\n" -; - -Py::Object -FT2Font::horiz_image_to_vert_image(const Py::Tuple & args) { - - // If we have already rotated, just return. - - if (image.bRotated) - return Py::Object(); - - - long width = image.width, height = image.height; - - long newWidth = image.height; - long newHeight = image.width; - - long numBytes = image.width * image.height; - - unsigned char * buffer = new unsigned char [numBytes]; - - long i, j, k, offset, nhMinusOne; - - nhMinusOne = newHeight-1; - - for (i=0; i<height; i++) { - - offset = i*width; - - for (j=0; j<width; j++) { - - k = nhMinusOne - j; - - buffer[i + k*newWidth] = image.buffer[j + offset]; - - } - - } - - delete [] image.buffer; - image.buffer = buffer; - image.width = newWidth; - image.height = newHeight; - image.bRotated = true; - - return Py::Object(); - -} - int FT2Font::setattr( const char *name, const Py::Object &value ) { _VERBOSE("FT2Font::setattr"); @@ -530,34 +879,6 @@ else return getattr_default( name ); } -char FT2Font::set_bitmap_size__doc__[] = -"set_bitmap_size(w, h)\n" -"\n" -"Manually set the bitmap size to render the glyps to. This is useful" -"in cases where you want to render several different glyphs to the bitmap" -; - -Py::Object -FT2Font::set_bitmap_size(const Py::Tuple & args) { - _VERBOSE("FT2Font::set_bitmap_size"); - args.verify_length(2); - - long width = Py::Int(args[0]); - long height = Py::Int(args[1]); - - image.width = (unsigned)width; - image.height = (unsigned)height; - - long numBytes = image.width * image.height; - - delete [] image.buffer; - image.buffer = new unsigned char [numBytes]; - for (long n=0; n<numBytes; n++) - image.buffer[n] = 0; - - return Py::Object(); -} - char FT2Font::clear__doc__[] = "clear()\n" "\n" @@ -569,13 +890,8 @@ _VERBOSE("FT2Font::clear"); args.verify_length(0); - //todo: move to image method? - delete [] image.buffer ; - image.buffer = NULL; - image.width = 0; - image.height = 0; - image.offsetx = 0; - image.offsety = 0; + if (image) + image->clear(); angle = 0.0; @@ -904,8 +1220,6 @@ _VERBOSE("FT2Font::get_width_height"); args.verify_length(0); - - FT_BBox bbox = compute_string_bbox(); Py::Tuple ret(2); @@ -930,152 +1244,6 @@ return Py::Int(- bbox.yMin);; } -void -FT2Font::draw_bitmap( FT_Bitmap* bitmap, - FT_Int x, - FT_Int y) { - _VERBOSE("FT2Font::draw_bitmap"); - FT_Int image_width = (FT_Int)image.width; - FT_Int image_height = (FT_Int)image.height; - FT_Int char_width = bitmap->width; - FT_Int char_height = bitmap->rows; - - FT_Int x1 = CLAMP(x, 0, image_width); - FT_Int y1 = CLAMP(y, 0, image_height); - FT_Int x2 = CLAMP(x + char_width, 0, image_width); - FT_Int y2 = CLAMP(y + char_height, 0, image_height); - - FT_Int x_start = MAX(0, -x); - FT_Int y_offset = y1 - MAX(0, -y); - - for ( FT_Int i = y1; i < y2; ++i ) { - unsigned char* dst = image.buffer + (i * image_width + x1); - unsigned char* src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start); - for ( FT_Int j = x1; j < x2; ++j, ++dst, ++src ) - *dst |= *src; - } -} - -char FT2Font::write_bitmap__doc__[] = -"write_bitmap(fname)\n" -"\n" -"Write the bitmap to file fname\n" -; -Py::Object -FT2Font::write_bitmap(const Py::Tuple & args) { - _VERBOSE("FT2Font::write_bitmap"); - - args.verify_length(1); - - FT_Int i, j; - - std::string filename = Py::String(args[0]); - - FILE *fh = fopen(filename.c_str(), "w"); - FT_Int width = (FT_Int)image.width; - FT_Int height = (FT_Int)image.height; - - for ( i = 0; i< height; i++) - for ( j = 0; j < width; ++j) - fputc(image.buffer[j + i*width], fh); - - fclose(fh); - - return Py::Object(); -} - -char FT2Font::draw_rect__doc__[] = -"draw_rect(x0, y0, x1, y1)\n" -"\n" -"Draw a rect to the image. It is your responsibility to set the dimensions\n" -"of the image, eg, with set_bitmap_size\n" -"\n" -; -Py::Object -FT2Font::draw_rect(const Py::Tuple & args) { - _VERBOSE("FT2Font::draw_rect"); - - args.verify_length(4); - - long x0 = Py::Int(args[0]); - long y0 = Py::Int(args[1]); - long x1 = Py::Int(args[2]); - long y1 = Py::Int(args[3]); - - FT_Int iwidth = (FT_Int)image.width; - FT_Int iheight = (FT_Int)image.height; - - if ( x0<0 || y0<0 || x1<0 || y1<0 || - x0>iwidth || x1>iwidth || - y0>iheight || y1>iheight ) - throw Py::ValueError("Rect coords outside image bounds"); - - for (long i=x0; i<x1+1; ++i) { - image.buffer[i + y0*iwidth] = 255; - image.buffer[i + y1*iwidth] = 255; - } - - for (long j=y0+1; j<y1; ++j) { - image.buffer[x0 + j*iwidth] = 255; - image.buffer[x1 + j*iwidth] = 255; - } - return Py::Object(); -} - -char FT2Font::draw_rect_filled__doc__[] = -"draw_rect_filled(x0, y0, x1, y1)\n" -"\n" -"Draw a filled rect to the image. It is your responsibility to set the\n" -"dimensions of the image, eg, with set_bitmap_size\n" -"\n" -; -Py::Object -FT2Font::draw_rect_filled(const Py::Tuple & args) { - _VERBOSE("FT2Font::draw_rect_filled"); - - args.verify_length(4); - - long x0 = Py::Int(args[0]); - long y0 = Py::Int(args[1]); - long x1 = Py::Int(args[2]); - long y1 = Py::Int(args[3]); - - FT_Int iwidth = (FT_Int)image.width; - FT_Int iheight = (FT_Int)image.height; - - x0 = CLAMP(x0, 0, iwidth); - y0 = CLAMP(y0, 0, iheight); - x1 = CLAMP(x1, 0, iwidth); - y1 = CLAMP(y1, 0, iheight); - - for (long j=y0; j<y1+1; j++) { - for (long i=x0; i<x1+1; i++) { - image.buffer[i + j*iwidth] = 255; - } - } - return Py::Object(); -} - -char FT2Font::image_as_str__doc__[] = -"width, height, s = image_as_str()\n" -"\n" -"Return the image buffer as a string\n" -"\n" -; -Py::Object -FT2Font::image_as_str(const Py::Tuple & args) { - _VERBOSE("FT2Font::image_as_str"); - args.verify_length(0); - - return Py::asObject( - Py_BuildValue("lls#", - image.width, - image.height, - image.buffer, - image.width*image.height) - ); -} - char FT2Font::draw_glyphs_to_bitmap__doc__[] = "draw_glyphs_to_bitmap()\n" "\n" @@ -1089,22 +1257,21 @@ args.verify_length(0); FT_BBox string_bbox = compute_string_bbox(); + size_t width = (string_bbox.xMax-string_bbox.xMin) / 64 + 2; + size_t height = (string_bbox.yMax-string_bbox.yMin) / 64 + 2; - image.width = (string_bbox.xMax-string_bbox.xMin) / 64 + 2; - image.height = (string_bbox.yMax-string_bbox.yMin) / 64 + 2; + if (!image) { + image = new FT2Image(width, height); + } else { + image->resize(width, height); + } - image.offsetx = (int)(string_bbox.xMin / 64.0); + image->offsetx = (int)(string_bbox.xMin / 64.0); if (angle==0) - image.offsety = -image.height; + image->offsety = -image->get_height(); else - image.offsety = (int)(-string_bbox.yMax/64.0); + image->offsety = (int)(-string_bbox.yMax/64.0); - size_t numBytes = image.width*image.height; - delete [] image.buffer; - image.buffer = new unsigned char [numBytes]; - for (size_t n=0; n<numBytes; n++) - image.buffer[n] = 0; - for ( size_t n = 0; n < glyphs.size(); n++ ) { FT_BBox bbox; @@ -1113,8 +1280,7 @@ error = FT_Glyph_To_Bitmap(&glyphs[n], ft_render_mode_normal, 0, - //&pos[n], - 1 //destroy image; + 1 ); if (error) throw Py::RuntimeError("Could not convert glyph to bitmap"); @@ -1126,7 +1292,7 @@ FT_Int x = (FT_Int)(bitmap->left - (string_bbox.xMin / 64.)); FT_Int y = (FT_Int)((string_bbox.yMax / 64.) - bitmap->top + 1); - draw_bitmap( &bitmap->bitmap, x, y); + image->draw_bitmap( &bitmap->bitmap, x, y); } return Py::Object(); @@ -1156,8 +1322,7 @@ error = FT_Glyph_To_Bitmap(&glyphs[n], ft_render_mode_normal, 0, - //&pos[n], - 1 //destroy image; + 1 ); if (error) throw Py::RuntimeError("Could not convert glyph to bitmap"); @@ -1181,7 +1346,7 @@ } char FT2Font::draw_glyph_to_bitmap__doc__[] = -"draw_glyph_to_bitmap(x, y, glyph)\n" +"draw_glyph_to_bitmap(bitmap, x, y, glyph)\n" "\n" "Draw a single glyph to the bitmap at pixel locations x,y\n" "Note it is your responsibility to set up the bitmap manually\n" @@ -1195,16 +1360,17 @@ Py::Object FT2Font::draw_glyph_to_bitmap(const Py::Tuple & args) { _VERBOSE("FT2Font::draw_glyph_to_bitmap"); - args.verify_length(3); + args.verify_length(4); - if (image.width==0 || image.height==0) - throw Py::RuntimeError("You must first set the size of the bitmap with set_bitmap_size"); + if (!FT2Image::check(args[0].ptr())) + throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)"); + FT2Image* im = static_cast<FT2Image*>(args[0].ptr()); - long x = Py::Int(args[0]); - long y = Py::Int(args[1]); - if (!Glyph::check(args[2].ptr())) - throw Py::TypeError("Usage: draw_glyph_to_bitmap(x,y,glyph)"); - Glyph* glyph = static_cast<Glyph*>(args[2].ptr()); + long x = Py::Int(args[1]); + long y = Py::Int(args[2]); + if (!Glyph::check(args[3].ptr())) + throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)"); + Glyph* glyph = static_cast<Glyph*>(args[3].ptr()); if ((size_t)glyph->glyphInd >= glyphs.size()) throw Py::ValueError("glyph num is out of range"); @@ -1219,7 +1385,7 @@ FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyph->glyphInd]; - draw_bitmap( &bitmap->bitmap, x + bitmap->left, y); + im->draw_bitmap( &bitmap->bitmap, x + bitmap->left, y); return Py::Object(); } @@ -1611,7 +1777,28 @@ } } +char FT2Font::get_image__doc__ [] = + "get_image()\n" + "\n" + "Returns the underlying image buffer for this font object.\n"; Py::Object +FT2Font::get_image (const Py::Tuple &args) { + args.verify_length(0); + Py_INCREF(image); + return Py::asObject(image); +} + +Py::Object +ft2font_module::new_ft2image (const Py::Tuple &args) { + args.verify_length(2); + + int width = Py::Int(args[0]); + int height = Py::Int(args[1]); + + return Py::asObject( new FT2Image(width, height) ); +} + +Py::Object ft2font_module::new_ft2font (const Py::Tuple &args) { _VERBOSE("ft2font_module::new_ft2font "); args.verify_length(1); @@ -1621,6 +1808,36 @@ } void +FT2Image::init_type() { + _VERBOSE("FT2Image::init_type"); + behaviors().name("FT2Image"); + behaviors().doc("FT2Image"); + + add_varargs_method("clear", &FT2Image::py_clear, + FT2Image::clear__doc__); + add_varargs_method("resize", &FT2Image::py_resize, + FT2Image::resize__doc__); + add_varargs_method("rotate", &FT2Image::py_rotate, + FT2Image::rotate__doc__); + add_varargs_method("write_bitmap", &FT2Image::py_write_bitmap, + FT2Image::write_bitmap__doc__); + add_varargs_method("draw_rect", &FT2Image::py_draw_rect, + FT2Image::draw_rect__doc__); + add_varargs_method("draw_rect_filled", &FT2Image::py_draw_rect_filled, + FT2Image::draw_rect_filled__doc__); + add_varargs_method("as_str", &FT2Image::py_as_str, + FT2Image::as_str__doc__); + add_varargs_method("as_rgb_str", &FT2Image::py_as_rgb_str, + FT2Image::as_rgb_str__doc__); + add_varargs_method("as_rgba_str", &FT2Image::py_as_rgba_str, + FT2Image::as_rgba_str__doc__); + add_varargs_method("get_width", &FT2Image::py_get_width, + "Returns the width of the image"); + add_varargs_method("get_height", &FT2Image::py_get_height, + "Returns the height of the image"); +} + +void Glyph::init_type() { _VERBOSE("Glyph::init_type"); behaviors().name("Glyph"); @@ -1637,14 +1854,6 @@ add_varargs_method("clear", &FT2Font::clear, FT2Font::clear__doc__); - add_varargs_method("write_bitmap", &FT2Font::write_bitmap, - FT2Font::write_bitmap__doc__); - add_varargs_method("set_bitmap_size", &FT2Font::set_bitmap_size, - FT2Font::load_char__doc__); - add_varargs_method("draw_rect",&FT2Font::draw_rect, - FT2Font::draw_rect__doc__); - add_varargs_method("draw_rect_filled",&FT2Font::draw_rect_filled, - FT2Font::draw_rect_filled__doc__); add_varargs_method("draw_glyph_to_bitmap", &FT2Font::draw_glyph_to_bitmap, FT2Font::draw_glyph_to_bitmap__doc__); add_varargs_method("draw_glyphs_to_bitmap", &FT2Font::draw_glyphs_to_bitmap, @@ -1656,8 +1865,6 @@ FT2Font::get_glyph__doc__); add_varargs_method("get_num_glyphs", &FT2Font::get_num_glyphs, FT2Font::get_num_glyphs__doc__); - add_varargs_method("image_as_str", &FT2Font::image_as_str, - FT2Font::image_as_str__doc__); add_keyword_method("load_char", &FT2Font::load_char, FT2Font::load_char__doc__); add_keyword_method("set_text", &FT2Font::set_text, @@ -1685,9 +1892,8 @@ FT2Font::get_ps_font_info__doc__); add_varargs_method("get_sfnt_table", &FT2Font::get_sfnt_table, FT2Font::get_sfnt_table__doc__); - add_varargs_method("horiz_image_to_vert_image", - &FT2Font::horiz_image_to_vert_image, - FT2Font::horiz_image_to_vert_image__doc__); + add_varargs_method("get_image", &FT2Font::get_image, + FT2Font::get_image__doc__); behaviors().supportGetattr(); behaviors().supportSetattr(); Modified: trunk/matplotlib/src/ft2font.h =================================================================== --- trunk/matplotlib/src/ft2font.h 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/src/ft2font.h 2007-08-31 19:25:17 UTC (rev 3764) @@ -20,15 +20,64 @@ // the freetype string rendered into a width, height buffer -class FT2Image { +class FT2Image : public Py::PythonExtension<FT2Image> { public: FT2Image(); + FT2Image(unsigned long width, unsigned long height); ~FT2Image(); - bool bRotated; - unsigned char *buffer; - unsigned long width; - unsigned long height; - int offsetx, offsety; + + static void init_type(); + + void resize(unsigned long width, unsigned long height); + void clear(); + void rotate(); + void draw_bitmap(FT_Bitmap* bitmap, FT_Int x, FT_Int y); + void write_bitmap(const char* filename) const; + void draw_rect(unsigned long x0, unsigned long y0, + unsigned long x1, unsigned long y1); + void draw_rect_filled(unsigned long x0, unsigned long y0, + unsigned long x1, unsigned long y1); + + unsigned int get_width() const { return _width; }; + unsigned int get_height() const { return _height; }; + const unsigned char *const get_buffer() const { return _buffer; }; + + static char clear__doc__ []; + Py::Object py_clear(const Py::Tuple & args); + static char resize__doc__ []; + Py::Object py_resize(const Py::Tuple & args); + static char rotate__doc__ []; + Py::Object py_rotate(const Py::Tuple & args); + static char write_bitmap__doc__ []; + Py::Object py_write_bitmap(const Py::Tuple & args); + static char draw_rect__doc__ []; + Py::Object py_draw_rect(const Py::Tuple & args); + static char draw_rect_filled__doc__ []; + Py::Object py_draw_rect_filled(const Py::Tuple & args); + static char as_str__doc__ []; + Py::Object py_as_str(const Py::Tuple & args); + static char as_rgb_str__doc__ []; + Py::Object py_as_rgb_str(const Py::Tuple & args); + static char as_rgba_str__doc__ []; + Py::Object py_as_rgba_str(const Py::Tuple & args); + + Py::Object py_get_width(const Py::Tuple & args); + Py::Object py_get_height(const Py::Tuple & args); + + unsigned long offsetx; + unsigned long offsety; + + private: + bool _bRotated; + bool _isDirty; + unsigned char *_buffer; + unsigned long _width; + unsigned long _height; + FT2Image* _rgbCopy; + FT2Image* _rgbaCopy; + + void makeRgbCopy(); + void makeRgbaCopy(); }; @@ -52,7 +101,6 @@ FT2Font(std::string); ~FT2Font(); static void init_type(void); - Py::Object set_bitmap_size(const Py::Tuple & args); Py::Object clear(const Py::Tuple & args); Py::Object set_size(const Py::Tuple & args); Py::Object set_charmap(const Py::Tuple & args); @@ -63,10 +111,7 @@ Py::Object load_char(const Py::Tuple & args, const Py::Dict & kws); Py::Object get_width_height(const Py::Tuple & args); Py::Object get_descent(const Py::Tuple & args); - Py::Object write_bitmap(const Py::Tuple & args); - Py::Object draw_rect(const Py::Tuple & args); Py::Object draw_rect_filled(const Py::Tuple & args); - Py::Object image_as_str(const Py::Tuple & args); Py::Object get_xys(const Py::Tuple & args); Py::Object draw_glyphs_to_bitmap(const Py::Tuple & args); Py::Object draw_glyph_to_bitmap(const Py::Tuple & args); @@ -76,10 +121,10 @@ Py::Object get_name_index(const Py::Tuple & args); Py::Object get_ps_font_info(const Py::Tuple & args); Py::Object get_sfnt_table(const Py::Tuple & args); - Py::Object horiz_image_to_vert_image(const Py::Tuple & args); + Py::Object get_image(const Py::Tuple & args); int setattr( const char *_name, const Py::Object &value ); Py::Object getattr( const char *_name ); - FT2Image image; + FT2Image* image; private: Py::Dict __dict__; @@ -96,10 +141,8 @@ FT_BBox compute_string_bbox(); - void draw_bitmap( FT_Bitmap* bitmap, FT_Int x, FT_Int y); void set_scalable_attributes(); - static char set_bitmap_size__doc__ []; static char clear__doc__ []; static char set_size__doc__ []; static char set_charmap__doc__ []; @@ -110,10 +153,6 @@ static char get_width_height__doc__ []; static char get_descent__doc__ []; static char get_kerning__doc__ []; - static char write_bitmap__doc__ []; - static char draw_rect__doc__ []; - static char draw_rect_filled__doc__ []; - static char image_as_str__doc__ []; static char draw_glyphs_to_bitmap__doc__ []; static char get_xys__doc__ []; static char draw_glyph_to_bitmap__doc__ []; @@ -123,7 +162,7 @@ static char get_name_index__doc__[]; static char get_ps_font_info__doc__[]; static char get_sfnt_table__doc__[]; - static char horiz_image_to_vert_image__doc__[]; + static char get_image__doc__[]; }; // the extension module @@ -134,11 +173,14 @@ ft2font_module() : Py::ExtensionModule<ft2font_module>( "ft2font" ) { + FT2Image::init_type(); Glyph::init_type(); FT2Font::init_type(); add_varargs_method("FT2Font", &ft2font_module::new_ft2font, "FT2Font"); + add_varargs_method("FT2Image", &ft2font_module::new_ft2image, + "FT2Image"); initialize( "The ft2font module" ); } @@ -148,6 +190,7 @@ private: Py::Object new_ft2font (const Py::Tuple &args); + Py::Object new_ft2image (const Py::Tuple &args); }; Modified: trunk/matplotlib/unit/agg_memleak.py =================================================================== --- trunk/matplotlib/unit/agg_memleak.py 2007-08-31 17:23:32 UTC (rev 3763) +++ trunk/matplotlib/unit/agg_memleak.py 2007-08-31 19:25:17 UTC (rev 3764) @@ -41,7 +41,7 @@ font.clear() font.set_text('hi mom', 60) font.set_size(12, 72) - o.draw_text( font, 30, 40, gc) + o.draw_text_image(font.get_image(), 30, 40, gc) o.write_png('aggtest%d.png'%i) val = report_memory(i) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |