Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: src/core/SkCanvas.cpp

Issue 1060583007: reduce alloc overhead for SkCanvas (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « include/core/SkCanvas.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2008 The Android Open Source Project 2 * Copyright 2008 The Android Open Source Project
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCanvas.h" 8 #include "SkCanvas.h"
9 #include "SkCanvasPriv.h" 9 #include "SkCanvasPriv.h"
10 #include "SkBitmapDevice.h" 10 #include "SkBitmapDevice.h"
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 } 226 }
227 }; 227 };
228 228
229 class SkDrawIter : public SkDraw { 229 class SkDrawIter : public SkDraw {
230 public: 230 public:
231 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) { 231 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
232 canvas = canvas->canvasForDrawIter(); 232 canvas = canvas->canvasForDrawIter();
233 fCanvas = canvas; 233 fCanvas = canvas;
234 canvas->updateDeviceCMCache(); 234 canvas->updateDeviceCMCache();
235 235
236 fClipStack = canvas->fClipStack.get(); 236 fClipStack = &canvas->fClipStack;
237 fCurrLayer = canvas->fMCRec->fTopLayer; 237 fCurrLayer = canvas->fMCRec->fTopLayer;
238 fSkipEmptyClips = skipEmptyClips; 238 fSkipEmptyClips = skipEmptyClips;
239 } 239 }
240 240
241 bool next() { 241 bool next() {
242 // skip over recs with empty clips 242 // skip over recs with empty clips
243 if (fSkipEmptyClips) { 243 if (fSkipEmptyClips) {
244 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) { 244 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
245 fCurrLayer = fCurrLayer->fNext; 245 fCurrLayer = fCurrLayer->fNext;
246 } 246 }
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 fCachedLocalClipBoundsDirty = true; 431 fCachedLocalClipBoundsDirty = true;
432 fAllowSoftClip = true; 432 fAllowSoftClip = true;
433 fAllowSimplifyClip = false; 433 fAllowSimplifyClip = false;
434 fDeviceCMDirty = true; 434 fDeviceCMDirty = true;
435 fSaveCount = 1; 435 fSaveCount = 1;
436 fMetaData = NULL; 436 fMetaData = NULL;
437 437
438 fMCRec = (MCRec*)fMCStack.push_back(); 438 fMCRec = (MCRec*)fMCStack.push_back();
439 new (fMCRec) MCRec(fConservativeRasterClip); 439 new (fMCRec) MCRec(fConservativeRasterClip);
440 440
441 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, NULL, NULL, fConservativeRaster Clip)); 441 SkASSERT(sizeof(DeviceCM) <= sizeof(fBaseLayerStorage));
442 fMCRec->fLayer = (DeviceCM*)fBaseLayerStorage;
443 new (fBaseLayerStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip);
444
442 fMCRec->fTopLayer = fMCRec->fLayer; 445 fMCRec->fTopLayer = fMCRec->fLayer;
443 446
444 fSurfaceBase = NULL; 447 fSurfaceBase = NULL;
445 448
446 fClipStack.reset(SkNEW(SkClipStack));
447
448 if (device) { 449 if (device) {
449 device->initForRootLayer(fProps.pixelGeometry()); 450 device->initForRootLayer(fProps.pixelGeometry());
450 if (device->forceConservativeRasterClip()) { 451 if (device->forceConservativeRasterClip()) {
451 fConservativeRasterClip = true; 452 fConservativeRasterClip = true;
452 } 453 }
453 device->onAttachToCanvas(this); 454 device->onAttachToCanvas(this);
454 fMCRec->fLayer->fDevice = SkRef(device); 455 fMCRec->fLayer->fDevice = SkRef(device);
455 fMCRec->fRasterClip.setRect(device->getGlobalBounds()); 456 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
456 } 457 }
457 return device; 458 return device;
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 732
732 ////////////////////////////////////////////////////////////////////////////// 733 //////////////////////////////////////////////////////////////////////////////
733 734
734 void SkCanvas::updateDeviceCMCache() { 735 void SkCanvas::updateDeviceCMCache() {
735 if (fDeviceCMDirty) { 736 if (fDeviceCMDirty) {
736 const SkMatrix& totalMatrix = this->getTotalMatrix(); 737 const SkMatrix& totalMatrix = this->getTotalMatrix();
737 const SkRasterClip& totalClip = fMCRec->fRasterClip; 738 const SkRasterClip& totalClip = fMCRec->fRasterClip;
738 DeviceCM* layer = fMCRec->fTopLayer; 739 DeviceCM* layer = fMCRec->fTopLayer;
739 740
740 if (NULL == layer->fNext) { // only one layer 741 if (NULL == layer->fNext) { // only one layer
741 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL); 742 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
742 } else { 743 } else {
743 SkRasterClip clip(totalClip); 744 SkRasterClip clip(totalClip);
744 do { 745 do {
745 layer->updateMC(totalMatrix, clip, *fClipStack, &clip); 746 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
746 } while ((layer = layer->fNext) != NULL); 747 } while ((layer = layer->fNext) != NULL);
747 } 748 }
748 fDeviceCMDirty = false; 749 fDeviceCMDirty = false;
749 } 750 }
750 } 751 }
751 752
752 /////////////////////////////////////////////////////////////////////////////// 753 ///////////////////////////////////////////////////////////////////////////////
753 754
754 void SkCanvas::checkForDeferredSave() { 755 void SkCanvas::checkForDeferredSave() {
755 if (fMCRec->fDeferredSaveCount > 0) { 756 if (fMCRec->fDeferredSaveCount > 0) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
812 for (int i = 0; i < n; ++i) { 813 for (int i = 0; i < n; ++i) {
813 this->restore(); 814 this->restore();
814 } 815 }
815 } 816 }
816 817
817 void SkCanvas::internalSave() { 818 void SkCanvas::internalSave() {
818 MCRec* newTop = (MCRec*)fMCStack.push_back(); 819 MCRec* newTop = (MCRec*)fMCStack.push_back();
819 new (newTop) MCRec(*fMCRec); // balanced in restore() 820 new (newTop) MCRec(*fMCRec); // balanced in restore()
820 fMCRec = newTop; 821 fMCRec = newTop;
821 822
822 fClipStack->save(); 823 fClipStack.save();
823 } 824 }
824 825
825 static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { 826 static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
826 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG 827 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
827 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0; 828 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
828 #else 829 #else
829 return true; 830 return true;
830 #endif 831 #endif
831 } 832 }
832 833
(...skipping 24 matching lines...) Expand all
857 return false; 858 return false;
858 } 859 }
859 } else { // no user bounds, so just use the clip 860 } else { // no user bounds, so just use the clip
860 ir = clipBounds; 861 ir = clipBounds;
861 } 862 }
862 SkASSERT(!ir.isEmpty()); 863 SkASSERT(!ir.isEmpty());
863 864
864 if (bounds_affects_clip(flags)) { 865 if (bounds_affects_clip(flags)) {
865 // Simplify the current clips since they will be applied properly during restore() 866 // Simplify the current clips since they will be applied properly during restore()
866 fCachedLocalClipBoundsDirty = true; 867 fCachedLocalClipBoundsDirty = true;
867 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op); 868 fClipStack.clipDevRect(ir, SkRegion::kReplace_Op);
868 fMCRec->fRasterClip.setRect(ir); 869 fMCRec->fRasterClip.setRect(ir);
869 } 870 }
870 871
871 if (intersection) { 872 if (intersection) {
872 *intersection = ir; 873 *intersection = ir;
873 } 874 }
874 return true; 875 return true;
875 } 876 }
876 877
877 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) { 878 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
966 return this->saveLayer(bounds, &tmpPaint, flags); 967 return this->saveLayer(bounds, &tmpPaint, flags);
967 } 968 }
968 } 969 }
969 970
970 void SkCanvas::internalRestore() { 971 void SkCanvas::internalRestore() {
971 SkASSERT(fMCStack.count() != 0); 972 SkASSERT(fMCStack.count() != 0);
972 973
973 fDeviceCMDirty = true; 974 fDeviceCMDirty = true;
974 fCachedLocalClipBoundsDirty = true; 975 fCachedLocalClipBoundsDirty = true;
975 976
976 fClipStack->restore(); 977 fClipStack.restore();
977 978
978 // reserve our layer (if any) 979 // reserve our layer (if any)
979 DeviceCM* layer = fMCRec->fLayer; // may be null 980 DeviceCM* layer = fMCRec->fLayer; // may be null
980 // now detach it from fMCRec so we can pop(). Gets freed after its drawn 981 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
981 fMCRec->fLayer = NULL; 982 fMCRec->fLayer = NULL;
982 983
983 // now do the normal restore() 984 // now do the normal restore()
984 fMCRec->~MCRec(); // balanced in save() 985 fMCRec->~MCRec(); // balanced in save()
985 fMCStack.pop_back(); 986 fMCStack.pop_back();
986 fMCRec = (MCRec*)fMCStack.back(); 987 fMCRec = (MCRec*)fMCStack.back();
987 988
988 /* Time to draw the layer's offscreen. We can't call the public drawSprite, 989 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
989 since if we're being recorded, we don't want to record this (the 990 since if we're being recorded, we don't want to record this (the
990 recorder will have already recorded the restore). 991 recorder will have already recorded the restore).
991 */ 992 */
992 if (layer) { 993 if (layer) {
993 if (layer->fNext) { 994 if (layer->fNext) {
994 const SkIPoint& origin = layer->fDevice->getOrigin(); 995 const SkIPoint& origin = layer->fDevice->getOrigin();
995 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), 996 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
996 layer->fPaint); 997 layer->fPaint);
997 // reset this, since internalDrawDevice will have set it to true 998 // reset this, since internalDrawDevice will have set it to true
998 fDeviceCMDirty = true; 999 fDeviceCMDirty = true;
1000 SkDELETE(layer);
1001 } else {
1002 // we're at the root
1003 SkASSERT(layer == (void*)fBaseLayerStorage);
1004 layer->~DeviceCM();
999 } 1005 }
1000 SkDELETE(layer);
1001 } 1006 }
1002 } 1007 }
1003 1008
1004 SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* p rops) { 1009 SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* p rops) {
1005 if (NULL == props) { 1010 if (NULL == props) {
1006 props = &fProps; 1011 props = &fProps;
1007 } 1012 }
1008 return this->onNewSurface(info, *props); 1013 return this->onNewSurface(info, *props);
1009 } 1014 }
1010 1015
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
1271 } 1276 }
1272 1277
1273 if (fMCRec->fMatrix.rectStaysRect()) { 1278 if (fMCRec->fMatrix.rectStaysRect()) {
1274 // for these simpler matrices, we can stay a rect even after applying 1279 // for these simpler matrices, we can stay a rect even after applying
1275 // the matrix. This means we don't have to a) make a path, and b) tell 1280 // the matrix. This means we don't have to a) make a path, and b) tell
1276 // the region code to scan-convert the path, only to discover that it 1281 // the region code to scan-convert the path, only to discover that it
1277 // is really just a rect. 1282 // is really just a rect.
1278 SkRect r; 1283 SkRect r;
1279 1284
1280 fMCRec->fMatrix.mapRect(&r, rect); 1285 fMCRec->fMatrix.mapRect(&r, rect);
1281 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle); 1286 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1282 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeSt yle == edgeStyle); 1287 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeSt yle == edgeStyle);
1283 } else { 1288 } else {
1284 // since we're rotated or some such thing, we convert the rect to a path 1289 // since we're rotated or some such thing, we convert the rect to a path
1285 // and clip against that, since it can handle any matrix. However, to 1290 // and clip against that, since it can handle any matrix. However, to
1286 // avoid recursion in the case where we are subclassed (e.g. Pictures) 1291 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1287 // we explicitly call "our" version of clipPath. 1292 // we explicitly call "our" version of clipPath.
1288 SkPath path; 1293 SkPath path;
1289 1294
1290 path.addRect(rect); 1295 path.addRect(rect);
1291 this->SkCanvas::onClipPath(path, op, edgeStyle); 1296 this->SkCanvas::onClipPath(path, op, edgeStyle);
(...skipping 19 matching lines...) Expand all
1311 SkRRect transformedRRect; 1316 SkRRect transformedRRect;
1312 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) { 1317 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
1313 AutoValidateClip avc(this); 1318 AutoValidateClip avc(this);
1314 1319
1315 fDeviceCMDirty = true; 1320 fDeviceCMDirty = true;
1316 fCachedLocalClipBoundsDirty = true; 1321 fCachedLocalClipBoundsDirty = true;
1317 if (!fAllowSoftClip) { 1322 if (!fAllowSoftClip) {
1318 edgeStyle = kHard_ClipEdgeStyle; 1323 edgeStyle = kHard_ClipEdgeStyle;
1319 } 1324 }
1320 1325
1321 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == ed geStyle); 1326 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edg eStyle);
1322 1327
1323 SkPath devPath; 1328 SkPath devPath;
1324 devPath.addRRect(transformedRRect); 1329 devPath.addRRect(transformedRRect);
1325 1330
1326 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeS tyle == edgeStyle); 1331 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeS tyle == edgeStyle);
1327 return; 1332 return;
1328 } 1333 }
1329 1334
1330 SkPath path; 1335 SkPath path;
1331 path.addRRect(rrect); 1336 path.addRRect(rrect);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1376 // made us empty. Note this can also happen if we contained NaN 1381 // made us empty. Note this can also happen if we contained NaN
1377 // values. computing the bounds detects this, and will set our 1382 // values. computing the bounds detects this, and will set our
1378 // bounds to empty if that is the case. (see SkRect::set(pts, count)) 1383 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1379 if (devPath.getBounds().isEmpty()) { 1384 if (devPath.getBounds().isEmpty()) {
1380 // resetting the path will remove any NaN or other wanky values 1385 // resetting the path will remove any NaN or other wanky values
1381 // that might upset our scan converter. 1386 // that might upset our scan converter.
1382 devPath.reset(); 1387 devPath.reset();
1383 } 1388 }
1384 1389
1385 // if we called path.swap() we could avoid a deep copy of this path 1390 // if we called path.swap() we could avoid a deep copy of this path
1386 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle); 1391 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1387 1392
1388 if (fAllowSimplifyClip) { 1393 if (fAllowSimplifyClip) {
1389 bool clipIsAA = getClipStack()->asPath(&devPath); 1394 bool clipIsAA = getClipStack()->asPath(&devPath);
1390 if (clipIsAA) { 1395 if (clipIsAA) {
1391 edgeStyle = kSoft_ClipEdgeStyle; 1396 edgeStyle = kSoft_ClipEdgeStyle;
1392 } 1397 }
1393 1398
1394 op = SkRegion::kReplace_Op; 1399 op = SkRegion::kReplace_Op;
1395 } 1400 }
1396 1401
1397 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle); 1402 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
1398 } 1403 }
1399 1404
1400 void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { 1405 void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
1401 this->checkForDeferredSave(); 1406 this->checkForDeferredSave();
1402 this->onClipRegion(rgn, op); 1407 this->onClipRegion(rgn, op);
1403 } 1408 }
1404 1409
1405 void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) { 1410 void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
1406 AutoValidateClip avc(this); 1411 AutoValidateClip avc(this);
1407 1412
1408 fDeviceCMDirty = true; 1413 fDeviceCMDirty = true;
1409 fCachedLocalClipBoundsDirty = true; 1414 fCachedLocalClipBoundsDirty = true;
1410 1415
1411 // todo: signal fClipStack that we have a region, and therefore (I guess) 1416 // todo: signal fClipStack that we have a region, and therefore (I guess)
1412 // we have to ignore it, and use the region directly? 1417 // we have to ignore it, and use the region directly?
1413 fClipStack->clipDevRect(rgn.getBounds(), op); 1418 fClipStack.clipDevRect(rgn.getBounds(), op);
1414 1419
1415 fMCRec->fRasterClip.op(rgn, op); 1420 fMCRec->fRasterClip.op(rgn, op);
1416 } 1421 }
1417 1422
1418 #ifdef SK_DEBUG 1423 #ifdef SK_DEBUG
1419 void SkCanvas::validateClip() const { 1424 void SkCanvas::validateClip() const {
1420 // construct clipRgn from the clipstack 1425 // construct clipRgn from the clipstack
1421 const SkBaseDevice* device = this->getDevice(); 1426 const SkBaseDevice* device = this->getDevice();
1422 if (!device) { 1427 if (!device) {
1423 SkASSERT(this->isClipEmpty()); 1428 SkASSERT(this->isClipEmpty());
1424 return; 1429 return;
1425 } 1430 }
1426 1431
1427 SkIRect ir; 1432 SkIRect ir;
1428 ir.set(0, 0, device->width(), device->height()); 1433 ir.set(0, 0, device->width(), device->height());
1429 SkRasterClip tmpClip(ir, fConservativeRasterClip); 1434 SkRasterClip tmpClip(ir, fConservativeRasterClip);
1430 1435
1431 SkClipStack::B2TIter iter(*fClipStack); 1436 SkClipStack::B2TIter iter(fClipStack);
1432 const SkClipStack::Element* element; 1437 const SkClipStack::Element* element;
1433 while ((element = iter.next()) != NULL) { 1438 while ((element = iter.next()) != NULL) {
1434 switch (element->getType()) { 1439 switch (element->getType()) {
1435 case SkClipStack::Element::kRect_Type: 1440 case SkClipStack::Element::kRect_Type:
1436 element->getRect().round(&ir); 1441 element->getRect().round(&ir);
1437 tmpClip.op(ir, element->getOp()); 1442 tmpClip.op(ir, element->getOp());
1438 break; 1443 break;
1439 case SkClipStack::Element::kEmpty_Type: 1444 case SkClipStack::Element::kEmpty_Type:
1440 tmpClip.setEmpty(); 1445 tmpClip.setEmpty();
1441 break; 1446 break;
1442 default: { 1447 default: {
1443 SkPath path; 1448 SkPath path;
1444 element->asPath(&path); 1449 element->asPath(&path);
1445 rasterclip_path(&tmpClip, this, path, element->getOp(), element- >isAA()); 1450 rasterclip_path(&tmpClip, this, path, element->getOp(), element- >isAA());
1446 break; 1451 break;
1447 } 1452 }
1448 } 1453 }
1449 } 1454 }
1450 } 1455 }
1451 #endif 1456 #endif
1452 1457
1453 void SkCanvas::replayClips(ClipVisitor* visitor) const { 1458 void SkCanvas::replayClips(ClipVisitor* visitor) const {
1454 SkClipStack::B2TIter iter(*fClipStack); 1459 SkClipStack::B2TIter iter(fClipStack);
1455 const SkClipStack::Element* element; 1460 const SkClipStack::Element* element;
1456 1461
1457 while ((element = iter.next()) != NULL) { 1462 while ((element = iter.next()) != NULL) {
1458 element->replay(visitor); 1463 element->replay(visitor);
1459 } 1464 }
1460 } 1465 }
1461 1466
1462 /////////////////////////////////////////////////////////////////////////////// 1467 ///////////////////////////////////////////////////////////////////////////////
1463 1468
1464 bool SkCanvas::isClipEmpty() const { 1469 bool SkCanvas::isClipEmpty() const {
(...skipping 1047 matching lines...) Expand 10 before | Expand all | Expand 10 after
2512 } 2517 }
2513 2518
2514 if (matrix) { 2519 if (matrix) {
2515 canvas->concat(*matrix); 2520 canvas->concat(*matrix);
2516 } 2521 }
2517 } 2522 }
2518 2523
2519 SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() { 2524 SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2520 fCanvas->restoreToCount(fSaveCount); 2525 fCanvas->restoreToCount(fSaveCount);
2521 } 2526 }
OLDNEW
« no previous file with comments | « include/core/SkCanvas.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698