1{target:win}
2//
3// AggPas 2.4 RM3 Demo application
4// Note: Press F1 key on run to see more info about this demo
5//
6// Paths: src;src\ctrl;src\svg;src\util;src\platform\win;expat-wrap
7//
8program
9 truetype_test ;
10
11uses
12 Windows ,SysUtils ,
13
14 agg_basics ,
15 agg_platform_support ,
16
17 agg_color ,
18 agg_pixfmt ,
19 agg_pixfmt_rgb ,
20
21 agg_ctrl ,
22 agg_slider_ctrl ,
23 agg_cbox_ctrl ,
24 agg_rbox_ctrl ,
25
26 agg_rendering_buffer ,
27 agg_renderer_base ,
28 agg_renderer_scanline ,
29 agg_renderer_primitives ,
30 agg_rasterizer_scanline_aa ,
31 agg_scanline ,
32 agg_scanline_u ,
33 agg_scanline_bin ,
34 agg_render_scanlines ,
35
36 agg_trans_affine ,
37 agg_curves ,
38 agg_conv_curve ,
39 agg_conv_contour ,
40 agg_gamma_lut ,
41 agg_gamma_functions ,
42 agg_font_win32_tt ,
43 agg_font_cache_manager ;
44
45{$I agg_mode.inc }
46
47const
48 flip_y = true;
49
50 angle_step = 0.5;
51
52 text_ : PChar =
53  //'0123456789ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmnoprstuvwxyz ' +
54  'Anti-Grain Geometry is designed as a set of loosely coupled ' +
55  'algorithms and class templates united with a common idea, ' +
56  'so that all the components can be easily combined. Also, ' +
57  'the template based design allows you to replace any part of ' +
58  'the library without the necessity to modify a single byte in ' +
59  'the existing code. ' +
60  'AGG is designed keeping in mind extensibility and flexibility. ' +
61  'Basically I just wanted to create a toolkit that would allow me ' +
62  '(and anyone else) to add new fancy algorithms very easily. ' +
63  'AGG does not dictate you any style of its use, you are free to ' +
64  'use any part of it. However, AGG is often associated with a tool ' +
65  'for rendering images in memory. That is not quite true, but it can ' +
66  'be a good starting point in studying. The tutorials describe the ' +
67  'use of AGG starting from the low level functionality that deals with ' +
68  'frame buffers and pixels. Then you will gradually understand how to ' +
69  'abstract different parts of the library and how to use them separately. ' +
70  'Remember, the raster picture is often not the only thing you want to ' +
71  'obtain, you will probably want to print your graphics with highest ' +
72  'possible quality and in this case you can easily combine the "vectorial" ' +
73  'part of the library with some API like Windows GDI, having a common ' +
74  'external interface. If that API can render multi-polygons with non-zero ' +
75  'and even-odd filling rules it''s all you need to incorporate AGG into ' +
76  'your application. For example, Windows API PolyPolygon perfectly fits ' +
77  'these needs, except certain advanced things like gradient filling, ' +
78  'Gouraud shading, image transformations, and so on. Or, as an alternative, ' +
79  'you can use all AGG algorithms producing high resolution pixel images and ' +
80  'then to send the result to the printer as a pixel map.' +
81  'Below is a typical brief scheme of the AGG rendering pipeline. ' +
82  'Please note that any component between the Vertex Source ' +
83  'and Screen Output is not mandatory. It all depends on your ' +
84  'particular needs. For example, you can use your own rasterizer, ' +
85  'based on Windows API. In this case you won''t need the AGG rasterizer ' +
86  'and renderers. Or, if you need to draw only lines, you can use the ' +
87  'AGG outline rasterizer that has certain restrictions but works faster. ' +
88  'The number of possibilities is endless. ' +
89  'Vertex Source is some object that produces polygons or polylines as ' +
90  'a set of consecutive 2D vertices with commands like MoveTo, LineTo. ' +
91  'It can be a container or some other object that generates vertices ' +
92  'on demand. ' +
93  'Coordinate conversion pipeline consists of a number of coordinate ' +
94  'converters. It always works with vectorial data (X,Y) represented ' +
95  'as floating point numbers (double). For example, it can contain an ' +
96  'affine transformer, outline (stroke) generator, some marker ' +
97  'generator (like arrowheads/arrowtails), dashed lines generator, ' +
98  'and so on. The pipeline can have branches and you also can have ' +
99  'any number of different pipelines. You also can write your own ' +
100  'converter and include it into the pipeline. ' +
101  'Scanline Rasterizer converts vectorial data into a number of ' +
102  'horizontal scanlines. The scanlines usually (but not obligatory) ' +
103  'carry information about Anti-Aliasing as coverage values. ' +
104  'Renderers render scanlines, sorry for the tautology. The simplest ' +
105  'example is solid filling. The renderer just adds a color to the ' +
106  'scanline and writes the result into the rendering buffer. ' +
107  'More complex renderers can produce multi-color result, ' +
108  'like gradients, Gouraud shading, image transformations, ' +
109  'patterns, and so on. Rendering Buffer is a buffer in memory ' +
110  'that will be displayed afterwards. Usually but not obligatory ' +
111  'it contains pixels in format that fits your video system. ' +
112  'For example, 24 bits B-G-R, 32 bits B-G-R-A, or 15 ' +
113  'bits R-G-B-555 for Windows. But in general, there''re no ' +
114  'restrictions on pixel formats or color space if you write ' +
115  'your own low level class that supports that format. ' +
116  'Colors in AGG appear only in renderers, that is, when you ' +
117  'actually put some data to the rendering buffer. In general, ' +
118  'there''s no general purpose structure or class like color, ' +
119  'instead, AGG always operates with concrete color space. ' +
120  'There are plenty of color spaces in the world, like RGB, ' +
121  'HSV, CMYK, etc., and all of them have certain restrictions. ' +
122  'For example, the RGB color space is just a poor subset of ' +
123  'colors that a human eye can recognize. If you look at the full ' +
124  'CIE Chromaticity Diagram, you will see that the RGB triangle ' +
125  'is just a little part of it. ' +
126  'In other words there are plenty of colors in the real world ' +
127  'that cannot be reproduced with RGB, CMYK, HSV, etc. Any color ' +
128  'space except the one existing in Nature is restrictive. Thus, ' +
129  'it was decided not to introduce such an object like color in ' +
130  'order not to restrict the possibilities in advance. Instead, ' +
131  'there are objects that operate with concrete color spaces. ' +
132  'Currently there are agg::rgba and agg::rgba8 that operate ' +
133  'with the most popular RGB color space (strictly speaking there''s ' +
134  'RGB plus Alpha). The RGB color space is used with different ' +
135  'pixel formats, like 24-bit RGB or 32-bit RGBA with different ' +
136  'order of color components. But the common property of all of ' +
137  'them is that they are essentially RGB. Although, AGG doesn''t ' +
138  'explicitly support any other color spaces, there is at least ' +
139  'a potential possibility of adding them. It means that all ' +
140  'class and function templates that depend on the color type ' +
141  'are parameterized with the ColorT argument. ' +
142  'Basically, AGG operates with coordinates of the output device. ' +
143  'On your screen there are pixels. But unlike many other libraries ' +
144  'and APIs AGG initially supports Subpixel Accuracy. It means ' +
145  'that the coordinates are represented as doubles, where fractional ' +
146  'values actually take effect. AGG doesn''t have an embedded ' +
147  'conversion mechanism from world to screen coordinates in order ' +
148  'not to restrict your freedom. It''s very important where and when ' +
149  'you do that conversion, so, different applications can require ' +
150  'different approaches. AGG just provides you a transformer of ' +
151  'that kind, namely, that can convert your own view port to the ' +
152  'device one. And it''s your responsibility to include it into ' +
153  'the proper place of the pipeline. You can also write your ' +
154  'own very simple class that will allow you to operate with ' +
155  'millimeters, inches, or any other physical units. ' +
156  'Internally, the rasterizers use integer coordinates of the ' +
157  'format 24.8 bits, that is, 24 bits for the integer part and 8 ' +
158  'bits for the fractional one. In other words, all the internal ' +
159  'coordinates are multiplied by 256. If you intend to use AGG in ' +
160  'some embedded system that has inefficient floating point ' +
161  'processing, you still can use the rasterizers with their ' +
162  'integer interfaces. Although, you won''t be able to use the ' +
163  'floating point coordinate pipelines in this case. ';
164
165var
166 text_flip : boolean;
167 font_name : AnsiString;
168
169type
170 the_application = object(platform_support )
171   m_ren_type : rbox_ctrl;
172
173   m_height ,
174   m_width  ,
175   m_weight ,
176   m_gamma  : slider_ctrl;
177
178   m_hinting     ,
179   m_kerning     ,
180   m_performance : cbox_ctrl;
181
182   m_feng : font_engine_win32_tt_int32;
183   m_fman : font_cache_manager;
184
185   m_old_height : double;
186   m_gamma_lut  : gamma_lut;
187
188  // Pipeline to process the vectors glyph paths (curves + contour)
189   m_curves  : conv_curve;
190   m_contour : conv_contour;
191
192   m_angle : double;
193
194   constructor Construct(dc : HDC; format_ : pix_format_e; flip_y_ : boolean );
195   destructor  Destruct;
196
197   function  draw_text(
198              ras : rasterizer_scanline_aa_ptr;
199              sl  : scanline_ptr;
200              ren_solid : renderer_scanline_aa_solid_ptr;
201              ren_bin   : renderer_scanline_bin_solid_ptr ) : unsigned;
202
203   procedure on_draw; virtual;
204
205   procedure on_key(x ,y : int; key ,flags : unsigned ); virtual;
206   procedure on_ctrl_change; virtual;
207
208  end;
209
210{ CONSTRUCT }
211constructor the_application.Construct;
212begin
213 inherited Construct(format_ ,flip_y_ );
214
215 m_ren_type.Construct   (5.0 ,5.0  ,5.0 + 150.0 ,110.0 ,not flip_y_ );
216 m_height.Construct     (160 ,10.0 ,640 - 5.0 ,18.0 ,not flip_y_ );
217 m_width.Construct      (160 ,30.0 ,640 - 5.0 ,38.0 ,not flip_y_ );
218 m_weight.Construct     (160 ,50.0 ,640 - 5.0 ,58.0 ,not flip_y_ );
219 m_gamma.Construct      (260 ,70.0 ,640 - 5.0 ,78.0 ,not flip_y_ );
220 m_hinting.Construct    (160 ,65.0 ,'Hinting' ,not flip_y_ );
221 m_kerning.Construct    (160 ,80.0 ,'Kerning' ,not flip_y_ );
222 m_performance.Construct(160 ,95.0 ,'Test Performance' ,not flip_y_ );
223
224 m_feng.Construct(dc );
225 m_fman.Construct(@m_feng );
226
227 m_old_height:=0.0;
228
229 m_gamma_lut.Construct_(8 ,16 );
230 m_curves.Construct    (m_fman.path_adaptor );
231 m_contour.Construct   (@m_curves );
232
233 m_ren_type.add_item ('Native Mono' );
234 m_ren_type.add_item ('Native Gray 8' );
235 m_ren_type.add_item ('AGG Outline' );
236 m_ren_type.add_item ('AGG Mono' );
237 m_ren_type.add_item ('AGG Gray 8' );
238 m_ren_type.cur_item_(1 );
239
240 add_ctrl(@m_ren_type );
241
242 m_ren_type.no_transform;
243
244 m_height.label_('Font Height=%.2f' );
245 m_height.range_(8, 32);
246 m_height.value_(18 );
247
248 m_height.num_steps_     (32 - 8 );
249 m_height.text_thickness_(1.5 );
250
251 add_ctrl(@m_height );
252
253 m_height.no_transform;
254
255 m_width.label_('Font Width=%.2f' );
256 m_width.range_(8 ,32 );
257 m_width.value_(18 );
258
259 m_width.num_steps_     (32 - 8 );
260 m_width.text_thickness_(1.5 );
261
262 add_ctrl(@m_width );
263
264 m_width.no_transform;
265
266 m_weight.label_('Font Weight=%.2f' );
267 m_weight.range_(-1 ,1 );
268
269 m_weight.text_thickness_(1.5 );
270
271 add_ctrl(@m_weight );
272
273 m_weight.no_transform;
274
275 m_gamma.label_('Gamma=%.2f' );
276 m_gamma.range_(0.1 ,2.0 );
277 m_gamma.value_(1.0 );
278
279 m_gamma.text_thickness_(1.5 );
280
281 add_ctrl(@m_gamma );
282
283 m_gamma.no_transform;
284
285 add_ctrl(@m_hinting );
286
287 m_hinting.status_(true );
288 m_hinting.no_transform;
289
290 add_ctrl(@m_kerning );
291
292 m_kerning.status_(true );
293 m_kerning.no_transform;
294
295 add_ctrl(@m_performance );
296
297 m_performance.no_transform;
298
299 //m_curves.approximation_method_(curve_div );
300 //m_curves.approximation_scale_ (0.5 );
301 //m_curves.angle_tolerance_     (0.3 );
302
303 m_contour.auto_detect_orientation_(false );
304
305end;
306
307{ DESTRUCT }
308destructor the_application.Destruct;
309begin
310 inherited Destruct;
311
312 m_ren_type.Destruct;
313 m_height.Destruct;
314 m_width.Destruct;
315 m_weight.Destruct;
316 m_gamma.Destruct;
317 m_hinting.Destruct;
318 m_kerning.Destruct;
319 m_performance.Destruct;
320
321 m_feng.Destruct;
322 m_fman.Destruct;
323
324 m_gamma_lut.Destruct;
325 m_curves.Destruct;
326 m_contour.Destruct;
327
328end;
329
330{ DRAW_TEXT }
331function the_application.draw_text;
332var
333 gren : glyph_rendering;
334
335 num_glyphs : unsigned;
336
337 mtx : trans_affine;
338 taw : trans_affine_skewing;
339 tar : trans_affine_rotation;
340
341 x ,y0 ,y : double;
342
343 p : int8u_ptr;
344
345 rgba  : aggclr;
346 glyph : glyph_cache_ptr;
347
348begin
349 gren:=glyph_ren_native_mono;
350
351 case m_ren_type._cur_item of
352  0 : gren:=glyph_ren_native_mono;
353  1 : gren:=glyph_ren_native_gray8;
354  2 : gren:=glyph_ren_outline;
355  3 : gren:=glyph_ren_agg_mono;
356  4 : gren:=glyph_ren_agg_gray8;
357
358 end;
359
360 num_glyphs:=0;
361
362 m_contour.width_(-m_weight._value * m_height._value * 0.05 );
363
364 m_feng.hinting_(m_hinting._status );
365 m_feng.height_ (m_height._value );
366
367// Font width in Windows is strange. MSDN says,
368// "specifies the average width", but there's no clue what
369// this "average width" means. It'd be logical to specify
370// the width with regard to the font height, like it's done in
371// FreeType. That is, width == height should mean the "natural",
372// not distorted glyphs. In Windows you have to specify
373// the absolute width, which is very stupid and hard to use
374// in practice.
375 if m_width._value = m_height._value then
376  m_feng.width_(0.0 )
377 else
378  m_feng.width_(m_width._value / 2.4 );
379
380// m_feng.italic_(true );
381 m_feng.flip_y_(text_flip );
382
383 mtx.Construct;
384
385 if m_angle <> 0 then
386  begin
387   tar.Construct(deg2rad(m_angle ) );
388   mtx.multiply (@tar );
389
390  end;
391
392 //taw.Construct(-0.3 ,0 ); mtx.multiply(@taw );
393
394 m_feng.transform_(@mtx );
395
396 if m_feng.create_font(@font_name[1 ] ,gren ) then
397  begin
398   m_fman.precache(unsigned(' ' ) ,127 );
399
400   x :=10.0;
401   y0:=_height - m_height._value - 10.0;
402   y :=y0;
403   p :=@text_[0 ];
404
405   while p^ <> 0 do
406    begin
407     glyph:=m_fman.glyph(p^ );
408
409     if glyph <> NIL then
410      begin
411       if m_kerning._status then
412        m_fman.add_kerning(@x ,@y );
413
414       if x >= _width - m_height._value then
415        begin
416         x :=10.0;
417         y0:=y0 - m_height._value;
418
419         if y0 <= 120 then
420          break;
421
422         y:=y0;
423
424        end;
425
426       m_fman.init_embedded_adaptors(glyph ,x ,y );
427
428       case glyph.data_type of
429        glyph_data_mono :
430         begin
431          rgba.ConstrInt(0 ,0 ,0 );
432          ren_bin.color_(@rgba );
433
434          render_scanlines(
435           m_fman.mono_adaptor ,
436           m_fman.mono_scanline ,
437           ren_bin );
438
439         end;
440
441        glyph_data_gray8 :
442         begin
443          rgba.ConstrInt  (0 ,0 ,0 );
444          ren_solid.color_(@rgba );
445
446          render_scanlines(
447           m_fman.gray8_adaptor ,
448           m_fman.gray8_scanline ,
449           ren_solid );
450
451         end;
452
453        glyph_data_outline :
454         begin
455          ras.reset;
456
457          if Abs(m_weight._value ) <= 0.01 then
458          // For the sake of efficiency skip the
459          // contour converter if the weight is about zero.
460           ras.add_path(@m_curves )
461          else
462           ras.add_path(@m_contour );
463
464          rgba.ConstrInt  (0 ,0 ,0 );
465          ren_solid.color_(@rgba );
466
467          render_scanlines(ras ,sl ,ren_solid );
468
469         end;
470
471       end;
472
473      // increment pen position
474       x:=x + glyph.advance_x;
475       y:=y + glyph.advance_y;
476
477       inc(num_glyphs );
478
479      end;
480
481     inc(ptrcomp(p ) ,sizeof(int8u ) );
482
483    end;
484
485  end;
486
487 result:=num_glyphs;
488
489end;
490
491{ ON_DRAW }
492procedure the_application.on_draw;
493var
494 pf : pixel_formats;
495
496 rgba : aggclr;
497
498 ren_base  : renderer_base;
499 ren_solid : renderer_scanline_aa_solid;
500 ren_bin   : renderer_scanline_bin_solid;
501
502 sl  : scanline_u8;
503 ras : rasterizer_scanline_aa;
504
505 gm_th : gamma_threshold;
506 gm_no : gamma_none;
507 gm_pw : gamma_power;
508
509begin
510// Initialize structures
511 pixfmt_bgr24_gamma(pf ,rbuf_window ,@m_gamma_lut );
512
513 ren_base.Construct (@pf );
514 ren_solid.Construct(@ren_base );
515 ren_bin.Construct  (@ren_base );
516
517 rgba.ConstrDbl(1 ,1 ,1 );
518 ren_base.clear(@rgba );
519
520 sl.Construct;
521 ras.Construct;
522
523 if m_height._value <> m_old_height then
524  begin
525   m_old_height:=m_height._value;
526
527   m_width.value_(m_old_height );
528
529  end;
530
531// Setup Gamma
532 if m_ren_type._cur_item = 3 then
533  begin
534  // When rendering in mono format,
535  // Set threshold gamma = 0.5
536   gm_th.Construct(m_gamma._value / 2.0 );
537   m_feng.gamma_  (@gm_th );
538
539  end
540 else
541  begin
542   gm_no.Construct;
543   m_feng.gamma_(@gm_no );
544
545   m_gamma_lut.gamma_(m_gamma._value );
546
547  end;
548
549// Render the text
550 draw_text(@ras ,@sl ,@ren_solid ,@ren_bin );
551
552// Render the controls
553 gm_pw.Construct(1.0 );
554 ras.gamma      (@gm_pw );
555
556 render_ctrl(@ras ,@sl ,@ren_solid ,@m_ren_type );
557 render_ctrl(@ras ,@sl ,@ren_solid ,@m_height );
558 render_ctrl(@ras ,@sl ,@ren_solid ,@m_width );
559 render_ctrl(@ras ,@sl ,@ren_solid ,@m_weight );
560 render_ctrl(@ras ,@sl ,@ren_solid ,@m_gamma );
561 render_ctrl(@ras ,@sl ,@ren_solid ,@m_hinting );
562 render_ctrl(@ras ,@sl ,@ren_solid ,@m_kerning );
563 render_ctrl(@ras ,@sl ,@ren_solid ,@m_performance );
564
565// Free AGG resources
566 sl.Destruct;
567 ras.Destruct;
568
569end;
570
571{ ON_KEY }
572procedure the_application.on_key;
573begin
574 if key = byte(' ' ) then
575  begin
576   text_flip:=not text_flip;
577
578   force_redraw;
579
580  end;
581
582 if key = key_kp_minus then
583  begin
584   m_angle:=m_angle + angle_step;
585
586   if m_angle > 360 then
587    m_angle:=0;
588
589   force_redraw;
590
591  end;
592
593 if key = key_kp_plus then
594  begin
595   m_angle:=m_angle - angle_step;
596
597   if m_angle < 0 then
598    m_angle:=360 - angle_step;
599
600   force_redraw;
601
602  end;
603
604 if key = key_f1 then
605  message_(
606   'This example demonstrates the use of the Win32 TrueType font engine with cache. '#13 +
607   'Cache can keep three types of data, vector path, Anti-Aliased scanline shape, '#13 +
608   'and monochrome scanline shape. In case of caching scanline shapes the speed '#13 +
609   'is pretty good and comparable with Windows hardware accelerated font rendering.'#13#13 +
610   'How to play with:'#13#13 +
611   'Press the spacebar to flip the text vertically.'#13#13 +
612   'Key Plus - Increase font angle (not for Natives)'#13 +
613   'Key Minus - Decrease font angle (not for Natives)' +
614   #13#13'Note: F2 key saves current "screenshot" file in this demo''s directory.  ' );
615
616end;
617
618{ ON_CTRL_CHANGE }
619procedure the_application.on_ctrl_change;
620var
621 pf : pixel_formats;
622
623 rgba : aggclr;
624
625 ren_base  : renderer_base;
626 ren_solid : renderer_scanline_aa_solid;
627 ren_bin   : renderer_scanline_bin_solid;
628
629 sl  : scanline_u8;
630 ras : rasterizer_scanline_aa;
631
632 num_glyphs ,i : unsigned;
633
634 t : double;
635
636 buf : array[0..99 ] of char;
637
638begin
639 if m_performance._status then
640  begin
641   pixfmt_bgr24_gamma(pf ,rbuf_window ,@m_gamma_lut );
642
643   ren_base.Construct (@pf );
644   ren_solid.Construct(@ren_base );
645   ren_bin.Construct  (@ren_base );
646
647   rgba.ConstrDbl(1 ,1 ,1 );
648   ren_base.clear(@rgba );
649
650   sl.Construct;
651   ras.Construct;
652
653   num_glyphs:=0;
654
655   start_timer;
656
657   for i:=0 to 49 do
658    inc(num_glyphs ,draw_text(@ras ,@sl ,@ren_solid ,@ren_bin ) );
659
660   t:=elapsed_time;
661
662   sprintf(@buf[0 ]             ,'Glyphs=%u, ' ,num_glyphs );
663   sprintf(@buf[StrLen(@buf ) ] ,'Time=%.3fms, ' ,t );
664   sprintf(@buf[StrLen(@buf ) ] ,'%.3f glyps/sec, ' ,(num_glyphs / t ) * 1000.0 );
665   sprintf(@buf[StrLen(@buf ) ] ,'%.3f microsecond/glyph' , (t / num_glyphs) * 1000.0);
666
667   message_(@buf[0 ] );
668
669   m_performance.status_(false );
670
671   force_redraw;
672
673   sl.Destruct;
674   ras.Destruct;
675
676  end;
677
678end;
679
680VAR
681 app : the_application;
682 dc  : HDC;
683
684BEGIN
685 text_flip:=false;
686 font_name:='Arial';
687
688{$IFDEF WIN32 }
689 if ParamCount > 0 then
690  font_name:=ParamStr(1 );
691
692{$ENDIF }
693
694 dc:=GetDC(0 );
695
696 app.Construct(dc ,pix_format_bgr24 ,flip_y );
697 app.caption_ ('AGG Example. Rendering TrueType Fonts with WinAPI (F1-Help)' );
698
699 if app.init(640 ,520 ,window_resize ) then
700  app.run;
701
702 app.Destruct;
703
704 ReleaseDC(0 ,dc );
705
706END.