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