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