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(INT32_MAX, INT32_MAX, INT32_MIN, INT32_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 150 void Finish(double x, double y) 151 { 152 if (fVector) { 153 if (fSubpixelAntiAliased) { 154 agg::render_scanlines(fRenderer.fSubpixRasterizer, 155 fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer); 156 } else { 157 agg::render_scanlines(fRenderer.fRasterizer, 158 fRenderer.fScanline, fRenderer.fSolidRenderer); 159 } 160 } 161 162 if (fNextCharPos) { 163 fNextCharPos->x = x; 164 fNextCharPos->y = y; 165 fTransform.Transform(fNextCharPos); 166 } 167 } 168 169 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 170 { 171 } 172 173 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 174 FontCacheEntry* entry, double x, double y) 175 { 176 // "glyphBounds" is the bounds of the glyph transformed 177 // by the x y location of the glyph along the base line, 178 // it is therefor yet "untransformed" in case there is an 179 // embedded transformation. 180 const agg::rect_i& r = glyph->bounds; 181 IntRect glyphBounds(int32(r.x1 + x), int32(r.y1 + y - 1), 182 int32(r.x2 + x + 1), int32(r.y2 + y + 1)); 183 // NOTE: "-1"/"+1" converts the glyph bounding box from pixel 184 // indices to pixel area coordinates 185 186 // track bounding box 187 if (glyphBounds.IsValid()) 188 fBounds = fBounds | glyphBounds; 189 190 // render the glyph if this is not a dry run 191 if (!fDryRun) { 192 // init the fontmanager's embedded adaptors 193 // NOTE: The initialization for the "location" of 194 // the glyph is different depending on whether we 195 // deal with non-(rotated/sheared) text, in which 196 // case we have a native FT bitmap. For rotated or 197 // sheared text, we use AGG vector outlines and 198 // a transformation pipeline, which will be applied 199 // _after_ we retrieve the outline, and that's why 200 // we simply pass x and y, which are untransformed. 201 202 // "glyphBounds" is now transformed into screen coords 203 // in order to stop drawing when we are already outside 204 // of the clipping frame 205 if (glyph->data_type != glyph_data_outline) { 206 // we cannot use the transformation pipeline 207 double transformedX = x + fTransformOffset.x; 208 double transformedY = y + fTransformOffset.y; 209 entry->InitAdaptors(glyph, transformedX, transformedY, 210 fRenderer.fMonoAdaptor, 211 fRenderer.fGray8Adaptor, 212 fRenderer.fPathAdaptor); 213 214 glyphBounds.OffsetBy(fTransformOffset); 215 } else { 216 entry->InitAdaptors(glyph, x, y, 217 fRenderer.fMonoAdaptor, 218 fRenderer.fGray8Adaptor, 219 fRenderer.fPathAdaptor); 220 221 int32 falseBoldWidth = (int32)fRenderer.fContour.width(); 222 if (falseBoldWidth != 0) 223 glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); 224 // TODO: not correct! this is later used for clipping, 225 // but it doesn't get the rect right 226 glyphBounds = fTransform.TransformBounds(glyphBounds); 227 } 228 229 if (fClippingFrame.Intersects(glyphBounds)) { 230 switch (glyph->data_type) { 231 case glyph_data_mono: 232 agg::render_scanlines(fRenderer.fMonoAdaptor, 233 fRenderer.fMonoScanline, fRenderer.fBinRenderer); 234 break; 235 236 case glyph_data_gray8: 237 agg::render_scanlines(fRenderer.fGray8Adaptor, 238 fRenderer.fGray8Scanline, fRenderer.fSolidRenderer); 239 break; 240 241 case glyph_data_subpix: 242 agg::render_scanlines(fRenderer.fGray8Adaptor, 243 fRenderer.fGray8Scanline, 244 fRenderer.fSubpixRenderer); 245 break; 246 247 case glyph_data_outline: { 248 fVector = true; 249 if (fSubpixelAntiAliased) { 250 if (fRenderer.fContour.width() == 0.0) { 251 fRenderer.fSubpixRasterizer.add_path( 252 fTransformedGlyph); 253 } else { 254 fRenderer.fSubpixRasterizer.add_path( 255 fTransformedContour); 256 } 257 } else { 258 if (fRenderer.fContour.width() == 0.0) { 259 fRenderer.fRasterizer.add_path( 260 fTransformedGlyph); 261 } else { 262 fRenderer.fRasterizer.add_path( 263 fTransformedContour); 264 } 265 } 266 #if SHOW_GLYPH_BOUNDS 267 agg::path_storage p; 268 p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); 269 p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); 270 p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); 271 p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); 272 p.close_polygon(); 273 agg::conv_stroke<agg::path_storage> ps(p); 274 ps.width(1.0); 275 if (fSubpixelAntiAliased) { 276 fRenderer.fSubpixRasterizer.add_path(ps); 277 } else { 278 fRenderer.fRasterizer.add_path(ps); 279 } 280 #endif 281 282 break; 283 } 284 default: 285 break; 286 } 287 } 288 } 289 return true; 290 } 291 292 IntRect Bounds() const 293 { 294 return fBounds; 295 } 296 297 private: 298 const Transformable& fTransform; 299 const BPoint& fTransformOffset; 300 const IntRect& fClippingFrame; 301 bool fDryRun; 302 bool fSubpixelAntiAliased; 303 bool fVector; 304 IntRect fBounds; 305 BPoint* fNextCharPos; 306 307 FontCacheEntry::TransformedOutline& fTransformedGlyph; 308 FontCacheEntry::TransformedContourOutline& fTransformedContour; 309 AGGTextRenderer& fRenderer; 310 }; 311 312 313 BRect 314 AGGTextRenderer::RenderString(const char* string, uint32 length, 315 const BPoint& baseLine, const BRect& clippingFrame, bool dryRun, 316 BPoint* nextCharPos, const escapement_delta* delta, 317 FontCacheReference* cacheReference) 318 { 319 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); 320 321 Transformable transform(fEmbeddedTransformation); 322 transform.TranslateBy(baseLine); 323 324 fCurves.approximation_scale(transform.scale()); 325 326 // use a transformation behind the curves 327 // (only if glyph->data_type == agg::glyph_data_outline) 328 // in the pipeline for the rasterizer 329 FontCacheEntry::TransformedOutline 330 transformedOutline(fCurves, transform); 331 FontCacheEntry::TransformedContourOutline 332 transformedContourOutline(fContour, transform); 333 334 // for when we bypass the transformation pipeline 335 BPoint transformOffset(0.0, 0.0); 336 transform.Transform(&transformOffset); 337 338 StringRenderer renderer(clippingFrame, dryRun, 339 gSubpixelAntialiasing && fAntialias, 340 transformedOutline, transformedContourOutline, 341 transform, transformOffset, nextCharPos, *this); 342 343 GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta, 344 fKerning, B_BITMAP_SPACING, NULL, cacheReference); 345 346 return transform.TransformBounds(renderer.Bounds()); 347 } 348 349 350 BRect 351 AGGTextRenderer::RenderString(const char* string, uint32 length, 352 const BPoint* offsets, const BRect& clippingFrame, bool dryRun, 353 BPoint* nextCharPos, FontCacheReference* cacheReference) 354 { 355 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); 356 357 Transformable transform(fEmbeddedTransformation); 358 359 fCurves.approximation_scale(transform.scale()); 360 361 // use a transformation behind the curves 362 // (only if glyph->data_type == agg::glyph_data_outline) 363 // in the pipeline for the rasterizer 364 FontCacheEntry::TransformedOutline 365 transformedOutline(fCurves, transform); 366 FontCacheEntry::TransformedContourOutline 367 transformedContourOutline(fContour, transform); 368 369 // for when we bypass the transformation pipeline 370 BPoint transformOffset(0.0, 0.0); 371 transform.Transform(&transformOffset); 372 373 StringRenderer renderer(clippingFrame, dryRun, 374 gSubpixelAntialiasing && fAntialias, 375 transformedOutline, transformedContourOutline, 376 transform, transformOffset, nextCharPos, *this); 377 378 GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, NULL, 379 fKerning, B_BITMAP_SPACING, offsets, cacheReference); 380 381 return transform.TransformBounds(renderer.Bounds()); 382 } 383