CANVAS Graphics API
This API is currently using a freeware Cairo Graphics Library wrapped with AngelScript. If you want to know more about some specific method usage and implementation and can look it up in Cairo API Reference, also the original Kt::Graphics reference can be helpful.
More usage examples can be found on the CANVAS page itself and in these drawing examples.
API Testbench
All examples on this page will use the following environment:
- ./Scripts/CanvasDemo/canvas.cxx // DSP part
- ./Scripts/CanvasDemo/canvas.kuiml // GUI part
- ./Scripts/CanvasDemo/canvas-data/render.as // render function
canvas.cxx contents:
canvas.kuiml contents:
- <?xml version="1.0" encoding="utf-8" ?>
- <SKIN>
- <!-- load a render script -->
- <SCRIPT src="$SCRIPT_DATA_PATH$/render.as" />
- <!-- create a canvas and run a render_script -->
- <CANVAS id="my_canvas" width="300" height="200"
- render_script="renderMyCanvas(this.height, this.width)"
- requires="my_canvas.width;my_canvas.height" />
- </SKIN>
canvas-data/render.as contents:
- // render function
- void renderMyCanvas(double h, double w) {
- // get current context
- Kt::Graphics::Context@ ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- // set the color
- ctx.source.SetRGBA(1,0,0, 0.8); // r, g, b, alpha
- // draw a simple circle using arc
- ctx.path.Clear();
- ctx.path.Arc(w/2, h/2, w/4, 0, 360); // cx, cy, radius, angle_start, angle_end
- ctx.FillPath();
- }
- }
Note on inline formatting
To have a render function in the same kuiml file (not in a separate AngelScript file) like in these examples, you can use inline scripting, just remember to replace all " with " and all < with < . Replace // comments with /* */
You can try our online converter to do that.
In the following examples we'll be talking about the contents of the renderMyCanvas function.
Context object
The first thing you usually do is get a context object, and then you can use its methods and attributes to do the drawing.
- // get current context
- Kt::Graphics::Context@ ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- ... do the drawing ...
- }
Using bitmaps
Instead of drawing on the screen you can create a bitmap object (or load it from file), get its context and draw on that. You can later use that bitmap as a source or mask pattern (see ctx.source.SetBitmap) and paint it on the screen or save it to a PNG file.
- // create a bitmap object
- Kt::Graphics::Bitmap@ bmp = Kt::Graphics::CreateBitmap(300,200);
- // or instead load it from PNG/JPG/GIF/BMP/TIFF/HEIC/SVG file
- // Kt::Graphics::Bitmap@ bmp = Kt::Graphics::LoadImageFile("/path/file.png");
- { // work with bitmap context in another scope
- Kt::Graphics::Context@ ctx = bmp.BuildContext();
- if (@ctx != null) {
- .. do the drawing ..
- // save bitmap to a file
- Kt::Graphics::SaveImageFile(bmp, "/path/to/file.png");
- }
- @ctx = null; // free context handle (just in case)
- }
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- // display bitmap on the screen
- ctx.source.SetBitmap(bmp);
- ctx.Paint();
- }
Context methods
- ctx.StrokePath(); // Strokes the current path using current pattern
- ctx.FillPath(); // Fills the path using current pattern
- ctx.Paint(); // Paints the current pattern (clipped by clip region)
- ctx.PaintWithAlpha(double alpha); // Paints the current pattern (clipped by clip region) with additional alpha channel blend
- ctx.PaintWithMask(); // Paints the current pattern (clipped by clip region) using the current mask
- ctx.ClipWithPath(); // Intersects the clip region with current path
- ctx.WriteText(const char *text); // Draw text using current text settings and source pattern, with current position defined with path
- ctx.SaveState(); // Save current surface state (stack)
- ctx.RestoreState(); // Restores last saved surface state (stack)
Context methods example:
- // utility function to convert HEX color to RGB and add it to given context
- Kt::Graphics::DrawPattern@ setColorHex(Kt::Graphics::Context@ ctx, string hexcolor = "", double opacity = 1, bool selectAsSource = true) {
- // verify hex color string
- if (hexcolor == "") hexcolor = "777777";
- if (hexcolor.substr(0,1) == "#") hexcolor = hexcolor.substr(1,6);
- // convert to r,g,b with 0-1 range
- double r = double(parseInt(hexcolor.substr(0,2),16))/255.0;
- double g = double(parseInt(hexcolor.substr(2,2),16))/255.0;
- double b = double(parseInt(hexcolor.substr(4,2),16))/255.0;
- // convert opacity if it's in 0-100 range
- if (opacity > 1) opacity = opacity/100.0;
- // add a new flat color
- Kt::Graphics::DrawPattern@ pattern = ctx.patterns.NewRGBA (r,g,b,opacity);
- // apply it as a source (if needed, by default)
- if (selectAsSource) { pattern.SelectAsSource(); }
- // return that color for future use
- return pattern;
- }
- // render function to show examples of all context methods
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 500;
- canvas_height = 350;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // load used images
- auto logo_bmp = Kt::Graphics::LoadImageFile("/path/to/LM_logo.png");
- auto pattern_bmp = Kt::Graphics::LoadImageFile("/path/to/letimix.png");
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- // paint whole area with white
- auto white = setColorHex(ctx, "f4f4f8"); // utility function
- ctx.PaintWithAlpha(0.9); // with slight transparency
- // add some vertical stripes
- // do some pre calculations
- double pad_x = w/15;
- double pad_y = w/20;
- double stripe_width = (w-pad_x*2)/4;
- double stripe_height = h - pad_y*2;
- // add color and fill the stripe
- auto red = setColorHex(ctx, "#fe4a49");
- ctx.path.Clear();
- ctx.path.Rectangle(pad_x, pad_y, stripe_width, stripe_height);
- ctx.FillPath();
- // add color and fill the stripe
- auto blue = setColorHex(ctx, "2ab7ca");
- ctx.path.Clear();
- ctx.path.Rectangle(pad_x + stripe_width, pad_y, stripe_width, stripe_height);
- ctx.FillPath();
- // add color and fill the stripe
- auto yellow = setColorHex(ctx, "fed766");
- ctx.path.Clear();
- ctx.path.Rectangle(pad_x + stripe_width*2, pad_y, stripe_width, stripe_height);
- ctx.FillPath();
- // add color and fill the stripe
- auto gray = setColorHex(ctx, "e6e6ea");
- ctx.path.Clear();
- ctx.path.Rectangle(pad_x + stripe_width*3, pad_y, stripe_width, stripe_height);
- ctx.FillPath();
- // save state to undo translate and scale later
- ctx.SaveState();
- // add repeated pattern with reflected logos
- ctx.transform.Scale(0.125, 0.125); // scale to make bmp smaller
- ctx.source.SetBitmap (pattern_bmp, 0, 0, Kt::Graphics::kExtendReflect);
- ctx.transform.Scale(8, 8); // get scale back
- // add another linear gradient as a mask
- auto gradient2 = ctx.patterns.NewLinearGradient(0,0,0,h);
- gradient2.AddColorStopRGBA(0.5, 0,0,0, 0.0);
- gradient2.AddColorStopRGBA(1, 0,0,0, 0.3);
- gradient2.SelectAsMask();
- // clip with center rectangle
- ctx.path.Clear();
- ctx.path.Rectangle(pad_x, pad_y, w-pad_x*2, h-pad_y*2);
- ctx.ClipWithPath();
- // paint it all
- ctx.PaintWithMask();
- // get back to default translate and scale
- ctx.RestoreState();
- // save state to undo translate and scale later
- ctx.SaveState();
- // add a white radial glow under the logo
- ctx.transform.Translate(w/2, h/2 - 10); // shift drawing to center
- auto gradient = ctx.patterns.NewRadialGradient(0,0,0, 0,0,h/2);
- gradient.AddColorStopRGBA(0.2, 1,1,1, 1);
- gradient.AddColorStopRGBA(1, 1,1,1, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- // get back to default translate and scale
- ctx.RestoreState();
- // save state for later
- ctx.SaveState();
- // add a center logo
- ctx.transform.Translate(w/2, h/2 - 10); // shift drawing to center
- ctx.transform.Scale(0.85, 0.85); // scale drawing
- // display a logo clipped with a circle
- ctx.path.Clear();
- ctx.path.Arc(0, 0, 90, 0, 360); // add a circle
- ctx.ClipWithPath();
- // add logo image as source and paint it
- ctx.source.SetBitmap (logo_bmp, logo_bmp.width/2, logo_bmp.height/2);
- ctx.Paint();
- // stroke a circle
- ctx.settings.set_lineWidth(5);
- gray.SelectAsSource();
- ctx.StrokePath();
- // restore state
- ctx.RestoreState();
- // write text under logo
- string s = "MAKE AND INSPIRE";
- ctx.font.SetFontSize(15);
- ctx.source.SetRGBA(0,0,0, 0.8);
- // center text
- double text_width = ctx.font.GetTextSize(s).x;
- ctx.path.MoveTo(w/2 - text_width/2,h/2+95);
- ctx.WriteText(s);
- // add a "shadow" under the text
- ctx.path.RelMoveTo(-text_width*1.025,2);
- ctx.transform.Scale(1.05, 1.4);
- ctx.source.SetRGBA(0,0,0, 0.05);
- ctx.WriteText(s);
- }
- }
Context attributes
ctx.path (class Kt::Graphics::DrawPath)
The current path used for stroke, fill and clip. Can get some info in Cairo Path docs.
- ctx.path.MoveTo (double x, double y)
- ctx.path.LineTo (double x, double y)
- ctx.path.CurveTo (double x1, double y1, double x2, double y2, double x3, double y3) // draw a cubic Bezier spline
- ctx.path.Arc (double xc, double yc, double radius, double angle1, double angle2)
- ctx.path.ArcNegative (double xc, double yc, double radius, double angle1, double angle2) // draw an arc or a circle in the opposite direction
- ctx.path.Rectangle (double x, double y, double width, double height)
- ctx.path.RelMoveTo (double dx, double dy) // move relative to last point
- ctx.path.RelLineTo (double dx, double dy) // line relative to last point
- ctx.path.RelCurveTo (double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) // curve relative to last point
- ctx.path.Text (const char *utf8) // adds text as path
- ctx.path.Clear () // starts new path
- ctx.path.NewSubPath () // forgets last point, esp. useful when drawing arcs
- ctx.path.Close () // connects first and last points
ctx.path examples:
- // draw a line (1)
- ctx.path.Clear();
- ctx.path.MoveTo(20,20);
- ctx.path.LineTo(100, 100);
- ctx.StrokePath();
- ctx.WriteText("1");
- // draw a curved line (2)
- ctx.path.Clear();
- ctx.path.CurveTo(100,20, 130,140, 160,20);
- ctx.StrokePath();
- ctx.WriteText("2");
- // draw an arc (3)
- ctx.path.Clear();
- // xcenter, ycenter, radius, angle_start, angle_end (degrees)
- ctx.path.Arc(150,150, 30, 45, 270);
- ctx.StrokePath();
- ctx.WriteText("3");
- // draw a negative Arc (opposite drawing direction) (4)
- ctx.path.Clear();
- ctx.path.ArcNegative(190,150, 30, 0, -90);
- ctx.StrokePath();
- ctx.WriteText("4");
- // draw a circle (5)
- ctx.path.NewSubPath(); // doesn't clear current path, but forgets (disconnects from) last point
- ctx.path.Arc(250,70, 30, 0, 360);
- ctx.WriteText("5");
- ctx.StrokePath();
- // draw a rectangle (6)
- ctx.path.Clear();
- ctx.path.Rectangle(320, 30, 60, 40);
- ctx.StrokePath();
- ctx.WriteText("6");
- // draw a triangle (7)
- ctx.path.RelMoveTo(0, 100);
- ctx.path.RelLineTo(-30,50);
- ctx.path.RelLineTo(60,0);
- ctx.path.Close();
- ctx.StrokePath();
- ctx.WriteText("7");
- // draw another curved line (8)
- ctx.path.RelCurveTo(40,0, 40,50, 80, 0);
- ctx.StrokePath();
- ctx.WriteText("8");
- // draw text as path (9)
- ctx.path.MoveTo(60, 240);
- ctx.SaveState(); // to save font size
- ctx.font.SetFontSize(50);
- ctx.path.Text("Path examples");
- ctx.StrokePath();
- ctx.RestoreState(); // to restore font size
- ctx.WriteText("9");
ctx.settings (class Kt::Graphics::DrawSettings)
The settings used for drawing operations
- ctx.settings.set_lineWidth (double width)
- ctx.settings.set_blendMode (BlendMode op)
- // line width and blend mode examples
- ctx.settings.set_lineWidth(1.5);
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpMultiply);
Line width examples:
- // narrow line
- ctx.path.Clear();
- ctx.path.MoveTo(10, 20);
- ctx.path.RelLineTo(180, 0);
- ctx.settings.set_lineWidth(1);
- ctx.StrokePath();
- ctx.WriteText(" lineWidth: 1");
- // wider line
- ctx.path.Clear();
- ctx.path.MoveTo(10, 48);
- ctx.path.RelLineTo(180, 0);
- ctx.settings.set_lineWidth(6);
- ctx.StrokePath();
- ctx.WriteText(" lineWidth: 6");
- // wide line
- ctx.path.Clear();
- ctx.path.MoveTo(10, 76);
- ctx.path.RelLineTo(180, 0);
- ctx.settings.set_lineWidth(12);
- ctx.StrokePath();
- ctx.source.SetRGBA(0,0,0,1);
- ctx.WriteText(" lineWidth: 12");
Note on blend modes
Blendmode (compositing mode) used for rendering on the target graphics can be:
- kDrawOpOver - draw source layer on top of destination layer [DEFAULT]
- kDrawOpXor - source and destination are shown where there is only one of them
- kDrawOpAdd - add source pattern and destination content.
- kDrawOpMultiply - multiply source and destination. The result is at least as dark as the darker inputs.
- kDrawOpDifference - takes the difference of the source and destination color.
Blendmodes are still experimental and may change in the future or not work in all contexts.
Example of different blend modes:
The same gradient repeated five times with different blend modes over multicolored background.
The gradient colors (RGBA) are #FFFFFFFF -> #FFFFFF00 -> #00000000 -> #000000FF
(white -> transparent white -> transparent black -> black)
Instead of black and white gradient using "rainbow" gradient without transparency.
- // render function to show examples of different blend modes
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 700;
- canvas_height = 300;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- ctx.SaveState();
- // color gradient for background
- auto rgbgrad = ctx.patterns.NewLinearGradient(0,0,0,h);
- rgbgrad.AddColorStopRGBA(0, 1,0,0, 1);
- rgbgrad.AddColorStopRGBA(0.16, 1,1,0, 1);
- rgbgrad.AddColorStopRGBA(0.33, 0,1,0, 1);
- rgbgrad.AddColorStopRGBA(0.5, 0,1,1, 1);
- rgbgrad.AddColorStopRGBA(0.66, 0,0,1, 1);
- rgbgrad.AddColorStopRGBA(0.83, 1,0,1, 1);
- rgbgrad.AddColorStopRGBA(1, 1,0,0, 1);
- rgbgrad.SetExtend(Kt::Graphics::kExtendNone);
- rgbgrad.SelectAsSource();
- ctx.Paint();
- // middle three lines
- // white
- ctx.path.Clear();
- ctx.path.Rectangle(0,h/2 - h/4,w,h/8);
- ctx.source.SetRGBA(1,1,1, 1);
- ctx.FillPath();
- // gray
- ctx.path.Clear();
- ctx.path.Rectangle(0,h/2 - h/16,w,h/8);
- ctx.source.SetRGBA(0.5,0.5,0.5, 1);
- ctx.FillPath();
- // black
- ctx.path.Clear();
- ctx.path.Rectangle(0,h/2 + h/8,w,h/8);
- ctx.source.SetRGBA(0,0,0, 1);
- ctx.FillPath();
- // some pre-calculations
- double grad_pad = 15;
- double grad_width = (w-grad_pad*6)/5;
- // add a test gradient
- auto gradient = ctx.patterns.NewLinearGradient(0,0,grad_width,0);
- // make gradient not repeating
- gradient.SetExtend(Kt::Graphics::kExtendNone);
- // for b/w gradient
- // gradient.AddColorStopRGBA(0, 1,1,1, 1); // full white
- // gradient.AddColorStopRGBA(0.49, 1,1,1, 0); // transparent white
- // gradient.AddColorStopRGBA(0.51, 0,0,0, 0); // transparent black
- // gradient.AddColorStopRGBA(1, 0,0,0, 1); // full black
- // for multicolor gradient
- gradient.AddColorStopRGBA(0, 1,0,0, 1);
- gradient.AddColorStopRGBA(0.16, 1,1,0, 1);
- gradient.AddColorStopRGBA(0.33, 0,1,0, 1);
- gradient.AddColorStopRGBA(0.5, 0,1,1, 1);
- gradient.AddColorStopRGBA(0.66, 0,0,1, 1);
- gradient.AddColorStopRGBA(0.83, 1,0,1, 1);
- gradient.AddColorStopRGBA(1, 1,0,0, 1);
- // paint gradient with various blend modes
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpOver);
- ctx.transform.Translate(grad_pad, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpXor);
- ctx.transform.Translate(grad_width+grad_pad, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpAdd);
- ctx.transform.Translate(grad_width+grad_pad, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpMultiply);
- ctx.transform.Translate(grad_width+grad_pad, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpDifference);
- ctx.transform.Translate(grad_width+grad_pad, 0);
- gradient.SelectAsSource();
- ctx.Paint();
- ctx.RestoreState();
- // draw separation lines between gradients
- ctx.source.SetRGBA(0,0,0, 0.3);
- ctx.settings.set_lineWidth(1.5);
- for (int n = 0; n<6; n++) {
- ctx.path.Clear();
- ctx.path.MoveTo(n*(grad_width+grad_pad) + grad_pad/2, 0);
- ctx.path.RelLineTo(0, h);
- ctx.StrokePath();
- }
- // write text
- ctx.source.SetRGBA(1,1,1, 1);
- ctx.path.MoveTo(47, h/2 - h/16 + 14);
- // ctx.WriteText("Paint b/w RGBA gradient #FFFFFFFF -> #FFFFFF00 -> #0000000 -> #000000FF");
- ctx.WriteText("Paint RGBA multicolor gradient");
- ctx.path.MoveTo(47, h/2 + 14);
- ctx.WriteText("5 times with different blend modes over multicolored background");
- double y = h/2 + h/4 - h/22;
- ctx.path.MoveTo(47, y);
- ctx.WriteText("OpOver");
- ctx.path.MoveTo(55 + grad_width+grad_pad, y);
- ctx.WriteText("OpXor");
- ctx.path.MoveTo(50 + 2*(grad_width+grad_pad), y);
- ctx.WriteText("OpAdd");
- ctx.path.MoveTo(33 + 3*(grad_width+grad_pad), y);
- ctx.WriteText("OpMultiply");
- ctx.path.MoveTo(38 + 4*(grad_width+grad_pad), y);
- ctx.WriteText("OpDifference");
- }
- }
ctx.patterns (class Kt::Graphics::DrawPatternFactory)
This is a way to create "patterns" that can be used for drawing, painting or writing text.
A "pattern" is a flat color, a gradient or a bitmap (see ctx.source.SetBitmap below).
Any pattern can be selected for being used a "source" for methods like ctx.StrokePath, ctx.FillPath, ctx.Paint, ctx.PaintWithAlpha, ctx.WriteText.
Also any pattern can be selected as a "mask" to be used with ctx.PaintWithMask.
- // FLAT COLORS
- // add a color
- Kt::Graphics::DrawPattern@ color = ctx.patterns.NewRGB (double red, double green, double blue)
- Kt::Graphics::DrawPattern@ color = ctx.patterns.NewRGBA (double red, double green, double blue, double alpha)
- // use it whenever you need
- color.SelectAsSource() // for stroke, paint or text operations
- // there's also a shortcut for setting a color with one line, like this:
- // ctx.source.SetRGBA(r,g,b) or ctx.source.SetRGBA(r,g,b,a) (see below)
- // it can be used if you don't need to select that color later again
- // GRADIENTS
- // add a linear gradient
- Kt::Graphics::GradientDrawPattern@ gradient = ctx.patterns.NewLinearGradient (double x0, double y0, double x1, double y1)
- // add a radial gradient
- Kt::Graphics::GradientDrawPattern@ gradient = ctx.patterns.NewRadialGradient (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1)
- // add colors to the gradient
- gradient.AddColorStopRGB (double offset, double r, double g, double b)
- gradient.AddColorStopRGBA (double offset, double r, double g, double b, double a)
- // set gradient properties (how to repeat it, how to resize)
- gradient.SetExtend (PatternExtend extend) // how should the gradient be extended
- gradient.SetFilter (PatternFilter filter) // the filter to use on resize
- // use the gradient
- gradient.SelectAsSource() // for stroke, paint or text operations
- gradient.SelectAsMask() // for PaintWithMask
- // BITMAPS
- // you can also use bitmaps as a "pattern"
- // via ctx.source.SetBitmap or ctx.mask.SetBitmap (see below)
Example on adding a color and using a gradient as mask:
For masks only the transparency value in the gradient (or bitmap) is used (the color is ignored).
- // add a color
- auto yellow = ctx.patterns.NewRGBA(1,1,0, 0.5);
- yellow.SelectAsSource();
- // add a gradient as a mask
- auto gradient = ctx.patterns.NewRadialGradient(w/2,h/2,0, w/2,h/2,h/2);
- gradient.AddColorStopRGBA(0.05, 0,0,0, 1); // opaque
- gradient.AddColorStopRGBA(0.95, 0,0,0, 0); // transparent
- gradient.SelectAsMask();
- ctx.PaintWithMask();
Example of a gradient and different PatternExtend modes:
For a linear gradient:
For a radial gradient:
- // render function to show examples of different Extend modes
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 400;
- canvas_height = 150;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- ctx.SaveState();
- // add a demo gradient
- //auto gradient = ctx.patterns.NewLinearGradient(w/2+70,0,w-70,0);
- auto gradient = ctx.patterns.NewRadialGradient(w/2 + w/4,h/4,0, w/2 + w/4,h/4, 35);
- gradient.AddColorStopRGBA(0.1, 0,1,0, 0.3); // green
- gradient.AddColorStopRGBA(0.5, 1,0,0, 0.3); // red
- gradient.AddColorStopRGBA(0.9, 0,0,1, 0.3); // blue
- // kExtendNone (gradient and bmp are not repeated)
- gradient.SetExtend(Kt::Graphics::kExtendNone);
- ctx.transform.Translate(-w/2,0);
- gradient.SelectAsSource();
- // add an area to clip gradient
- ctx.path.Clear();
- ctx.path.Rectangle(w/2,0,w/2,h/2-5);
- ctx.ClipWithPath();
- // paint the gradient
- ctx.Paint();
- ctx.RestoreState(); // to undo ClipWithPath
- ctx.SaveState();
- // kExtendRepeat (gradient and bmp are repeated)
- gradient.SetExtend(Kt::Graphics::kExtendRepeat);
- ctx.transform.Translate(0,0);
- gradient.SelectAsSource();
- // add an area to clip gradient
- ctx.path.Clear();
- ctx.path.Rectangle(w/2+5,0,w/2,h/2-5);
- ctx.ClipWithPath();
- // paint the gradient
- ctx.Paint();
- ctx.RestoreState();
- ctx.SaveState();
- // kExtendReflect (gradient and bmp are reflected)
- ctx.transform.Translate(-w/2,h/2);
- gradient.SetExtend(Kt::Graphics::kExtendReflect);
- gradient.SelectAsSource();
- ctx.transform.Translate(w/2,-h/2);
- ctx.path.Clear();
- ctx.path.Rectangle(0,h/2+5,w/2-10,h/2);
- ctx.ClipWithPath();
- ctx.Paint();
- ctx.RestoreState();
- ctx.SaveState();
- // kExtendPad (gradient edges are repeaded, bmp like kExtendRepeat)
- ctx.transform.Translate(0,h/2);
- gradient.SetExtend(Kt::Graphics::kExtendPad);
- gradient.SelectAsSource();
- ctx.transform.Translate(0,-h/2);
- ctx.path.Clear();
- ctx.path.Rectangle(w/2+5,h/2+5,w/2-5,h/2);
- ctx.ClipWithPath();
- ctx.Paint();
- ctx.RestoreState();
- // write text
- ctx.source.SetRGB(0,0,0);
- ctx.font.SetFontSize(16);
- string s;
- s = "kExtendNone";
- ctx.path.MoveTo(w/4 - ctx.font.GetTextSize(s).x/2, h/4 +5);
- ctx.WriteText(s);
- s = "kExtendRepeat";
- ctx.path.MoveTo(3*w/4 - ctx.font.GetTextSize(s).x/2, h/4 +5);
- ctx.WriteText(s);
- s = "kExtendReflect";
- ctx.path.MoveTo(w/4 - ctx.font.GetTextSize(s).x/2, 3*h/4 + 6);
- ctx.WriteText(s);
- s = "kExtendPad";
- ctx.path.MoveTo(3*w/4 - ctx.font.GetTextSize(s).x/2, 3*h/4 + 6);
- ctx.WriteText(s);
- }
- }
Note on PatternExtend
PatternExtend is used to describe how pattern color/alpha will be determined for areas "outside" the pattern's natural area (for example, outside the surface bounds or outside the gradient geometry).
- kExtendNone - pixels outside of the source pattern are fully transparent
- kExtendRepeat - the pattern is tiled by repeating
- kExtendReflect - the pattern is tiled by reflecting at the edges (only implemented for surface patterns)
- kExtendPad - pixels outside of the pattern copy the closest pixel from the source (only implemented for surface patterns)
The default extend mode is kExtendNone for surface patterns and kExtendPad for gradient patterns (according to Cairo docs).
ctx.source (class Kt::Graphics::DrawPatternSelect)
Quick access to source pattern selection used for drawing and painting.
Basically this is a quick way to set a color instead of adding it as a pattern and then selecting it (see ctx.patterns above).
Also it has a method for using a bitmap as a source (with x, y coordinates for positioning).
- // set flat color as source
- ctx.source.SetRGB (double red, double green, double blue)
- ctx.source.SetRGBA (double red, double green, double blue, double alpha)
- // set bitmap as source
- ctx.source.SetBitmap (const Bitmap &bitmap, double x, double y, PatternExtend ext=kExtendNone, PatternFilter filter=kFilterBilinear)
Examples on ctx.source methods
Trying various PatternExtend options with a bitmap:
- // render function to show examples of different ctx.source methods
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 400;
- canvas_height = 150;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- auto bmp = Kt::Graphics::LoadImageFile("/path/to/image.png");
- // kExtendNone
- ctx.source.SetBitmap(bmp, -w/4+bmp.width/2, 0, Kt::Graphics::kExtendNone);
- ctx.PaintWithAlpha(0.33);
- // kExtendRepeat
- ctx.SaveState();
- ctx.path.Clear();
- ctx.path.Rectangle(w/2+5,0,w/2,h/2-5);
- ctx.ClipWithPath();
- ctx.source.SetBitmap(bmp, 5, 0, Kt::Graphics::kExtendRepeat);
- ctx.PaintWithAlpha(0.33);
- ctx.RestoreState();
- // kExtendReflect
- ctx.SaveState();
- ctx.path.Clear();
- ctx.path.Rectangle(0,h/2,w/2-5,h/2 -5);
- ctx.ClipWithPath();
- ctx.source.SetBitmap(bmp, 0, 14, Kt::Graphics::kExtendReflect);
- ctx.PaintWithAlpha(0.33);
- ctx.RestoreState();
- // kExtendPad
- ctx.SaveState();
- ctx.path.Clear();
- ctx.path.Rectangle(w/2+5,h/2,w/2,h/2 -5);
- ctx.StrokePath();
- ctx.ClipWithPath();
- ctx.source.SetBitmap(bmp, 5, 14, Kt::Graphics::kExtendPad);
- ctx.PaintWithAlpha(0.33);
- ctx.RestoreState();
- // write text
- ctx.source.SetRGB(0,0,0);
- ctx.font.SetFontSize(16);
- string s;
- s = "kExtendNone";
- ctx.path.MoveTo(w/4 - ctx.font.GetTextSize(s).x/2, h/4 +5);
- ctx.WriteText(s);
- s = "kExtendRepeat";
- ctx.path.MoveTo(3*w/4 - ctx.font.GetTextSize(s).x/2, h/4 +5);
- ctx.WriteText(s);
- s = "kExtendReflect";
- ctx.path.MoveTo(w/4 - ctx.font.GetTextSize(s).x/2, 3*h/4 + 6);
- ctx.WriteText(s);
- s = "kExtendPad";
- ctx.path.MoveTo(3*w/4 - ctx.font.GetTextSize(s).x/2, 3*h/4 );
- ctx.WriteText(s);
- ctx.source.SetRGBA(0,0,0, 0.7);
- ctx.font.SetFontSize(12);
- s = "= kExtendRepeat for bitmaps";
- ctx.path.MoveTo(3*w/4 - ctx.font.GetTextSize(s).x/2, 3*h/4 + 16);
- ctx.WriteText(s);
- }
- }
Examples of different PatternFilters:
- // render function to show examples of different Filter types
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 455;
- canvas_height = 420;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- auto bmp = Kt::Graphics::LoadImageFile("/path/to/logo.png");
- // paint the original
- ctx.source.SetBitmap(bmp, -w/2 + bmp.width/2);
- ctx.Paint();
- ctx.transform.Translate(0, 10);
- ctx.SaveState();
- // now scale to show various filters
- ctx.transform.Translate(0, bmp.height);
- ctx.transform.Scale(5, 5);
- // kFilterFast
- ctx.source.SetBitmap(bmp, 0, 0, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterFast);
- ctx.Paint();
- // kFilterGood
- ctx.source.SetBitmap(bmp, -bmp.width, 0, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterGood);
- ctx.Paint();
- // kFilterBest
- ctx.source.SetBitmap(bmp, -bmp.width*2, 0, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterBest);
- ctx.Paint();
- // kFilterNearest
- ctx.source.SetBitmap(bmp, 0, -bmp.height-8, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterNearest);
- ctx.Paint();
- // kFilterBilinear
- ctx.source.SetBitmap(bmp, -bmp.width, -bmp.height-8, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterBilinear);
- ctx.Paint();
- // kFilterGaussian
- ctx.source.SetBitmap(bmp, -bmp.width*2, -bmp.height-8, Kt::Graphics::kExtendNone, Kt::Graphics::kFilterGaussian);
- ctx.Paint();
- ctx.RestoreState();
- // write text
- ctx.source.SetRGBA(0,0,0, 0.7);
- ctx.font.SetFontSize(15);
- string s;
- s = "kFilterFast";
- ctx.path.MoveTo(w/6 - ctx.font.GetTextSize(s).x/2, h/2 -15);
- ctx.WriteText(s);
- s = "kFilterGood";
- ctx.path.MoveTo(w/2 - ctx.font.GetTextSize(s).x/2, h/2 -15);
- ctx.WriteText(s);
- s = "kFilterBest";
- ctx.path.MoveTo(w/2 + w/3 - 5 - ctx.font.GetTextSize(s).x/2, h/2 -15);
- ctx.WriteText(s);
- s = "kFilterNearest";
- ctx.path.MoveTo(w/6 - ctx.font.GetTextSize(s).x/2, h-35);
- ctx.WriteText(s);
- s = "kFilterBilinear";
- ctx.path.MoveTo(w/2 - ctx.font.GetTextSize(s).x/2, h-35);
- ctx.WriteText(s);
- ctx.SaveState();
- ctx.source.SetRGBA(0,0,0, 0.6);
- ctx.font.SetFontSize(13);
- s = "(default for bitmaps)";
- ctx.path.MoveTo(w/2 - ctx.font.GetTextSize(s).x/2, h-18);
- ctx.WriteText(s);
- ctx.RestoreState();
- s = "kFilterGaussian";
- ctx.path.MoveTo(w/2 + w/3 - 5 - ctx.font.GetTextSize(s).x/2, h-35);
- ctx.WriteText(s);
- }
- }
Note on PatternFilter
PatternFilter is used to indicate what filtering should be applied when reading pixel values from patterns.
- kFilterFast - A high-performance filter, with quality similar to kFilterNearest
- kFilterGood - A reasonable-performance filter, with quality similar to kFilterBilinear
- kFilterBest - The highest-quality available, performance may not be suitable for interactive use.
- kFilterNearest - Nearest-neighbor filtering
- kFilterBilinear - Linear interpolation in two dimensions
- kFilterGaussian - This filter value is currently unimplemented, and should not be used in current code.
The default filter for bitmaps is kFilterBilinear.
ctx.mask (class Kt::Graphics::DrawPatternSelect)
Can access the same methods as ctx.source (see above). Sets the mask pattern used for ctx.PaintWithMask.
Practically can be useful for using bitmaps with transparency as masks via ctx.mask.SetBitmap.
ctx.transform (class Kt::Graphics::DrawTransform)
2D geometrical transform applied to graphics context
These methods can help you move the center point with Translate (so that for example 0,0 becomes a center point, instead of top left corner), scale or even flip the coordinates with Scale (so that you can have your canvas with virtual coordinates like -1 to 1 and with Y coords going from bottom to top) and rotate the surface with Rotate.
- ctx.transform.Translate (double tx, double ty) // translate by (tx,ty)
- ctx.transform.Scale (double sx, double sy) // Scale using sx and sy factors
- ctx.transform.Rotate (double angle) // Rotation by angle, in degrees
Examples on ctx.transform
Using a ctx.path.Arc method and some transformations.
- // render function to show examples of different tranform operations
- void renderMyCanvas(Kt::Param & canvas_height, Kt::Param & canvas_width) {
- // set canvas width from the render function
- canvas_width = 220;
- canvas_height = 220;
- // get these values as a double
- double h = canvas_height;
- double w = canvas_width;
- // get current context (the screen)
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- // draw a rectangle on canvas border
- ctx.path.Clear();
- ctx.path.Rectangle(0,0,w,h);
- ctx.StrokePath();
- // set line width, blend mode and font size
- ctx.settings.set_lineWidth(3);
- ctx.settings.set_blendMode(Kt::Graphics::kDrawOpMultiply);
- ctx.font.SetFontSize(20);
- // move a position to center
- ctx.transform.Translate(w/2,h/2); // since now 0,0 is canvas center
- // draw a bottom half-circle in red
- ctx.path.Clear();
- ctx.path.Arc(0,0,50,0,180); // cx, cy, radius, angle_start, angle_end
- ctx.source.SetRGBA(1,0,0,1);
- ctx.StrokePath();
- ctx.path.RelMoveTo(5, 20);
- ctx.WriteText(" 1");
- // flip the Y coordinate (to count from bottom to top)
- ctx.transform.Scale(1,-1);
- // draw a top half-circle in green, using the same Arc call
- // just to demostrate that coords are flipped
- ctx.path.Clear();
- ctx.path.Arc(0,0,50,0,180);
- ctx.path.Close();
- ctx.source.SetRGBA(0,0.8,0,1);
- ctx.StrokePath();
- ctx.path.RelMoveTo(-40, 20);
- ctx.WriteText(" 2");
- // stretch the coords (and flip Y back)
- ctx.transform.Scale(1,-2);
- // draw the blue vertical oval
- ctx.path.Clear();
- ctx.path.Arc(0,0,50,0,360);
- ctx.source.SetRGBA(0,0,1,1);
- ctx.StrokePath();
- ctx.path.RelMoveTo(-2, -5);
- ctx.WriteText(" 3");
- // rotate 45%
- ctx.transform.Scale(1,0.5); // scale back
- ctx.transform.Rotate(45); // rotate
- ctx.transform.Scale(1,2); // scale (stretch) again
- // draw magenta oval rotated 45%
- ctx.path.Clear();
- ctx.path.Arc(0,0,50,0,360);
- ctx.source.SetRGBA(1,0,1,1);
- ctx.StrokePath();
- ctx.path.RelMoveTo(5, 0);
- ctx.WriteText(" 4");
- }
- }
ctx.font (class Kt::Graphics::DrawFont)
Font and text drawing settings.
- // set font size
- ctx.font.SetFontSize (double size)
- // set font face
- ctx.font.SelectFontFace (const char *family, FontSlant slant=kFontSlantNormal, FontWeight weight=kFontWeightNormal)
- // slant can be: kFontSlantNormal, kFontSlantItalic
- // weight can be: kFontWeightNormal, kFontWeightBold
- //////// GET METHODS ////////
- // get text coords
- Kt::Graphics::XYCoordinates@ coords = ctx.font.GetTextSize (const string &text, TextExtentMode mode=kTextAsTypographicLine)
- // coords has coords.x and coords.y
- // get text rectangle
- Kt::Graphics::Rectangle@ rect = ctx.font.GetTextExtents (const string &text, TextExtentMode mode=kTextAsTypographicLine)
- // rect has rect.origin.x, rect.origin.y, rect.size.x, rect.size.y
- // mode can be:
- // kTextBoundaries - strict boundaries of the text,
- // kTextAsTypographicLine - boundaries of the text as part of a typographic line (usually larger than text boundaries)
ctx.font examples:
To quickly center some text you can use something like:
- // text horizontal center
- string s = "Some text";
- ctx.path.MoveTo(w/2 - ctx.font.GetTextSize(s).x/2, h/2);
- ctx.WriteText(s);
To learn more about ctx.font methods you can check out this demo:
- // demo of GetTextSize and GetTextExtents
- void renderMyCanvas(double h, double w) {
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- // a demo text
- string s = "Graphics";
- // set text properties
- ctx.font.SetFontSize(45);
- ctx.font.SelectFontFace("Courier New", Kt::Graphics::kFontSlantNormal, Kt::Graphics::kFontWeightBold);
- // get text extents (dimentions) for current font and size
- // this gives only text size
- auto coords = ctx.font.GetTextSize (s);
- auto coords2 = ctx.font.GetTextSize (s, Kt::Graphics::kTextBoundaries);
- // this gives text size and origin coords
- auto rect = ctx.font.GetTextExtents (s);
- auto rect2 = ctx.font.GetTextExtents (s, Kt::Graphics::kTextBoundaries);
- // shift one pixel down
- ctx.transform.Translate(0,1);
- // draw the demo text centered on top
- ctx.path.Clear();
- ctx.path.MoveTo(w/2 - rect2.size.x/2, -rect2.origin.y);
- ctx.path.Text(s);
- // select color, line with and do the text drawing
- ctx.source.SetRGBA (0.7, 0.2, 0.2, 1);
- ctx.settings.set_lineWidth(1);
- ctx.StrokePath();
- // draw rectangle around text to show calculated size
- ctx.path.Clear();
- ctx.path.MoveTo(w/2 - rect2.size.x/2,0);
- ctx.path.RelLineTo(0,rect2.size.y);
- ctx.path.RelLineTo(rect2.size.x,0);
- ctx.path.RelLineTo(0,-rect2.size.y);
- ctx.path.Close();
- ctx.source.SetRGBA (0.2, 0.2, 0.6, 0.5);
- ctx.settings.set_lineWidth(1);
- ctx.StrokePath();
- // now write measured coordinates
- ctx.source.SetRGBA (0, 0, 0, 1);
- ctx.font.SetFontSize(12);
- ctx.path.MoveTo(5, 65);
- ctx.font.SetFontSize(16);
- ctx.WriteText("GetTextSize");
- ctx.font.SetFontSize(12);
- ctx.path.MoveTo(5, 85);
- ctx.WriteText("as typographic line: "+coords.x+", "+coords.y);
- ctx.path.MoveTo(5, 103);
- ctx.WriteText("strict boundaries: "+coords2.x+", "+coords2.y);
- ctx.path.MoveTo(5, 130);
- ctx.font.SetFontSize(16);
- ctx.WriteText("GetTextExtents");
- ctx.font.SetFontSize(12);
- ctx.path.MoveTo(5, 150);
- ctx.WriteText("typo: "+rect.origin.x+", "+rect.origin.y+", "+rect.size.x+", "+rect.size.y);
- ctx.path.MoveTo(5, 168);
- ctx.WriteText("strict: "+rect2.origin.x+", "+rect2.origin.y+", "+rect2.size.x+", "+rect2.size.y);
- }
- }