no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / 2d / SourceSurfaceSkia.cpp
blob21f15f62e64eab75215bb053ea3d9122ebf74e04
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/. */
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"
17 namespace mozilla::gfx {
19 SourceSurfaceSkia::SourceSurfaceSkia()
20 : mFormat(SurfaceFormat::UNKNOWN),
21 mStride(0),
22 mDrawTarget(nullptr),
23 mChangeMutex("SourceSurfaceSkia::mChangeMutex"),
24 mIsMapped(false) {}
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);
32 IntSize SourceSurfaceSkia::GetSize() const { return mSize; }
34 SurfaceFormat SourceSurfaceSkia::GetFormat() const { return mFormat; }
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;
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);
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();
59 } else {
60 DrawTargetWillChange();
62 sk_sp<SkImage> image = mImage;
63 return image;
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());
75 return SkData::MakeFromMalloc(mem, size.value());
78 return nullptr;
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 SkImage::MakeRasterData(aInfo, data, aStride);
90 return nullptr;
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;
100 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
101 mImage = SkImage::MakeRasterData(info, data, aStride);
102 if (!mImage) {
103 return false;
106 mSize = aSize;
107 mFormat = aFormat;
108 mStride = aStride;
109 return true;
112 bool SourceSurfaceSkia::InitFromImage(const sk_sp<SkImage>& aImage,
113 SurfaceFormat aFormat,
114 DrawTargetSkia* aOwner) {
115 if (!aImage) {
116 return false;
119 mSize = IntSize(aImage->width(), aImage->height());
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 mStride = pixmap.rowBytes();
135 } else if (aFormat != SurfaceFormat::UNKNOWN) {
136 mFormat = aFormat;
137 SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
138 mStride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
139 if (!mStride) {
140 return false;
142 } else {
143 return false;
146 mImage = aImage;
148 if (aOwner) {
149 mDrawTarget = aOwner;
152 return true;
155 already_AddRefed<SourceSurface> SourceSurfaceSkia::ExtractSubrect(
156 const IntRect& aRect) {
157 if (!mImage || aRect.IsEmpty() || !GetRect().Contains(aRect)) {
158 return nullptr;
160 SkImageInfo info = MakeSkiaImageInfo(aRect.Size(), mFormat);
161 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
162 if (!stride) {
163 return nullptr;
165 sk_sp<SkImage> subImage = ReadSkImage(mImage, info, stride, aRect.x, aRect.y);
166 if (!subImage) {
167 return nullptr;
169 RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia;
170 if (!surface->InitFromImage(subImage)) {
171 return nullptr;
173 return surface.forget().downcast<SourceSurface>();
176 uint8_t* SourceSurfaceSkia::GetData() {
177 if (!mImage) {
178 return nullptr;
180 SkPixmap pixmap;
181 if (!mImage->peekPixels(&pixmap)) {
182 gfxCriticalError() << "Failed accessing pixels for Skia raster image";
184 return reinterpret_cast<uint8_t*>(pixmap.writable_addr());
187 bool SourceSurfaceSkia::Map(MapType, MappedSurface* aMappedSurface)
188 MOZ_NO_THREAD_SAFETY_ANALYSIS {
189 mChangeMutex.Lock();
190 aMappedSurface->mData = GetData();
191 aMappedSurface->mStride = Stride();
192 mIsMapped = !!aMappedSurface->mData;
193 bool isMapped = mIsMapped;
194 if (!mIsMapped) {
195 mChangeMutex.Unlock();
197 // Static analysis will warn due to a conditional Unlock
198 MOZ_PUSH_IGNORE_THREAD_SAFETY
199 return isMapped;
200 MOZ_POP_THREAD_SAFETY
203 void SourceSurfaceSkia::Unmap() MOZ_NO_THREAD_SAFETY_ANALYSIS {
204 mChangeMutex.AssertCurrentThreadOwns();
205 MOZ_ASSERT(mIsMapped);
206 mIsMapped = false;
207 mChangeMutex.Unlock();
210 void SourceSurfaceSkia::DrawTargetWillChange() {
211 MutexAutoLock lock(mChangeMutex);
212 if (mDrawTarget.exchange(nullptr)) {
213 // Raster snapshots do not use Skia's internal copy-on-write mechanism,
214 // so we need to do an explicit copy here.
215 // GPU snapshots, for which peekPixels is false, will already be dealt
216 // with automatically via the internal copy-on-write mechanism, so we
217 // don't need to do anything for them here.
218 SkPixmap pixmap;
219 if (mImage->peekPixels(&pixmap)) {
220 mImage = ReadSkImage(mImage, pixmap.info(), pixmap.rowBytes());
221 if (!mImage) {
222 gfxCriticalError() << "Failed copying Skia raster snapshot";
228 } // namespace mozilla::gfx