Source code

Revision control

Other Tools

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 "ImageContainer.h"
8
#include <string.h> // for memcpy, memset
9
#include "GLImages.h" // for SurfaceTextureImage
10
#include "gfx2DGlue.h"
11
#include "gfxPlatform.h" // for gfxPlatform
12
#include "gfxUtils.h" // for gfxUtils
13
#include "libyuv.h"
14
#include "mozilla/RefPtr.h" // for already_AddRefed
15
#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
16
#include "mozilla/layers/CompositorTypes.h"
17
#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
18
#include "mozilla/layers/ImageClient.h" // for ImageClient
19
#include "mozilla/layers/ImageDataSerializer.h" // for SurfaceDescriptorBuffer
20
#include "mozilla/layers/LayersMessages.h"
21
#include "mozilla/layers/SharedPlanarYCbCrImage.h"
22
#include "mozilla/layers/SharedSurfacesChild.h" // for SharedSurfacesAnimation
23
#include "mozilla/layers/SharedRGBImage.h"
24
#include "mozilla/layers/TextureClientRecycleAllocator.h"
25
#include "mozilla/gfx/gfxVars.h"
26
#include "nsISupportsUtils.h" // for NS_IF_ADDREF
27
#include "YCbCrUtils.h" // for YCbCr conversions
28
#include "gfx2DGlue.h"
29
#include "mozilla/gfx/2D.h"
30
#include "mozilla/CheckedInt.h"
31
32
#ifdef XP_MACOSX
33
# include "mozilla/gfx/QuartzSupport.h"
34
#endif
35
36
#ifdef XP_WIN
37
# include "gfxWindowsPlatform.h"
38
# include <d3d10_1.h>
39
# include "mozilla/gfx/DeviceManagerDx.h"
40
# include "mozilla/layers/D3D11YCbCrImage.h"
41
#endif
42
43
namespace mozilla {
44
namespace layers {
45
46
using namespace mozilla::gfx;
47
using namespace mozilla::ipc;
48
49
Atomic<int32_t> Image::sSerialCounter(0);
50
51
Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
52
53
static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
54
const gfx::IntSize& aSize, int32_t aStride,
55
int32_t aSkip);
56
57
RefPtr<PlanarYCbCrImage> ImageFactory::CreatePlanarYCbCrImage(
58
const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) {
59
return new RecyclingPlanarYCbCrImage(aRecycleBin);
60
}
61
62
BufferRecycleBin::BufferRecycleBin()
63
: mLock("mozilla.layers.BufferRecycleBin.mLock")
64
// This member is only valid when the bin is not empty and will be
65
// properly initialized in RecycleBuffer, but initializing it here avoids
66
// static analysis noise.
67
,
68
mRecycledBufferSize(0) {}
69
70
void BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,
71
uint32_t aSize) {
72
MutexAutoLock lock(mLock);
73
74
if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
75
mRecycledBuffers.Clear();
76
}
77
mRecycledBufferSize = aSize;
78
mRecycledBuffers.AppendElement(std::move(aBuffer));
79
}
80
81
UniquePtr<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize) {
82
MutexAutoLock lock(mLock);
83
84
if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) {
85
return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]);
86
}
87
88
uint32_t last = mRecycledBuffers.Length() - 1;
89
UniquePtr<uint8_t[]> result = std::move(mRecycledBuffers[last]);
90
mRecycledBuffers.RemoveElementAt(last);
91
return result;
92
}
93
94
void BufferRecycleBin::ClearRecycledBuffers() {
95
MutexAutoLock lock(mLock);
96
if (!mRecycledBuffers.IsEmpty()) {
97
mRecycledBuffers.Clear();
98
}
99
mRecycledBufferSize = 0;
100
}
101
102
ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer)
103
: mLock("mozilla.layers.ImageContainerListener.mLock"),
104
mImageContainer(aImageContainer) {}
105
106
ImageContainerListener::~ImageContainerListener() {}
107
108
void ImageContainerListener::NotifyComposite(
109
const ImageCompositeNotification& aNotification) {
110
MutexAutoLock lock(mLock);
111
if (mImageContainer) {
112
mImageContainer->NotifyComposite(aNotification);
113
}
114
}
115
116
void ImageContainerListener::NotifyDropped(uint32_t aDropped) {
117
MutexAutoLock lock(mLock);
118
if (mImageContainer) {
119
mImageContainer->NotifyDropped(aDropped);
120
}
121
}
122
123
void ImageContainerListener::ClearImageContainer() {
124
MutexAutoLock lock(mLock);
125
mImageContainer = nullptr;
126
}
127
128
void ImageContainerListener::DropImageClient() {
129
MutexAutoLock lock(mLock);
130
if (mImageContainer) {
131
mImageContainer->DropImageClient();
132
}
133
}
134
135
already_AddRefed<ImageClient> ImageContainer::GetImageClient() {
136
RecursiveMutexAutoLock mon(mRecursiveMutex);
137
EnsureImageClient();
138
RefPtr<ImageClient> imageClient = mImageClient;
139
return imageClient.forget();
140
}
141
142
void ImageContainer::DropImageClient() {
143
RecursiveMutexAutoLock mon(mRecursiveMutex);
144
if (mImageClient) {
145
mImageClient->ClearCachedResources();
146
mImageClient = nullptr;
147
}
148
}
149
150
void ImageContainer::EnsureImageClient() {
151
// If we're not forcing a new ImageClient, then we can skip this if we don't
152
// have an existing ImageClient, or if the existing one belongs to an IPC
153
// actor that is still open.
154
if (!mIsAsync) {
155
return;
156
}
157
if (mImageClient &&
158
mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
159
return;
160
}
161
162
RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
163
if (imageBridge) {
164
mImageClient =
165
imageBridge->CreateImageClient(CompositableType::IMAGE, this);
166
if (mImageClient) {
167
mAsyncContainerHandle = mImageClient->GetAsyncHandle();
168
} else {
169
// It's okay to drop the async container handle since the ImageBridgeChild
170
// is going to die anyway.
171
mAsyncContainerHandle = CompositableHandle();
172
}
173
}
174
}
175
176
SharedSurfacesAnimation* ImageContainer::EnsureSharedSurfacesAnimation() {
177
if (!mSharedAnimation) {
178
mSharedAnimation = new SharedSurfacesAnimation();
179
}
180
return mSharedAnimation;
181
}
182
183
ImageContainer::ImageContainer(Mode flag)
184
: mRecursiveMutex("ImageContainer.mRecursiveMutex"),
185
mGenerationCounter(++sGenerationCounter),
186
mPaintCount(0),
187
mDroppedImageCount(0),
188
mImageFactory(new ImageFactory()),
189
mRecycleBin(new BufferRecycleBin()),
190
mIsAsync(flag == ASYNCHRONOUS),
191
mCurrentProducerID(-1) {
192
if (flag == ASYNCHRONOUS) {
193
mNotifyCompositeListener = new ImageContainerListener(this);
194
EnsureImageClient();
195
}
196
}
197
198
ImageContainer::ImageContainer(const CompositableHandle& aHandle)
199
: mRecursiveMutex("ImageContainer.mRecursiveMutex"),
200
mGenerationCounter(++sGenerationCounter),
201
mPaintCount(0),
202
mDroppedImageCount(0),
203
mImageFactory(nullptr),
204
mRecycleBin(nullptr),
205
mIsAsync(true),
206
mAsyncContainerHandle(aHandle),
207
mCurrentProducerID(-1) {
208
MOZ_ASSERT(mAsyncContainerHandle);
209
}
210
211
ImageContainer::~ImageContainer() {
212
if (mNotifyCompositeListener) {
213
mNotifyCompositeListener->ClearImageContainer();
214
}
215
if (mAsyncContainerHandle) {
216
if (RefPtr<ImageBridgeChild> imageBridge =
217
ImageBridgeChild::GetSingleton()) {
218
imageBridge->ForgetImageContainer(mAsyncContainerHandle);
219
}
220
}
221
if (mSharedAnimation) {
222
mSharedAnimation->Destroy();
223
}
224
}
225
226
RefPtr<PlanarYCbCrImage> ImageContainer::CreatePlanarYCbCrImage() {
227
RecursiveMutexAutoLock lock(mRecursiveMutex);
228
EnsureImageClient();
229
if (mImageClient && mImageClient->AsImageClientSingle()) {
230
return new SharedPlanarYCbCrImage(mImageClient);
231
}
232
return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
233
}
234
235
RefPtr<SharedRGBImage> ImageContainer::CreateSharedRGBImage() {
236
RecursiveMutexAutoLock lock(mRecursiveMutex);
237
EnsureImageClient();
238
if (!mImageClient || !mImageClient->AsImageClientSingle()) {
239
return nullptr;
240
}
241
return new SharedRGBImage(mImageClient);
242
}
243
244
void ImageContainer::SetCurrentImageInternal(
245
const nsTArray<NonOwningImage>& aImages) {
246
RecursiveMutexAutoLock lock(mRecursiveMutex);
247
248
mGenerationCounter = ++sGenerationCounter;
249
250
if (!aImages.IsEmpty()) {
251
NS_ASSERTION(mCurrentImages.IsEmpty() ||
252
mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
253
mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
254
"frame IDs shouldn't go backwards");
255
if (aImages[0].mProducerID != mCurrentProducerID) {
256
mCurrentProducerID = aImages[0].mProducerID;
257
}
258
}
259
260
nsTArray<OwningImage> newImages;
261
262
for (uint32_t i = 0; i < aImages.Length(); ++i) {
263
NS_ASSERTION(aImages[i].mImage, "image can't be null");
264
NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
265
"Multiple images require timestamps");
266
if (i > 0) {
267
NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
268
"Timestamps must not decrease");
269
NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
270
"FrameIDs must increase");
271
NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
272
"ProducerIDs must be the same");
273
}
274
OwningImage* img = newImages.AppendElement();
275
img->mImage = aImages[i].mImage;
276
img->mTimeStamp = aImages[i].mTimeStamp;
277
img->mFrameID = aImages[i].mFrameID;
278
img->mProducerID = aImages[i].mProducerID;
279
for (const auto& oldImg : mCurrentImages) {
280
if (oldImg.mFrameID == img->mFrameID &&
281
oldImg.mProducerID == img->mProducerID) {
282
img->mComposited = oldImg.mComposited;
283
break;
284
}
285
}
286
}
287
288
mCurrentImages.SwapElements(newImages);
289
}
290
291
void ImageContainer::ClearImagesFromImageBridge() {
292
RecursiveMutexAutoLock lock(mRecursiveMutex);
293
SetCurrentImageInternal(nsTArray<NonOwningImage>());
294
}
295
296
void ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages) {
297
MOZ_ASSERT(!aImages.IsEmpty());
298
RecursiveMutexAutoLock lock(mRecursiveMutex);
299
if (mIsAsync) {
300
if (RefPtr<ImageBridgeChild> imageBridge =
301
ImageBridgeChild::GetSingleton()) {
302
imageBridge->UpdateImageClient(this);
303
}
304
}
305
SetCurrentImageInternal(aImages);
306
}
307
308
void ImageContainer::ClearAllImages() {
309
if (mImageClient) {
310
// Let ImageClient release all TextureClients. This doesn't return
311
// until ImageBridge has called ClearCurrentImageFromImageBridge.
312
if (RefPtr<ImageBridgeChild> imageBridge =
313
ImageBridgeChild::GetSingleton()) {
314
imageBridge->FlushAllImages(mImageClient, this);
315
}
316
return;
317
}
318
319
RecursiveMutexAutoLock lock(mRecursiveMutex);
320
SetCurrentImageInternal(nsTArray<NonOwningImage>());
321
}
322
323
void ImageContainer::ClearCachedResources() {
324
RecursiveMutexAutoLock lock(mRecursiveMutex);
325
if (mImageClient && mImageClient->AsImageClientSingle()) {
326
if (!mImageClient->HasTextureClientRecycler()) {
327
return;
328
}
329
mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
330
return;
331
}
332
return mRecycleBin->ClearRecycledBuffers();
333
}
334
335
void ImageContainer::SetCurrentImageInTransaction(Image* aImage) {
336
AutoTArray<NonOwningImage, 1> images;
337
images.AppendElement(NonOwningImage(aImage));
338
SetCurrentImagesInTransaction(images);
339
}
340
341
void ImageContainer::SetCurrentImagesInTransaction(
342
const nsTArray<NonOwningImage>& aImages) {
343
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
344
NS_ASSERTION(!mImageClient,
345
"Should use async image transfer with ImageBridge.");
346
347
SetCurrentImageInternal(aImages);
348
}
349
350
bool ImageContainer::IsAsync() const { return mIsAsync; }
351
352
CompositableHandle ImageContainer::GetAsyncContainerHandle() {
353
NS_ASSERTION(IsAsync(),
354
"Shared image ID is only relevant to async ImageContainers");
355
NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
356
RecursiveMutexAutoLock mon(mRecursiveMutex);
357
EnsureImageClient();
358
return mAsyncContainerHandle;
359
}
360
361
bool ImageContainer::HasCurrentImage() {
362
RecursiveMutexAutoLock lock(mRecursiveMutex);
363
364
return !mCurrentImages.IsEmpty();
365
}
366
367
void ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
368
uint32_t* aGenerationCounter) {
369
RecursiveMutexAutoLock lock(mRecursiveMutex);
370
371
*aImages = mCurrentImages;
372
if (aGenerationCounter) {
373
*aGenerationCounter = mGenerationCounter;
374
}
375
}
376
377
gfx::IntSize ImageContainer::GetCurrentSize() {
378
RecursiveMutexAutoLock lock(mRecursiveMutex);
379
380
if (mCurrentImages.IsEmpty()) {
381
return gfx::IntSize(0, 0);
382
}
383
384
return mCurrentImages[0].mImage->GetSize();
385
}
386
387
void ImageContainer::NotifyComposite(
388
const ImageCompositeNotification& aNotification) {
389
RecursiveMutexAutoLock lock(mRecursiveMutex);
390
391
// An image composition notification is sent the first time a particular
392
// image is composited by an ImageHost. Thus, every time we receive such
393
// a notification, a new image has been painted.
394
++mPaintCount;
395
396
if (aNotification.producerID() == mCurrentProducerID) {
397
for (auto& img : mCurrentImages) {
398
if (img.mFrameID == aNotification.frameID()) {
399
img.mComposited = true;
400
}
401
}
402
}
403
404
if (!aNotification.imageTimeStamp().IsNull()) {
405
mPaintDelay = aNotification.firstCompositeTimeStamp() -
406
aNotification.imageTimeStamp();
407
}
408
}
409
410
void ImageContainer::NotifyDropped(uint32_t aDropped) {
411
mDroppedImageCount += aDropped;
412
}
413
414
#ifdef XP_WIN
415
D3D11YCbCrRecycleAllocator* ImageContainer::GetD3D11YCbCrRecycleAllocator(
416
KnowsCompositor* aAllocator) {
417
if (mD3D11YCbCrRecycleAllocator &&
418
aAllocator == mD3D11YCbCrRecycleAllocator->GetAllocator()) {
419
return mD3D11YCbCrRecycleAllocator;
420
}
421
422
if (!aAllocator->SupportsD3D11() ||
423
!gfx::DeviceManagerDx::Get()->GetImageDevice()) {
424
return nullptr;
425
}
426
427
mD3D11YCbCrRecycleAllocator = new D3D11YCbCrRecycleAllocator(aAllocator);
428
return mD3D11YCbCrRecycleAllocator;
429
}
430
#endif
431
432
PlanarYCbCrImage::PlanarYCbCrImage()
433
: Image(nullptr, ImageFormat::PLANAR_YCBCR),
434
mOffscreenFormat(SurfaceFormat::UNKNOWN),
435
mBufferSize(0) {}
436
437
nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
438
SurfaceDescriptorBuffer& aSdBuffer) {
439
const PlanarYCbCrData* pdata = GetData();
440
MOZ_ASSERT(pdata, "must have PlanarYCbCrData");
441
MOZ_ASSERT(pdata->mYSkip == 0 && pdata->mCbSkip == 0 && pdata->mCrSkip == 0,
442
"YCbCrDescriptor doesn't hold skip values");
443
MOZ_ASSERT(pdata->mPicX == 0 && pdata->mPicY == 0,
444
"YCbCrDescriptor doesn't hold picx or picy");
445
446
uint32_t yOffset;
447
uint32_t cbOffset;
448
uint32_t crOffset;
449
ImageDataSerializer::ComputeYCbCrOffsets(
450
pdata->mYStride, pdata->mYSize.height, pdata->mCbCrStride,
451
pdata->mCbCrSize.height, yOffset, cbOffset, crOffset);
452
453
aSdBuffer.desc() = YCbCrDescriptor(
454
pdata->mYSize, pdata->mYStride, pdata->mCbCrSize, pdata->mCbCrStride,
455
yOffset, cbOffset, crOffset, pdata->mStereoMode, pdata->mColorDepth,
456
pdata->mYUVColorSpace,
457
/*hasIntermediateBuffer*/ false);
458
459
uint8_t* buffer = nullptr;
460
const MemoryOrShmem& memOrShmem = aSdBuffer.data();
461
switch (memOrShmem.type()) {
462
case MemoryOrShmem::Tuintptr_t:
463
buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
464
break;
465
case MemoryOrShmem::TShmem:
466
buffer = memOrShmem.get_Shmem().get<uint8_t>();
467
break;
468
default:
469
MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
470
}
471
MOZ_ASSERT(buffer, "no valid buffer available to copy image data");
472
if (!buffer) {
473
return NS_ERROR_INVALID_ARG;
474
}
475
476
CopyPlane(buffer + yOffset, pdata->mYChannel, pdata->mYSize, pdata->mYStride,
477
pdata->mYSkip);
478
CopyPlane(buffer + cbOffset, pdata->mCbChannel, pdata->mCbCrSize,
479
pdata->mCbCrStride, pdata->mCbSkip);
480
CopyPlane(buffer + crOffset, pdata->mCrChannel, pdata->mCbCrSize,
481
pdata->mCbCrStride, pdata->mCrSkip);
482
return NS_OK;
483
}
484
485
RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
486
if (mBuffer) {
487
mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize);
488
}
489
}
490
491
size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
492
MallocSizeOf aMallocSizeOf) const {
493
// Ignoring:
494
// - mData - just wraps mBuffer
495
// - Surfaces should be reported under gfx-surfaces-*:
496
// - mSourceSurface
497
// - Base class:
498
// - mImplData is not used
499
// Not owned:
500
// - mRecycleBin
501
size_t size = aMallocSizeOf(mBuffer.get());
502
503
// Could add in the future:
504
// - mBackendData (from base class)
505
506
return size;
507
}
508
509
UniquePtr<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize) {
510
return mRecycleBin->GetBuffer(aSize);
511
}
512
513
static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
514
const gfx::IntSize& aSize, int32_t aStride,
515
int32_t aSkip) {
516
int32_t height = aSize.height;
517
int32_t width = aSize.width;
518
519
MOZ_RELEASE_ASSERT(width <= aStride);
520
521
if (!aSkip) {
522
// Fast path: planar input.
523
memcpy(aDst, aSrc, height * aStride);
524
} else {
525
for (int y = 0; y < height; ++y) {
526
const uint8_t* src = aSrc;
527
uint8_t* dst = aDst;
528
// Slow path
529
for (int x = 0; x < width; ++x) {
530
*dst++ = *src++;
531
src += aSkip;
532
}
533
aSrc += aStride;
534
aDst += aStride;
535
}
536
}
537
}
538
539
bool RecyclingPlanarYCbCrImage::CopyData(const Data& aData) {
540
// update buffer size
541
// Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
542
const auto checkedSize =
543
CheckedInt<uint32_t>(aData.mCbCrStride) * aData.mCbCrSize.height * 2 +
544
CheckedInt<uint32_t>(aData.mYStride) * aData.mYSize.height;
545
546
if (!checkedSize.isValid()) return false;
547
548
const auto size = checkedSize.value();
549
550
// get new buffer
551
mBuffer = AllocateBuffer(size);
552
if (!mBuffer) return false;
553
554
// update buffer size
555
mBufferSize = size;
556
557
mData = aData;
558
mData.mYChannel = mBuffer.get();
559
mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
560
mData.mCrChannel =
561
mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
562
mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0;
563
564
CopyPlane(mData.mYChannel, aData.mYChannel, aData.mYSize, aData.mYStride,
565
aData.mYSkip);
566
CopyPlane(mData.mCbChannel, aData.mCbChannel, aData.mCbCrSize,
567
aData.mCbCrStride, aData.mCbSkip);
568
CopyPlane(mData.mCrChannel, aData.mCrChannel, aData.mCbCrSize,
569
aData.mCbCrStride, aData.mCrSkip);
570
571
mSize = aData.mPicSize;
572
mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
573
return true;
574
}
575
576
gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const {
577
return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat()
578
: mOffscreenFormat;
579
}
580
581
bool PlanarYCbCrImage::AdoptData(const Data& aData) {
582
mData = aData;
583
mSize = aData.mPicSize;
584
mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
585
return true;
586
}
587
588
already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
589
if (mSourceSurface) {
590
RefPtr<gfx::SourceSurface> surface(mSourceSurface);
591
return surface.forget();
592
}
593
594
gfx::IntSize size(mSize);
595
gfx::SurfaceFormat format =
596
gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
597
gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
598
if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
599
mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
600
NS_ERROR("Illegal image dest width or height");
601
return nullptr;
602
}
603
604
RefPtr<gfx::DataSourceSurface> surface =
605
gfx::Factory::CreateDataSourceSurface(size, format);
606
if (NS_WARN_IF(!surface)) {
607
return nullptr;
608
}
609
610
DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
611
if (NS_WARN_IF(!mapping.IsMapped())) {
612
return nullptr;
613
}
614
615
gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(),
616
mapping.GetStride());
617
618
mSourceSurface = surface;
619
620
return surface.forget();
621
}
622
623
NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {}
624
625
NVImage::~NVImage() = default;
626
627
IntSize NVImage::GetSize() const { return mSize; }
628
629
IntRect NVImage::GetPictureRect() const { return mData.GetPictureRect(); }
630
631
already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
632
if (mSourceSurface) {
633
RefPtr<gfx::SourceSurface> surface(mSourceSurface);
634
return surface.forget();
635
}
636
637
// Convert the current NV12 or NV21 data to YUV420P so that we can follow the
638
// logics in PlanarYCbCrImage::GetAsSourceSurface().
639
const int bufferLength = mData.mYSize.height * mData.mYStride +
640
mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
641
auto* buffer = new uint8_t[bufferLength];
642
643
Data aData = mData;
644
aData.mCbCrStride = aData.mCbCrSize.width;
645
aData.mCbSkip = 0;
646
aData.mCrSkip = 0;
647
aData.mYChannel = buffer;
648
aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
649
aData.mCrChannel =
650
aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
651
652
if (mData.mCbChannel < mData.mCrChannel) { // NV12
653
libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel,
654
mData.mCbCrStride, aData.mYChannel, aData.mYStride,
655
aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
656
aData.mCbCrStride, aData.mYSize.width,
657
aData.mYSize.height);
658
} else { // NV21
659
libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel,
660
mData.mCbCrStride, aData.mYChannel, aData.mYStride,
661
aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
662
aData.mCbCrStride, aData.mYSize.width,
663
aData.mYSize.height);
664
}
665
666
// The logics in PlanarYCbCrImage::GetAsSourceSurface().
667
gfx::IntSize size(mSize);
668
gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(
669
gfxPlatform::GetPlatform()->GetOffscreenFormat());
670
gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
671
if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
672
mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
673
NS_ERROR("Illegal image dest width or height");
674
return nullptr;
675
}
676
677
RefPtr<gfx::DataSourceSurface> surface =
678
gfx::Factory::CreateDataSourceSurface(size, format);
679
if (NS_WARN_IF(!surface)) {
680
return nullptr;
681
}
682
683
DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
684
if (NS_WARN_IF(!mapping.IsMapped())) {
685
return nullptr;
686
}
687
688
gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(),
689
mapping.GetStride());
690
691
mSourceSurface = surface;
692
693
// Release the temporary buffer.
694
delete[] buffer;
695
696
return surface.forget();
697
}
698
699
bool NVImage::IsValid() const { return !!mBufferSize; }
700
701
uint32_t NVImage::GetBufferSize() const { return mBufferSize; }
702
703
NVImage* NVImage::AsNVImage() { return this; };
704
705
bool NVImage::SetData(const Data& aData) {
706
MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
707
MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
708
709
// Calculate buffer size
710
// Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
711
const auto checkedSize =
712
CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
713
CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
714
715
if (!checkedSize.isValid()) return false;
716
717
const auto size = checkedSize.value();
718
719
// Allocate a new buffer.
720
mBuffer = AllocateBuffer(size);
721
if (!mBuffer) {
722
return false;
723
}
724
725
// Update mBufferSize.
726
mBufferSize = size;
727
728
// Update mData.
729
mData = aData;
730
mData.mYChannel = mBuffer.get();
731
mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
732
mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
733
734
// Update mSize.
735
mSize = aData.mPicSize;
736
737
// Copy the input data into mBuffer.
738
// This copies the y-channel and the interleaving CbCr-channel.
739
memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
740
741
return true;
742
}
743
744
const NVImage::Data* NVImage::GetData() const { return &mData; }
745
746
UniquePtr<uint8_t> NVImage::AllocateBuffer(uint32_t aSize) {
747
UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
748
return buffer;
749
}
750
751
SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize,
752
gfx::SourceSurface* aSourceSurface)
753
: Image(nullptr, ImageFormat::CAIRO_SURFACE),
754
mSize(aSize),
755
mSourceSurface(aSourceSurface),
756
mTextureFlags(TextureFlags::DEFAULT) {}
757
758
SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
759
: Image(nullptr, ImageFormat::CAIRO_SURFACE),
760
mSize(aSourceSurface->GetSize()),
761
mSourceSurface(aSourceSurface),
762
mTextureFlags(TextureFlags::DEFAULT) {}
763
764
SourceSurfaceImage::~SourceSurfaceImage() = default;
765
766
TextureClient* SourceSurfaceImage::GetTextureClient(
767
KnowsCompositor* aKnowsCompositor) {
768
if (!aKnowsCompositor) {
769
return nullptr;
770
}
771
772
auto entry = mTextureClients.LookupForAdd(aKnowsCompositor->GetSerial());
773
if (entry) {
774
return entry.Data();
775
}
776
777
RefPtr<TextureClient> textureClient;
778
RefPtr<SourceSurface> surface = GetAsSourceSurface();
779
MOZ_ASSERT(surface);
780
if (surface) {
781
// gfx::BackendType::NONE means default to content backend
782
textureClient = TextureClient::CreateFromSurface(
783
aKnowsCompositor, surface, BackendSelector::Content, mTextureFlags,
784
ALLOC_DEFAULT);
785
}
786
if (textureClient) {
787
textureClient->SyncWithObject(aKnowsCompositor->GetSyncObject());
788
entry.OrInsert([&textureClient]() { return textureClient; });
789
return textureClient;
790
}
791
792
// Remove the speculatively added entry.
793
entry.OrRemove();
794
return nullptr;
795
}
796
797
ImageContainer::ProducerID ImageContainer::AllocateProducerID() {
798
// Callable on all threads.
799
static Atomic<ImageContainer::ProducerID> sProducerID(0u);
800
return ++sProducerID;
801
}
802
803
} // namespace layers
804
} // namespace mozilla