This article will demonstrate how to transfer the image data from a skDevice (ex.: a SkBitmap object) to cascades::image so that it can be displayed at UI. Also, this article will demonstrate some basic functionalities of the Skia lib:
- Loading image data into a SkBitmap object from an image file.
- Drawing some Skia primitives (for example: drawing circle, drawing rect, and drawing text) on a Canvas
- Drawing a bitmap on another bitmap.
- Scale an image
Please note that the main focus of this article will not be showing how to use Skia library itself. For this, please refer to the Skia on BlackBerry® GitHub®:
Also, although there is a pre-built Skia lib shipped with the BlackBerry® 10 Native SDK (10.0.6.545), it is intended to be used only by the OS itself. we need to build the Skia lib from source code so that we can use Skia lib in our Application. ( See
How to build the Source Code for details.)
These steps represent a typical procedure for drawing graphics using Skia Lib:
- Initialize a SKDevice. ( ex.: SkBitmap. The skBitmap uses a memory block (buffer) to represent the pixels of an image. The SkBitmap can be initized as a blank image or an image from a file.)
- Prepare a Canvas, e.g.: SkCanvas. A SkCanvas will be created on a SkDevice.
- Draw on a Canvas
- Transfer the drawing result from the skDevice to a cascades::image for display.
The SkiaImageScratchPad application(see the attachment) follows what QImageScratchPad does to use SkBitmap and SkCanvas as a “scratchpad” to prepare an image to be displayed in cascades.
Thereby, the UI and code organization are similar as that of QImageScratchPad.In this article. We will only focus on the differences between the drawing mechanism of Skia Lib and that of QImage/Qpainter.
When drawing vector graphic using Skia Lib, SkBitmap is an object users often need to use. For example, loading image file into an SkBitmap so that it can be further processed, drawing on it or drawing this image on another image. Let's first look at how to load an image file into a SkBitmap object.
Loading an asset image into a SkBitmap
A png file can be loaded into a SkBitmap object by using SkImageDecoder.
(SkImageDecoder is also able to load an image file in other formats into SkBitmap, please refer to Skia documentation for details.)
SkBitmap tempImage; QString where = getImagePath(url); SkImageDecoder::DecodeFile(where.toUtf8().constDat
Manipulating SkBitmap using SkCanvas and SkPainter.
We can not directly manipulate a SkBitmap object which can be initialized with an image file or a blank image.
A SkBitmap object has to be used as a SkDevice to create SkCanavas object; then, all the drawings on the SkCanvas object will be reflected on the underlying SkBitmap object.
drawing circles, drawing texts, drawing a rect, or even drawing a bitmap on the canvas.
Also, when drawing a object (ex: circle) on the Canvas, we have to use a SkPainter object to define the drawing attributes.
Prepare a Canvas:
SkBitmap image; // Create an image of the appropriate size. // The underlying data is reference counted so is cleaned up as needed. image.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); image.allocPixels(); image.eraseColor(0); SkCanvas canvas(image);
Prepare the paint:
SkPaint myPaint; myPaint.setAntiAlias(true); myPaint.setStyle(SkPaint::kStroke_Style); myPaint.setColor(SK_ColorYELLOW); myPaint.setStrokeWidth(SkIntToScalar(3));
Fill the background and draw the circle:
//fill the background canvas.drawColor(SK_ColorBLUE); //draw circle canvas.drawCircle(centerX, centerY, radius, myPaint);
When drawing text, we need to prepare the paint (e.g. text font, text size, text color, etc.) before we can draw the text itself.
SkPaint textPaint; SkTypeface* myFont = SkTypeface::CreateFromName(NULL/*default font*/,SkTypeface::kBold); textPaint.setTypeface(myFont); textPaint.setAntiAlias(true); textPaint.setTextSize(SkIntToScalar(12)); textPaint.setColor(SK_ColorYELLOW); textPaint.setTextAlign(SkPaint::kCenter_Align); canvas.drawText("OK", 2, centerX, centerY, textPaint);
Drawing a square:
Drawing square is similar as drawing other primitives, e.g. prepare paint and draw the primitive.
SkPaint myPaint; myPaint.setAntiAlias(true); myPaint.setColor(SK_ColorYELLOW); SkRect rect; //Left, Top, Right, Bottom rect.set(centerX - w / 2, centerY - h / 2, centerX + w / 2, centerY + h / 2); canvas.drawRect(rect, myPaint);
Scaling a image:
Scaling an image is not straightforward.
First, we need to prepare a canvas ( e.g. create it on a blank target bitmap with desired size) and set its scale factor. All the later drawings (primitives or a bitmap) on this Canvas will be scaled.
Note: in the following example code, "replicant" is a SKBitmap (image to be scaled):
//scale is not straightforward. //we have to setup the scaled bitmap. //then draw the original bitmap to the scaled canvas SkBitmap dst; dst.setConfig(replicant.config(), desiredReplicantSize.width(), desiredReplicantSize.height()); dst.allocPixels(); dst.eraseColor(0); SkCanvas replicantCanvas(dst); replicantCanvas.scale( float(desiredReplicantSize.width()) / float(replicant.width()), float(desiredReplicantSize.height()) / float(replicant.height())); replicantCanvas.drawBitmap(replicant, 0, 0, NULL);
Create a Cascades::image object from a SkBitmap object.
A SkBitmap object has an internal buffer to contain the pixel data. The way the pixel data is packaged depends on the SkBitmap object's configuration.
In this example, we will use SkBitmap::kARGB_8888_Config, which is equivalent to QImage::Format_ARGB32_Premultiplied. In your applicaton, you can convert the configuration of the skBitmap object to be SkBitmap::kARGB_8888_Config using
SkBitmap::copyTo(SkBitmap* dst, Config c, Allocator* allocator = NULL).
In this example code, we will demonstrate how to convert the data from an intermal memory buffer within a SkBitmap to the format defined by PixelBufferData::RGBX, which can be used by cascades::image.
The code below was borrowed from qt for doing rgb swap operation.
//original skbitmap pixels are organized as ARGB //((*p << 16) & 0xff0000), we get -B-- //((*p >> 16) & 0xff), we get ---R //(*p & 0xff00ff00), we get A-G- // combine all of them, we get ABGR *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
With this operation, the memory buffer is ready to be used for contructring a bb::cascades:ixelBufferData object.
The attached file contains the SkiaImageScratchPad source code.
When this app was written, the pre-built Skia lib shipped with the BlackBerry 10 Native SDK (10.0.6.545) is intended to be used only by OS. We will have to build Skia lib from source code and bundle it together with our application, e.g.: overriding the skia lib shipped with the OS on the target.
Steps to build the attached sample:
1) Download skia source code from: https://github.com/blackberry/Skia
2) Add the skia eclipse project to your workspace.
3) Download the attached sample project code.
Note: the Skia source code and the application source code shall be located side by side. e.g.: the app source code shall be extracted under the same directory where the skia lib source code is located.
e.g.: ./Skia , ./skiaimage
4) Add the SkiaImageScratchPad to your workspace.
5) build the skia lib
6) build SkiaImageScratchPad code
Note: make sure to build the Skia lib and SKiaImageScratchPad using same build configuration
The skiaimagescratchpad.pro file and the bar-descriptor.xml within the source code have been modified so that the built Skia lib can be linked properly and bundled together with the app.
It assumes that the source code of skia lib and the source code of this app are located side by side. If you prefer to organize the skia lib source code and your project source code differently, you can refer the following KB: