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