xref: /haiku/src/servers/app/drawing/Painter/AGGTextRenderer.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
1 /*
2  * Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
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 "GlyphLayoutEngine.h"
28 #include "IntRect.h"
29 
30 
31 // constructor
32 AGGTextRenderer::AGGTextRenderer(renderer_type& solidRenderer,
33 		renderer_bin_type& binRenderer, scanline_unpacked_type& scanline)
34 	: fPathAdaptor()
35 	, fGray8Adaptor()
36 	, fGray8Scanline()
37 	, fMonoAdaptor()
38 	, fMonoScanline()
39 
40 	, fCurves(fPathAdaptor)
41 	, fContour(fCurves)
42 
43 	, fSolidRenderer(solidRenderer)
44 	, fBinRenderer(binRenderer)
45 	, fScanline(scanline)
46 	, fRasterizer()
47 
48 	, fHinted(true)
49 	, fAntialias(true)
50 	, fKerning(true)
51 	, fEmbeddedTransformation()
52 {
53 	fCurves.approximation_scale(2.0);
54 	fContour.auto_detect_orientation(false);
55 }
56 
57 // destructor
58 AGGTextRenderer::~AGGTextRenderer()
59 {
60 }
61 
62 // SetFont
63 void
64 AGGTextRenderer::SetFont(const ServerFont &font)
65 {
66 	fFont = font;
67 
68 	// construct an embedded transformation (rotate & shear)
69 	fEmbeddedTransformation.Reset();
70 	fEmbeddedTransformation.ShearBy(B_ORIGIN,
71 		(90.0 - font.Shear()) * PI / 180.0, 0.0);
72 	fEmbeddedTransformation.RotateBy(B_ORIGIN,
73 		-font.Rotation() * PI / 180.0);
74 
75 	fContour.width(font.FalseBoldWidth() * 2.0);
76 }
77 
78 // SetHinting
79 void
80 AGGTextRenderer::SetHinting(bool hinting)
81 {
82 	fHinted = hinting;
83 //	fFontEngine.hinting(fEmbeddedTransformation.IsIdentity() && fHinted);
84 }
85 
86 // SetAntialiasing
87 void
88 AGGTextRenderer::SetAntialiasing(bool antialiasing)
89 {
90 	if (fAntialias != antialiasing) {
91 		fAntialias = antialiasing;
92 		if (!fAntialias)
93 			fRasterizer.gamma(agg::gamma_threshold(0.5));
94 		else
95 			fRasterizer.gamma(agg::gamma_power(1.0));
96 	}
97 }
98 
99 typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable>
100 	conv_font_trans_type;
101 
102 typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
103 	conv_font_contour_trans_type;
104 
105 
106 
107 class AGGTextRenderer::StringRenderer {
108  public:
109 	StringRenderer(const IntRect& clippingFrame, bool dryRun,
110 			FontCacheEntry::TransformedOutline& transformedGlyph,
111 			FontCacheEntry::TransformedContourOutline& transformedContour,
112 			const Transformable& transform,
113 			const BPoint& transformOffset,
114 			BPoint* nextCharPos,
115 			AGGTextRenderer& renderer)
116 
117 		: fTransform(transform)
118 		, fTransformOffset(transformOffset)
119 		, fClippingFrame(clippingFrame)
120 		, fDryRun(dryRun)
121 		, fBounds(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN)
122 		, fNextCharPos(nextCharPos)
123 		, fVector(false)
124 
125 		, fTransformedGlyph(transformedGlyph)
126 		, fTransformedContour(transformedContour)
127 
128 		, fRenderer(renderer)
129 	{
130 	}
131 
132 	void Start()
133 	{
134 		fRenderer.fRasterizer.reset();
135 	}
136 	void Finish(double x, double y)
137 	{
138 		if (fVector) {
139 			agg::render_scanlines(fRenderer.fRasterizer, fRenderer.fScanline,
140 				fRenderer.fSolidRenderer);
141 		}
142 
143 		if (fNextCharPos) {
144 			fNextCharPos->x = x;
145 			fNextCharPos->y = y;
146 			fTransform.Transform(fNextCharPos);
147 		}
148 	}
149 
150 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
151 	{
152 	}
153 
154 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
155 		FontCacheEntry* entry, double x, double y)
156 	{
157 		// "glyphBounds" is the bounds of the glyph transformed
158 		// by the x y location of the glyph along the base line,
159 		// it is therefor yet "untransformed" in case there is an
160 		// embedded transformation.
161 		const agg::rect_i& r = glyph->bounds;
162 		IntRect glyphBounds(r.x1 + x, r.y1 + y - 1,
163 			r.x2 + x + 1, r.y2 + y + 1);
164 			// NOTE: "-1"/"+1" converts the glyph bounding box from pixel
165 			// indices to pixel area coordinates
166 
167 		// track bounding box
168 		if (glyphBounds.IsValid())
169 			fBounds = fBounds | glyphBounds;
170 
171 		// render the glyph if this is not a dry run
172 		if (!fDryRun) {
173 			// init the fontmanager's embedded adaptors
174 			// NOTE: The initialization for the "location" of
175 			// the glyph is different depending on wether we
176 			// deal with non-(rotated/sheared) text, in which
177 			// case we have a native FT bitmap. For rotated or
178 			// sheared text, we use AGG vector outlines and
179 			// a transformation pipeline, which will be applied
180 			// _after_ we retrieve the outline, and that's why
181 			// we simply pass x and y, which are untransformed.
182 
183 			// "glyphBounds" is now transformed into screen coords
184 			// in order to stop drawing when we are already outside
185 			// of the clipping frame
186 			if (glyph->data_type != glyph_data_outline) {
187 				// we cannot use the transformation pipeline
188 				double transformedX = x + fTransformOffset.x;
189 				double transformedY = y + fTransformOffset.y;
190 				entry->InitAdaptors(glyph, transformedX, transformedY,
191 					fRenderer.fMonoAdaptor,
192 					fRenderer.fGray8Adaptor,
193 					fRenderer.fPathAdaptor);
194 
195 				glyphBounds.OffsetBy(fTransformOffset);
196 			} else {
197 				entry->InitAdaptors(glyph, x, y,
198 					fRenderer.fMonoAdaptor,
199 					fRenderer.fGray8Adaptor,
200 					fRenderer.fPathAdaptor);
201 
202 				float falseBoldWidth = fRenderer.fContour.width();
203 				if (falseBoldWidth != 0.0)
204 					glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth);
205 				// TODO: not correct! this is later used for clipping,
206 				// but it doesn't get the rect right
207 				glyphBounds = fTransform.TransformBounds(glyphBounds);
208 			}
209 
210 			if (fClippingFrame.Intersects(glyphBounds)) {
211 				switch (glyph->data_type) {
212 					case glyph_data_mono:
213 						agg::render_scanlines(fRenderer.fMonoAdaptor,
214 							fRenderer.fMonoScanline, fRenderer.fBinRenderer);
215 						break;
216 
217 					case glyph_data_gray8:
218 						agg::render_scanlines(fRenderer.fGray8Adaptor,
219 							fRenderer.fGray8Scanline, fRenderer.fSolidRenderer);
220 						break;
221 
222 					case glyph_data_outline: {
223 						fVector = true;
224 						if (fRenderer.fContour.width() == 0.0) {
225 							fRenderer.fRasterizer.add_path(fTransformedGlyph);
226 						} else {
227 							fRenderer.fRasterizer.add_path(fTransformedContour);
228 						}
229 #if SHOW_GLYPH_BOUNDS
230 	agg::path_storage p;
231 	p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5);
232 	p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5);
233 	p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5);
234 	p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5);
235 	p.close_polygon();
236 	agg::conv_stroke<agg::path_storage> ps(p);
237 	ps.width(1.0);
238 	fRenderer.fRasterizer.add_path(ps);
239 #endif
240 
241 						break;
242 					}
243 					default:
244 						break;
245 				}
246 			}
247 		}
248 		return true;
249 	}
250 
251 	IntRect Bounds() const
252 	{
253 		return fBounds;
254 	}
255 
256  private:
257  	const Transformable& fTransform;
258 	const BPoint&		fTransformOffset;
259 	const IntRect&		fClippingFrame;
260 	bool				fDryRun;
261 	IntRect				fBounds;
262 	BPoint*				fNextCharPos;
263 	bool				fVector;
264 
265 	FontCacheEntry::TransformedOutline& fTransformedGlyph;
266 	FontCacheEntry::TransformedContourOutline& fTransformedContour;
267 	AGGTextRenderer&	fRenderer;
268 };
269 
270 // RenderString
271 BRect
272 AGGTextRenderer::RenderString(const char* string,
273 							  uint32 length,
274 							  const BPoint& baseLine,
275 							  const BRect& clippingFrame,
276 							  bool dryRun,
277 							  BPoint* nextCharPos,
278 							  const escapement_delta* delta,
279 							  FontCacheReference* cacheReference)
280 {
281 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
282 
283 	Transformable transform(fEmbeddedTransformation);
284 	transform.TranslateBy(baseLine);
285 
286 	fCurves.approximation_scale(transform.scale());
287 
288 	// use a transformation behind the curves
289 	// (only if glyph->data_type == agg::glyph_data_outline)
290 	// in the pipeline for the rasterizer
291 	FontCacheEntry::TransformedOutline
292 		transformedOutline(fCurves, transform);
293 	FontCacheEntry::TransformedContourOutline
294 		transformedContourOutline(fContour, transform);
295 
296 	// for when we bypass the transformation pipeline
297 	BPoint transformOffset(0.0, 0.0);
298 	transform.Transform(&transformOffset);
299 
300 	StringRenderer renderer(clippingFrame, dryRun,
301 		transformedOutline, transformedContourOutline,
302 		transform, transformOffset, nextCharPos, *this);
303 
304 	GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta,
305 		fKerning, B_BITMAP_SPACING, cacheReference);
306 
307 	return transform.TransformBounds(renderer.Bounds());
308 }
309