Home

asimonov-rim edited this page Jan 27, 2012 · 10 revisions
Clone this wiki locally

Skia is an open-source 2D graphics library for drawing text, geometry and images. What follows is a brief overview of its features.

Creating a Canvas

The SkCanvas is the drawing context for Skia. It provides an abstraction layer that allows for drawing to a variety of devices and maintains a stack of matrices and clips.

To render with software Skia, you must create a native window and then wrap a pointer to its buffer as an SkBitmap. This bitmap can then be used to initialize an SkCanvas, allowing it to draw to the screen.

To render with hardware-accelerated Skia, you must create a GLES2 window or framebuffer and the appropriate GrContext, SkGpuDevice, and SkGpuCanvas.

Refer here for a more detailed description of the SkCanvas API.

Basic Primitives

SkCanvas supports a number of basic primitives, including rectangles, rounded rectangles, ovals, circles, arcs, paths, lines, text, bitmaps and sprites.

Basic Drawing Demo

To draw a rectangle, call drawRect with a rectangle and the paint to be used.

SkRect rect;
rect.set(0, 0, 150, 120);
canvas->drawRect(rect, shapePaint);

To draw a rounded rectangle, call drawRoundRect with the rectangle bounds of the roundRect, the x- and y- radii of the oval to be used to round the corners, and the paint to be used.

canvas->drawRoundRect(rect, 15, 15, shapePaint);

To draw an oval, call drawOval with the rectangular bounds of the oval, as well as the paint to be used.

canvas->drawOval(rect, shapePaint);

To draw a circle, call drawCircle with the x- and y- coordinates of its center, its radius, and the paint to be used.

canvas->drawCircle(60, 60, 60, shapePaint);

To draw an arc, call drawArc with the rectangular bounds of the oval defining the shape of the arc, the start angle (in degrees) of the arc, the sweep angle (measured clockwise), a flag indicating whether the center of the oval should be included when filling, and a paint.

// Arc without a wedge
canvas->drawArc(rect, 0, 255, false, shapePaint);
// Arc with a wedge from the center
canvas->drawArc(rect, 0, 255, true, shapePaint);

To draw a line, call drawLine with the x- and y- coordinates of the start and end points of the line, as well as the paint to be used.

canvas->drawLine(0, 0, 150, 120, shapePaint);

To draw text, call drawText with the text string, the number of bytes to be read from the string, the x- and y- coordinates for the origin of the text to be drawn, and the paint to be used.

const char str[] = "Hello World!";
canvas->drawText(str, strlen(str), 75, 145, textPaint);

To draw a bitmap, call drawBitmap with the bitmap to be drawn, the coordinates of the its top-left corner, and an optional paint.

// Load a bitmap from an image and draw it only if the load succeeds
SkBitmap bitmap;
if(SkImageDecoder::DecodeFile("app/native/icon.png", bitmap)) {
    canvas->drawBitmap(*bitmap, 0, 0, NULL);
}

Paths

Paths allow for the creation of more advanced shapes than what can be drawn with the basic primitives. Each path can be made up of multiple contours (or continuous sections), each consisting of linear, quadratic, and cubic segments.

Refer here for a more detailed description of the SkPath API.

To move to the beginning of the next contour without creating a new segment, call moveTo with the desired x- and y- coordinates.

SkPath path;
path.moveTo(50, 50);

To create a line segment starting from the last point, call lineTo with the x- and y- coordinates of the end point for the new segment.

// Specify endpoint using absolute coordinates
path.lineTo(100, 100);
// Specify endpoint using a relative offset from the last point
path.rLineTo(50, 50);

To create a quadratic segment starting from the last point, call quadTo with the x- and y- coordinates of the control point on the curve, as well as the end point for the new segment.

// Specify endpoint using absolute coordinates
path.quadTo(120, 125, 150, 150);
// Specify endpoint using a relative offset from the last point
path.rQuadTo(20, 25, 50, 50);

To create a cubic bezier segment starting from the last point, call cubicTo with the x- and y- coordinates of the two control points on the cubic curve, as well as the end point for the new segment.

// Specify endpoint using absolute coordinates
path.cubicTo(175, 175, 140, 120, 200, 200);
// Specify endpoint using a relative offset from the last point
path.rQuadTo(25, 25, -10, -30, 50, 50);

To draw the path, call drawPath with the completed path and the paint to be used.

canvas->drawPath(path, shapePaint);

Path Fill Types

Paths with a closed contour are filled with one of four fill types - kWinding_FillType, kEvenOdd_FillType, kInverseWinding_FillType, or kInverseEvenOdd_FillType. These change how the computation is made to check if a given point is on the "inside" or "outside" of the contour.

