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/. */
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
),
23 mChangeMutex("SourceSurfaceSkia::mChangeMutex"),
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
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.
60 DrawTargetWillChange();
62 sk_sp
<SkImage
> image
= mImage
;
66 static sk_sp
<SkData
> MakeSkData(void* aData
, int32_t aHeight
, size_t aStride
) {
67 CheckedInt
<size_t> size
= aStride
;
70 void* mem
= sk_malloc_flags(size
.value(), 0);
73 memcpy(mem
, aData
, size
.value());
75 return SkData::MakeFromMalloc(mem
, size
.value());
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
);
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
);
100 SkImageInfo info
= MakeSkiaImageInfo(aSize
, aFormat
);
101 mImage
= SkImage::MakeRasterData(info
, data
, aStride
);
112 bool SourceSurfaceSkia::InitFromImage(const sk_sp
<SkImage
>& aImage
,
113 SurfaceFormat aFormat
,
114 DrawTargetSkia
* aOwner
) {
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
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.
129 if (aImage
->peekPixels(&pixmap
)) {
131 aFormat
!= SurfaceFormat::UNKNOWN
133 : SkiaColorTypeToGfxFormat(pixmap
.colorType(), pixmap
.alphaType());
134 mStride
= pixmap
.rowBytes();
135 } else if (aFormat
!= SurfaceFormat::UNKNOWN
) {
137 SkImageInfo info
= MakeSkiaImageInfo(mSize
, mFormat
);
138 mStride
= GetAlignedStride
<4>(info
.width(), info
.bytesPerPixel());
149 mDrawTarget
= aOwner
;
155 already_AddRefed
<SourceSurface
> SourceSurfaceSkia::ExtractSubrect(
156 const IntRect
& aRect
) {
157 if (!mImage
|| aRect
.IsEmpty() || !GetRect().Contains(aRect
)) {
160 SkImageInfo info
= MakeSkiaImageInfo(aRect
.Size(), mFormat
);
161 size_t stride
= GetAlignedStride
<4>(info
.width(), info
.bytesPerPixel());
165 sk_sp
<SkImage
> subImage
= ReadSkImage(mImage
, info
, stride
, aRect
.x
, aRect
.y
);
169 RefPtr
<SourceSurfaceSkia
> surface
= new SourceSurfaceSkia
;
170 if (!surface
->InitFromImage(subImage
)) {
173 return surface
.forget().downcast
<SourceSurface
>();
176 uint8_t* SourceSurfaceSkia::GetData() {
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
{
190 aMappedSurface
->mData
= GetData();
191 aMappedSurface
->mStride
= Stride();
192 mIsMapped
= !!aMappedSurface
->mData
;
193 bool isMapped
= mIsMapped
;
195 mChangeMutex
.Unlock();
197 // Static analysis will warn due to a conditional Unlock
198 MOZ_PUSH_IGNORE_THREAD_SAFETY
200 MOZ_POP_THREAD_SAFETY
203 void SourceSurfaceSkia::Unmap() MOZ_NO_THREAD_SAFETY_ANALYSIS
{
204 mChangeMutex
.AssertCurrentThreadOwns();
205 MOZ_ASSERT(mIsMapped
);
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.
219 if (mImage
->peekPixels(&pixmap
)) {
220 mImage
= ReadSkImage(mImage
, pixmap
.info(), pixmap
.rowBytes());
222 gfxCriticalError() << "Failed copying Skia raster snapshot";
228 } // namespace mozilla::gfx