forked from mirrors/gecko-dev
		
	 dc0ab2eed4
			
		
	
	
		dc0ab2eed4
		
	
	
	
	
		
			
			This just tries to address fairly random changes in the Skia API and correct our usage of it in Moz2D and some other places. Differential Revision: https://phabricator.services.mozilla.com/D173324
		
			
				
	
	
		
			228 lines
		
	
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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
 |