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
30AGGTextRenderer::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
68AGGTextRenderer::~AGGTextRenderer()
69{
70}
71
72
73void
74AGGTextRenderer::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
89void
90AGGTextRenderer::SetHinting(bool hinting)
91{
92	fHinted = hinting;
93}
94
95
96void
97AGGTextRenderer::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
111typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable>
112	conv_font_trans_type;
113
114typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
115	conv_font_contour_trans_type;
116
117
118
119class AGGTextRenderer::StringRenderer {
120public:
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
147	bool NeedsVector()
148	{
149		return !fTransform.IsTranslationOnly();
150	}
151
152	void Start()
153	{
154		fRenderer.fRasterizer.reset();
155		fRenderer.fSubpixRasterizer.reset();
156	}
157
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
223	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
224	{
225	}
226
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
355	IntRect Bounds() const
356	{
357		return fBounds;
358	}
359
360private:
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
377BRect
378AGGTextRenderer::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
418BRect
419AGGTextRenderer::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