Paint Fill Type Demo

To set the fill type for a path, call setFillType on it with the desired fill type.

donutPath.setFillType(kWinding_FillType);

More details on the fill rules can be found here.

Path Effects

Path effects can be applied to paths, lines and most other primitives to perturb their geometry in a variety of ways.

Path Effects Demo

To set up a dashed path effect, create an SkDashPathEffect with an array specifying the "on" and "off" intervals for the dash pattern, the number of entries in the array, and an offset into the interval array representing the phase.

SkScalar interval[2];
interval[0] = 4; // on
interval[1] = 4; // off
SkPathEffect* dashEffect = new SkDashPathEffect(interval, 2, 0);

To set up an effect that will round the corners of any contour (including that of a rectangle or arc), create an SkCornerPathEffect with the distance from each corner that should be rounded.

SkPathEffect* cornerEffect = new SkCornerPathEffect(10);

To set up an effect that will apply random displacements to the contour, create an SkDiscretePathEffect with a segment length and a deviation. The effect will break each contour into segments of specified length and move their endpoints away from the original path by a random distance, bounded above by the deviation.

SkPathEffect* discreteEffect = new SkDiscretePathEffect(10, 5);

To apply the effect, call setPathEffect on a paint object.

shapePaint.setPathEffect(discreteEffect);

Text on Path

To draw text along a path, call drawTextOnPath with the text string, its length in bytes, the path along which the text will be drawn, an optional matrix to be applied to the text before it is mapped onto the path, and the paint to be used.

const char str[] = "Some text to draw along a path";
canvas->drawTextOnPath(str, strlen(str), path, NULL, textPaint);

Creating a Paint

An SkPaint is a light-weight object that determines the look of primitives drawn to a canvas. It encompasses stylistic attributes such as color, stroke width, fill shaders and effects.

Refer here for a more detailed description of the SkPaint API.

Paint Color

To set a solid paint color, call either setColor or setARGB on a paint object. The setColor method takes an SkColor, which is a 32-bit representation of a color's ARGB components. The setARGB method takes four separate values in the range of 0 to 255, which similarly correspond to a color's ARGB components.

// Create paths
SkPaint myPaint1, myPaint2, myPaint3;
myPaint1.setAntiAlias(true);
// Set all three paints to the same blue color
myPaint1.setColor(SK_ColorBLUE);
myPaint2.setColor(0xFF0000FF);
myPaint3.setARGB(255, 0, 0, 255);

Paint Styles

Paint Styles Demo

To draw filled primitives, set the paint's style to kFill_Style. Similarly, to draw shape outlines, set the paint's style to kStroke_Style. The stroke width can be modified by calling setStrokeWidth and only takes effect when kStroke_Style is set or when drawing non-closed contours, such as lines.

fillPaint.setStyle(SkPaint::kFill_Style);
strokePaint.setStyle(SkPaint::kStroke_Stule);
strokePaint.setStrokeWidth(10);

The two paint styles can be combined to draw filled shapes with an outline of a different color. First, draw the shape with the kFill_Style, then change the paint's color, and draw the shape again with kStroke_Style.

Paint Stroke Cap & Join

For the drawing of lines and paths, it is possible to set the stroke cap to one of three values - kButt_Cap, kRound_Cap, or kSquare_Cap. The attribute changes the treatment that is applied to the beginning and end of each non-closed contour. Thus, primitives made up of closed contours like rectangles, arcs, ovals and circles, are unaffected.

linePaint1.setStrokeCap(SkPaint::kButt_Cap);
linePaint2.setStrokeCap(SkPaint::kRound_Cap);
linePaint3.setStrokeCap(SkPaint::kSquare_Cap);

Paint Stroke Cap Demo

For drawing rectangles, arcs and paths, the stroke join can be set to one of three values - kMiter_Join, kRound_Join, or kBevel_Join. The attribute specifies the treatment that is applied to corners in these primitives.

pathPaint.setStrokeJoin(SkPaint::kMiter_Join);
arcPaint.setStrokeJoin(SkPaint::kRound_Join);
rectPaint.setStrokeJoin(SkPaint::kBevel_Join);

Paint Stroke Join Demo

Text Paints

To set up a paint object that can be used for drawing text, first create a SkTypeface. To do so, call SkTypeface::CreateFromName with the desired font family name and style, which can be one of kNormal, kBold, or kItalic. Finally, call setTypeface on the paint object and pass it the SkTypeface. Some other useful attributes for text paint objects are text alignment and text size, which can be set with setTextAlign and setTextSize respectively.

