[ruby-gnome2-doc-cvs] [Ruby-GNOME2 Project Website] create - tut-gtk2-dancr-rbcatut-intro

Back to archive index

ruby-****@sourc***** ruby-****@sourc*****
2013年 3月 30日 (土) 08:39:54 JST


-------------------------
REMOTE_ADDR = 70.49.48.128
REMOTE_HOST = 
        URL = http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk2-dancr-rbcatut-intro
-------------------------
TITLE       = tut-gtk2-dancr-rbcatut-intro
KEYWORD     = 
= (12.3) Ruby Cairo Tutorial
{{link "tut-gtk2-dancr-wr-dapxb2file", "tut-gtk2-dancr", "tut-gtk", "tut-gtk2-crdrmd"}}



# = Sorry, this sub-chapter is still under construction

# (12.3) [current file: ... tut-gtk2-dancr-rbcatut-intro]
#        [next file: ...... tut-gtk2-dancr-rbcatut-crdrmd]


##  * 12.3           Ruby Cairo Tutorial (this page)
##    * 12.3.0         Intro To Ruby Cairo Tutorial
##      * 12.3.0.1       Establishing Gtk-Cairo Framework To Run the Tutorial Example Code
##        * 12.3.0.1.1     Cairo Backends
##          * 12.3.0.1.1.1     Programmer's Note
##          * 12.3.0.1.1.2     Student's Note
##        * 12.3.0.1.2     The Template Ruby File To Run Arbitrary Cairo Code
##        * 12.3.0.1.3     A Real Life Example With Runing Cairo Code
##
##      * 12.3.0.2       Running Cairo Code From Non-Gtk Ruby Environment
##
##      * 12.3.0.3       Hiki Gtk With Cairo Module Download File

