diff options
Diffstat (limited to 'gfx/2d/SourceSurfaceSkia.cpp')
-rw-r--r-- | gfx/2d/SourceSurfaceSkia.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp new file mode 100644 index 0000000000..21f15f62e6 --- /dev/null +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Logging.h" +#include "SourceSurfaceSkia.h" +#include "HelpersSkia.h" +#include "DrawTargetSkia.h" +#include "skia/include/core/SkData.h" +#include "skia/include/core/SkImage.h" +#include "skia/include/core/SkSurface.h" +#include "skia/include/private/base/SkMalloc.h" +#include "mozilla/CheckedInt.h" + +namespace mozilla::gfx { + +SourceSurfaceSkia::SourceSurfaceSkia() + : mFormat(SurfaceFormat::UNKNOWN), + mStride(0), + mDrawTarget(nullptr), + mChangeMutex("SourceSurfaceSkia::mChangeMutex"), + mIsMapped(false) {} + +SourceSurfaceSkia::~SourceSurfaceSkia() { + // if mIsMapped is true then mChangeMutex will be locked + // which will cause problems during destruction. + MOZ_RELEASE_ASSERT(!mIsMapped); +} + +IntSize SourceSurfaceSkia::GetSize() const { return mSize; } + +SurfaceFormat SourceSurfaceSkia::GetFormat() const { return mFormat; } + +// This is only ever called by the DT destructor, which can only ever happen +// from one place at a time. Therefore it doesn't need to hold the ChangeMutex +// as mSurface is never read to directly and is just there to keep the object +// alive, which itself is refcounted in a thread-safe manner. +void SourceSurfaceSkia::GiveSurface(SkSurface* aSurface) { + mSurface.reset(aSurface); + mDrawTarget = nullptr; +} + +sk_sp<SkImage> SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) { + // If we were provided a lock object, we can let the caller access + // a shared SkImage and we know it won't go away while the lock is held. + // Otherwise we need to call DrawTargetWillChange to ensure we have our + // own SkImage. + if (aLock) { + MOZ_ASSERT(aLock->isNothing()); + aLock->emplace(mChangeMutex); + + // Now that we are locked, we can check mDrawTarget. If it's null, then + // we're not shared and we can unlock eagerly. + if (!mDrawTarget) { + aLock->reset(); + } + } else { + DrawTargetWillChange(); + } + sk_sp<SkImage> image = mImage; + return image; +} + +static sk_sp<SkData> MakeSkData(void* aData, int32_t aHeight, size_t aStride) { + CheckedInt<size_t> size = aStride; + size *= aHeight; + if (size.isValid()) { + void* mem = sk_malloc_flags(size.value(), 0); + if (mem) { + if (aData) { + memcpy(mem, aData, size.value()); + } + return SkData::MakeFromMalloc(mem, size.value()); + } + } + return nullptr; +} + +static sk_sp<SkImage> ReadSkImage(const sk_sp<SkImage>& aImage, + const SkImageInfo& aInfo, size_t aStride, + int aX = 0, int aY = 0) { + if (sk_sp<SkData> data = MakeSkData(nullptr, aInfo.height(), aStride)) { + if (aImage->readPixels(aInfo, data->writable_data(), aStride, aX, aY, + SkImage::kDisallow_CachingHint)) { + return SkImage::MakeRasterData(aInfo, data, aStride); + } + } + return nullptr; +} + +bool SourceSurfaceSkia::InitFromData(unsigned char* aData, const IntSize& aSize, + int32_t aStride, SurfaceFormat aFormat) { + sk_sp<SkData> data = MakeSkData(aData, aSize.height, aStride); + if (!data) { + return false; + } + + SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat); + mImage = SkImage::MakeRasterData(info, data, aStride); + if (!mImage) { + return false; + } + + mSize = aSize; + mFormat = aFormat; + mStride = aStride; + return true; +} + +bool SourceSurfaceSkia::InitFromImage(const sk_sp<SkImage>& aImage, + SurfaceFormat aFormat, + DrawTargetSkia* aOwner) { + if (!aImage) { + return false; + } + + mSize = IntSize(aImage->width(), aImage->height()); + + // For the raster image case, we want to use the format and stride + // information that the underlying raster image is using, which is + // reliable. + // For the GPU case (for which peekPixels is false), we can't easily + // figure this information out. It is better to report the originally + // intended format and stride that we will convert to if this GPU + // image is ever read back into a raster image. + SkPixmap pixmap; + if (aImage->peekPixels(&pixmap)) { + mFormat = + aFormat != SurfaceFormat::UNKNOWN + ? aFormat + : SkiaColorTypeToGfxFormat(pixmap.colorType(), pixmap.alphaType()); + mStride = pixmap.rowBytes(); + } else if (aFormat != SurfaceFormat::UNKNOWN) { + mFormat = aFormat; + SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat); + mStride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); + if (!mStride) { + return false; + } + } else { + return false; + } + + mImage = aImage; + + if (aOwner) { + mDrawTarget = aOwner; + } + + return true; +} + +already_AddRefed<SourceSurface> SourceSurfaceSkia::ExtractSubrect( + const IntRect& aRect) { + if (!mImage || aRect.IsEmpty() || !GetRect().Contains(aRect)) { + return nullptr; + } + SkImageInfo info = MakeSkiaImageInfo(aRect.Size(), mFormat); + size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); + if (!stride) { + return nullptr; + } + sk_sp<SkImage> subImage = ReadSkImage(mImage, info, stride, aRect.x, aRect.y); + if (!subImage) { + return nullptr; + } + RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia; + if (!surface->InitFromImage(subImage)) { + return nullptr; + } + return surface.forget().downcast<SourceSurface>(); +} + +uint8_t* SourceSurfaceSkia::GetData() { + if (!mImage) { + return nullptr; + } + SkPixmap pixmap; + if (!mImage->peekPixels(&pixmap)) { + gfxCriticalError() << "Failed accessing pixels for Skia raster image"; + } + return reinterpret_cast<uint8_t*>(pixmap.writable_addr()); +} + +bool SourceSurfaceSkia::Map(MapType, MappedSurface* aMappedSurface) + MOZ_NO_THREAD_SAFETY_ANALYSIS { + mChangeMutex.Lock(); + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + mIsMapped = !!aMappedSurface->mData; + bool isMapped = mIsMapped; + if (!mIsMapped) { + mChangeMutex.Unlock(); + } + // Static analysis will warn due to a conditional Unlock + MOZ_PUSH_IGNORE_THREAD_SAFETY + return isMapped; + MOZ_POP_THREAD_SAFETY +} + +void SourceSurfaceSkia::Unmap() MOZ_NO_THREAD_SAFETY_ANALYSIS { + mChangeMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(mIsMapped); + mIsMapped = false; + mChangeMutex.Unlock(); +} + +void SourceSurfaceSkia::DrawTargetWillChange() { + MutexAutoLock lock(mChangeMutex); + if (mDrawTarget.exchange(nullptr)) { + // Raster snapshots do not use Skia's internal copy-on-write mechanism, + // so we need to do an explicit copy here. + // GPU snapshots, for which peekPixels is false, will already be dealt + // with automatically via the internal copy-on-write mechanism, so we + // don't need to do anything for them here. + SkPixmap pixmap; + if (mImage->peekPixels(&pixmap)) { + mImage = ReadSkImage(mImage, pixmap.info(), pixmap.rowBytes()); + if (!mImage) { + gfxCriticalError() << "Failed copying Skia raster snapshot"; + } + } + } +} + +} // namespace mozilla::gfx |