SkTypeface *pTypeface = SkTypeface::CreateFromName("Arial", SkTypeface::kNormal);
textPaint.setTypeface(pTypeface);
textPaint.setTextSize(20);
textPaint.setTextAlign(SkPaint::kCenter_Align);
pTypeface->unref();

Paint Shaders

Gradient and bitmap shaders make it possible to fill primitives with more than just a solid color. Most shaders, with the exception of the sweep gradient, support three tiling modes - kClamp_TileMode, kRepeat_TileMode, and kMirror_TileMode. These modes define how the shader's content is repeated when filling a given primitive.

Refer here for more details on bitmap shaders or here for more details on gradient shaders.

A bitmap shader fills a shape with the user-specified image. To create it, call SkShader::CreateBitmapShader with a bitmap, and the tiling modes to be used for the x- and y-directions.

// A bitmap shader using the clamp tiling mode for the x-direction
// and repeat tiling mode for the y-direction
SkShader *shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode);

Bitmap Shader Join Demo

A linear gradient shader generates a linear gradient between the two specified points. To create it, call SkGradientShader::CreateLinear with the start and end points for the gradient, an array of colors to be distributed between the two points, an optional array of relative positions for each of the colors, the number of colors in the gradient, and the tiling mode.

// Start/end points for the gradient
SkPoint pts[2];
pts[0].set(40, 60);
pts[1].set(80, 60);
// Colors for the gradient
SkColor colors[3];
colors[0] = SK_ColorBLUE;
colors[1] = SK_ColorWHITE;
colors[2] = SK_ColorRED;
// Relative positions of the colors in gradient
SkScalar pos[3];
pos[0] = 0.0f;
pos[1] = 0.25f;
pos[2] = 1.0f;
// Create linear gradient shader
Shader *linShader = SkGradientShader::CreateLinear(pts, colors, pos, 3, SkShader::kClamp_TileMode);

A radial gradient shader generates a radial gradient given a center point and radius. To create it, call SkGradientShader::CreateRadial with the center of the circle for the gradient, the circle's radius, an array of colors to be distributed between the center and the edge of the circle, an optional array of relative positions for each of the colors, the number of colors in the gradient, and the tiling mode.

// Center point of circle
SkPoint center;
center.set(60, 60);
// Circle radius
SkScalar r = 29;
// Colors for the gradient
SkColor colors[3];
colors[0] = SK_ColorBLUE;
colors[1] = SK_ColorWHITE;
colors[2] = SK_ColorRED;
// Relative positions of the colors in gradient
SkScalar pos[3];
pos[0] = 0.0f;
pos[1] = 0.4f;
pos[2] = 1.0f;
// Create radial gradient shader
Shader *radShader = SkGradientShader::CreateRadial(center, r, colors, pos, 3, SkShader::kClamp_TileMode);

A two point radial shader generates a radial gradient given a start position, start radius, end position and end radius. To create it, call SkGradientShader::CreateTwoPointRadial with the center of the start circle, the radius of the start circle, the center of the end circle, the end radius, an array of colors to be distributed between the center and the edge of the circle, an optional array of relative positions for each of the colors, the number of colors in the gradient, and the tiling mode.

// Center points of start and end circles
SkPoint pts[2];
pts[0].set(40, 40);
pts[1].set(50, 50);
// Circle radii
SkScalar r[2];
r[0] = 10;
r[1] = 32;
// Colors for the gradient
SkColor colors[3];
colors[0] = SK_ColorBLUE;
colors[1] = SK_ColorWHITE;
colors[2] = SK_ColorRED;
// Create two point radial gradient shader
// Do not provide a position array for colors
SkShader *r2Pt = SkGradientShader::CreateTwoPointRadial(pts[0], r[0], pts[1], r[1], colors, NULL, 3,     SkShader::kClamp_TileMode);

To create a sweep gradient around a center point, call SkGradientShader::CreateSweep with the x- and y- coordinates of the center, an array of colors to be distributed between the center and the edge of the circle, an optional array of relative positions for each of the colors, and the number of colors in the gradient.

// Center point of circle
SkPoint center;
center.set(60, 60);
// Colors for the gradient
SkColor colors[3];
colors[0] = SK_ColorBLUE;
colors[1] = SK_ColorWHITE;
colors[2] = SK_ColorRED;
// Create sweep gradient shader
// Do not provide a position array for colors
SkShader *sweepShader = SkGradientShader::CreateSweep(center.x(), center.y(), colors, NULL, 3);

To assign a shader to a paint object, call setShader.

shapePaint.setShader(sweepShader);

Gradient Shaders Demo