tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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