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
AGGTextRenderer(renderer_subpix_type & subpixRenderer,renderer_type & solidRenderer,renderer_bin_type & binRenderer,scanline_unpacked_type & scanline,scanline_unpacked_subpix_type & subpixScanline,rasterizer_subpix_type & subpixRasterizer,scanline_unpacked_masked_type * & maskedScanline,agg::trans_affine & viewTransformation)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
~AGGTextRenderer()68 AGGTextRenderer::~AGGTextRenderer()
69 {
70 }
71
72
73 void
SetFont(const ServerFont & font)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
SetHinting(bool hinting)90 AGGTextRenderer::SetHinting(bool hinting)
91 {
92 fHinted = hinting;
93 }
94
95
96 void
SetAntialiasing(bool antialiasing)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:
StringRenderer(const IntRect & clippingFrame,bool dryRun,bool subpixelAntiAliased,bool underscore,FontCacheEntry::TransformedOutline & transformedGlyph,FontCacheEntry::TransformedContourOutline & transformedContour,const Transformable & transform,const BPoint & transformOffset,BPoint * nextCharPos,AGGTextRenderer & renderer)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
NeedsVector()147 bool NeedsVector()
148 {
149 return !fTransform.IsTranslationOnly();
150 }
151
Start()152 void Start()
153 {
154 fRenderer.fRasterizer.reset();
155 fRenderer.fSubpixRasterizer.reset();
156 }
157
Finish(double x,double y)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
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)223 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
224 {
225 }
226
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)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
Bounds() const355 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
RenderString(const char * string,uint32 length,const BPoint & baseLine,const BRect & clippingFrame,bool dryRun,BPoint * nextCharPos,const escapement_delta * delta,FontCacheReference * cacheReference)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
RenderString(const char * string,uint32 length,const BPoint * offsets,const BRect & clippingFrame,bool dryRun,BPoint * nextCharPos,FontCacheReference * cacheReference)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