Painting in Qyoto II

In this part of the Qyoto C# programming tutorial we will continue with painting. We will present some more complex examples.

Donut Shape

The firs example creates a complex shape by rotating a bunch of ellipses.

using System;
using QtCore;
using QtGui;

/**
 * ZetCode Qyoto C# tutorial
 *
 * This program draws a donut
 * shape.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{    
    public QyotoApp() 
    {
        WindowTitle = "Donut";

        PaintEvent += OnPaintEvent;
        
        Resize(350, 280);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    {
        QPainter ptr = new QPainter(this);
        DrawDonut(ptr);
        
        ptr.End();
     }
    
    void DrawDonut(QPainter ptr) 
    {
        QColor col = new QColor();
        col.SetNamedColor("#333333");

        ptr.Pen = new QPen(col, 0.5);

        ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);

        int h = Height;
        int w = Width;

        ptr.Translate(new QPoint(w/2, h/2));

         for (double rot=0; rot < 360.0; rot+=5.0 ) 
         {
             ptr.DrawEllipse(-125, -40, 250, 80);
             ptr.Rotate(5.0);
         }
    }

    [STAThread]
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

In this example, we create a donut. The shape resembles a cookie, hence the name donut.

QColor color = new QColor();
color.SetNamedColor("#333333");

We can use a hexadecimal notation to create a colour object.

int h = Height;
int w = Width;

Here we determine the width and height of the window.

ptr.Translate(new QPoint(w/2, h/2));

We move the coordinate system to the middle of the window. This way we make the drawing mathematically easier.

for (double rot=0; rot < 360.0; rot+=5.0 ) 
{
    ptr.DrawEllipse(-125, -40, 250, 80);
    ptr.Rotate(5.0);
}

We draw an ellipse object 72 times. Each time, we rotate the ellipse by 5 degrees. This will create our donut shape.

Donut
Figure: Donut

Grayscale image

In the following example, we will create a gray scale image.

using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this example, we create a 
 * grayscale image.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{    
    QImage sid;
    int w, h = 0;

    public QyotoApp() 
    {
        WindowTitle = "Gray scale";
         
        PaintEvent += OnPaintEvent;        

        LoadImage();
        Resize(320, 150);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    { 
        QPainter ptr = new QPainter(this);
        DrawImages(ptr);
        
        ptr.End();
    }
    
    void DrawImages(QPainter ptr) 
    {
        ptr.DrawImage(5, 15, sid);
        ptr.DrawImage(w + 10, 15, GrayScale(sid.Copy()));
    }

    void LoadImage()
    {
        sid = new QImage("smallsid.jpg");

        w = sid.Width();
        h = sid.Height();
    }

    QImage GrayScale(QImage img)
    {        
        for (int i=0; i < w; i++)
        {
            for (int j=0; j < h; j++)
            {
                uint c = img.Pixel(i, j);
                int gray = Global.qGray(c);
                int alpha = Global.qAlpha(c);
                img.SetPixel(i, j, Global.qRgba(gray, gray, 
                   gray, alpha));                
            }
        }  

        return img;
    }          
    
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

We have a colour JPG image. We draw it on the window. We create a copy of the image, convert it to gray scale and draw it on the window next to the original image.

void LoadImage()
{
    sid = new QImage("smallsid.jpg");

    w = sid.Width();
    h = sid.Height();
}

In the LoadImage() method we load the image and get its width and height.

QImage GrayScale(QImage img)
{        
    for (int i=0; i < w; i++)
    {
        for (int j=0; j < h; j++)
        {
            uint c = img.Pixel(i, j);
            int gray = Global.qGray(c);
            int alpha = Global.qAlpha(c);
            img.SetPixel(i, j, Global.qRgba(gray, gray, 
                gray, alpha));                
        }
    }  

    return img;
}    

The GrayScale() method transforms an image to gray scale and returns it. We go through all pixels of the image. The Pixel() method returns a pixel in question. We use the Global.qGray() method to get the gray value of the specific pixel. Similarly, we get the alpha value. Finally, we modify the pixel with the SetPixel() method. We use the gray value for the red, green and blue parts of the colour.

Grayscale image
Figure: Grayscale image

Reflection

In the next example we show a reflected image. The effect makes an illusion as if the image was reflected in water.

using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this example we create a reflected image.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{
    QImage img;
    QImage reflected_img;

    int iw, ih = 0;
    double initial_opacity = 0.7;
    double opacity = 0.7;
    double step = 0;
    const int GAP = 30;
    
    public QyotoApp() 
    {
        WindowTitle = "Reflection";
        
        PaintEvent += OnPaintEvent;

        InitExample();  

        Resize(300, 400);
        Move(150, 150);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    { 
        QPainter ptr = new QPainter(this);
        DrawImages(ptr);
        
        ptr.End();
    }

    void InitExample()
    {
        img = new QImage("slanec.png");

        if (img.IsNull()) 
        {
            Console.WriteLine("Error loading image");
        }

        iw = img.Width();
        ih = img.Height();

        step = opacity / ih;    

        reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);
        CreateReflectedImage();
    }       

    void CreateReflectedImage()
    {
        QPainter fptr = new QPainter(reflected_img);

        int i = 0;
        double opacity = 0.7;    

        while (i < ih)
        {        
            i++;
            opacity = opacity - step;

            fptr.Opacity = initial_opacity-opacity;
            fptr.DrawImage(0, i, img, 0, i, -1, 1);
        }          

        fptr.End();
    }
    
    void DrawImages(QPainter ptr) 
    {
        int w = Width;
        int h = Height;

        ptr.FillRect(0, 0, w, h, Qt.GlobalColor.black);
        ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);

        QRect r = new QRect(25, 15, iw, ih);
        ptr.DrawImage(r, img);

        ptr.Translate(0, 2 * ih + GAP);
        ptr.Scale(1, -1);     

        ptr.DrawImage(25, 0, reflected_img);      
    }
    
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

We load an image from the current working directory. We create another empty image of the same size. We copy the original image to the new empty image line by line, applying the gradually increasing transparency.

img = new QImage("slanec.png");

if (img.IsNull()) 
{
    Console.WriteLine("Error loading image");
}

We load a PNG image and do some error checking.

iw = img.Width();
ih = img.Height();

step = opacity / ih;     

We get the image width and height. The step variable controls the intensity of the fading out of second image.

reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);

A new empty image is created. It has the size of the original image.

void CreateReflectedImage()
{
    QPainter fptr = new QPainter(reflected_img);
    ...

In the CreateReflectedImage() method, we draw on the empty image.

while (i < ih)
{        
    i++;
    opacity = opacity - step;

    fptr.Opacity = initial_opacity-opacity;
    fptr.DrawImage(0, i, img, 0, i, -1, 1);
}     

We copy the original image to the new image. Line by line. The opacity decreases by step each loop.

QRect r = new QRect(25, 15, iw, ih);
ptr.DrawImage(r, img);

The first image is drawn on the window.

ptr.Translate(0, 2 * ih + GAP);
ptr.Scale(1, -1);     

ptr.DrawImage(25, 0, reflected_img);   

Here we move the second image down, some space below the original one. The Scale() method flips the image upside down. Note that the translation is twice the image height. This is necessary, because the scaling operation not only flips the image, but also moves the image up. To understand this, simply take a photograph, place it on the table and flip it.

A reflected image
Figure: A reflected image

Waiting effect

In this example, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion that a line is moving. Such effects are often used to inform users that a lengthy task is going on behind the scenes. An example is streaming video over the Internet.

using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * This program draws basic shapes
 * available in Qyoto.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{
    int count = 0;
    
    double[,] trs = 
    {
        { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
        { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
        { 0.9, 1.0,  0.0,  0.15, 0.3, 0.5, 0.65, 0.8 },
        { 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5, 0.65 },
        { 0.65, 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5 },
        { 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15, 0.3 },
        { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15 },
        { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0 }
    };
    
    public QyotoApp() 
    {
        WindowTitle = "Waiting";

        PaintEvent += OnPaintEvent;

        InitExample();
        
        Resize(300, 200);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    {
        QPainter ptr = new QPainter(this);
        DrawLines(ptr);
        
        ptr.End();
    }
    
    void InitExample()
    {    
        count = 0;
        StartTimer(105);
    }

    void DrawLines(QPainter ptr)
    {
        QPen pen = new QPen();
        pen.Width = 3;
        pen.CapStyle = PenCapStyle.RoundCap;

        int w = Width;
        int h = Height;
       
        ptr.Translate(w/2, h/2);
        ptr.Pen = pen;

        int len = trs.GetLength(0);

        for (int i=0; i < len; i++)
        {
            ptr.Opacity = trs[count%8, i];
            ptr.DrawLine(0, -10, 0, -40);
            ptr.Rotate(45);
        }
    }

    protected override void OnTimerEvent(QTimerEvent e)
    {
        count++;
        Repaint();
    }
    
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

We draw eight lines with eight different alpha values.

double[,] trs = 
{
    { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
    { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
...

This is an array of transparency values. There are 8 rows, each for one position. Each of the 8 lines will continuously use these values.

count = 0;
StartTimer(105);

Here we initiate the count value and start a timer.

QPen pen = new QPen();
pen.Width = 3;
pen.CapStyle = PenCapStyle.RoundCap;

We make the lines a bit thicker, so that they are more visible. We draw the lines with rounded caps. Lines with rounded caps look better.

for (int i=0; i < len; i++)
{
    ptr.Opacity = trs[count%8, i];
    ptr.DrawLine(0, -10, 0, -40);
    ptr.Rotate(45);
}

In this loop, we set an opacity value. We draw the line and rotate it. This creates an illusion of a moving and fading line.

protected override void OnTimerEvent(QTimerEvent e)
{
    count++;
    Repaint();
}

Each time the timer event is called, we increase the count value and repaint the window area.

Waiting effect
Figure: Waiting effect

In this part of the Qyoto C# programming tutorial, we finished talking about painting in Qyoto.