File: z186.html

package info (click to toggle)
ebook-dev-ggad 199908-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 2,264 kB
  • ctags: 1,163
  • sloc: sh: 44; makefile: 35
file content (1002 lines) | stat: -rw-r--r-- 36,522 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>
      Drawing Methods
    </title>
    <meta name="GENERATOR" content=
    "Modular DocBook HTML Stylesheet Version 1.45">
    <link rel="HOME" title="GTK+ / Gnome Application Development"
    href="ggad.html">
    <link rel="UP" title="Writing a GnomeCanvasItem" href= 
    "cha-canvasitem.html">
    <link rel="PREVIOUS" title="Writing a GnomeCanvasItem" href= 
    "cha-canvasitem.html">
    <link rel="NEXT" title="Other Methods" href="z192.html">
  </head>
  <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink= 
  "#840084" alink="#0000FF">
    <div class="NAVHEADER">
      <table width="100%" border="0" bgcolor="#ffffff" cellpadding= 
      "1" cellspacing="0">
        <tr>
          <th colspan="4" align="center">
            <font color="#000000" size="2">GTK+ / Gnome Application
            Development</font>
          </th>
        </tr>
        <tr>
          <td width="25%" bgcolor="#ffffff" align="left">
            <a href="cha-canvasitem.html"><font color="#0000ff"
            size="2"><b>&lt;&lt;&lt; Previous</b></font></a>
          </td>
          <td width="25%" colspan="2" bgcolor="#ffffff" align= 
          "center">
            <font color="#0000ff" size="2"><b><a href="ggad.html">
            <font color="#0000ff" size="2"><b>
            Home</b></font></a></b></font>
          </td>
          <td width="25%" bgcolor="#ffffff" align="right">
            <a href="z192.html"><font color="#0000ff" size="2"><b>
            Next &gt;&gt;&gt;</b></font></a>
          </td>
        </tr>
      </table>
    </div>
    <div class="SECT1">
      <h1 class="SECT1">
        <a name="Z186">Drawing Methods</a>
      </h1>
      <p>
        The most important task of any canvas item is rendering
        itself onto the canvas. Rendering is a two-stage process
        for efficiency reasons. The first stage, implemented in a
        <span class="STRUCTNAME">GnomeCanvasItem</span>'s <span
        class="STRUCTNAME">update</span> method, is guaranteed to
        happen only once per item per rendering cycle; the idea is
        to do any expensive affine transformations or other
        calculations in the update method. In the second stage, the
        canvas item renders itself to some region on the screen.
        The <span class="STRUCTNAME">render</span> method
        implements stage two for antialiased items, while the <span
        class="STRUCTNAME">draw</span> method implements stage two
        for GDK items. An item's render or draw method may be
        invoked multiple times during a canvas repaint.
      </p>
      <p>
        Rendering occurs in a one-shot idle function. That is,
        whenever the canvas receives an expose event or otherwise
        determines that a redraw is needed, it adds an idle
        function which removes itself after a single invocation.
        (An idle function runs when no GTK+ events are pending and
        the flow of execution is in the GTK+ main loop---see <a
        href="sec-mainloop.html">the section called <i>The Main
        Loop</i> in the chapter called <i>GTK+ Basics</i></a> for
        details.) The canvas maintains a list of redraw regions and
        adds to it whenever a redraw request is received, so it
        knows which areas to repaint when the idle handler is
        finally invoked.
      </p>
      <p>
        Canvas items carry a flag indicating whether they need to
        be updated. Whenever a canvas item "changes" (for example,
        if you set a new fill color for <span class="STRUCTNAME">
        GnomeCanvasRect</span>), it will call <tt class="FUNCTION">
        gnome_canvas_item_request_update()</tt> to set the "update
        needed" flag for itself and the groups that contain it, up
        to and including the root canvas group. (The <tt class= 
        "CLASSNAME">GnomeCanvas</tt> widget is only aware of a
        single canvas item, the root group---all other items are
        handled recursively when methods are invoked on the root
        group.) In its one-shot idle function, the canvas invokes
        the update method of the root canvas item if its update
        flag is set, then clears the flag so the update method will
        not be run next time. The <span class="STRUCTNAME">
        GnomeCanvasGroup</span> update method does the same for
        each child item.
      </p>
      <p>
        Once all canvas items have been updated, the rendering
        process begins. The canvas creates an RGB or <span class= 
        "STRUCTNAME">GdkPixmap</span> buffer, converts its list of
        redraw regions into a list of buffer-sized rectangles, then
        invokes the render or draw method of the root canvas group
        once per rectangle. After each rectangle is rendered, the
        buffer is copied to the screen.
      </p>
      <div class="SECT2">
        <h2 class="SECT2">
          <a name="Z187">The Update Method</a>
        </h2>
        <p>
          The update method is primarily used by antialiased canvas
          items. <tt class="APPLICATION">libart_lgpl</tt> can
          prebuild a vector path to be rendered, performing
          clipping and affine transformation in advance. The render
          method stamps the pre-assembled path into the RGB buffer.
        </p>
        <p>
          The update method is one of the two that <span class= 
          "STRUCTNAME">GnomeCanvasRect</span> and <span class= 
          "STRUCTNAME">GnomeCanvasEllipse</span> have to implement
          differently. Here is the <span class="STRUCTNAME">
          GnomeCanvasRect</span> implementation:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;static void
