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