Draw tutorial
Table of contents
1. Basic drawing operations
2. Offsets and clipping
3. Fonts and font metrics
4. DrawingDraw
5. ImageDraw
6. Printing
1. Basic drawing operations
Draw class is base class representing graphical output. It is intentionally designed with quite limited set of easy to use drawing primitives. Unlike most of other similar classes in competing toolkits, U++ drawing operations are stateless - there is no separate setup of e.g. line width, pen color etc, all necessary painting attributes are parameters of respective methods.
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
void Paint(Draw& w) override {
w.DrawRect(GetSize(), White());
w.DrawRect(10, 10, 60, 80, Green());
w.DrawLine(100, 10, 160, 80, 0, Black());
w.DrawLine(160, 10, 100, 80, 4, Red());
w.DrawLine(160, 40, 100, 50, PEN_DOT, Red());
w.DrawEllipse(210, 20, 80, 60, Blue());
w.DrawEllipse(310, 20, 80, 60, LtBlue(), 5, Red());
w.DrawArc(RectC(410, 20, 80, 60), Point(10, 10), Point(450, 80), 3, Cyan);
Vector<Point> p;
p << Point(30, 110) << Point(60, 180) << Point(10, 150) << Point(70, 150);
w.DrawPolyline(p, 4, Black);
p.Clear();
p << Point(130, 110) << Point(160, 180) << Point(110, 150) << Point(170, 120)
<< Point(130, 110);
w.DrawPolygon(p, Blue);
p.Clear();
p << Point(230, 110) << Point(260, 180) << Point(210, 150) << Point(270, 120)
<< Point(230, 110);
w.DrawPolygon(p, Cyan, 5, Magenta);
p.Clear();
p << Point(330, 110) << Point(360, 180) << Point(310, 150) << Point(370, 120)
<< Point(330, 110);
w.DrawPolygon(p, Cyan, 5, Magenta, I64(0xaa55aa55aa55aa55));
w.DrawImage(40, 240, CtrlImg::save());
w.DrawImage(110, 210, 80, 80, CtrlImg::save());
w.DrawImage(240, 240, CtrlImg::save(), Blue);
w.DrawImage(310, 210, 80, 80, CtrlImg::save(), Blue);
w.DrawText(20, 330, "Hello world!");
w.DrawText(120, 330, "Hello world!", Arial(15).Bold());
w.DrawText(220, 330, "Hello world!", Roman(15).Italic(), Red);
w.DrawText(320, 380, 400, "Hello world!", Courier(15).Underline());
}
};
GUI_APP_MAIN
{
MyApp().Sizeable().Run();
}
The examples shows the full range of drawing operations in action.
2. Offsets and clipping
You can offset the drawing position and clip the drawing operations to specified rectangle. Offsetting and clipping in U++ is stack based operation - End method restores the previous state. Particularly important is combined offset/clip operation Clipoff that in fact creates a new Draw for subregion of current Draw area.
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
void DoPainting(Draw& w) {
w.DrawEllipse(0, 0, 100, 30, WhiteGray(), 1, Cyan);
w.DrawText(0, 0, "Hello world", Roman(30).Bold());
}
void Paint(Draw& w) override {
w.DrawRect(GetSize(), White());
DoPainting(w);
w.Offset(30, 50);
DoPainting(w);
w.End();
w.Offset(20, 100);
w.Clip(5, 5, 40, 20);
DoPainting(w);
w.End();
w.End();
w.Clipoff(10, 150, 60, 20);
DoPainting(w);
w.End();
}
};
GUI_APP_MAIN
{
MyApp().Sizeable().Run();
}
3. Fonts and font metrics
Font is a simple font description value type - it contains an index of typeface, height of font and additional attributes (e.g. italic flag). Font also provides metrics information about the font and individual characters.
The most important information of Font is the GetAscent value - distance from the baseline to the top of character, the GetDescent value - distance from the baseline to the bottom of character cell (height of character cell - GetHeight - is simply the sum of both values) and the individual character width that can be obtained by Font's operator[] (where index is in UNICODE).
To get the list all available typefaces and respective use GetFaceCount and GetFaceName static methods of Font.
Position given in DrawText specifies the top-left corner of the first letter of text.
Different than default character spacing can be specified by the C array with integer widths of characters.
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
DropList font_list;
void Paint(Draw& w) override {
const String text = "Programming is fun";
Font fnt(~font_list, 60);
w.DrawRect(GetSize(), White);
w.Offset(50, 50);
int x = 0;
Vector<int> dx;
for(char letter : text) {
int width = fnt[letter];
w.DrawRect(x, 0, width - 1, fnt.GetAscent(), Color(255, 255, 200));
w.DrawRect(x, fnt.GetAscent(), width - 1, fnt.GetDescent(), Color(255, 200, 255));
w.DrawRect(x + width - 1, 0, 1, fnt.GetHeight(), Black());
dx.Add(width + 4);
x += width;
}
w.DrawRect(0, 0, 4, 4, Black());
w.DrawText(0, 0, text, fnt);
w.DrawText(0, 70, text, fnt, Blue(), dx.GetCount(), dx.Begin());
w.End();
}
void NewFont() {
Refresh();
}
MyApp() {
for(int i = 0; i < Font::GetFaceCount(); i++)
font_list.Add(i, Font::GetFaceName(i));
Add(font_list.TopPos(0, MINSIZE).LeftPosZ(0, 200));
font_list <<= 0;
font_list << [=] { NewFont(); };
}
};
GUI_APP_MAIN
{
MyApp().Sizeable().Run();
}
In this lesson we used operator~ to obtain current value stored by font_list. Alternatively, the GetData() function can be used for more verbosity. More information about this topic can be found in Value of widget section of GUI Tutorial.
The final results of program operation is shown below:
4. DrawingDraw
Drawing object contains a set of drawing operations and in fact represents a vector image with smooth rescaling. DrawingDraw serves as the target Draw when creating Drawing, Drawing can be painted to any Draw target using the DrawDrawing method:
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
Drawing drawing;
void Paint(Draw& w) override {
w.DrawRect(GetSize(), White());
w.DrawDrawing(10, 10, 50, 60, drawing);
w.DrawDrawing(100, 10, 150, 100, drawing);
w.DrawDrawing(10, 110, 300, 300, drawing);
}
MyApp() {
DrawingDraw iw(200, 200);
iw.DrawEllipse(10, 10, 180, 100, Cyan());
iw.DrawImage(100, 100, CtrlImg::exclamation());
iw.DrawRect(20, 100, 30, 30, Blue);
drawing = iw;
}
};
GUI_APP_MAIN
{
MyApp().Sizeable().Run();
}
5. ImageDraw
ImageDraw provides a Draw target that creates an Image object. To define the alpha component, use Draw target returned by Alpha method and GrayColor to define the value (if you never use Alpha, it is considered to be 255 for the whole Image, otherwise it is set to 0 when Alpha is called for the first time).
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
Image image;
void Paint(Draw& w) override {
w.DrawRect(GetSize(), Cyan());
w.DrawImage(10, 10, image);
}
MyApp() {
ImageDraw iw(100, 40);
iw.Alpha().DrawRect(0, 0, 100, 40, GrayColor(0));
iw.Alpha().DrawEllipse(0, 0, 100, 40, GrayColor(255));
iw.DrawEllipse(0, 0, 100, 40, Yellow());
iw.DrawText(26, 10, "Image", Arial(16).Bold());
image = iw;
}
};
GUI_APP_MAIN
{
MyApp().Sizeable().Run();
}
6. Printing
Printing is quite similar to painting on the screen. To acquire Draw target for the printer, you need to use PrinterJob (CtrlLib feature). Each page printed should be started by calling StartPage method and ended with EndPage. Drawing coordinates when printing are in dots, defined as 1/600 of inch (in fact, a pixel on 600dpi printer).
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
GUI_APP_MAIN
{
PrinterJob pd("My printer job");
pd.CurrentPage(0);
pd.MinMaxPage(0, 1);
if(pd.Execute()) {
Draw& w = pd.GetDraw();
w.StartPage();
w.DrawText(200, 1200, "Helo world!", Arial(600));
w.EndPage();
w.StartPage();
w.DrawText(200, 1200, "Second page", Roman(600));
w.EndPage();
}
}
Recommended tutorials:
If you want to learn more, we have several tutorials that you can find useful:
Image tutorial - here we move things related to images. We show how to create, use and embed icons directly in application.
|