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_stroke.h> 13 #include <agg_conv_transform.h> 14 #include <agg_path_storage.h> 15 #include <agg_scanline_boolean_algebra.h> 16 #include <agg_trans_affine.h> 17 18 #include <math.h> 19 #include <malloc.h> 20 #include <stdio.h> 21 #include <string.h> 22 23 #define SHOW_GLYPH_BOUNDS 0 24 25 #include "GlobalSubpixelSettings.h" 26 #include "GlyphLayoutEngine.h" 27 #include "IntRect.h" 28 29 30 AGGTextRenderer::AGGTextRenderer(renderer_subpix_type& subpixRenderer, 31 renderer_type& solidRenderer, renderer_bin_type& binRenderer, 32 scanline_unpacked_type& scanline, 33 scanline_unpacked_subpix_type& subpixScanline, 34 rasterizer_subpix_type& subpixRasterizer, 35 scanline_unpacked_masked_type*& maskedScanline, 36 agg::trans_affine& viewTransformation) 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 fMaskedScanline(maskedScanline), 55 56 fRasterizer(), 57 58 fHinted(true), 59 fAntialias(true), 60 fEmbeddedTransformation(), 61 fViewTransformation(viewTransformation) 62 { 63 fCurves.approximation_scale(2.0); 64 fContour.auto_detect_orientation(false); 65 } 66 67 68 AGGTextRenderer::~AGGTextRenderer() 69 { 70 } 71 72 73 void 74 AGGTextRenderer::SetFont(const ServerFont& font) 75 { 76 fFont = font; 77 78 // construct an embedded transformation (rotate & shear) 79 fEmbeddedTransformation.Reset(); 80 fEmbeddedTransformation.ShearBy(B_ORIGIN, 81 (90.0 - font.Shear()) * M_PI / 180.0, 0.0); 82 fEmbeddedTransformation.RotateBy(B_ORIGIN, 83 -font.Rotation() * M_PI / 180.0); 84 85 fContour.width(font.FalseBoldWidth() * 2.0); 86 } 87 88 89 void 90 AGGTextRenderer::SetHinting(bool hinting) 91 { 92 fHinted = hinting; 93 } 94 95 96 void 97 AGGTextRenderer::SetAntialiasing(bool antialiasing) 98 { 99 if (fAntialias != antialiasing) { 100 fAntialias = antialiasing; 101 // NOTE: The fSubpixRasterizer is not used when anti-aliasing is 102 // disbaled. 103 if (!fAntialias) 104 fRasterizer.gamma(agg::gamma_threshold(0.5)); 105 else 106 fRasterizer.gamma(agg::gamma_power(1.0)); 107 } 108 } 109 110 111 typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable> 112 conv_font_trans_type; 113 114 typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable> 115 conv_font_contour_trans_type; 116 117 118 119 class AGGTextRenderer::StringRenderer { 120 public: 121 StringRenderer(const IntRect& clippingFrame, bool dryRun, 122 bool subpixelAntiAliased, bool underscore, 123 FontCacheEntry::TransformedOutline& transformedGlyph, 124 FontCacheEntry::TransformedContourOutline& transformedContour, 125 const Transformable& transform, 126 const BPoint& transformOffset, 127 BPoint* nextCharPos, 128 AGGTextRenderer& renderer) 129 : 130 fTransform(transform), 131 fTransformOffset(transformOffset), 132 fClippingFrame(clippingFrame), 133 fDryRun(dryRun), 134 fSubpixelAntiAliased(subpixelAntiAliased), 135 fVector(false), 136 fUnderscore(underscore), 137 fBounds(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN), 138 fNextCharPos(nextCharPos), 139 140 fTransformedGlyph(transformedGlyph), 141 fTransformedContour(transformedContour), 142 143 fRenderer(renderer) 144 { 145 } 146 147 bool NeedsVector() 148 { 149 return !fTransform.IsTranslationOnly(); 150 } 151 152 void Start() 153 { 154 fRenderer.fRasterizer.reset(); 155 fRenderer.fSubpixRasterizer.reset(); 156 } 157 158 void Finish(double x, double y) 159 { 160 if (fVector) { 161 if (fRenderer.fMaskedScanline != NULL) { 162 agg::render_scanlines(fRenderer.fRasterizer, 163 *fRenderer.fMaskedScanline, fRenderer.fSolidRenderer); 164 } else if (fSubpixelAntiAliased) { 165 agg::render_scanlines(fRenderer.fSubpixRasterizer, 166 fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer); 167 } else { 168 agg::render_scanlines(fRenderer.fRasterizer, 169 fRenderer.fScanline, fRenderer.fSolidRenderer); 170 } 171 } 172 173 if (fUnderscore && !fDryRun) { 174 agg::path_storage p; 175 IntRect b = fBounds; 176 b.bottom = (int)y; 177 b.OffsetBy(fTransformOffset); 178 p.move_to(b.left + 0.5, b.bottom + 2.5); 179 p.line_to(b.right + 0.5, b.bottom + 2.5); 180 p.close_polygon(); 181 agg::conv_stroke<agg::path_storage> ps(p); 182 ps.width(fRenderer.fFont.Size() / 12.0f); 183 if (fRenderer.fMaskedScanline != NULL) { 184 fRenderer.fRasterizer.add_path(ps); 185 agg::render_scanlines(fRenderer.fRasterizer, 186 *fRenderer.fMaskedScanline, fRenderer.fSolidRenderer); 187 } else if (fSubpixelAntiAliased) { 188 fRenderer.fSubpixRasterizer.add_path(ps); 189 agg::render_scanlines(fRenderer.fSubpixRasterizer, 190 fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer); 191 } else { 192 /* 193 scanline_unpacked_type sl1; 194 scanline_unpacked_type sl2; 195 196 rasterizer_type ras1; 197 rasterizer_type ras2; 198 199 ras1.add_path(ps); 200 ras2.add_path(fTransformedContour); 201 202 agg::render_scanlines(ras1, 203 sl1, fRenderer.fSolidRenderer); 204 agg::render_scanlines(ras2, 205 sl2, fRenderer.fSolidRenderer); 206 207 agg::sbool_combine_shapes_aa(agg::sbool_a_minus_b, 208 ras1, ras2, sl1, sl2, fRenderer.fScanline, fRenderer.fSolidRenderer); 209 */ 210 fRenderer.fRasterizer.add_path(ps); 211 agg::render_scanlines(fRenderer.fRasterizer, 212 fRenderer.fScanline, fRenderer.fSolidRenderer); 213 } 214 } 215 216 if (fNextCharPos) { 217 fNextCharPos->x = x; 218 fNextCharPos->y = y; 219 fTransform.Transform(fNextCharPos); 220 } 221 } 222 223 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 224 { 225 } 226 227 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 228 FontCacheEntry* entry, double x, double y, double advanceX, 229 double advanceY) 230 { 231 // "glyphBounds" is the bounds of the glyph transformed 232 // by the x y location of the glyph along the base line, 233 // it is therefor yet "untransformed" in case there is an 234 // embedded transformation. 235 const agg::rect_i& r = glyph->bounds; 236 IntRect glyphBounds(int32(r.x1 + x), int32(r.y1 + y - 1), 237 int32(r.x2 + x + 1), int32(r.y2 + y + 1)); 238 // NOTE: "-1"/"+1" converts the glyph bounding box from pixel 239 // indices to pixel area coordinates 240 241 // track bounding box 242 if (glyphBounds.IsValid()) 243 fBounds = fBounds | glyphBounds; 244 245 // render the glyph if this is not a dry run 246 if (!fDryRun) { 247 // init the fontmanager's embedded adaptors 248 // NOTE: The initialization for the "location" of 249 // the glyph is different depending on whether we 250 // deal with non-(rotated/sheared) text, in which 251 // case we have a native FT bitmap. For rotated or 252 // sheared text, we use AGG vector outlines and 253 // a transformation pipeline, which will be applied 254 // _after_ we retrieve the outline, and that's why 255 // we simply pass x and y, which are untransformed. 256 257 // "glyphBounds" is now transformed into screen coords 258 // in order to stop drawing when we are already outside 259 // of the clipping frame 260 if (glyph->data_type != glyph_data_outline) { 261 // we cannot use the transformation pipeline 262 double transformedX = x + fTransformOffset.x; 263 double transformedY = y + fTransformOffset.y; 264 entry->InitAdaptors(glyph, transformedX, transformedY, 265 fRenderer.fMonoAdaptor, 266 fRenderer.fGray8Adaptor, 267 fRenderer.fPathAdaptor); 268 269 glyphBounds.OffsetBy(fTransformOffset); 270 } else { 271 entry->InitAdaptors(glyph, x, y, 272 fRenderer.fMonoAdaptor, 273 fRenderer.fGray8Adaptor, 274 fRenderer.fPathAdaptor); 275 276 int32 falseBoldWidth = (int32)fRenderer.fContour.width(); 277 if (falseBoldWidth != 0) 278 glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); 279 // TODO: not correct! this is later used for clipping, 280 // but it doesn't get the rect right 281 glyphBounds = fTransform.TransformBounds(glyphBounds); 282 } 283 284 if (fClippingFrame.Intersects(glyphBounds)) { 285 switch (glyph->data_type) { 286 case glyph_data_mono: 287 agg::render_scanlines(fRenderer.fMonoAdaptor, 288 fRenderer.fMonoScanline, fRenderer.fBinRenderer); 289 break; 290 291 case glyph_data_gray8: 292 if (fRenderer.fMaskedScanline != NULL) { 293 agg::render_scanlines(fRenderer.fGray8Adaptor, 294 *fRenderer.fMaskedScanline, 295 fRenderer.fSolidRenderer); 296 } else { 297 agg::render_scanlines(fRenderer.fGray8Adaptor, 298 fRenderer.fGray8Scanline, 299 fRenderer.fSolidRenderer); 300 } 301 break; 302 303 case glyph_data_subpix: 304 // TODO: Handle alpha mask (fRenderer.fMaskedScanline) 305 agg::render_scanlines(fRenderer.fGray8Adaptor, 306 fRenderer.fGray8Scanline, 307 fRenderer.fSubpixRenderer); 308 break; 309 310 case glyph_data_outline: { 311 fVector = true; 312 if (fSubpixelAntiAliased) { 313 if (fRenderer.fContour.width() == 0.0) { 314 fRenderer.fSubpixRasterizer.add_path( 315 fTransformedGlyph); 316 } else { 317 fRenderer.fSubpixRasterizer.add_path( 318 fTransformedContour); 319 } 320 } else { 321 if (fRenderer.fContour.width() == 0.0) { 322 fRenderer.fRasterizer.add_path( 323 fTransformedGlyph); 324 } else { 325 fRenderer.fRasterizer.add_path( 326 fTransformedContour); 327 } 328 } 329 #if SHOW_GLYPH_BOUNDS 330 agg::path_storage p; 331 p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); 332 p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); 333 p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); 334 p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); 335 p.close_polygon(); 336 agg::conv_stroke<agg::path_storage> ps(p); 337 ps.width(1.0); 338 if (fSubpixelAntiAliased) { 339 fRenderer.fSubpixRasterizer.add_path(ps); 340 } else { 341 fRenderer.fRasterizer.add_path(ps); 342 } 343 #endif 344 345 break; 346 } 347 default: 348 break; 349 } 350 } 351 } 352 return true; 353 } 354 355 IntRect Bounds() const 356 { 357 return fBounds; 358 } 359 360 private: 361 const Transformable& fTransform; 362 const BPoint& fTransformOffset; 363 const IntRect& fClippingFrame; 364 bool fDryRun; 365 bool fSubpixelAntiAliased; 366 bool fVector; 367 bool fUnderscore; 368 IntRect fBounds; 369 BPoint* fNextCharPos; 370 371 FontCacheEntry::TransformedOutline& fTransformedGlyph; 372 FontCacheEntry::TransformedContourOutline& fTransformedContour; 373 AGGTextRenderer& fRenderer; 374 }; 375 376 377 BRect 378 AGGTextRenderer::RenderString(const char* string, uint32 length, 379 const BPoint& baseLine, const BRect& clippingFrame, bool dryRun, 380 BPoint* nextCharPos, const escapement_delta* delta, 381 FontCacheReference* cacheReference) 382 { 383 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); 384 385 Transformable transform(fEmbeddedTransformation); 386 transform.TranslateBy(baseLine); 387 transform *= fViewTransformation; 388 389 fCurves.approximation_scale(transform.scale()); 390 391 // use a transformation behind the curves 392 // (only if glyph->data_type == agg::glyph_data_outline) 393 // in the pipeline for the rasterizer 394 FontCacheEntry::TransformedOutline 395 transformedOutline(fCurves, transform); 396 FontCacheEntry::TransformedContourOutline 397 transformedContourOutline(fContour, transform); 398 399 // for when we bypass the transformation pipeline 400 BPoint transformOffset(0.0, 0.0); 401 transform.Transform(&transformOffset); 402 IntRect clippingIntFrame(clippingFrame); 403 404 bool underscore = fFont.Face() & B_UNDERSCORE_FACE; 405 406 StringRenderer renderer(clippingIntFrame, dryRun, 407 gSubpixelAntialiasing && fAntialias, underscore, 408 transformedOutline, transformedContourOutline, 409 transform, transformOffset, nextCharPos, *this); 410 411 GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, INT32_MAX, 412 delta, fFont.Spacing(), NULL, cacheReference); 413 414 return transform.TransformBounds(renderer.Bounds()); 415 } 416 417 418 BRect 419 AGGTextRenderer::RenderString(const char* string, uint32 length, 420 const BPoint* offsets, const BRect& clippingFrame, bool dryRun, 421 BPoint* nextCharPos, FontCacheReference* cacheReference) 422 { 423 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); 424 425 Transformable transform(fEmbeddedTransformation); 426 transform *= fViewTransformation; 427 428 fCurves.approximation_scale(transform.scale()); 429 430 // use a transformation behind the curves 431 // (only if glyph->data_type == agg::glyph_data_outline) 432 // in the pipeline for the rasterizer 433 FontCacheEntry::TransformedOutline 434 transformedOutline(fCurves, transform); 435 FontCacheEntry::TransformedContourOutline 436 transformedContourOutline(fContour, transform); 437 438 // for when we bypass the transformation pipeline 439 BPoint transformOffset(0.0, 0.0); 440 transform.Transform(&transformOffset); 441 IntRect clippingIntFrame(clippingFrame); 442 443 bool underscore = fFont.Face() & B_UNDERSCORE_FACE; 444 445 StringRenderer renderer(clippingIntFrame, dryRun, 446 gSubpixelAntialiasing && fAntialias, underscore, 447 transformedOutline, transformedContourOutline, 448 transform, transformOffset, nextCharPos, *this); 449 450 GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, INT32_MAX, 451 NULL, fFont.Spacing(), offsets, cacheReference); 452 453 return transform.TransformBounds(renderer.Bounds()); 454 } 455