Home | History | Annotate | Download | only in api
      1 SkCanvas Creation
      2 =========================
      3 
      4 First, read about [the SkCanvas API](skcanvas_overview).
      5 
      6 Skia has multiple backends which receive SkCanvas drawing commands,
      7 including:
      8 
      9 -   [Raster](#raster) - CPU-only.
     10 -   [GPU](#gpu) - Skia's GPU-accelerated backend.
     11 -   [SkPDF](#skpdf) - PDF document creation.
     12 -   [SkPicture](#skpicture) - Skia's display list format.
     13 -   [NullCanvas](#nullcanvas)  - Useful for testing only.
     14 -   [SkXPS](#skxps) - Experimental XPS backend.
     15 -   [SkSVG](#sksvg) - Experimental SVG backend.
     16 
     17 Each backend has a unique way of creating a SkCanvas.  This page gives
     18 an example for each:
     19 
     20 <span id="raster"></span>
     21 Raster
     22 ------
     23 
     24 The raster backend draws to a block of memory. This memory can be
     25 managed by Skia or by the client.
     26 
     27 The recommended way of creating a canvas for the Raster and Ganesh
     28 backends is to use a `SkSurface`, which is an object that manages
     29 the memory into which the canvas commands are drawn.
     30 
     31 <!--?prettify lang=cc?-->
     32 
     33     #include "SkData.h"
     34     #include "SkImage.h"
     35     #include "SkStream.h"
     36     #include "SkSurface.h"
     37     void raster(int width, int height,
     38                 void (*draw)(SkCanvas*),
     39                 const char* path) {
     40         sk_sp<SkSurface> rasterSurface =
     41                 SkSurface::MakeRasterN32Premul(width, height);
     42         SkCanvas* rasterCanvas = rasterSurface->getCanvas();
     43         draw(rasterCanvas);
     44         sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
     45         if (!img) { return; }
     46         sk_sp<SkData> png(img->encode());
     47         if (!png) { return; }
     48         SkFILEWStream out(path);
     49         (void)out.write(png->data(), png->size());
     50     }
     51 
     52 Alternatively, we could have specified the memory for the surface
     53 explicitly, instead of asking Skia to manage it.
     54 
     55 <!--?prettify lang=cc?-->
     56 
     57     #include <vector>
     58     #include "SkSurface.h"
     59     std::vector<char> raster_direct(int width, int height,
     60                                     void (*draw)(SkCanvas*)) {
     61         SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
     62         size_t rowBytes = info.minRowBytes();
     63         size_t size = info.getSafeSize(rowBytes);
     64         std::vector<char> pixelMemory(size);  // allocate memory
     65         sk_sp<SkSurface> surface =
     66                 SkSurface::MakeRasterDirect(
     67                         info, &pixelMemory[0], rowBytes);
     68         SkCanvas* canvas = surface->getCanvas();
     69         draw(canvas);
     70         return pixelMemory;
     71     }
     72 
     73 <span id="gpu"></span>
     74 GPU
     75 ------
     76 
     77 GPU Surfaces must have a `GrContext` object which manages the
     78 GPU context, and related caches for textures and fonts. GrContexts
     79 are matched one to one with OpenGL contexts or Vulkan devices. That is, all
     80 SkSurfaces that will be rendered to using the same OpenGL context or Vulkan
     81 device should share a GrContext. Skia does not create a OpenGL context or Vulkan
     82 device for you. In OpenGL mode it also assumes that the correct OpenGL context
     83 has been made current to the current thread when Skia calls are made.
     84 
     85 <!--?prettify lang=cc?-->
     86 
     87     #include "GrContext.h"
     88     #include "gl/GrGLInterface.h"
     89     #include "SkData.h"
     90     #include "SkImage.h"
     91     #include "SkStream.h"
     92     #include "SkSurface.h"
     93 
     94     void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
     95         // You've already created your OpenGL context and bound it.
     96         const GrGLInterface* interface = nullptr;
     97         // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
     98         // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and
     99         // initialize it however you like to attach to an alternate OpenGL implementation or intercept
    100         // Skia's OpenGL calls.
    101         sk_sp<GrContext> context = GrContext::MakeGL(interface);
    102         SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
    103         sk_sp<SkSurface> gpuSurface(
    104                 SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
    105         if (!gpuSurface) {
    106             SkDebugf("SkSurface::MakeRenderTarget returned null\n");
    107             return;
    108         }
    109         SkCanvas* gpuCanvas = gpuSurface->getCanvas();
    110         draw(gpuCanvas);
    111         sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
    112         if (!img) { return; }
    113         sk_sp<SkData> png(img->encode());
    114         if (!png) { return; }
    115         SkFILEWStream out(path);
    116         (void)out.write(png->data(), png->size());
    117     }
    118 
    119 <span id="skpdf"></span>
    120 SkPDF
    121 -----
    122 
    123 The SkPDF backend uses `SkDocument` instead of `SkSurface`, since
    124 a document must include multiple pages.
    125 
    126 <!--?prettify lang=cc?-->
    127 
    128     #include "SkPDFDocument.h"
    129     #include "SkStream.h"
    130     void skpdf(int width, int height,
    131                void (*draw)(SkCanvas*),
    132                const char* path) {
    133         SkFILEWStream pdfStream(path);
    134         auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
    135         SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
    136                                                 SkIntToScalar(height));
    137         draw(pdfCanvas);
    138         pdfDoc->close();
    139     }
    140 
    141 <span id="skpicture"></span>
    142 SkPicture
    143 ---------
    144 
    145 The SkPicture backend uses SkPictureRecorder instead of SkSurface.
    146 
    147 <!--?prettify lang=cc?-->
    148 
    149     #include "SkPictureRecorder.h"
    150     #include "SkPicture.h"
    151     #include "SkStream.h"
    152     void picture(int width, int height,
    153                  void (*draw)(SkCanvas*),
    154                  const char* path) {
    155         SkPictureRecorder recorder;
    156         SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
    157                                                             SkIntToScalar(height));
    158         draw(recordingCanvas);
    159         sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
    160         SkFILEWStream skpStream(path);
    161         // Open SKP files with `viewer --skps PATH_TO_SKP --slide SKP_FILE`
    162         picture->serialize(&skpStream);
    163     }
    164 
    165 <span id="nullcanvas"></span>
    166 NullCanvas
    167 ----------
    168 
    169 The null canvas is a canvas that ignores all drawing commands and does
    170 nothing.
    171 
    172 <!--?prettify lang=cc?-->
    173 
    174     #include "SkNullCanvas.h"
    175     void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
    176         std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
    177         draw(nullCanvas.get());  // NoOp
    178     }
    179 
    180 <span id="skxps"></span>
    181 SkXPS
    182 -----
    183 
    184 The (*still experimental*) SkXPS canvas writes into an XPS document.
    185 
    186 <!--?prettify lang=cc?-->
    187 
    188     #include "SkDocument.h"
    189     #include "SkStream.h"
    190     #ifdef SK_BUILD_FOR_WIN
    191     void skxps(IXpsOMObjectFactory* factory;
    192                int width, int height,
    193                void (*draw)(SkCanvas*),
    194                const char* path) {
    195         SkFILEWStream xpsStream(path);
    196         sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
    197         SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
    198                                                 SkIntToScalar(height));
    199         draw(xpsCanvas);
    200         xpsDoc->close();
    201     }
    202     #endif
    203 
    204 <span id="sksvg"></span>
    205 SkSVG
    206 -----
    207 
    208 The (*still experimental*) SkSVG canvas writes into an SVG document.
    209 
    210 <!--?prettify lang=cc?-->
    211 
    212     #include "SkStream.h"
    213     #include "SkSVGCanvas.h"
    214     #include "SkXMLWriter.h"
    215     void sksvg(int width, int height,
    216                void (*draw)(SkCanvas*),
    217                const char* path) {
    218         SkFILEWStream svgStream(path);
    219         std::unique_ptr<SkXMLWriter> xmlWriter(
    220                 new SkXMLStreamWriter(&svgStream));
    221         SkRect bounds = SkRect::MakeIWH(width, height);
    222         std::unique_ptr<SkCanvas> svgCanvas =
    223             SkSVGCanvas::Make(bounds, xmlWriter.get());
    224         draw(svgCanvas.get());
    225     }
    226 
    227 <span id="example"></span>
    228 Example
    229 -------
    230 
    231 To try this code out, make a [new unit test using instructions
    232 here](/dev/testing/tests) and wrap these funtions together:
    233 
    234 <!--?prettify lang=cc?-->
    235 
    236     #include "SkCanvas.h"
    237     #include "SkPath.h"
    238     #include "Test.h"
    239     void example(SkCanvas* canvas) {
    240         const SkScalar scale = 256.0f;
    241         const SkScalar R = 0.45f * scale;
    242         const SkScalar TAU = 6.2831853f;
    243         SkPath path;
    244         for (int i = 0; i < 5; ++i) {
    245             SkScalar theta = 2 * i * TAU / 5;
    246             if (i == 0) {
    247                 path.moveTo(R * cos(theta), R * sin(theta));
    248             } else {
    249                 path.lineTo(R * cos(theta), R * sin(theta));
    250             }
    251         }
    252         path.close();
    253         SkPaint p;
    254         p.setAntiAlias(true);
    255         canvas->clear(SK_ColorWHITE);
    256         canvas->translate(0.5f * scale, 0.5f * scale);
    257         canvas->drawPath(path, p);
    258     }
    259     DEF_TEST(FourBackends, r) {
    260         raster(     256, 256, example, "out_raster.png" );
    261         gl_example( 256, 256, example, "out_gpu.png"    );
    262         skpdf(      256, 256, example, "out_skpdf.pdf"  );
    263         picture(    256, 256, example, "out_picture.skp");
    264     }
    265