skia2/site/docs/user/api/skcanvas_creation.md
Joe Gregorio e296c56735 [docs] Fix all 404s.
Change-Id: I1b89db473582f9fcc4d87436a590ee36e869fe45
No-Try: true
Docs-Preview: https://skia.org/docs/user/build?cl=392717
Docs-Preview: https://skia.org/docs/dev/contrib/?cl=392717
Docs-Preview: https://skia.org/docs/dev/gardening/?cl=392717
Docs-Preview: https://skia.org/docs/user/tips?cl=392717
Docs-Preview: https://skia.org/docs/dev/tools/debugger?cl=392717
Docs-Preview: https://skia.org/docs/user/modules/pathkit?cl=392717
Docs-Preview: https://skia.org/docs/dev/testing/fuzz?cl=392717
Docs-Preview: https://skia.org/docs/dev/testing/ios?cl=392717
Docs-Preview: https://skia.org/docs/roles?cl=392717
Docs-Preview: https://skia.org/docs/user/api/?cl=392717
Docs-Preview: https://skia.org/docs/dev/testing/swarmingbots?cl=392717
Docs-Preview: https://skia.org/docs/dev/chrome/changes?cl=392717
Docs-Preview: https://skia.org/docs/user/download?cl=392717
Docs-Preview: https://skia.org/docs/dev/flutter/?cl=392717
Docs-Preview: https://skia.org/docs/user/modules/quickstart?cl=392717
Docs-Preview: https://skia.org/docs/user/api/skcanvas_creation?cl=392717
Docs-Preview: https://skia.org/docs/user/sample/viewer?cl=392717
Docs-Preview: https://skia.org/docs/dev/design/pdftheory?cl=392717
Docs-Preview: https://skia.org/docs/user/api/SkBlendMode_Overview?cl=392717
Docs-Preview: https://skia.org/docs/user/api/skcanvas_overview?cl=392717
Docs-Preview: https://skia.org/docs/?cl=392717
Docs-Preview: https://skia.org/docs/dev/contrib/submit?cl=392717
Docs-Preview: https://skia.org/docs/user/api/SkPath_Overview?cl=392717
Docs-Preview: https://skia.org/docs/user/modules/skottie?cl=392717
Docs-Preview: https://skia.org/docs/user/modules/canvaskit?cl=392717
Fixed: skia:404
Docs-Preview: https://skia.org/docs/dev/tools/calendar?cl=392717
Docs-Preview: https://skia.org/docs/dev/gardening/android?cl=392717
Docs-Preview: https://skia.org/docs/dev/contrib/style?cl=392717
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/392717
Reviewed-by: Heather Miller <hcm@google.com>
Commit-Queue: Joe Gregorio <jcgregorio@google.com>
2021-04-05 16:20:10 +00:00

8.6 KiB

title linkTitle weight
SkCanvas Creation SkCanvas Creation 250

First, read about the SkCanvas API.

Skia has multiple backends which receive SkCanvas drawing commands, including:

  • Raster - CPU-only.
  • GPU - Skia's GPU-accelerated backend.
  • SkPDF - PDF document creation.
  • SkPicture - Skia's display list format.
  • NullCanvas - Useful for testing only.
  • SkXPS - Experimental XPS backend.
  • SkSVG - Experimental SVG backend.

Each backend has a unique way of creating a SkCanvas. This page gives an example for each:

Raster


The raster backend draws to a block of memory. This memory can be managed by Skia or by the client.

The recommended way of creating a canvas for the Raster and Ganesh backends is to use a SkSurface, which is an object that manages the memory into which the canvas commands are drawn.

#include "SkData.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkSurface.h"
void raster(int width, int height,
            void (*draw)(SkCanvas*),
            const char* path) {
    sk_sp<SkSurface> rasterSurface =
            SkSurface::MakeRasterN32Premul(width, height);
    SkCanvas* rasterCanvas = rasterSurface->getCanvas();
    draw(rasterCanvas);
    sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
    if (!img) { return; }
    sk_sp<SkData> png(img->encode());
    if (!png) { return; }
    SkFILEWStream out(path);
    (void)out.write(png->data(), png->size());
}

Alternatively, we could have specified the memory for the surface explicitly, instead of asking Skia to manage it.

#include <vector>
#include "SkSurface.h"
std::vector<char> raster_direct(int width, int height,
                                void (*draw)(SkCanvas*)) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
    size_t rowBytes = info.minRowBytes();
    size_t size = info.getSafeSize(rowBytes);
    std::vector<char> pixelMemory(size);  // allocate memory
    sk_sp<SkSurface> surface =
            SkSurface::MakeRasterDirect(
                    info, &pixelMemory[0], rowBytes);
    SkCanvas* canvas = surface->getCanvas();
    draw(canvas);
    return pixelMemory;
}

GPU


