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