1 /* 2 * Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "AGGTextRenderer.h" 8 9 #include <agg_basics.h> 10 #include <agg_bounding_rect.h> 11 #include <agg_conv_segmentator.h> 12 #include <agg_conv_transform.h> 13 #include <agg_trans_affine.h> 14 15 #include <math.h> 16 #include <malloc.h> 17 #include <stdio.h> 18 #include <string.h> 19 20 #define SHOW_GLYPH_BOUNDS 0 21 22 #if SHOW_GLYPH_BOUNDS 23 # include <agg_conv_stroke.h> 24 # include <agg_path_storage.h> 25 #endif 26 27 #include "GlyphLayoutEngine.h" 28 #include "IntRect.h" 29 30 31 // constructor 32 AGGTextRenderer::AGGTextRenderer(renderer_type& solidRenderer, 33 renderer_bin_type& binRenderer, scanline_unpacked_type& scanline) 34 : fPathAdaptor() 35 , fGray8Adaptor() 36 , fGray8Scanline() 37 , fMonoAdaptor() 38 , fMonoScanline() 39 40 , fCurves(fPathAdaptor) 41 , fContour(fCurves) 42 43 , fSolidRenderer(solidRenderer) 44 , fBinRenderer(binRenderer) 45 , fScanline(scanline) 46 , fRasterizer() 47 48 , fHinted(true) 49 , fAntialias(true) 50 , fKerning(true) 51 , fEmbeddedTransformation() 52 { 53 fCurves.approximation_scale(2.0); 54 fContour.auto_detect_orientation(false); 55 } 56 57 // destructor 58 AGGTextRenderer::~AGGTextRenderer() 59 { 60 } 61 62 // SetFont 63 void 64 AGGTextRenderer::SetFont(const ServerFont &font) 65 { 66 fFont = font; 67 68 // construct an embedded transformation (rotate & shear) 69 fEmbeddedTransformation.Reset(); 70 fEmbeddedTransformation.ShearBy(B_ORIGIN, 71 (90.0 - font.Shear()) * PI / 180.0, 0.0); 72 fEmbeddedTransformation.RotateBy(B_ORIGIN, 73 -font.Rotation() * PI / 180.0); 74 75 fContour.width(font.FalseBoldWidth() * 2.0); 76 } 77 78 // SetHinting 79 void 80 AGGTextRenderer::SetHinting(bool hinting) 81 { 82 fHinted = hinting; 83 // fFontEngine.hinting(fEmbeddedTransformation.IsIdentity() && fHinted); 84 } 85 86 // SetAntialiasing 87 void 88 AGGTextRenderer::SetAntialiasing(bool antialiasing) 89 { 90 if (fAntialias != antialiasing) { 91 fAntialias = antialiasing; 92 if (!fAntialias) 93 fRasterizer.gamma(agg::gamma_threshold(0.5)); 94 else 95 fRasterizer.gamma(agg::gamma_power(1.0)); 96 } 97 } 98 99 typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable> 100 conv_font_trans_type; 101 102 typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable> 103 conv_font_contour_trans_type; 104 105 106 107 class AGGTextRenderer::StringRenderer { 108 public: 109 StringRenderer(const IntRect& clippingFrame, bool dryRun, 110 FontCacheEntry::TransformedOutline& transformedGlyph, 111 FontCacheEntry::TransformedContourOutline& transformedContour, 112 const Transformable& transform, 113 const BPoint& transformOffset, 114 BPoint* nextCharPos, 115 AGGTextRenderer& renderer) 116 117 : fTransform(transform) 118 , fTransformOffset(transformOffset) 119 , fClippingFrame(clippingFrame) 120 , fDryRun(dryRun) 121 , fBounds(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN) 122 , fNextCharPos(nextCharPos) 123 , fVector(false) 124 125 , fTransformedGlyph(transformedGlyph) 126 , fTransformedContour(transformedContour) 127 128 , fRenderer(renderer) 129 { 130 } 131 132 void Start() 133 { 134 fRenderer.fRasterizer.reset(); 135 } 136 void Finish(double x, double y) 137 { 138 if (fVector) { 139 agg::render_scanlines(fRenderer.fRasterizer, fRenderer.fScanline, 140 fRenderer.fSolidRenderer); 141 } 142 143 if (fNextCharPos) { 144 fNextCharPos->x = x; 145 fNextCharPos->y = y; 146 fTransform.Transform(fNextCharPos); 147 } 148 } 149 150 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 151 { 152 } 153 154 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 155 FontCacheEntry* entry, double x, double y) 156 { 157 // "glyphBounds" is the bounds of the glyph transformed 158 // by the x y location of the glyph along the base line, 159 // it is therefor yet "untransformed" in case there is an 160 // embedded transformation. 161 const agg::rect_i& r = glyph->bounds; 162 IntRect glyphBounds(r.x1 + x, r.y1 + y - 1, 163 r.x2 + x + 1, r.y2 + y + 1); 164 // NOTE: "-1"/"+1" converts the glyph bounding box from pixel 165 // indices to pixel area coordinates 166 167 // track bounding box 168 if (glyphBounds.IsValid()) 169 fBounds = fBounds | glyphBounds; 170 171 // render the glyph if this is not a dry run 172 if (!fDryRun) { 173 // init the fontmanager's embedded adaptors 174 // NOTE: The initialization for the "location" of 175 // the glyph is different depending on wether we 176 // deal with non-(rotated/sheared) text, in which 177 // case we have a native FT bitmap. For rotated or 178 // sheared text, we use AGG vector outlines and 179 // a transformation pipeline, which will be applied 180 // _after_ we retrieve the outline, and that's why 181 // we simply pass x and y, which are untransformed. 182 183 // "glyphBounds" is now transformed into screen coords 184 // in order to stop drawing when we are already outside 185 // of the clipping frame 186 if (glyph->data_type != glyph_data_outline) { 187 // we cannot use the transformation pipeline 188 double transformedX = x + fTransformOffset.x; 189 double transformedY = y + fTransformOffset.y; 190 entry->InitAdaptors(glyph, transformedX, transformedY, 191 fRenderer.fMonoAdaptor, 192 fRenderer.fGray8Adaptor, 193 fRenderer.fPathAdaptor); 194 195 glyphBounds.OffsetBy(fTransformOffset); 196 } else { 197 entry->InitAdaptors(glyph, x, y, 198 fRenderer.fMonoAdaptor, 199 fRenderer.fGray8Adaptor, 200 fRenderer.fPathAdaptor); 201 202 float falseBoldWidth = fRenderer.fContour.width(); 203 if (falseBoldWidth != 0.0) 204 glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); 205 // TODO: not correct! this is later used for clipping, 206 // but it doesn't get the rect right 207 glyphBounds = fTransform.TransformBounds(glyphBounds); 208 } 209 210 if (fClippingFrame.Intersects(glyphBounds)) { 211 switch (glyph->data_type) { 212 case glyph_data_mono: 213 agg::render_scanlines(fRenderer.fMonoAdaptor, 214 fRenderer.fMonoScanline, fRenderer.fBinRenderer); 215 break; 216 217 case glyph_data_gray8: 218 agg::render_scanlines(fRenderer.fGray8Adaptor, 219 fRenderer.fGray8Scanline, fRenderer.fSolidRenderer); 220 break; 221 222 case glyph_data_outline: { 223 fVector = true; 224 if (fRenderer.fContour.width() == 0.0) { 225 fRenderer.fRasterizer.add_path(fTransformedGlyph); 226 } else { 227 fRenderer.fRasterizer.add_path(fTransformedContour); 228 } 229 #if SHOW_GLYPH_BOUNDS 230 agg::path_storage p; 231 p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); 232 p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); 233 p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); 234 p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); 235 p.close_polygon(); 236 agg::conv_stroke<agg::path_storage> ps(p); 237 ps.width(1.0); 238 fRenderer.fRasterizer.add_path(ps); 239 #endif 240 241 break; 242 } 243 default: 244 break; 245 } 246 } 247 } 248 return true; 249 } 250 251 IntRect Bounds() const 252 { 253 return fBounds; 254 } 255 256 private: 257 const Transformable& fTransform; 258 const BPoint& fTransformOffset; 259 const IntRect& fClippingFrame; 260 bool fDryRun; 261 IntRect fBounds; 262 BPoint* fNextCharPos; 263 bool fVector; 264 265 FontCacheEntry::TransformedOutline& fTransformedGlyph; 266 FontCacheEntry::TransformedContourOutline& fTransformedContour; 267 AGGTextRenderer& fRenderer; 268 }; 269 270 // RenderString 271 BRect 272 AGGTextRenderer::RenderString(const char* string, 273 uint32 length, 274 const BPoint& baseLine, 275 const BRect& clippingFrame, 276 bool dryRun, 277 BPoint* nextCharPos, 278 const escapement_delta* delta, 279 FontCacheReference* cacheReference) 280 { 281 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); 282 283 Transformable transform(fEmbeddedTransformation); 284 transform.TranslateBy(baseLine); 285 286 fCurves.approximation_scale(transform.scale()); 287 288 // use a transformation behind the curves 289 // (only if glyph->data_type == agg::glyph_data_outline) 290 // in the pipeline for the rasterizer 291 FontCacheEntry::TransformedOutline 292 transformedOutline(fCurves, transform); 293 FontCacheEntry::TransformedContourOutline 294 transformedContourOutline(fContour, transform); 295 296 // for when we bypass the transformation pipeline 297 BPoint transformOffset(0.0, 0.0); 298 transform.Transform(&transformOffset); 299 300 StringRenderer renderer(clippingFrame, dryRun, 301 transformedOutline, transformedContourOutline, 302 transform, transformOffset, nextCharPos, *this); 303 304 GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta, 305 fKerning, B_BITMAP_SPACING, cacheReference); 306 307 return transform.TransformBounds(renderer.Bounds()); 308 } 309