Coverage Report

Created: 2018-09-25 14:53

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