void* context, const SkSurfaceProps* = NULL);
/**
- * Return a new surface, with the memory for the pixels automatically
- * allocated.
+ * Return a new surface, with the memory for the pixels automatically allocated, but respecting
+ * the specified rowBytes. If rowBytes==0, then a default value will be chosen. If a non-zero
+ * rowBytes is specified, then any images snapped off of this surface (via newImageSnapshot())
+ * are guaranteed to have the same rowBytes.
*
* If the requested surface cannot be created, or the request is not a
* supported configuration, NULL will be returned.
*/
+ static SkSurface* NewRaster(const SkImageInfo&, size_t rowBytes, const SkSurfaceProps*);
+
+ /**
+ * Allocate a new surface, automatically computing the rowBytes.
+ */
static SkSurface* NewRaster(const SkImageInfo&, const SkSurfaceProps* = NULL);
/**
private:
SkBitmap fBitmap;
+ size_t fRowBytes;
bool fWeOwnThePixels;
typedef SkSurface_Base INHERITED;
: INHERITED(info, props)
{
fBitmap.installPixels(info, pixels, rb, nullptr, releaseProc, context);
+ fRowBytes = 0; // don't need to track the rowbytes
fWeOwnThePixels = false; // We are "Direct"
}
{
const SkImageInfo& info = pr->info();
- fBitmap.setInfo(info, info.minRowBytes());
+ fBitmap.setInfo(info, pr->rowBytes());
fBitmap.setPixelRef(pr);
+ fRowBytes = pr->rowBytes(); // we track this, so that subsequent re-allocs will match
fWeOwnThePixels = true;
}
if (SkBitmapImageGetPixelRef(this->getCachedImage(kNo_Budgeted)) == fBitmap.pixelRef()) {
SkASSERT(fWeOwnThePixels);
if (kDiscard_ContentChangeMode == mode) {
- fBitmap.setPixelRef(nullptr);
fBitmap.allocPixels();
} else {
SkBitmap prev(fBitmap);
- prev.deepCopyTo(&fBitmap);
+ fBitmap.allocPixels();
+ prev.lockPixels();
+ SkASSERT(prev.info() == fBitmap.info());
+ SkASSERT(prev.rowBytes() == fBitmap.rowBytes());
+ memcpy(fBitmap.getPixels(), prev.getPixels(), fBitmap.getSafeSize());
}
+ SkASSERT(fBitmap.rowBytes() == fRowBytes); // be sure we always use the same value
+
// Now fBitmap is a deep copy of itself (and therefore different from
// what is being used by the image. Next we update the canvas to use
// this as its backend, so we can't modify the image's pixels anymore.
return NewRasterDirectReleaseProc(info, pixels, rowBytes, nullptr, nullptr, props);
}
-SkSurface* SkSurface::NewRaster(const SkImageInfo& info, const SkSurfaceProps* props) {
+SkSurface* SkSurface::NewRaster(const SkImageInfo& info, size_t rowBytes,
+ const SkSurfaceProps* props) {
if (!SkSurface_Raster::Valid(info)) {
return nullptr;
}
- SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewZeroed(info, 0, nullptr));
+ SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewZeroed(info, rowBytes, nullptr));
if (nullptr == pr.get()) {
return nullptr;
}
+ if (rowBytes) {
+ SkASSERT(pr->rowBytes() == rowBytes);
+ }
return new SkSurface_Raster(pr, props);
}
+
+SkSurface* SkSurface::NewRaster(const SkImageInfo& info, const SkSurfaceProps* props) {
+ return NewRaster(info, 0, props);
+}
}
}
#endif
+
+static void check_rowbytes_remain_consistent(SkSurface* surface, skiatest::Reporter* reporter) {
+ SkImageInfo info;
+ size_t rowBytes;
+ REPORTER_ASSERT(reporter, surface->peekPixels(&info, &rowBytes));
+
+ SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+ SkImageInfo im_info;
+ size_t im_rowbytes;
+ REPORTER_ASSERT(reporter, image->peekPixels(&im_info, &im_rowbytes));
+
+ REPORTER_ASSERT(reporter, rowBytes == im_rowbytes);
+
+ // trigger a copy-on-write
+ surface->getCanvas()->drawPaint(SkPaint());
+ SkAutoTUnref<SkImage> image2(surface->newImageSnapshot());
+ REPORTER_ASSERT(reporter, image->uniqueID() != image2->uniqueID());
+
+ SkImageInfo im_info2;
+ size_t im_rowbytes2;
+ REPORTER_ASSERT(reporter, image2->peekPixels(&im_info2, &im_rowbytes2));
+
+ REPORTER_ASSERT(reporter, im_rowbytes2 == im_rowbytes);
+}
+
+DEF_TEST(surface_rowbytes, reporter) {
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
+
+ SkAutoTUnref<SkSurface> surf0(SkSurface::NewRaster(info));
+ check_rowbytes_remain_consistent(surf0, reporter);
+
+ // specify a larger rowbytes
+ SkAutoTUnref<SkSurface> surf1(SkSurface::NewRaster(info, 500, nullptr));
+ check_rowbytes_remain_consistent(surf1, reporter);
+
+ // Try some illegal rowByte values
+ SkSurface* s = SkSurface::NewRaster(info, 396, nullptr); // needs to be at least 400
+ REPORTER_ASSERT(reporter, nullptr == s);
+ s = SkSurface::NewRaster(info, 1 << 30, nullptr); // allocation to large
+ REPORTER_ASSERT(reporter, nullptr == s);
+}