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