SourceSurfaceSkia.cpp (7482B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "Logging.h" 8 #include "SourceSurfaceSkia.h" 9 #include "HelpersSkia.h" 10 #include "DrawTargetSkia.h" 11 #include "skia/include/core/SkData.h" 12 #include "skia/include/core/SkImage.h" 13 #include "skia/include/core/SkSurface.h" 14 #include "skia/include/private/base/SkMalloc.h" 15 #include "mozilla/CheckedInt.h" 16 17 namespace mozilla::gfx { 18 19 SourceSurfaceSkia::SourceSurfaceSkia() 20 : mFormat(SurfaceFormat::UNKNOWN), 21 mStride(0), 22 mDrawTarget(nullptr), 23 mChangeMutex("SourceSurfaceSkia::mChangeMutex"), 24 mIsMapped(false) {} 25 26 SourceSurfaceSkia::~SourceSurfaceSkia() { 27 // if mIsMapped is true then mChangeMutex will be locked 28 // which will cause problems during destruction. 29 MOZ_RELEASE_ASSERT(!mIsMapped); 30 } 31 32 IntSize SourceSurfaceSkia::GetSize() const { return mSize; } 33 34 SurfaceFormat SourceSurfaceSkia::GetFormat() const { return mFormat; } 35 36 // This is only ever called by the DT destructor, which can only ever happen 37 // from one place at a time. Therefore it doesn't need to hold the ChangeMutex 38 // as mSurface is never read to directly and is just there to keep the object 39 // alive, which itself is refcounted in a thread-safe manner. 40 void SourceSurfaceSkia::GiveSurface(SkSurface* aSurface) { 41 mSurface.reset(aSurface); 42 mDrawTarget = nullptr; 43 } 44 45 sk_sp<SkImage> SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) { 46 // If we were provided a lock object, we can let the caller access 47 // a shared SkImage and we know it won't go away while the lock is held. 48 // Otherwise we need to call DrawTargetWillChange to ensure we have our 49 // own SkImage. 50 if (aLock) { 51 MOZ_ASSERT(aLock->isNothing()); 52 aLock->emplace(mChangeMutex); 53 54 // Now that we are locked, we can check mDrawTarget. If it's null, then 55 // we're not shared and we can unlock eagerly. 56 if (!mDrawTarget) { 57 aLock->reset(); 58 } 59 } else { 60 DrawTargetWillChange(); 61 } 62 sk_sp<SkImage> image = mImage; 63 return image; 64 } 65 66 static sk_sp<SkData> MakeSkData(void* aData, int32_t aHeight, size_t aStride) { 67 CheckedInt<size_t> size = aStride; 68 size *= aHeight; 69 if (size.isValid()) { 70 void* mem = sk_malloc_flags(size.value(), 0); 71 if (mem) { 72 if (aData) { 73 memcpy(mem, aData, size.value()); 74 } 75 return SkData::MakeFromMalloc(mem, size.value()); 76 } 77 } 78 return nullptr; 79 } 80 81 static sk_sp<SkImage> ReadSkImage(const sk_sp<SkImage>& aImage, 82 const SkImageInfo& aInfo, size_t aStride, 83 int aX = 0, int aY = 0) { 84 if (sk_sp<SkData> data = MakeSkData(nullptr, aInfo.height(), aStride)) { 85 if (aImage->readPixels(aInfo, data->writable_data(), aStride, aX, aY, 86 SkImage::kDisallow_CachingHint)) { 87 return SkImages::RasterFromData(aInfo, data, aStride); 88 } 89 } 90 return nullptr; 91 } 92 93 bool SourceSurfaceSkia::InitFromData(unsigned char* aData, const IntSize& aSize, 94 int32_t aStride, SurfaceFormat aFormat) { 95 sk_sp<SkData> data = MakeSkData(aData, aSize.height, aStride); 96 if (!data) { 97 return false; 98 } 99 100 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat); 101 mImage = SkImages::RasterFromData(info, data, aStride); 102 if (!mImage) { 103 return false; 104 } 105 106 mSize = aSize; 107 mFormat = aFormat; 108 mStride = aStride; 109 return true; 110 } 111 112 bool SourceSurfaceSkia::InitFromImage(const sk_sp<SkImage>& aImage, 113 SurfaceFormat aFormat, 114 DrawTargetSkia* aOwner) { 115 if (!aImage) { 116 return false; 117 } 118 119 mSize = IntSize(aImage->width(), aImage->height()); 120 121 // For the raster image case, we want to use the format and stride 122 // information that the underlying raster image is using, which is 123 // reliable. 124 // For the GPU case (for which peekPixels is false), we can't easily 125 // figure this information out. It is better to report the originally 126 // intended format and stride that we will convert to if this GPU 127 // image is ever read back into a raster image. 128 SkPixmap pixmap; 129 if (aImage->peekPixels(&pixmap)) { 130 mFormat = 131 aFormat != SurfaceFormat::UNKNOWN 132 ? aFormat 133 : SkiaColorTypeToGfxFormat(pixmap.colorType(), pixmap.alphaType()); 134 if (pixmap.info().bytesPerPixel() != BytesPerPixel(mFormat)) { 135 return false; 136 } 137 mStride = pixmap.rowBytes(); 138 } else if (aFormat != SurfaceFormat::UNKNOWN) { 139 mFormat = aFormat; 140 const SkImageInfo& info = aImage->imageInfo(); 141 if (info.bytesPerPixel() != BytesPerPixel(mFormat)) { 142 return false; 143 } 144 mStride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); 145 if (mStride <= 0 || size_t(mStride) < info.minRowBytes64()) { 146 return false; 147 } 148 } else { 149 return false; 150 } 151 152 mImage = aImage; 153 154 if (aOwner) { 155 mDrawTarget = aOwner; 156 } 157 158 return true; 159 } 160 161 already_AddRefed<SourceSurface> SourceSurfaceSkia::ExtractSubrect( 162 const IntRect& aRect) { 163 if (!mImage || aRect.IsEmpty() || !GetRect().Contains(aRect)) { 164 return nullptr; 165 } 166 SkImageInfo info = MakeSkiaImageInfo(aRect.Size(), mFormat); 167 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); 168 if (!stride) { 169 return nullptr; 170 } 171 sk_sp<SkImage> subImage = ReadSkImage(mImage, info, stride, aRect.x, aRect.y); 172 if (!subImage) { 173 return nullptr; 174 } 175 RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia; 176 if (!surface->InitFromImage(subImage)) { 177 return nullptr; 178 } 179 return surface.forget().downcast<SourceSurface>(); 180 } 181 182 uint8_t* SourceSurfaceSkia::GetData() { 183 if (!mImage) { 184 return nullptr; 185 } 186 SkPixmap pixmap; 187 if (!mImage->peekPixels(&pixmap)) { 188 gfxCriticalError() << "Failed accessing pixels for Skia raster image"; 189 } 190 return reinterpret_cast<uint8_t*>(pixmap.writable_addr()); 191 } 192 193 bool SourceSurfaceSkia::Map(MapType, MappedSurface* aMappedSurface) 194 MOZ_NO_THREAD_SAFETY_ANALYSIS { 195 mChangeMutex.Lock(); 196 aMappedSurface->mData = GetData(); 197 aMappedSurface->mStride = Stride(); 198 mIsMapped = !!aMappedSurface->mData; 199 bool isMapped = mIsMapped; 200 if (!mIsMapped) { 201 mChangeMutex.Unlock(); 202 } 203 // Static analysis will warn due to a conditional Unlock 204 MOZ_PUSH_IGNORE_THREAD_SAFETY 205 return isMapped; 206 MOZ_POP_THREAD_SAFETY 207 } 208 209 void SourceSurfaceSkia::Unmap() MOZ_NO_THREAD_SAFETY_ANALYSIS { 210 mChangeMutex.AssertCurrentThreadOwns(); 211 MOZ_ASSERT(mIsMapped); 212 mIsMapped = false; 213 mChangeMutex.Unlock(); 214 } 215 216 void SourceSurfaceSkia::DrawTargetWillChange() { 217 MutexAutoLock lock(mChangeMutex); 218 if (mDrawTarget.exchange(nullptr)) { 219 // Raster snapshots do not use Skia's internal copy-on-write mechanism, 220 // so we need to do an explicit copy here. 221 // GPU snapshots, for which peekPixels is false, will already be dealt 222 // with automatically via the internal copy-on-write mechanism, so we 223 // don't need to do anything for them here. 224 SkPixmap pixmap; 225 if (mImage->peekPixels(&pixmap)) { 226 mImage = ReadSkImage(mImage, pixmap.info(), pixmap.rowBytes()); 227 if (!mImage) { 228 gfxCriticalError() << "Failed copying Skia raster snapshot"; 229 } 230 } 231 } 232 } 233 234 } // namespace mozilla::gfx