1Creating SkCanvas Objects 2========================= 3 4First, read about [the SkCanvas API](skcanvas). 5 6Skia has multiple backends which receive SkCanvas drawing commands, 7including: 8 9- [Raster](#raster) - CPU-only. 10- [Ganesh](#ganesh) - 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 XPS backend. 16 17Each backend has a unique way of creating a SkCanvas. This page gives 18an example for each: 19 20<span id="raster"></span> 21Raster 22------ 23 24The raster backend draws to a block of memory. This memory can be 25managed by Skia or by the client. 26 27The recommended way of creating a canvas for the Raster and Ganesh 28backends is to use a `SkSurface`, which is an object that manages 29the 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 SkAutoTUnref<SkSurface> rasterSurface( 41 SkSurface::NewRasterN32Premul(width, height)); 42 SkCanvas* rasterCanvas = rasterSurface->getCanvas(); 43 draw(rasterCanvas); 44 SkAutoTUnref<SkImage> img(s->newImageSnapshot()); 45 if (!img) { return; } 46 SkAutoTUnref<SkData> png(img->encode()); 47 if (!png) { return; } 48 SkFILEWStream out(path); 49 (void)out.write(png->data(), png->size()); 50 } 51 52Alternatively, we could have specified the memory for the surface 53explicitly, instead of asking Skia to manage it. 54 55<!--?prettify lang=cc?--> 56 57 std::vector<char> raster_direct(int width, int height, 58 void(*draw)(SkCanvas*)) { 59 SkImageInfo info = SkImageInfo::MakeN32(width, height); 60 size_t rowBytes = info.minRowBytes(); 61 size_t size = info.getSafeSize(rowBytes); 62 std::vector<char> pixelMemory(size); // allocate memory 63 SkAutoTUnref<SkSurface> surface( 64 SkSurface::NewRasterDirect( 65 info, &pixelMemory[0], rowBytes)); 66 SkCanvas* canvas = surface.getCanvas(); 67 draw(canvas); 68 return std::move(pixelMemory); 69 } 70 71<span id="ganesh"></span> 72Ganesh 73------ 74 75Ganesh Surfaces must have a `GrContext` object which manages the 76GPU context, and related caches for textures and fonts. In this 77example, we use a `GrContextFactory` to create a context. 78 79<!--?prettify lang=cc?--> 80 81 #include "GrContextFactory.h" 82 #include "SkData.h" 83 #include "SkImage.h" 84 #include "SkStream.h" 85 #include "SkSurface.h" 86 void ganesh(int width, int height, 87 void(*draw)(SkCanvas*), 88 const char* path) { 89 GrContextFactory grFactory; 90 GrContext* context = grFactory.get(GrContextFactory::kNative_GLContextType); 91 SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height); 92 SkAutoTUnref<SkSurface> gpuSurface( 93 SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info)); 94 if (!gpuSurface) { 95 SkDebugf("SkSurface::NewRenderTarget returned null\n"); 96 return; 97 } 98 SkCanvas* gpuCanvas = gpuSurface->getCanvas(); 99 draw(gpuCanvas); 100 SkAutoTUnref<SkImage> img(s->newImageSnapshot()); 101 if (!img) { return; } 102 SkAutoTUnref<SkData> png(img->encode()); 103 if (!png) { return; } 104 SkFILEWStream out(path); 105 (void)out.write(png->data(), png->size()); 106 } 107 108<span id="skpdf"></span> 109SkPDF 110----- 111 112The SkPDF backend uses `SkDocument` instead of `SkSurface`, since 113a document must include multiple pages. 114 115<!--?prettify lang=cc?--> 116 117 #include "SkDocument.h" 118 #include "SkStream.h" 119 void skpdf(int width, int height, 120 void(*draw)(SkCanvas*), 121 const char* path) { 122 SkFILEWStream pdfStream(path); 123 SkAutoTUnref<SkDocument> pdfDoc(SkDocument::CreatePDF(&pdfStream)); 124 SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width), 125 SkIntToScalar(height)); 126 draw(pdfCanvas); 127 pdfDoc->close(); 128 } 129 130<span id="skpicture"></span> 131SkPicture 132--------- 133 134The SkPicture backend uses SkPictureRecorder instead of SkSurface. 135 136<!--?prettify lang=cc?--> 137 138 #include "SkPictureRecorder" 139 #include "SkPicture" 140 #include "SkStream.h" 141 void picture(int width, int height, 142 void(*draw)(SkCanvas*), 143 const char* path) { 144 SkPictureRecorder recorder; 145 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), 146 SkIntToScalar(height)); 147 draw(recordingCanvas); 148 SkAutoTUnref<SkPicture> picture(recorder.endRecordingAsPicture()); 149 SkFILEWStream skpStream(path); 150 // Open SKP files with `SampleApp --picture SKP_FILE` 151 picture->serialize(&skpStream); 152 } 153 154<span id="nullcanvas"></span> 155NullCanvas 156---------- 157 158The null canvas is a canvas that ignores all drawing commands and does 159nothing. 160 161<!--?prettify lang=cc?--> 162 163 #include "SkNullCanvas.h" 164 void picture(int, int, void(*draw)(SkCanvas*), const char*) { 165 SkAutoTDelete<SkCanvas> nullCanvas(SkCreateNullCanvas()); 166 draw(nullCanvas); // NoOp 167 } 168 169<span id="skxps"></span> 170SkXPS 171----- 172 173The (*still experimental*) SkXPS canvas writes into an XPS document. 174 175<!--?prettify lang=cc?--> 176 177 #include "SkDocument.h" 178 #include "SkStream.h" 179 void skxps(int width, int height, 180 void(*draw)(SkCanvas*), 181 const char* path) { 182 SkFILEWStream xpsStream(path); 183 SkAutoTUnref<SkDocument> xpsDoc(SkDocument::CreateXPS(&pdfStream)); 184 SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width), 185 SkIntToScalar(height)); 186 draw(xpsCanvas); 187 xpsDoc->close(); 188 } 189 190<span id="sksvg"></span> 191SkSVG 192----- 193 194The (*still experimental*) SkSVG canvas writes into an SVG document. 195 196<!--?prettify lang=cc?--> 197 198 #include "SkStream.h" 199 #include "SkSVGCanvas.h" 200 #include "SkXMLWriter.h" 201 void sksvg(int width, int height, 202 void(*draw)(SkCanvas*), 203 const char* path) { 204 SkFILEWStream svgStream(path); 205 SkAutoTDelete<SkXMLWriter> xmlWriter(new SkXMLStreamWriter(&svgStream)); 206 SkAutoTUnref<SkCanvas> svgCanvas(SkSVGCanvas::Create( 207 SkRect::MakeWH(SkIntToScalar(src.size().width()), 208 SkIntToScalar(src.size().height())), 209 xmlWriter)); 210 draw(svgCanvas); 211 } 212 213<span id="example"></span> 214Example 215------- 216 217To try this code out, make a [new unit test using instructions 218here](/dev/testing/tests) and wrap these funtions together: 219 220<!--?prettify lang=cc?--> 221 222 #include "SkCanvas.h" 223 #include "SkPath.h" 224 #include "Test.h" 225 void example(SkCanvas* canvas) { 226 const SkScalar scale = 256.0f; 227 const SkScalar R = 0.45f * scale; 228 const SkScalar TAU = 6.2831853f; 229 SkPath path; 230 for (int i = 0; i < 5; ++i) { 231 SkScalar theta = 2 * i * TAU / 5; 232 if (i == 0) { 233 path.moveTo(R * cos(theta), R * sin(theta)); 234 } else { 235 path.lineTo(R * cos(theta), R * sin(theta)); 236 } 237 } 238 path.close(); 239 SkPaint p; 240 p.setAntiAlias(true); 241 canvas->clear(SK_ColorWHITE); 242 canvas->translate(0.5f * scale, 0.5f * scale); 243 canvas->drawPath(path, p); 244 } 245 DEF_TEST(FourBackends, r) { 246 raster( 256, 256, example, "out_raster.png" ); 247 ganesh( 256, 256, example, "out_ganesh.png" ); 248 skpdf( 256, 256, example, "out_skpdf.pdf" ); 249 picture(256, 256, example, "out_picture.skp"); 250 } 251