GPU Surfaces must have a GrContext object which manages the GPU context, and related caches for textures and fonts. GrContexts are matched one to one with OpenGL contexts or Vulkan devices. That is, all SkSurfaces that will be rendered to using the same OpenGL context or Vulkan device should share a GrContext. Skia does not create a OpenGL context or Vulkan device for you. In OpenGL mode it also assumes that the correct OpenGL context has been made current to the current thread when Skia calls are made.

#include "GrDirectContext.h"
#include "gl/GrGLInterface.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkSurface.h"

void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
    // You've already created your OpenGL context and bound it.
    const GrGLInterface* interface = nullptr;
    // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
    // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and
    // initialize it however you like to attach to an alternate OpenGL implementation or intercept
    // Skia's OpenGL calls.
    sk_sp<GrContext> context = GrContext::MakeGL(interface);
    SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
    sk_sp<SkSurface> gpuSurface(
            SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
    if (!gpuSurface) {
        SkDebugf("SkSurface::MakeRenderTarget returned null\n");
        return;
    }
    SkCanvas* gpuCanvas = gpuSurface->getCanvas();
    draw(gpuCanvas);
    sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
    if (!img) { return; }
    sk_sp<SkData> png(img->encode());
    if (!png) { return; }
    SkFILEWStream out(path);
    (void)out.write(png->data(), png->size());
}

SkPDF


The SkPDF backend uses SkDocument instead of SkSurface, since a document must include multiple pages.

#include "SkPDFDocument.h"
#include "SkStream.h"
void skpdf(int width, int height,
           void (*draw)(SkCanvas*),
           const char* path) {
    SkFILEWStream pdfStream(path);
    auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
    SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
                                            SkIntToScalar(height));
    draw(pdfCanvas);
    pdfDoc->close();
}

SkPicture


The SkPicture backend uses SkPictureRecorder instead of SkSurface.

#include "SkPictureRecorder.h"
#include "SkPicture.h"
#include "SkStream.h"
void picture(int width, int height,
             void (*draw)(SkCanvas*),
             const char* path) {
    SkPictureRecorder recorder;
    SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
                                                        SkIntToScalar(height));
    draw(recordingCanvas);
    sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
    SkFILEWStream skpStream(path);
    // Open SKP files with `viewer --skps PATH_TO_SKP --slide SKP_FILE`
    picture->serialize(&skpStream);
}

NullCanvas


The null canvas is a canvas that ignores all drawing commands and does nothing.

#include "SkNullCanvas.h"
void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
    std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
    draw(nullCanvas.get());  // NoOp
}

SkXPS


The (still experimental) SkXPS canvas writes into an XPS document.

#include "SkDocument.h"
#include "SkStream.h"
#ifdef SK_BUILD_FOR_WIN
void skxps(IXpsOMObjectFactory* factory;
           int width, int height,
           void (*draw)(SkCanvas*),
           const char* path) {
    SkFILEWStream xpsStream(path);
    sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
    SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
                                            SkIntToScalar(height));
    draw(xpsCanvas);
    xpsDoc->close();
}
#endif

SkSVG


The (still experimental) SkSVG canvas writes into an SVG document.

#include "SkStream.h"
#include "SkSVGCanvas.h"
#include "SkXMLWriter.h"
void sksvg(int width, int height,
           void (*draw)(SkCanvas*),
           const char* path) {
    SkFILEWStream svgStream(path);
    std::unique_ptr<SkXMLWriter> xmlWriter(
            new SkXMLStreamWriter(&svgStream));
    SkRect bounds = SkRect::MakeIWH(width, height);
    std::unique_ptr<SkCanvas> svgCanvas =
        SkSVGCanvas::Make(bounds, xmlWriter.get());
    draw(svgCanvas.get());
}

Example


To try this code out, make a new unit test using instructions here and wrap these functions together:

#include "SkCanvas.h"
#include "SkPath.h"
#include "Test.h"
void example(SkCanvas* canvas) {
    const SkScalar scale = 256.0f;
    const SkScalar R = 0.45f * scale;
    const SkScalar TAU = 6.2831853f;
    SkPath path;
    for (int i = 0; i < 5; ++i) {
        SkScalar theta = 2 * i * TAU / 5;
        if (i == 0) {
            path.moveTo(R * cos(theta), R * sin(theta));
        } else {
            path.lineTo(R * cos(theta), R * sin(theta));
        }
    }
    path.close();
    SkPaint p;
    p.setAntiAlias(true);
    canvas->clear(SK_ColorWHITE);
    canvas->translate(0.5f * scale, 0.5f * scale);
    canvas->drawPath(path, p);
}
DEF_TEST(FourBackends, r) {
    raster(     256, 256, example, "out_raster.png" );
    gl_example( 256, 256, example, "out_gpu.png"    );
    skpdf(      256, 256, example, "out_skpdf.pdf"  );
    picture(    256, 256, example, "out_picture.skp");
}