gnome_canvas_rect_update (GnomeCanvasItem *item, double affine[6], 
                          ArtSVP *clip_path, gint flags)
{
  GnomeCanvasRE *re;
  ArtVpath vpath[11];
  ArtVpath *vpath2;
  double x0, y0, x1, y1;
  double dx, dy;
  double halfwidth;
  int i;

  gnome_canvas_re_update_shared (item, affine, clip_path, flags);

  re = GNOME_CANVAS_RE (item);

  if (item-&gt;canvas-&gt;aa) {
    x0 = re-&gt;x1;
    y0 = re-&gt;y1;
    x1 = re-&gt;x2;
    y1 = re-&gt;y2;

    gnome_canvas_item_reset_bounds (item);

    if (re-&gt;fill_set) {
      vpath[0].code = ART_MOVETO;
      vpath[0].x = x0;
      vpath[0].y = y0;
      vpath[1].code = ART_LINETO;
      vpath[1].x = x0;
      vpath[1].y = y1;
      vpath[2].code = ART_LINETO;
      vpath[2].x = x1;
      vpath[2].y = y1;
      vpath[3].code = ART_LINETO;
      vpath[3].x = x1;
      vpath[3].y = y0;
      vpath[4].code = ART_LINETO;
      vpath[4].x = x0;
      vpath[4].y = y0;
      vpath[5].code = ART_END;
      vpath[5].x = 0;
      vpath[5].y = 0;

      vpath2 = art_vpath_affine_transform (vpath, affine);

      gnome_canvas_item_update_svp_clip (item, &amp;re-&gt;fill_svp, art_svp_from_vpath (vpath2), clip_path);
      art_free (vpath2);
    } else
      gnome_canvas_item_update_svp (item, &amp;re-&gt;fill_svp, NULL);

    if (re-&gt;outline_set) {
      if (re-&gt;width_pixels)
        halfwidth = re-&gt;width * 0.5;
      else
        halfwidth = re-&gt;width * item-&gt;canvas-&gt;pixels_per_unit * 0.5;

      if (halfwidth &lt; 0.25)
        halfwidth = 0.25;

      i = 0;
      vpath[i].code = ART_MOVETO;
      vpath[i].x = x0 - halfwidth;
      vpath[i].y = y0 - halfwidth;
      i++;
      vpath[i].code = ART_LINETO;
      vpath[i].x = x0 - halfwidth;
      vpath[i].y = y1 + halfwidth;
      i++;
      vpath[i].code = ART_LINETO;
      vpath[i].x = x1 + halfwidth;
      vpath[i].y = y1 + halfwidth;
      i++;
      vpath[i].code = ART_LINETO;
      vpath[i].x = x1 + halfwidth;
      vpath[i].y = y0 - halfwidth;
      i++;
      vpath[i].code = ART_LINETO;
      vpath[i].x = x0 - halfwidth;
      vpath[i].y = y0 - halfwidth;
      i++;

      if (x1 - halfwidth &gt; x0 + halfwidth &amp;&amp;
          y1 - halfwidth &gt; y0 + halfwidth) {
        vpath[i].code = ART_MOVETO;
        vpath[i].x = x0 + halfwidth;
        vpath[i].y = y0 + halfwidth;
        i++;
        vpath[i].code = ART_LINETO;
        vpath[i].x = x1 - halfwidth;
        vpath[i].y = y0 + halfwidth;
        i++;
        vpath[i].code = ART_LINETO;
        vpath[i].x = x1 - halfwidth;
        vpath[i].y = y1 - halfwidth;
        i++;
        vpath[i].code = ART_LINETO;
        vpath[i].x = x0 + halfwidth;
        vpath[i].y = y1 - halfwidth;
        i++;
        vpath[i].code = ART_LINETO;
        vpath[i].x = x0 + halfwidth;
        vpath[i].y = y0 + halfwidth;
        i++;
      }
      vpath[i].code = ART_END;
      vpath[i].x = 0;
      vpath[i].y = 0;

      vpath2 = art_vpath_affine_transform (vpath, affine);

      gnome_canvas_item_update_svp_clip (item, &amp;re-&gt;outline_svp, art_svp_from_vpath (vpath2), clip_path);
      art_free (vpath2);
    } else
      gnome_canvas_item_update_svp (item, &amp;re-&gt;outline_svp, NULL);
  } else {
    get_bounds (re, &amp;x0, &amp;y0, &amp;x1, &amp;y1);
    gnome_canvas_update_bbox (item, x0, y0, x1, y1);
  }
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          As you can see, the first thing this function does is
          invoke an update function shared by <span class= 
          "STRUCTNAME">GnomeCanvasRect</span> and <span class= 
          "STRUCTNAME">GnomeCanvasEllipse</span>; here is that
          function:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;static void
gnome_canvas_re_update_shared (GnomeCanvasItem *item, double *affine, 
                               ArtSVP *clip_path, int flags)
{
  GnomeCanvasRE *re;

  re = GNOME_CANVAS_RE (item);

  if (re_parent_class-&gt;update)
    (* re_parent_class-&gt;update) (item, affine, clip_path, flags);

  if (!item-&gt;canvas-&gt;aa) {
    set_gc_foreground (re-&gt;fill_gc, re-&gt;fill_pixel);
    set_gc_foreground (re-&gt;outline_gc, re-&gt;outline_pixel);
    set_stipple (re-&gt;fill_gc, &amp;re-&gt;fill_stipple, 
                 re-&gt;fill_stipple, TRUE);
    set_stipple (re-&gt;outline_gc, &amp;re-&gt;outline_stipple, 
                 re-&gt;outline_stipple, TRUE);
    set_outline_gc_width (re);
  } 
}

      
</pre>
            </td>
          </tr>
        </table>
        <p>
          There is a lot of code involved here; the update method
          is almost always the most complicated one, since it does
          all the work of preparing to render a canvas item. Also,
          the update method is different for GDK and antialiased
          mode; notice the code which depends on the <span class= 
          "STRUCTNAME">item-&gt;canvas-&gt;aa</span> flag.
        </p>
        <p>
          The first thing <span class="STRUCTNAME">
          GnomeCanvasRE</span> does during an update is invoke the
          update method of its parent class. The <span class= 
          "STRUCTNAME">GnomeCanvasItem</span> default update method
          does nothing whatsoever in Gnome 1.0, but it is good
          practice to chain up for future robustness. Then, <span
          class="STRUCTNAME">GnomeCanvasRE</span> calls a series of
          utility routines to fill in its graphics contexts with
          their correct values. These are straightforward
          functions, so their implementations are omitted here.
        </p>
        <p>
          Next <tt class="FUNCTION">gnome_canvas_rect_update()</tt>
          continues with <span class="STRUCTNAME">
          GnomeCanvasRect</span>-specific details. Several tasks
          are accomplished:
        </p>
        <ul>
          <li>
            <p>
              The bounding box of the canvas item is updated. Every
              canvas item has an associated bounding box; the <span
              class="STRUCTNAME">GnomeCanvasGroup</span> draw and
              render methods use this box to determine which items
              are in the redraw region. The bounding box must be
              updated in both GDK and antialiased mode.
            </p>
          </li>
          <li>
            <p>
              In antialiased mode, a <i class="FIRSTTERM">sorted
              vector path</i> is created. A sorted vector path is
              simply a series of drawing instructions, similar to
              primitive PostScript operations, that <tt class= 
              "APPLICATION">libart_lgpl</tt> can render to an RGB
              buffer.
            </p>
          </li>
          <li>
            <p>
              In antialiased mode, the <span class="STRUCTNAME">
              affine</span> and <span class="STRUCTNAME">
              clip_path</span> arguments to the update method are
              used to transform the sorted vector path; thus the
              affine and clip path are implicitly stored for use in
              the render method. If you do not use <tt class= 
              "APPLICATION">libart_lgpl</tt>'s sorted vector paths
              in your own canvas items, you must arrange some other
              way to ensure the affine and clip are taken into
              account when you render.
            </p>
          </li>
          <li>
            <p>
              In both modes, a redraw is requested for both the
              region the item used to occupy, <i class="EMPHASIS">
              and</i> the region the item will now occupy.
            </p>
          </li>
        </ul>
        <p>
          Much of this work takes place behind the scenes in
          utility functions from <tt class="FILENAME">
          libgnomeui/gnome-canvas-util.h</tt>. <tt class=
          "FUNCTION">gnome_canvas_update_bbox()</tt> sets the
          item's new bounding box and requests a redraw on both the
          old and new bounding boxes; it is used in GDK mode. (<tt
          class="FUNCTION">gnome_canvas_update_bbox()</tt> expects
          canvas pixel coordinates; <tt class="FUNCTION">
          get_bounds()</tt> is a trivial function which computes
          the rectangle's bounds in canvas pixel coordinates.)
        </p>
        <p>
          So you know what's happening behind the scenes, here is
          the implementation of <tt class="FUNCTION">
          gnome_canvas_update_bbox()</tt>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_update_bbox (GnomeCanvasItem *item, 
                          int x1, int y1, 
                          int x2, int y2)
{
  gnome_canvas_request_redraw (item-&gt;canvas, 
                               item-&gt;x1, item-&gt;y1, 
                               item-&gt;x2, item-&gt;y2);
  item-&gt;x1 = x1;
  item-&gt;y1 = y1;
  item-&gt;x2 = x2;
  item-&gt;y2 = y2;
  gnome_canvas_request_redraw (item-&gt;canvas, 
                               item-&gt;x1, item-&gt;y1, 
                               item-&gt;x2, item-&gt;y2);
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          Of course you're free to do the equivalent yourself, this
          is merely a convenience function.
        </p>
        <p>
          In GDK mode, that's about all that happens; we update the
          bounds and then return. Antialiased mode is a bit more
          complex, but essentially the same tasks are performed.
          First, <tt class="FUNCTION">
          gnome_canvas_item_reset_bounds()</tt> sets the item's
          bounds back to an empty rectangle. Then, two sorted
          vector paths are prepared; one for the solid part of the
          rectangle (if any), and one for the rectangle's outline
          (if any). The same procedure is followed each time.
          First, a vector path for <tt class="APPLICATION">
          libart_lgpl</tt> is prepared; next, the path is affine
          transformed; then <tt class="FUNCTION">
          gnome_canvas_item_update_svp_clip()</tt> is used to
          request a redraw on the old path, free the old path, clip
          the new path, request a redraw on the new one, and save
          the new one for use in rendering. If the rectangle's fill
          or outline has been turned off, a redraw is requested on
          the old vector path, but no new path is created.
        </p>
        <p>
          To give you a clearer idea what is happening, here is the
          implementation of <tt class="FUNCTION">
          gnome_canvas_item_update_svp_clip()</tt>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, 
                                   ArtSVP **p_svp, ArtSVP *new_svp,
                                   ArtSVP *clip_svp)
{
  ArtSVP *clipped_svp;

  if (clip_svp != NULL) 
    {
      clipped_svp = art_svp_intersect (new_svp, clip_svp);
      art_svp_free (new_svp);
    } 
  else 
    {
      clipped_svp = new_svp;
    }

  gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
}

      
</pre>
            </td>
          </tr>
        </table>
        <p>
          and <tt class="FUNCTION">
          gnome_canvas_item_update_svp()</tt>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_item_update_svp (GnomeCanvasItem *item, 
                              ArtSVP **p_svp, ArtSVP *new_svp)
{
  ArtDRect bbox;

  gnome_canvas_update_svp (item-&gt;canvas, p_svp, new_svp);
  if (new_svp) 
    {
      bbox.x0 = item-&gt;x1;
      bbox.y0 = item-&gt;y1;
      bbox.x1 = item-&gt;x2;
      bbox.y1 = item-&gt;y2;
      art_drect_svp_union (&amp;bbox, new_svp);
      item-&gt;x1 = bbox.x0;
      item-&gt;y1 = bbox.y0;
      item-&gt;x2 = bbox.x1;
      item-&gt;y2 = bbox.y1;
    }
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          and then <tt class="FUNCTION">
          gnome_canvas_update_svp()</tt>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_update_svp (GnomeCanvas *canvas, 
                         ArtSVP **p_svp, ArtSVP *new_svp)
{
  ArtSVP *old_svp;
  ArtSVP *diff;
  ArtUta *repaint_uta;

  old_svp = *p_svp;
  if (old_svp != NULL &amp;&amp; new_svp != NULL) 
    {
      repaint_uta = art_uta_from_svp (old_svp);
      gnome_canvas_request_redraw_uta (canvas, repaint_uta);
      repaint_uta = art_uta_from_svp (new_svp);
      gnome_canvas_request_redraw_uta (canvas, repaint_uta);
    } 
  else if (old_svp != NULL) 
    {
      repaint_uta = art_uta_from_svp (old_svp);
      art_svp_free (old_svp);
      gnome_canvas_request_redraw_uta (canvas, repaint_uta);
    }
  *p_svp = new_svp;
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          Again, all of these are in <tt class="FILENAME">
          libgnomeui/gnome-canvas-util.h</tt> for any canvas item
          to use. Ignore the implementation details; the idea is
          simply to see what work is being done. The code may be
          easier to understand if you know that an <span class= 
          "STRUCTNAME">ArtDRect</span> is a "rectangle defined with
          doubles," from <tt class="APPLICATION">libart_lgpl</tt>,
          and that an <span class="STRUCTNAME">ArtUta</span> is a
          "microtile array," basically a list of small regions.
          (The antialiased canvas tracks the redraw region in a
          fairly sophisticated way. Note that the "U" in "<span
          class="STRUCTNAME">Uta</span>" is supposed to suggest the
          greek letter symbolizing "micro," it does not stand for a
          word beginning with "U".)
        </p>
        <div class="SECT3">
          <h3 class="SECT3">
            <a name="Z188">Requesting Updates</a>
          </h3>
          <p>
            It is the canvas item's responsibility to request an
            update or redraw when the properties of the item are
            changed and the screen should be refreshed. This is
            straightforward. For example, here is a snippet of code
            from <tt class="FUNCTION">
            gnome_canvas_re_set_arg()</tt>, which sets the <span
            class="STRUCTNAME">"y2"</span> argument:
          </p>
          <table border="0" bgcolor="#E0E0E0" width="100%">
            <tr>
              <td>
<pre class="PROGRAMLISTING">
&#13;  case ARG_Y2:
    re-&gt;y2 = GTK_VALUE_DOUBLE (*arg);

    gnome_canvas_item_request_update (item);
    break;

    
</pre>
              </td>
            </tr>
          </table>
          <p>
            Since <span class="STRUCTNAME">"y2"</span> modifies the
            shape of the rectangle, the path must be recreated and
            an update is necessary. Note that <tt class="FUNCTION">
            gnome_canvas_item_request_update()</tt> simply sets a
            flag and installs an idle handler if none is pending,
            so it can be called many times without a performance
            penalty.
          </p>
          <p>
            Not all changes require an update; a redraw may be
            sufficient, or perhaps the argument is unrelated to the
            display. It depends on the canvas item and what exactly
            is being changed.
          </p>
        </div>
      </div>
      <div class="SECT2">
        <h2 class="SECT2">
          <a name="Z189">The Render Method (Antialiased Mode)</a>
        </h2>
        <p>
          The render method is shared between <span class= 
          "STRUCTNAME">GnomeCanvasRect</span> and <span class= 
          "STRUCTNAME">GnomeCanvasEllipse</span>; all it does is
          stamp the two paths created in the update method into the
          RGB buffer:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;static void
gnome_canvas_re_render (GnomeCanvasItem *item,
                        GnomeCanvasBuf *buf)
{
  GnomeCanvasRE *re;
  guint32 fg_color, bg_color;

  re = GNOME_CANVAS_RE (item);

  if (re-&gt;fill_svp != NULL) {
    gnome_canvas_render_svp (buf, re-&gt;fill_svp, re-&gt;fill_color);
  }

  if (re-&gt;outline_svp != NULL) {
    gnome_canvas_render_svp (buf, re-&gt;outline_svp, re-&gt;outline_color);
  }
}

      
</pre>
            </td>
          </tr>
        </table>
        <p>
          As you can see, most of the work takes place in <tt
          class="FUNCTION">gnome_canvas_render_svp()</tt>, another
          function from <tt class="FILENAME">
          libgnomeui/gnome-canvas-util.h</tt>; here is its
          implementation:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
{
  guint32 fg_color, bg_color;

  if (buf-&gt;is_bg) {
    bg_color = buf-&gt;bg_color;
    fg_color = rgba &gt;&gt; 8;
    art_rgb_svp_aa (svp,
                    buf-&gt;rect.x0, buf-&gt;rect.y0, buf-&gt;rect.x1, buf-&gt;rect.y1,
                    fg_color, bg_color,
                    buf-&gt;buf, buf-&gt;buf_rowstride,
                    NULL);
    buf-&gt;is_bg = 0;
    buf-&gt;is_buf = 1;
  } else {
    art_rgb_svp_alpha (svp,
                       buf-&gt;rect.x0, buf-&gt;rect.y0, buf-&gt;rect.x1, buf-&gt;rect.y1,
                       rgba,
                       buf-&gt;buf, buf-&gt;buf_rowstride,
                       NULL);
  }
}

      
</pre>
            </td>
          </tr>
        </table>
        <p>
          To understand <tt class="FUNCTION">
          gnome_canvas_render_svp()</tt>, or to do your own RGB
          buffer drawing (without using <tt class="APPLICATION">
          libart_lgpl</tt>), you will need to know what a <span
          class="STRUCTNAME">GnomeCanvasBuf</span> is:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;typedef struct {
  guchar *buf;

  int buf_rowstride;

  ArtIRect rect;

  guint32 bg_color;

  unsigned int is_bg : 1;
  unsigned int is_buf : 1;
} GnomeCanvasBuf;
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          The <span class="STRUCTNAME">buf</span> member is an RGB
          buffer, as explained in <a href="z132.html#SEC-GDKRGB">
          the section called <i>RGB Buffers</i> in the chapter
          called <i>GDK Basics</i></a>. The <span class=
          "STRUCTNAME">buf_rowstride</span> is the buffer's
          rowstride, also explained in <a href=
          "z132.html#SEC-GDKRGB">the section called <i>RGB
          Buffers</i> in the chapter called <i>GDK Basics</i></a>.
          An <span class="STRUCTNAME">ArtIRect</span> is an integer
          rectangle; <span class="STRUCTNAME">rect</span> defines
          the redraw region in canvas pixel coordinates that this
          buffer represents. <span class="STRUCTNAME">
          rect.x0</span> and <span class="STRUCTNAME">
          rect.y0</span> are the buffer offsets and correspond to
          row 0, column 0 in the RGB buffer; you can convert from
          canvas pixel coordinates to RGB buffer coordinates by
          subtracting these values.
        </p>
        <p>
          As an optimization, the canvas does not initialize the
          RGB buffer with the background color, because the first
          canvas item might cover the entire background anyway.
          Thus, if your canvas item is the first one to render, you
          must put some pixel value in every pixel of the redraw
          region defined by the buffer's <span class="STRUCTNAME">
          rect</span>. If your item does not cover a pixel, you
          should fill that pixel with the <span class="STRUCTNAME">
          bg_color</span>; <span class="STRUCTNAME">bg_color</span>
          is a packed RGB value (no alpha). If you do this
          manually, unpack an RGB value <span class="STRUCTNAME">
          rgb</span> like this:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;    guchar r, g, b;

    r = (rgb &gt;&gt; 16) &amp; 0xff;
    g = (rgb &gt;&gt; 8) &amp; 0xff;
    b = rgb &amp; 0xff;

      
</pre>
            </td>
          </tr>
        </table>
        <p>
          However, a convenience function is provided to fill a
          <span class="STRUCTNAME">GnomeCanvasBuf</span> with its
          <span class="STRUCTNAME">bg_color</span>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;void
gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
{
  guchar *bufptr;
  int y;

  if (!buf-&gt;is_buf) {
    bufptr = buf-&gt;buf;
    for (y = buf-&gt;rect.y0; y &lt; buf-&gt;rect.y1; y++) {
      art_rgb_fill_run (bufptr,
                        buf-&gt;bg_color &gt;&gt; 16,
                        (buf-&gt;bg_color &gt;&gt; 8) &amp; 0xff,
                        buf-&gt;bg_color &amp; 0xff,
                        buf-&gt;rect.x1 - buf-&gt;rect.x0);
      bufptr += buf-&gt;buf_rowstride;
    }
    buf-&gt;is_buf = 1;
  }
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          As you can see from the implementation of <tt class= 
          "FUNCTION">gnome_canvas_buf_ensure_buf()</tt>, <span
          class="STRUCTNAME">is_bg</span> is a flag indicating that
          the RGB buffer still contains random memory garbage; it
          has not been initialized with RGB pixels. <span class= 
          "STRUCTNAME">is_buf</span> indicates that the buffer <i
          class="EMPHASIS">has</i> been initialized, and subsequent
          items should only draw themselves, ignoring background
          pixels. These two flags are mutually exclusive; if your
          item receives a buffer with <span class="STRUCTNAME">
          is_bg</span> set, it should take steps to fill the
          buffer, unset <span class="STRUCTNAME">is_bg</span>, and
          set <span class="STRUCTNAME">is_buf</span>:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;  if (buf-&gt;is_bg)
    {
      gnome_canvas_buf_ensure_buf(buf);
      buf-&gt;is_bg = FALSE;
    }

      
</pre>
            </td>
          </tr>
        </table>
        <div class="SECT3">
          <h3 class="SECT3">
            <a name="Z190">Speed and RGB Rendering</a>
          </h3>
          <p>
            If you have a large number of objects, RGB mode can be
            faster than GDK mode. Drawing to an RGB buffer is a
            simple matter of assigning to an array, which is much,
            much faster than making a GDK call (since GDK has to
            contact the X server and ask it to do the actual
            drawing). The expensive part is copying the RGB buffer
            to the X server when you're done. However, the copy
            takes the same amount of time no matter how many canvas
            items you have, since it is done only once, when all
            the items have been rendered.
          </p>
          <p>
            This is a big win in an application called "Guppi" I'm
            in the process of writing. Guppi is a plot program. One
            of the things it has to do is render a scatter plot
            with tens of thousands of points. Each point is a small
            colored shape; if I called GDK to render each, there
            would be tens of thousands of trips to the X server,
            possibly across a network. Instead, I use the canvas in
            RGB mode, with a custom canvas item representing the
            scatter plot. This allows me to do all the rendering on
            the client side, and then the canvas copies the RGB
            buffer to the server in a single burst. It's quite fast
            and responsive. For less speed-intensive elements of
            the plot, such as the legend, I can save time and use
            the built-in canvas items.
          </p>
          <p>
            The one difficulty with direct-to-RGB rendering is that
            you need a rasterization library comparable to the GDK
            drawing primitives if you want to draw anything
            interesting. <tt class="APPLICATION">libart_lgpl</tt>
            is a very high-quality antialiased rasterization
            library, used by the default canvas items. You can use
            it in your custom items as well, and it is the best
            choice if you will only be drawing hundreds of shapes.
            If you're drawing thousands of shapes, however, you'll
            quickly see the need for something faster. Fortunately,
            this is available; the maintainers of a package called
            GNU Plotutils extracted the rasterization library from
            the X distribution, and during the development of Guppi
            I extracted it from Plotutils and hacked it to work
            with the canvas's RGB buffers. I also added alpha
            transparency support. The resulting library allows you
            to draw on an RGB buffer much as you would draw using
            GDK. The library is distributed under the same license
            as the X Window System, and is free for anyone to
            include with their application.
          </p>
          <p>
            Raph Levien, author of <span class="STRUCTNAME">
            libart_lgpl</span> and the GdkRGB module, tells me that
            still faster routines could be written; if you need
            more speed, consider this a challenge.
          </p>
        </div>
      </div>
      <div class="SECT2">
        <h2 class="SECT2">
          <a name="Z191">The Draw Method (GDK Mode)</a>
        </h2>
        <p>
          Drawing with GDK is much less complicated than drawing
          with <tt class="APPLICATION">libart_lgpl</tt>, though it
          is also less flexible and produces lower-quality results.
          Here is the <span class="STRUCTNAME">
          GnomeCanvasRect</span> implementation of the draw method:
        </p>
        <table border="0" bgcolor="#E0E0E0" width="100%">
          <tr>
            <td>
<pre class="PROGRAMLISTING">
&#13;static void
gnome_canvas_rect_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 
                        int x, int y, int width, int height)
{
  GnomeCanvasRE *re;
  double i2w[6], w2c[6], i2c[6];
  int x1, y1, x2, y2;
  ArtPoint i1, i2;
  ArtPoint c1, c2;

  re = GNOME_CANVAS_RE (item);

  /* Get canvas pixel coordinates */

  gnome_canvas_item_i2w_affine (item, i2w);
  gnome_canvas_w2c_affine (item-&gt;canvas, w2c);
  art_affine_multiply (i2c, i2w, w2c);

  i1.x = re-&gt;x1;
  i1.y = re-&gt;y1;
  i2.x = re-&gt;x2;
  i2.y = re-&gt;y2;
  art_affine_point (&amp;c1, &amp;i1, i2c);
  art_affine_point (&amp;c2, &amp;i2, i2c);
  x1 = c1.x;
  y1 = c1.y;
  x2 = c2.x;
  y2 = c2.y;
        
  if (re-&gt;fill_set) {
    if (re-&gt;fill_stipple)
      gnome_canvas_set_stipple_origin (item-&gt;canvas, re-&gt;fill_gc);

    gdk_draw_rectangle (drawable,
                        re-&gt;fill_gc,
                        TRUE,
                        x1 - x,
                        y1 - y,
                        x2 - x1 + 1,
                        y2 - y1 + 1);
  }

  if (re-&gt;outline_set) {
    if (re-&gt;outline_stipple)
      gnome_canvas_set_stipple_origin (item-&gt;canvas, re-&gt;outline_gc);

    gdk_draw_rectangle (drawable,
                        re-&gt;outline_gc,
                        FALSE,
                        x1 - x,
                        y1 - y,
                        x2 - x1,
                        y2 - y1);
  }
}
      
</pre>
            </td>
          </tr>
        </table>
        <p>
          The draw method receives a drawable (the buffer), the
          buffer offsets (<span class="STRUCTNAME">x</span> and
          <span class="STRUCTNAME">y</span>---the canvas pixel
          coordinates of the buffer), and the buffer's size (<span
          class="STRUCTNAME">width</span> and <span class= 
          "STRUCTNAME">height</span>). <span class="STRUCTNAME">
          GnomeCanvasRect</span>'s draw method obtains the
          item-to-world and world-to-canvas affines, then composes
          (multiplies) them to create an item-to-canvas affine.
          (See <a href="z174.html#SEC-AFFINES">the section called
          <i>Affine Transformations</i> in the chapter called <i>
          <tt class="CLASSNAME">GnomeCanvas</tt></i></a> for more
          on affines.) Using this affine, it converts the
          rectangle's corner points to canvas pixel coordinates;
          then it draws the rectangle, converting the canvas
          coordinates to buffer coordinates by subtracting the
          buffer offsets.
        </p>
      </div>
    </div>
    <div class="NAVFOOTER">
      <br>
      <br>
      <table width="100%" border="0" bgcolor="#ffffff" cellpadding= 
      "1" cellspacing="0">
        <tr>
          <td width="25%" bgcolor="#ffffff" align="left">
            <a href="cha-canvasitem.html"><font color="#0000ff"
            size="2"><b>&lt;&lt;&lt; Previous</b></font></a>
          </td>
          <td width="25%" colspan="2" bgcolor="#ffffff" align= 
          "center">
            <font color="#0000ff" size="2"><b><a href="ggad.html">
            <font color="#0000ff" size="2"><b>
            Home</b></font></a></b></font>
          </td>
          <td width="25%" bgcolor="#ffffff" align="right">
            <a href="z192.html"><font color="#0000ff" size="2"><b>
            Next &gt;&gt;&gt;</b></font></a>
          </td>
        </tr>
        <tr>
          <td colspan="2" align="left">
            <font color="#000000" size="2"><b>Writing a <span
            class="STRUCTNAME">GnomeCanvasItem</span></b></font>
          </td>
          <td colspan="2" align="right">
            <font color="#000000" size="2"><b>Other
            Methods</b></font>
          </td>
        </tr>
      </table>
    </div>
  </body>
</html>