:Contents of this chapter:


  * 12.3 ((<Ruby Cairo Tutorial|?tut-gtk2-dancr-rbcatut>)) (this page)
    * 12.3.0 ((<Intro To Ruby Cairo Tutorial|tut-gtk2-dancr-rbcatut-intro#Intro To Ruby Cairo Tutorial>))
      * 12.3.0.1 ((<Establishing Gtk-Cairo Framework To Run the Tutorial Example Code|tut-gtk2-dancr-rbcatut-intro#Establishing Gtk-Cairo Framework To Run the Tutorial Example Code>))
        * 12.3.0.1.1 ((<Cairo Backends|tut-gtk2-dancr-rbcatut-intro#Cairo Backends>))
          * 12.3.0.1.1.1 ((<Programmer's Note|tut-gtk2-dancr-rbcatut-intro#Programmer's Note:>))
            * 12.3.0.1.1.1.1 ((<Student's Note|tut-gtk2-dancr-rbcatut-intro#Student's Note:>))
        * 12.3.0.1.2 ((<The Template Ruby File To Run Arbitrary Cairo Code|tut-gtk2-dancr-rbcatut-intro#The Template Ruby File To Run Arbitrary Cairo Code:>))
          * 12.3.0.1.2.1 ((<A Real Life Example With Runing Cairo Code|tut-gtk2-dancr-rbcatut-intro#A Real Life Example With Runing Cairo Code>))

        * 12.3.0.2 ((<Running Cairo Code From Non-Gtk Ruby Environment|tut-gtk2-dancr-rbcatut-intro#Running Cairo Code From Non-Gtk Ruby Environment>))

        * 12.3.0.3 ((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>))




    * 12.3.1 ((<Cairo's Drawing Model|tut-gtk2-dancr-rbcatut-crdrmd#Cairo's Drawing Model>))
      * 12.3.1.1 ((<Nouns|tut-gtk2-dancr-rbcatut-crdrmd#Nouns>))
        * 12.3.1.1.1 ((<Destination|tut-gtk2-dancr-rbcatut-crdrmd#Destination>))
        * 12.3.1.1.2 ((<Source|tut-gtk2-dancr-rbcatut-crdrmd#Source>))
        * 12.3.1.1.3 ((<Mask|tut-gtk2-dancr-rbcatut-crdrmd#Mask>))
        * 12.3.1.1.4 ((<Path|tut-gtk2-dancr-rbcatut-crdrmd#Path>))
        * 12.3.1.1.5 ((<Context|tut-gtk2-dancr-rbcatut-crdrmd#Context>))


      * 12.3.1.2 ((<Verbs|tut-gtk2-dancr-rbcatut-crdrmd#Verbs>))
        * 12.3.1.2.1 ((<Stroke|tut-gtk2-dancr-rbcatut-crdrmd#Stroke>))
          * 12.3.1.2.1.A1 ((<Scale|tut-gtk2-dancr-rbcatut-crdrmd#Scale>))
          * 12.3.1.2.1.A2 ((<Default Scale Example|tut-gtk2-dancr-rbcatut-crdrmd#Default Scale Example>))
          * 12.3.1.2.1.A3 ((<Rule of thumb|tut-gtk2-dancr-rbcatut-crdrmd#Rule of thumb>))

        * 12.3.1.2.2 ((<Fill|tut-gtk2-dancr-rbcatut-crdrmd#Fill>))
        * 12.3.1.2.3 ((<Show Text - Glyphs|tut-gtk2-dancr-rbcatut-crdrmd#Show Text - Glyphs>))
        * 12.3.1.2.4 ((<Paint|tut-gtk2-dancr-rbcatut-crdrmd#Paint>))
          * 12.3.1.2.4.A1 ((<Paint And Colour Transparency In Ruby Cairo Library|tut-gtk2-dancr-rbcatut-crdrmd#Paint And Colour Transparency In Ruby Cairo Library>))

        
        * 12.3.1.2.5 ((<Mask|tut-gtk2-dancr-rbcatut-crdrmd#Mask>))
          * 12.3.1.2.5.A1 ((<Ruby Gtk-Cairo Library Fails To Support Gradient Patterns|tut-gtk2-dancr-rbcatut-crdrmd#Ruby Gtk-Cairo Library Fails To Support Gradient Patterns>))




    * 12.3.2 ((<Drawing with Cairo|tut-gtk2-dancr-rbcatut-dwc#Drawing with Cairo>))
      * 12.3.2.1 ((<Preparing and Selecting a Source|tut-gtk2-dancr-rbcatut-dwc#Preparing and Selecting a Source>))
      * 12.3.2.2 ((<Creating a Path|tut-gtk2-dancr-rbcatut-dwc#Creating a Path>))

    * 12.3.3 ((<Understanding Text|tut-gtk2-dancr-rbcatut-dwc#Understanding Text>))
    * 12.3.4 ((<Working with Transforms|tut-gtk2-dancr-rbcatut-dwc#Working with Transforms>))
    * 12.3.5 ((<Where to Go Next|tut-gtk2-dancr-rbcatut-dwc#Where to Go Next>))

    * 12.3.6 ((<Tips and Tricks|tut-gtk2-dancr-rbcatut-dwc#Tips and Tricks>))
      * 12.3.6.1 ((<Line Width|tut-gtk2-dancr-rbcatut-dwc#Line Width>))
      * 12.3.6.2 ((<Text Alignment|tut-gtk2-dancr-rbcatut-dwc#Text Alignment>))



{{br}}

== Intro To Ruby Cairo Tutorial
(12.3.0){{br}}

This tutorial is derived primarily from ((<the official Cairo Tutorial|URL:http://www.cairographics.org/tutorial/>)), which in turn was derived from from  ((<Michael Urman's cairo tutorial for python programmers|URL:http://www.tortall.net/mu/wiki/CairoTutorial>)), and the original code snippets have been translated to C, the text has only been changed as much as necessary. All that has been said here for the 'official Cairo Tutorial' holds also for my 'Ruby Cairo Tutorial', except that the original code snippets in either of the above, have now been translated to Ruby. I also have reused the diagrams developed for the original Cairo tutorials. These diagrams were created by Donn Ingle in SVG. They require 'Inkscape' (or similar) to view, as well as two specific fonts for correct appearance. Donn requests that you download and share the diagrams on your own website if you find them useful. (((<You can find the files to download on this link|URL:https://docs.google.com/leaf?id=
 0ByXVQD8awBcLYjc2ZWVmZmItMDlmMi00MWUzLThkMTUtMzNhNTU0YjI3M2M2&amp;hl=en>)).)

# As you know Cairo 2D vector graphic library became part of GTK+ release 2.8 distribution.



{{br}}

=== Establishing Gtk-Cairo Framework To Run The Tutorial Example Code
(12.3.0.1){{br}}

A great number of the examples that introduce Cairo code at the beginning of almost all contemporary Cairo tutorials try to avoid unnecessarily discussing how Cairo cooperates with the X, or any other Window system. Plain C/GTK Cairo library includes a number of functions which allow you create ((*cairo context*)) independently of any particular back-end, and then bind it to a backend of your choice, however, currently you can not do the same Ruby Gtk/Cairo. Before we continue this discussion let's see what these backends are.

:Cairo Backends

    (12.3.0.1.1){{br}}
    Plain C/GTK Cairo library Cairo supports various backends. Unfortunately currently, Ruby Gtk/Cairo library does not! Nevertheless, as we will see in the next paragraph (12.3.0.2) "((<Running Cairo Code From Non-Gtk Ruby Environment|tut-gtk2-dancr-rbcatut-intro#Running Cairo Code From Non-Gtk Ruby Environment>))", it is possible to harness entire Cairo library power in Ruby, hence we may very well provide a short description of what these backends are. Namely, they represent different output devices or media for displaying the created graphics. Followig is the short list:

    :List of backends
        * Mac OS X Quartz
        * Win32 GDI
        * X Window System
        * PDF
        * PNG
        * PostScript
        * SVG

    This means, providing we use the Gtk/Cairo library version, supporting these backends, we can draw on windows on OSX, Windows, Linux/BSDs as well as that we can use this library to create PNG or JPG image files, PDF files, PostScript files and SVG files. 



{{br}}
{{br}}
(12.3.0.1 /continued/){{br}}

The fact that in Ruby, Gtk/Cairo library does not provide functionality to create((*cairo context*))that could exist independently without being tied to an active Gtk Window, requires us to always run programs requiring Cairo functionality within Gtk main-loop. This makes it difficult to replicate the tutorial example code, that do not require Gtk Window and main loop. The best way to emulate the program examples from these tutorials is to provide some((*"Ruby Gtk/Cairo Framework"*))which will hide the presence of Gtk main loop and its infrastructure away as much as possible. The trouble is, in order to do that you have to use cairo library, of which at this point our students are still rather ignorant.

Though, once you will learn the basics of cairo library this((*Ruby Gtk/Cairo Framework*))will become a rather important programming device for all Ruby Gtk programmers, I should ask you not to worry about its inner workings at this point, and postpone your desire to understand it all before you get through a few program examples, that explain features used to build this framework. All you need to know at this point is to require the 'hiki2-gtk-w-cairo.rb' file and include 'GtkHiki' module it defines, and add the wrapper code as shown in the following minimal ruby cairo program:

# (12.3.0.1.1)
:Programmer's Note:

    (12.3.0.1.1.1)
    Note, we are switching the module file here. See "Student's Note" (12.3.0.1.1.1) below! The new module file 'hiki2-gtk-w-cairo.rb' is listed and explained at the end of this chapter in section 12.3.0.3 "((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>))". It can be copied from there. 


# (12.3.0.1.1.1.1)
    :Student's Note:

        (12.3.0.1.1.1.1)
        For use with cairo program examples we are introducing new ((*HikiGtk*)) module ((*'hiki2-gtk-w-cairo.rb'*)). Note, the module filename has changed, while the module name (HikiGtk) stays the same! 

        You should copy 'hiki2-gtk-w-cairo.rb' file into your program directory (folder), or into another directory of your choice. In any event you should make sure the directory in which the module file resides, is be included in Ruby's load path, just as was required earlier, and as originally explained in section:  12.3.1.1 [((<Time To Start Using Object-Oriented Programming Paradigm|tut-gtk2-dnd-intro#Time To Start Using Object-Oriented Programming Paradigm>))].




    ((*minimal-rb-cr.rb*))

    {{image_right("1203-min-cr-rb-prg-s1.png")}}
     #!/usr/bin/env ruby

     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk

     window=CairoWindow.new("Minimal Cairo Program") 
     window.show_all
     Gtk.main

    As you can see, if you run this program it displays an empty grey cairo drawing area.

# ((*"Establishing Gtk/Cairo Framework To Run The Tutorial Example Code"*))

    However, as the title of this paragraph (12.3.0.1) suggests, we need the Gtk/Cairo framework because we would like to run some cairo code of our own. It would be desirable, if we had a template file into which we could always include the new code. This is what we will explain next.

# (12.3.0.1.2)
:The Template Ruby File To Run Arbitrary Cairo Code:
    (12.3.0.1.2){{br}}

    Indeed, to create the template we are going to use the code shown in the above 'minimal-rb-cr.rb'program. Eventually, the new template will act as wrapper code, with a stub method called((*draw(cr, drawing_area).*)) Basically, the template is an executable program in which you create a new class inheriting directly from the CairoWindow, and overriding its((*draw*))method. This 'draw' method is where you will place your cairo code. Lets look at the empty ready to use template: 

     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk
     class MyCairoCodeClass < CairoWindow            # Your own CairoWindow class
       def draw(cr, drawing_area)                    # Override the draw method
         width, height = drawing_area.window.size    # You will likely need w, h
         # Your code comes here:                     #
         # ---------------------                     #
               o o o                                 # Your code comes here.
         # ---------------------                     #
       end                                           #					
     end                                             #
     window=MyCairoCodeClass.new        # Replace 'CairoWindow' with your new class name.
                                        # Optionally, supply the your name to the constructor. 
     window.show_all
     Gtk.main

    The lines containing comments are the additions that turn the above 'minimal-rb-cr.rb' program into our template file. Note, your cairo code will go between the two dashed lines. Also do not overlook the following line:

         width, height = drawing_area.window.size    # You will likely need w, h

    As the comment suggests, you are likely to need the size of the cairo drawing area, especially, when you would want the x,y coordinates depend on the size of the cairo "canvas". I suggest, you make it a habit to always include this line, hence it should be part of the template code.

    :A Real Life Example With Runing Cairo Code   

        (12.3.0.1.2.1)    {{image_right("1203-smiley-face-s1.png")}}
        In Michael Urman's Cairo tutorial called ((<Cairo Tutorial for PyGTK Programmers|URL:http://www.tortall.net/mu/wiki/PyGTKCairoTutorial>)), where Michael creates a similar Gtk/Cairo framework as is ours, you can find a neat piece of Python cairo code generating a((*smiley face.*)) I translated his smiley face Python code to Ruby, and run it in our Ruby Gtk/Cairo wrapper.

        ((*smiley-face.rb*))

         #!/usr/bin/env ruby
         $: << '~/work/HikiLib'
         require 'hiki2-gtk-w-cairo.rb'
         include HikiGtk

         class SmileyFace < CairoWindow
           def draw(cr, drawing_area)

             width, height = drawing_area.window.size

             # Your code comes here:
             # ---------------------
             # Fill the background with a colour of your choice
             cr.set_source_rgb(0.5, 0.5, 0.9)
             cr.rectangle(0, 0, width, height)
             cr.fill
             # draw a rectangle
             cr.set_source_rgb(1.0, 1.0, 1.0)
             cr.rectangle(10, 10, width - 20, height - 20)
             cr.fill
             # and a circle
             cr.set_source_rgb(1.0, 0.0, 0.0)
             radius = [width, height].min
             cr.arc(width / 2.0, height / 2.0, radius / 2.0 - 20, 0, 2 * Math::PI)
             cr.stroke
             cr.arc(width / 2.0, height / 2.0, radius / 3.0 - 10, Math::PI / 3, 2 * Math::PI / 3)
             cr.stroke
             # draw lines
             cr.set_source_rgb(0.0, 0.0, 0.8)
             cr.move_to(width / 3.0, height / 3.0)
             cr.rel_line_to(0, height / 6.0)
             cr.move_to(2 * width / 3.0, height / 3.0)
             cr.rel_line_to(0, height / 6.0)
             cr.stroke
             # ---------------------
           end
         end

         w=SmileyFace.new("Smiley Face")
         w.show_all
         w.set_size_request(75, 50)
         Gtk.main

Now, all you need to run these programs above is to copy the((*'hiki2-gtk-w-cairo.rb'*))module file into your working directory. You will find it near the end of this chapter (look for the section 12.3.0.3 ((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>)).

{{br}}
{{br}}





=== Running Cairo Code From Non-Gtk Ruby Environment

One reason I separated this paragraph from the rest of the tutorial is to stress the difference between using 'the into Ruby Gtk integrated Cairo Library' from the original pure (you can say stand-alone) Cairo Library. The first, integrated one, is available to Gtk programmers automatically, i.e. requiring Gtk module is all that is needed. But, if you want the full featured Cairo library, you need to((*require 'cairo'*)) yourself. Indeed, you can do that even if you are not running Gtk at all, that is you do not need to((*require 'gtk2',*)) and obviously, without this module you also can not run the gtk-main loop.

The examples in ((<the official Cairo Tutorial|URL:http://www.cairographics.org/tutorial/>)) run outside any particular GUI system, hence, we should not, and we do not, have any trouble running them in Ruby directly without the Gtk. But, since this is the Gtk Tutorial, which, as anybody who read most of it before any Cairo features even came up, knows that Cairo should be part of the Gtk. Unfortunately, for those of us who have been struggling to learn or master Gtk for a while, encountering Cairo outside our GUI, it may seem odd, why would anyone consider running 2D graphic programs from a text based terminal. Why would I even try to look for solutions outside my GUI environment which as I could see includes Cairo Library. It never crossed my mind, that by requiring 'cairo' module, I'd gain power of pure Cairo beyond that available in my GUI!

The fact is, that's exactly what you should do, to translate and run the official Cairo Tutorial examples in Ruby. Hence, you should forget, about Ruby's Gdk::Drawable#create_cairo_context, and resort to plain old Cairo ways of creating it via:

 surface = Cairo::XyzSurface.new(width, height)
 cr = Cairo::Context.new(surface)
 # -- Your cairo code starts here -------------- -s-
 #      o o o
 # -- Your cairo code ends here ---------------- -e-
 cr.target.write_to_png("output-file.png")

by replacing the word "Xyz" above with one of the following { Image, PDF, PS, SVG, ... }, including your cairo code and at the end write the contents of the cairo surface you created to the file.

In this tutorial we will favour the integrated Gtk/Cairo, rather than "stand-alone" Cairo, drawing approach, though sometimes it would be more convenient to use Cairo in a non-graphical text based environment. In paragraph 12.3.1.2.3 "((<Show Text - Glyphs|tut-gtk2-dancr-rbcatut-intro#Show Text - Glyphs>))" you can see both of these two approaches.


#


{{br}}
{{br}}


# {{image_right("")}}
# ((<|URL:http://...>))


== Cairo's Drawing Model
(12.3.1){{br}}

In order to explain the operations used by cairo, we first delve into a model of how cairo models drawing. There are only a few concepts involved, which are then applied over and over by the different methods. First I'll describe the nouns: destination, source, mask, path, and context. After that I'll describe the verbs which offer ways to manipulate the nouns and draw the graphics you wish to create.




{{br}}

=== Nouns
(12.3.1.1){{br}}

Cairo's nouns are somewhat abstract. To make them concrete I'm including diagrams that depict how they interact. The first three nouns are the three layers in the diagrams you see in this section. The fourth noun, the path, is drawn on the middle layer when it is relevant. The final noun, the context, isn't shown.



:Destination
    (12.3.1.1.1){{br}}

    {{image_right("123-01-destination.png")}}

    The destination is the surface on which you're drawing. It may be tied to an array of pixels like in this tutorial, or it might be tied to a SVG or PDF file, or something else. This surface collects the elements of your graphic as you apply them, allowing you to build up a complex work as though painting on a canvas.


:Source
    (12.3.1.1.2){{br}}

    {{image_right("123-02-source.png")}}

    The ((<source|URL:http://www.cairographics.org/manual/cairo-surfaces.html>)) is the "paint" you're about to work with. I show this as it is用lain black for several examples傭ut translucent to show lower layers. Unlike real paint, it doesn't have to be a single color; it can be a pattern or even a previously created destination surface (see: ((<How do I paint from one surface to another?|URL:http://cairographics.org/FAQ/#paint_from_a_surface>))). Also unlike real paint it can contain transparency information葉he Alpha channel.







:Mask
    (12.3.1.1.3){{br}}

    {{image_right("123-03-mask.png")}}

    The mask is the most important piece: it controls where you apply the source to the destination. I will show it as a yellow layer with holes where it lets the source through. When you apply a drawing verb, it's like you stamp the source to the destination. Anywhere the mask allows, the source is copied. Anywhere the mask disallows, nothing happens.


:Path
    (12.3.1.1.4){{br}}

    The path is somewhere between part of the mask and part of the context. I will show it as thin green lines on the mask layer. It is manipulated by path verbs, then used by drawing verbs.


:Context
    (12.3.1.1.5){{br}}


    The context keeps track of everything that verbs affect. It tracks one source, one destination, and one mask. It also tracks several helper variables like your line width and style, your font face and size, and more. Most importantly it tracks the path, which is turned into a mask by drawing verbs.

    Before you can start to draw something with cairo, you need to create the context. In Ruby you obtain context with Gdk::Drawable#create_cairo_context instance method. Note, you do not supply the ((<source|URL:http://www.cairographics.org/manual/cairo-surfaces.html>)) to cairo create method. Instead, you invoke the((*create_cairo_context*)) instance method on a Gdk::Drawable object, which can be either Gdk::Pixmap,  or a Gdk::Window. (Original GTK API for Gdk::Drawable also lists((*bitmap,*)) however, it fails to identify it sufficiently?)

    :Caveat
        {{image_right("dialog-warning.png")}}
        In traditional 'C' GTK Cairo implementations and in its cousins like 'gtkmm', cairo context is typically bound to different surfaces, however, currently in Ruby, other than active GTK Window surfaces are not available!  (see section 12.3.0.1.1.)





# {{image_right("")}}
# ((<|URL:http://...>))



{{br}}

=== Verbs
(12.3.1.2){{br}}

The reason you are using cairo in a program is to draw. Cairo internally draws with one fundamental drawing operation: the source and mask are freely placed somewhere over the destination. Then the layers are all pressed together and the paint from the source is transferred to the destination wherever the mask allows it. To that extent the following five drawing verbs, or operations, are all similar. They differ by how they construct the mask.


:Stroke
    {{image_right("123-04-stroke.png")}}
    (12.3.1.2.1)

    The ((<cairo_stroke()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-stroke>)) operation takes a virtual pen along the path. It allows the source to transfer through the mask in a thin (or thick) line around the path, according to the pen's ((<line width|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-width>)), ((<dash style|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash>)), and ((<line caps|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-line-cap>)).

    {{br}}{{image_right("1203-p04-00-stroke.png")}}

     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk

     class StrokeDemo < CairoWindow
       def draw(cr, da)
         #width, height = da.window.size
         # Your code goes between the two dashed lines:
         # -- your code - start -------------------- -s-
         cr.scale(120, 120)
         cr.line_width = 0.1
         cr.set_source_rgb(0, 0, 0)
         cr.rectangle(0.25, 0.25, 0.5, 0.5)
         cr.stroke
         # -- your code - end ---------------------- -e-
       end
     end # // EndOf class StrokeDemo
 
     window = StrokeDemo.new("Stroke Tut. Example")
     window.show_all
     Gtk.main

    :Scale
        (12.3.1.2.1.A1){{br}}


        This example introduces an unnecessary complication, that needs additional explanation, which in original Cairo tutorial, Michael defers until paragraph 12.3.4 ((<Working with Transforms|tut-gtk2-dancr-rbcatut-dwc#Working with Transforms>)) below. I found such treatment of this issue rather confusing for a beginner learning Cairo library basics. Namely, the transforms are too advanced for you to understand at this point, whereas, the scale method is far less complicated than a beginner might think after introducing it too quickly without explaining what it really means.

        Looking at the output of the above (12.3.1.2.1) example code does not reveal the important details. A much better results would be achieved, if the((*default scale*))were used. The default scale, namely is set as((*scale(1, 1).*)) However, this would require, that rectangle coordinates were given in pixels rather than in relative notation (i.e. as fractions), which calls for a short detour explaining these issues. This actually is exactly what we here are doing.

        The most important thing here is understanding the parameters passed to the scale method:((*scale(sx, sy).*)) The((*sx*))and((*sy*)) parameters actually represent ((*pixels.*)) Namely,((*scale(1,1)*)) means that your working area will be measured in pixels. The size of 1 pixel depends on the device (display) you are using. Commonly one pixel is 1/75" = 0.0133". This means that if you use a 75dpi display, for instance 100 pixels will translate to 1.33" (3.378 cm). Now, if you set cairo context's scale to((*scale(2,2),*)) 100 pixels would translate to twice that, i.e. 2.66" (6.756 cm). Imagine using((*scale(100,100).*)) That would translate to  133" (337.8 cm). Unless you are very rich, I do not think, you will ever have a display capable of displaying such a large image!

        However, as you have seen in the above example (12.3.1.2.1), it is possible to use scale with values of their((*sx*))and((*sy*))arguments that resemble sizes of images in pixels. In this cases you should not use pixels as units for the coordinates of the shapes you are drawing. Instead, you should use relative notation in the form of fractions, most likely, but not exclusively, between 0 and 1, where for ((*scale(sx=100, sy=150)*))x=1 would mean 100 pixels, and x=1 would translate to 150 pixels on your display.
        
        Another thing to be aware of when modifying scale is the line thickness. Namely, it too is affected when switching between single digit scale settings to settings in hundreds of pixels. This may become very confusing if one is not careful, particularly when your line thickness becomes larger than your object or a geometric shape you are drawing. 

    :Default Scale Example
        (12.3.1.2.1.A2){{br}}
        {{image_left("1203-p04-scale_1,1-rect_0,0,50,50-in-drwArea-w-rulers-line-colors-c-s1.png")}}

        For your convenience I also include an example using the default scale, i.e.:((*scale(1,1)*)) and pixels as x,y coordinates used in arguments to shape drawing methods. I also included the image collage, showing three different aspects of the graphics presentation. The first is the usual top Gtk window. For the second and the third, which are the images of the entire cairo drawing area, and the image only of the cr.rectangle(0, 0, 50, 50) respectively, I use GIMP graphic editor with rulers, to enlarge the relevant parts of drawing area and more clearly expose the sizes of both items. I intentionally changed Michael's rectangle's coordinates x1, y1 from (0.25, 0.25) to (0, 0), so the 0.5x0.5 square (now in pixels (50x50) is drawn directly from the 'centre' of the graphics coordinate system (in which, as you know the positive x axis runs from the top left corner of the drawing area to the right, and the positive y coordinate runs down along the left.) If you look careful
 ly, you will see, that the line thickness along x,y axis is only 1/2 of the true size (at the right and at the bottom sides of the square.) I changed the colour of the line from black (0, 0, 0) to pinkish (0.5, 0, 0) in order to better facilitate viewing of different colouration schemes in graphic editors (see the dashed line in the image above on the left).

        {{image_right("1203-p04-scale_1,1-rect_0,0,50,50-collage.png")}}
        
         #!/usr/bin/env ruby
         $: << '~/work/HikiLib'
         require 'hiki2-gtk-w-cairo.rb'
         include HikiGtk
         
         class DefaultScaleDemo < CairoWindow
         
           def draw(cr, da)
             #width, height = da.window.size
             # Your code goes between the two dashed lines:
             # -- your code - start -------------------- -s-
             # cr.scale(1, 1)  # scale(1, 1) is the default
             cr.line_width = 1
             cr.set_source_rgb(0.5, 0, 0)
             cr.rectangle(0, 0, 50, 50)
             cr.stroke
             # -- your code - end ---------------------- -e-
           end
         end # // EndOf class DefaultScaleDemo
         
         window = DefaultScaleDemo.new("12. default scale")
         window.set_size_request(100, 75)
         window.show_all
         Gtk.main

    Feel free to experiment with both, the original Michael's version of stroke.rb of code above (12.3.1.2.1), and my version (12.3.1.2.1.A2) using the default scale(1, 1), rather than scale(120, 120). Most importantly, you have to realize that the two examples use different kinds of coordinate arguments in shape drawing methods. 

    :Rule of thumb
        (12.3.1.2.1.A3){{br}}

        As a rule of thumb, for scale(1, 1) drawings you should use pixels, and for drawings whose scale argument values resemble image size in pixels (i.e. values in hundreds rather than single digits) you should use relative notation in the form of small fractions not much larger than 5 or 10. Though, if you insist and do not care to simplify your work, you can try using very large or very small fractions when needed. (For instance line thickness 0.01 on one end, and say 150 on the other, is rather disconcerting, if you are not an abstract minded person, and prefer to operate in with naked eye observable reality.) I just think, the above rule of thumb makes sense.






:Fill
    (12.3.1.2.2){{br}}

    {{image_right("123-05-fill.png")}}

    The ((<cairo_fill()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill>)) operation instead uses the path like the lines of a coloring book, and allows the source through the mask within the hole whose boundaries are the path. For complex paths (paths with multiple closed sub-paths様ike a donut熔r paths that self-intersect) this is influenced by the ((<fill rule|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-fill-rule>)). Note that while stroking the path transfers the source for half of the line width on each side of the path, filling a path fills directly up to the edge of the path and no further.

    {{br}}{{image_right("1203-p05-00-fill.png")}}

     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk
     
     class FillDemo < CairoWindow
     
       def draw(cr, da)
     #     width, height = da.window.size
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
         cr.scale(120, 120)
         cr.line_width = 0.1
         cr.set_source_rgb(0, 0, 0)
         cr.rectangle(0.25, 0.25, 0.5, 0.5)
         cr.fill
         # -- your code - end ------------------------------------------- -e-
       end
       end # // EndOf class FillDemo

     window = FillDemo.new("Fill Tut. Example")
     window.show_all
     Gtk.main



{{br}}
:Show Text - Glyphs
    (12.3.1.2.3){{br}}

    {{image_right("123-06-showtext.png")}}

    The ((<cairo_show_text()|URL:http://www.cairographics.org/manual/cairo-text.html#cairo-show-text>)) operation forms the mask from text. It may be easier to think of cairo_show_text() as a shortcut for creating a path with ((<cairo_text_path()|URL:http://www.cairographics.org/manual/cairo-Paths.html#cairo-text-path>)) and then using ((<cairo_fill()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill>)) to transfer it. Be aware cairo_show_text() caches glyphs so is much more efficient if you work with a lot of text.


    {{br}}{{image_right("1203-p06-showtext-a.png")}}

     #!/usr/bin/env ruby
     require 'cairo'
     surface = Cairo::ImageSurface.new(120, 120)
     cr = Cairo::Context.new(surface)
     cr.scale(120, 120)
     cr.select_font_face(
       font_family = "Georgia", 
       font_slant  = Cairo::FONT_SLANT_NORMAL,
       font_weight = Cairo::FONT_WEIGHT_BOLD
     )
     cr.set_font_size(1.2)
     cr.set_source_rgb(0, 0, 1)
     cr_te = cr.text_extents("a")
     cr.move_to(0.5 - cr_te.width / 2  - cr_te.x_bearing, 
                0.5 - cr_te.height / 2 - cr_te.y_bearing)
     cr.show_text("a")
     puts "DEBUG: cr.target.class=#{cr.target.class}"	#=> Cairo::ImageSurface
     cr.target.write_to_png("textextents-pure-cr.png")


    This code, written for non-GUI environment, produces the output png image file, shown above on the right. Note, that the letter is printed on transparent surface. Indeed, to view the output of this program we have to write the contents of the surface to a file utilizing surface's write-to file method. Note, that the surface written to a file here is saved in "cairo context's target attribute". 


    Now, lets look at the same cairo drawing as we would write it in a Ruby Gtk/Cairo version of the "Show Text / Glyphs" example program. I took liberty to add and change a few things. If you'd like to write the contents of the cairo drawing area to a file, of course, you should employ Ruby Gtk's file manipulation behaviour, explained in section 12.2 "((<Writing the Image Of a Drawing Area Into a Pixbuf File|tut-gtk2-dancr-wr-dapxb2file>))".

    #{{br}}{{image_right("1203-p06-showtext-rb-s1.png")}}

    {{br}}{{image_right("1203-p06-showtext-in-gtkCairo.png")}}

     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk
     class TextExtendsDemo < CairoWindow
       def draw(cr, da)
         #width, height = da.window.size
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
     
         # ------------------- clone: drawing surface creation ---- -s-
         # Frame what would be the drawing area, if image_surface_create 
         # were available in Ruby:
         cr.scale(120, 120)
         cr.line_width = 0.01
         cr.set_source_rgb(1, 0, 0)
         cr.rectangle(0, 0, 1, 1)
         cr.stroke
         # ------------------- clone: drawing surface creation ---- -e-
        cr.select_font_face(
           font_family = "Georgia", 
           font_slant  = Cairo::FONT_SLANT_NORMAL,	# .... must: require 'cairo'
           font_weight = Cairo::FONT_WEIGHT_BOLD	# .... must: require 'cairo'
        )
         cr.set_font_size(1.2)
         cr.set_source_rgb(0, 0, 1)
     
         cr_te = cr.text_extents("a")
         cr.move_to(0.5 - cr_te.width / 2  - cr_te.x_bearing, 
                    0.5 - cr_te.height / 2 - cr_te.y_bearing)
         cr.show_text("a")
         # -- your code - end ------------------------------------------- -e-
       end
     end # // EndOf class TextExtendsDemo
     
     window = TextExtendsDemo.new("TextExtends Tut. Example")
     window.show_all
     Gtk.main




{{br}}
:Paint
    (12.3.1.2.4){{br}}

    {{image_right("123-07-paint.png")}}

    The ((<cairo_paint()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-paint>)) operation uses a mask that transfers the entire source to the destination. Some people consider this an infinitely large mask, and others consider it no mask; the result is the same. The related operation ((<cairo_paint_with_alpha()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-paint-with-alpha>)) similarly allows transfer of the full source to destination, but it transfers only the provided percentage of the color.

     # Your code goes between the two dashed lines:
     # -- your code - start ----------------------------------------- -s-
     cr.set_source_rgb(0, 0, 0.9)
     cr.paint(0.2)
     # -- your code - end ------------------------------------------- -e-

    :Paint And Colour Transparency In Ruby Cairo Library
        (12.3.1.2.4.A1){{br}}
        In Ruby both cairo_paint() and cairo_paint_with_alpha() functions were replaced with a single cairo context's method ((*cr.paint(alpha=nil).*)) Invoking this method without the argument gives you the behaviour of "cairo_paint()", with the parameter you get the "cairo_paint_with_alpha()" behaviour. 

        The above example is not a very interesting one. It merely paints the background, a.k.a. cairo drawing surface with a transparent light blue colour. You cannot even see the effect of this colour transparency. In Cairo library, you can use a transparent pen or paint-brush (i.e. source) by specifying the alpha foreground colour by setting it via cairo context's ((*set_source_rgba(red, green, blue, alpha)*)) rather than ((*set_source_rgb(red, green, blue)*)) method. 

        Now, that you you know a bit about the transparency in Cairo, let's make the above example program a bit more interesting. We are going to write three versions. The first (1), without using the paint section. Second (2), by painting the background with transparent light blue colour, ant third (3), by painting the background transparently light greenish.

        1. White background:
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
         ### cr.set_source_rgb(0, 0, 0.9) # ......... commented out!
         ### cr.paint(0.2) # ........................ commented out!
         # -- your code - end ------------------------------------------- -e-

        2. Transparent light blue background
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
         cr.set_source_rgb(0, 0, 0.9)
         cr.paint(0.2)
         # -- your code - end ------------------------------------------- -e-

        3. Transparent light green background
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
         cr.set_source_rgb(0, 1, 0)
         cr.paint(0.2)
         # -- your code - end ------------------------------------------- -e-

        Following is the entire program. Indeed, you have to write three versions, by modifying the paint sections as described above: 

        {{image_right("1203-p7-paint-w-alpha-s1.png")}}


         #!/usr/bin/env ruby
         $: << '~/work/HikiLib'
         require 'hiki2-gtk-w-cairo.rb'
         include HikiGtk
         class PaintTransparencyDemo < CairoWindow
           def draw(cr, da)
             #width, height = da.window.size

             # making the example program more interesting ----- -s-
             cr.scale(120, 120)
             cr.set_source_rgba(1, 0, 1, 0.5)
             cr.rectangle(0.25, 0.25, 0.5, 0.5)
             cr.fill

             cr.line_width = 0.05
             cr.set_source_rgb(0, 1, 0)
             cr.move_to(0, 0)
             cr.rel_line_to(1.2, 1.2)
             cr.stroke

             cr.set_source_rgba(1, 0, 1, 0.5)
             cr.rectangle(0.25+0.1, 0.25+0.3, 0.5, 0.5)
             cr.fill
             # making the example program more interesting ----- -e-

             # Your code goes between the two dashed lines:
             # -- your code - start ----------------------------------------- -s-    
             cr.set_source_rgb(0, 0, 0.9)
             cr.paint(0.2)
             # -- your code - end ------------------------------------------- -e-
           end
         end # // EndOf class PaintTransparencyDemo
         window = PaintTransparencyDemo.new("Paint Transparency Tut. Example")
         window.show_all
         Gtk.main


# {{image_right("")}}
# ((<|URL:http://...>))



{{br}}
:Mask
    (12.3.1.2.5){{br}}

    {{image_right("123-08-mask.png")}}

    The ((<cairo_mask()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-mask>)) and ((<cairo_mask_surface()|URL:http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-mask-surface>)) operations allow transfer according to the transparency/opacity of a second source pattern or surface. Where the pattern or surface is opaque, the current source is transferred to the destination. Where the pattern or surface is transparent, nothing is transferred.

    :Ruby Gtk-Cairo Library Fails To Support Gradient Patterns
    (12.3.1.2.5.A1){{br}}
    However, as mentioned in section 12.3.0.2 ((<Running Cairo Code From Non-Gtk Ruby Environment|tut-gtk2-dancr-rbcatut-intro#Running Cairo Code From Non-Gtk Ruby Environment>)), we may go around this restriction, if we resort to pure Cairo library. Note, that you have to((*require 'cairo'*)) explicitly, unless you use our 'hiki2-gtk-w-cairo.rb' module which unlike the((*'gtk2'*)) requires it for you. Pay attention to how we create a non-GUI cairo context, how the size of the image is identical to our scale. Also, since we are not using the Gtk, at the end we have to make sure our 'art' work can be viewed - we save our cairo drawing surface into a file on the disk.

    Following is the complete example program using Cairo library from a non-Gtk environment: 

    {{br}}{{image_right("1203-p08-mask-in-C.png")}}
     #!/usr/bin/env ruby
     require 'cairo'

     # Create cairo context for image surface
     surface = Cairo::ImageSurface.new(120, 120)
     cr = Cairo::Context.new(surface)
     cr.scale(120, 120)

     # Set the gradient patterns
     linearg = Cairo::LinearPattern.new(0, 0, 1, 1)
     linearg.add_color_stop_rgb(0,    0, 0.3, 0.8)
     linearg.add_color_stop_rgb(1,    0, 0.8, 0.3)
     radialg = Cairo::RadialPattern.new(0.5, 0.5, 0.25, 0.5, 0.5, 0.75)
     radialg.add_color_stop_rgba(0,   0, 0,   0,    1)
     radialg.add_color_stop_rgba(0.5, 0, 0,   0,    0)
     
     cr.set_source(linearg)
     cr.mask(radialg)
     
     cr.target.write_to_png("mask-cairo-no-gtk.png")
     puts "DEBUG: cr.target.class=#{cr.target.class}"	#=> Cairo::ImageSurface


    {{br}}Let's look at a Gtk version of the same program.

    As mentioned at the beginning of this paragraph, if you use our 'hiki2-gtk-w-cairo.rb' module it includes the needed ((*require 'cairo'*)) declarative, hence, you should now have access to otherwise unsupported Cairo functionality even from your Gtk programs. Following is the above program, run from within the Ruby Gtk main-loop, indeed, I had to change it just a little bit, so we can learn few new things. Most importantly, you have to be aware of the scale. Here, we are now using the default scale ((*(cr.scale(1,1)*)), which requires that we use pixel, rather than relative coordinates (see:  section 12.3.1.2.1.A1 ((<Scale|tut-gtk2-dancr-rbcatut-crdrmd#Scale>)).)

    {{br}}{{image_right("1203-p08-mask-in-gtkCairo.png")}}
     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk
     class MaskDemo < CairoWindow
     
       def draw(cr, da)
         #width, height = da.window.size
         # ------------------- clone: drawing surface creation ---- -s-
         # Frame what would be the drawing area, if image_surface_create 
         # were available in Ruby:
         # cr.scale(120, 120)	# NOTE: Using default scale: scale(1,1)
         cr.line_width = 1
         cr.set_source_rgb(1, 0, 0)
         cr.rectangle(0, 0, 100, 100)
         cr.stroke
         # ------------------- clone: drawing surface creation ---- -e-
     
         # Your code goes between the two dashed lines:
         # -- your code - start ----------------------------------------- -s-
         # Draw a shape to be effected by the gradient patterns below 
         cr.line_width = 2
         cr.set_source_rgb(0, 0.5, 0)
         cr.rectangle(15, 15, 70, 70)
         cr.stroke
         # Set the gradient patterns
         linearg = Cairo::LinearPattern.new(0, 0, 100, 100)
         linearg.add_color_stop_rgb(0,    0, 0.3, 0.8)
         linearg.add_color_stop_rgb(1,    0, 0.8, 0.3)
         radialg = Cairo::RadialPattern.new(50,   50,  25,   50,  50,  75)
         radialg.add_color_stop_rgba(0,   0, 0,   0,    1)
         radialg.add_color_stop_rgba(0.5, 0, 0,   0,    0)
         cr.set_source(linearg)
         cr.mask(radialg)
         # -- your code - end ------------------------------------------- -e-
       end
     end # // EndOf class MaskDemo
     
     window = MaskDemo.new("Mask Tut. Example")
     window.show_all
     Gtk.main
{{br}}
{{br}}





# ------- AS PROMMISED Hiki Gtk With Cairo Module Download File -----------
{{br}}
{{br}}
# (12.3.0.3) // uset to be marked: (12.3.XXXX)
== Hiki Gtk With Cairo Module Download File
(12.3.0.3){{br}}

#### (12.3.XXXX) "'Hiki Gtk With Cairo' module file" ------ (section:  12.3.1.1 [((<Time To Start Using Object-Oriented Programming Paradigm|tut-gtk2-dnd-intro#Time To Start Using Object-Oriented Programming Paradigm>))], however, as you will see after this example program, we are going to expand and upgrade our((*HikiGtk*))module to include background manipulation functionality. ----- , however, as you will see after this example program, we are going to expand and upgrade our((*HikiGtk*))module to include background manipulation functionality. 



# ------- REQUIRED FILES: -----------
{{br}}
{{br}}
(12.3.XXXX+1){{br}}
:To run the examples in this article download the following files into your program directory:

#    See: (12.1.3)
   
# {{image_left("")}} ....... Name it: ((*"".*))
{{br}}

# {{image_right("")}}
# ((<|URL:http://...>))




ruby-gnome2-cvs メーリングリストの案内
Back to archive index