image/ISurfaceProvider.h
author Seth Fowler <mark.seth.fowler@gmail.com>
Thu, 18 Aug 2016 12:43:16 -0700
changeset 402959 e7caba6197ba3500be336d88ca2571aa6c0b8192
parent 402958 0f9b465ebb5fab7f1dd2d7eb32e172292a618b21
child 402994 6d4e430fbd45ed519c81bc6fde02dd9e31d56251
permissions -rw-r--r--
Bug 1296147 (Part 2) - Rename imgFrame::GetSurface() to imgFrame::GetSourceSurface() for clarity. r=edwin

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * An interface for objects which can either store a surface or dynamically
 * generate one, and various implementations.
 */

#ifndef mozilla_image_ISurfaceProvider_h
#define mozilla_image_ISurfaceProvider_h

#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/NotNull.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
#include "mozilla/gfx/2D.h"

#include "imgFrame.h"
#include "SurfaceCache.h"

namespace mozilla {
namespace image {

class CachedSurface;
class DrawableSurface;

/**
 * An interface for objects which can either store a surface or dynamically
 * generate one.
 */
class ISurfaceProvider
{
public:
  // Subclasses may or may not be XPCOM classes, so we just require that they
  // implement AddRef and Release.
  NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
  NS_IMETHOD_(MozExternalRefCountType) Release() = 0;

  /// @return a (potentially lazily computed) drawable reference to a surface.
  virtual DrawableSurface Surface();

  /// @return true if DrawableRef() will return a completely decoded surface.
  virtual bool IsFinished() const = 0;

  /// @return the number of bytes of memory this ISurfaceProvider is expected to
  /// require. Optimizations may result in lower real memory usage. Trivial
  /// overhead is ignored.
  virtual size_t LogicalSizeInBytes() const = 0;

  /// @return the availability state of this ISurfaceProvider, which indicates
  /// whether DrawableRef() could successfully return a surface. Should only be
  /// called from SurfaceCache code as it relies on SurfaceCache for
  /// synchronization.
  AvailabilityState& Availability() { return mAvailability; }
  const AvailabilityState& Availability() const { return mAvailability; }

protected:
  explicit ISurfaceProvider(AvailabilityState aAvailability)
    : mAvailability(aAvailability)
  { }

  virtual ~ISurfaceProvider() { }

  /// @return an eagerly computed drawable reference to a surface.
  virtual DrawableFrameRef DrawableRef() = 0;

  /// @return true if this ISurfaceProvider is locked. (@see SetLocked())
  /// Should only be called from SurfaceCache code as it relies on SurfaceCache
  /// for synchronization.
  virtual bool IsLocked() const = 0;

  /// If @aLocked is true, hint that this ISurfaceProvider is in use and it
  /// should avoid releasing its resources. Should only be called from
  /// SurfaceCache code as it relies on SurfaceCache for synchronization.
  virtual void SetLocked(bool aLocked) = 0;

private:
  friend class CachedSurface;
  friend class DrawableSurface;

  AvailabilityState mAvailability;
};


/**
 * A reference to a surface (stored in an imgFrame) that holds the surface in
 * memory, guaranteeing that it can be drawn. If you have a DrawableSurface
 * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and
 * |surf->GetSourceSurface()| are guaranteed to succeed.
 *
 * Note that the surface may be computed lazily, so a DrawableSurface should not
 * be dereferenced (i.e., operator->() should not be called) until you're
 * sure that you want to draw it.
 */
class MOZ_STACK_CLASS DrawableSurface final
{
public:
  DrawableSurface() : mHaveSurface(false) { }

  explicit DrawableSurface(DrawableFrameRef&& aDrawableRef)
    : mDrawableRef(Move(aDrawableRef))
    , mHaveSurface(bool(mDrawableRef))
  { }

  explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider)
    : mProvider(aProvider)
    , mHaveSurface(true)
  { }

  DrawableSurface(DrawableSurface&& aOther)
    : mDrawableRef(Move(aOther.mDrawableRef))
    , mProvider(Move(aOther.mProvider))
    , mHaveSurface(aOther.mHaveSurface)
  {
    aOther.mHaveSurface = false;
  }

  DrawableSurface& operator=(DrawableSurface&& aOther)
  {
    MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
    mDrawableRef = Move(aOther.mDrawableRef);
    mProvider = Move(aOther.mProvider);
    mHaveSurface = aOther.mHaveSurface;
    aOther.mHaveSurface = false;
    return *this;
  }

  explicit operator bool() const { return mHaveSurface; }
  imgFrame* operator->() { return DrawableRef().get(); }

private:
  DrawableSurface(const DrawableSurface& aOther) = delete;
  DrawableSurface& operator=(const DrawableSurface& aOther) = delete;

  DrawableFrameRef& DrawableRef()
  {
    MOZ_ASSERT(mHaveSurface);

    // If we weren't created with a DrawableFrameRef directly, we should've been
    // created with an ISurfaceProvider which can give us one.
    if (!mDrawableRef) {
      MOZ_ASSERT(mProvider);
      mDrawableRef = mProvider->DrawableRef();
    }

    MOZ_ASSERT(mDrawableRef);
    return mDrawableRef;
  }

  DrawableFrameRef mDrawableRef;
  RefPtr<ISurfaceProvider> mProvider;
  bool mHaveSurface;
};


// Surface() is implemented here so that DrawableSurface's definition is visible.
inline DrawableSurface
ISurfaceProvider::Surface()
{
  return DrawableSurface(DrawableRef());
}


/**
 * An ISurfaceProvider that stores a single surface.
 */
class SimpleSurfaceProvider final : public ISurfaceProvider
{
public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override)

  explicit SimpleSurfaceProvider(NotNull<imgFrame*> aSurface)
    : ISurfaceProvider(AvailabilityState::StartAvailable())
    , mSurface(aSurface)
  { }

  bool IsFinished() const override { return mSurface->IsFinished(); }

  size_t LogicalSizeInBytes() const override
  {
    gfx::IntSize size = mSurface->GetSize();
    return size.width * size.height * mSurface->GetBytesPerPixel();
  }

protected:
  DrawableFrameRef DrawableRef() override { return mSurface->DrawableRef(); }
  bool IsLocked() const override { return bool(mLockRef); }

  void SetLocked(bool aLocked) override
  {
    if (aLocked == IsLocked()) {
      return;  // Nothing changed.
    }

    // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep
    // any volatile buffer it owns in memory.
    mLockRef = aLocked ? mSurface->DrawableRef()
                       : DrawableFrameRef();
  }

private:
  virtual ~SimpleSurfaceProvider() { }

  NotNull<RefPtr<imgFrame>> mSurface;
  DrawableFrameRef mLockRef;
};

} // namespace image
} // namespace mozilla

#endif // mozilla_image_ISurfaceProvider_h