diff options
Diffstat (limited to 'site/user/api/canvas.md')
-rw-r--r-- | site/user/api/canvas.md | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/site/user/api/canvas.md b/site/user/api/canvas.md new file mode 100644 index 0000000000..eacd1ccbd4 --- /dev/null +++ b/site/user/api/canvas.md @@ -0,0 +1,250 @@ +Creating SkCanvas Objects +========================= + +First, read about [the SkCanvas API](skcanvas). + +Skia has multiple backends which receive SkCanvas drawing commands, +including: + +- [Raster](#raster) - CPU-only. +- [Ganesh](#ganesh) - Skia's GPU-accelerated backend. +- [SkPDF](#skpdf) - PDF document creation. +- [SkPicture](#skpicture) - Skia's display list format. +- [NullCanvas](#nullcanvas) - Useful for testing only. +- [SkXPS](#skxps) - Experimental XPS backend. +- [SkSVG](#sksvg) - Experimental XPS backend. + +Each backend has a unique way of creating a SkCanvas. This page gives +an example for each: + +<span id="raster"></span> +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. + +<!--?prettify lang=cc?--> + + #include "SkData.h" + #include "SkImage.h" + #include "SkStream.h" + #include "SkSurface.h" + void raster(int width, int height, + void(*draw)(SkCanvas*), + const char* path) { + SkAutoTUnref<SkSurface> rasterSurface( + SkSurface::NewRasterN32Premul(width, height)); + SkCanvas* rasterCanvas = rasterSurface->getCanvas(); + draw(rasterCanvas); + SkAutoTUnref<SkImage> img(s->newImageSnapshot()); + if (!img) { return; } + SkAutoTUnref<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. + +<!--?prettify lang=cc?--> + + std::vector<char> raster_direct(int width, int height, + void(*draw)(SkCanvas*)) { + SkImageInfo info = SkImageInfo::MakeN32(width, height); + size_t rowBytes = info.minRowBytes(); + size_t size = info.getSafeSize(rowBytes); + std::vector<char> pixelMemory(size); // allocate memory + SkAutoTUnref<SkSurface> surface( + SkSurface::NewRasterDirect( + info, &pixelMemory[0], rowBytes)); + SkCanvas* canvas = surface.getCanvas(); + draw(canvas); + return std::move(pixelMemory); + } + +<span id="ganesh"></span> +Ganesh +------ + +Ganesh Surfaces must have a `GrContext` object which manages the +GPU context, and related caches for textures and fonts. In this +example, we use a `GrContextFactory` to create a context. + +<!--?prettify lang=cc?--> + + #include "GrContextFactory.h" + #include "SkData.h" + #include "SkImage.h" + #include "SkStream.h" + #include "SkSurface.h" + void ganesh(int width, int height, + void(*draw)(SkCanvas*), + const char* path) { + GrContextFactory grFactory; + GrContext* context = grFactory.get(GrContextFactory::kNative_GLContextType); + SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height); + SkAutoTUnref<SkSurface> gpuSurface( + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info)); + if (!gpuSurface) { + SkDebugf("SkSurface::NewRenderTarget returned null\n"); + return; + } + SkCanvas* gpuCanvas = gpuSurface->getCanvas(); + draw(gpuCanvas); + SkAutoTUnref<SkImage> img(s->newImageSnapshot()); + if (!img) { return; } + SkAutoTUnref<SkData> png(img->encode()); + if (!png) { return; } + SkFILEWStream out(path); + (void)out.write(png->data(), png->size()); + } + +<span id="skpdf"></span> +SkPDF +----- + +The SkPDF backend uses `SkDocument` instead of `SkSurface`, since +a document must include multiple pages. + +<!--?prettify lang=cc?--> + + #include "SkDocument.h" + #include "SkStream.h" + void skpdf(int width, int height, + void(*draw)(SkCanvas*), + const char* path) { + SkFILEWStream pdfStream(path); + SkAutoTUnref<SkDocument> pdfDoc(SkDocument::CreatePDF(&pdfStream)); + SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width), + SkIntToScalar(height)); + draw(pdfCanvas); + pdfDoc->close(); + } + +<span id="skpicture"></span> +SkPicture +--------- + +The SkPicture backend uses SkPictureRecorder instead of SkSurface. + +<!--?prettify lang=cc?--> + + #include "SkPictureRecorder" + #include "SkPicture" + #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); + SkAutoTUnref<SkPicture> picture(recorder.endRecordingAsPicture()); + SkFILEWStream skpStream(path); + // Open SKP files with `SampleApp --picture SKP_FILE` + picture->serialize(&skpStream); + } + +<span id="nullcanvas"></span> +NullCanvas +---------- + +The null canvas is a canvas that ignores all drawing commands and does +nothing. + +<!--?prettify lang=cc?--> + + #include "SkNullCanvas.h" + void picture(int, int, void(*draw)(SkCanvas*), const char*) { + SkAutoTDelete<SkCanvas> nullCanvas(SkCreateNullCanvas()); + draw(nullCanvas); // NoOp + } + +<span id="skxps"></span> +SkXPS +----- + +The (*still experimental*) SkXPS canvas writes into an XPS document. + +<!--?prettify lang=cc?--> + + #include "SkDocument.h" + #include "SkStream.h" + void skxps(int width, int height, + void(*draw)(SkCanvas*), + const char* path) { + SkFILEWStream xpsStream(path); + SkAutoTUnref<SkDocument> xpsDoc(SkDocument::CreateXPS(&pdfStream)); + SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width), + SkIntToScalar(height)); + draw(xpsCanvas); + xpsDoc->close(); + } + +<span id="sksvg"></span> +SkSVG +----- + +The (*still experimental*) SkSVG canvas writes into an SVG document. + +<!--?prettify lang=cc?--> + + #include "SkStream.h" + #include "SkSVGCanvas.h" + #include "SkXMLWriter.h" + void sksvg(int width, int height, + void(*draw)(SkCanvas*), + const char* path) { + SkFILEWStream svgStream(path); + SkAutoTDelete<SkXMLWriter> xmlWriter(SkNEW_ARGS(SkXMLStreamWriter, (&svgStream))); + SkAutoTUnref<SkCanvas> svgCanvas(SkSVGCanvas::Create( + SkRect::MakeWH(SkIntToScalar(src.size().width()), + SkIntToScalar(src.size().height())), + xmlWriter)); + draw(svgCanvas); + } + +<span id="example"></span> +Example +------- + +To try this code out, make a [new unit test using instructions +here](/dev/testing/tests) and wrap these funtions together: + +<!--?prettify lang=cc?--> + + #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" ); + ganesh( 256, 256, example, "out_ganesh.png" ); + skpdf( 256, 256, example, "out_skpdf.pdf" ); + picture(256, 256, example, "out_picture.skp"); + } |