C++程序  |  440行  |  20.74 KB

/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkSurface_DEFINED
#define SkSurface_DEFINED

#include "SkRefCnt.h"
#include "SkImage.h"
#include "SkSurfaceProps.h"

#include "GrTypes.h"

class SkCanvas;
class SkDeferredDisplayList;
class SkPaint;
class SkSurfaceCharacterization;
class GrBackendRenderTarget;
class GrBackendSemaphore;
class GrContext;
class GrRenderTarget;

/**
 *  SkSurface is responsible for managing the pixels that a canvas draws into. The pixels can be
 *  allocated either in CPU memory (a Raster surface) or on the GPU (a RenderTarget surface).
 *
 *  SkSurface takes care of allocating a SkCanvas that will draw into the surface. Call
 *  surface->getCanvas() to use that canvas (but don't delete it, it is owned by the surface).
 *
 *  SkSurface always has non-zero dimensions. If there is a request for a new surface, and either
 *  of the requested dimensions are zero, then NULL will be returned.
 */
class SK_API SkSurface : public SkRefCnt {
public:
    /**
     *  Create a new surface, using the specified pixels/rowbytes as its
     *  backend.
     *
     *  If the requested surface cannot be created, or the request is not a
     *  supported configuration, NULL will be returned.
     *
     *  Callers are responsible for initialiazing the surface pixels.
     */
    static sk_sp<SkSurface> MakeRasterDirect(const SkImageInfo& imageInfo, void* pixels,
                                             size_t rowBytes,
                                             const SkSurfaceProps* surfaceProps = nullptr);

    /**
     *  The same as NewRasterDirect, but also accepts a call-back routine, which is invoked
     *  when the surface is deleted, and is passed the pixel memory and the specified context.
     */
    static sk_sp<SkSurface> MakeRasterDirectReleaseProc(const SkImageInfo& imageInfo, void* pixels,
                                    size_t rowBytes,
                                    void (*releaseProc)(void* pixels, void* context),
                                    void* context, const SkSurfaceProps* surfaceProps = nullptr);

    /**
     *  Return a new surface, with the memory for the pixels automatically allocated and
     *  zero-initialized, but respecting the specified rowBytes. If rowBytes==0, then a default
     *  value will be chosen. If a non-zero rowBytes is specified, then any images snapped off of
     *  this surface (via makeImageSnapshot()) are guaranteed to have the same rowBytes.
     *
     *  If the requested surface cannot be created, or the request is not a
     *  supported configuration, NULL will be returned.
     */
    static sk_sp<SkSurface> MakeRaster(const SkImageInfo& imageInfo, size_t rowBytes,
                                       const SkSurfaceProps* surfaceProps);

    /**
     *  Allocate a new surface, automatically computing the rowBytes.
     */
    static sk_sp<SkSurface> MakeRaster(const SkImageInfo& imageInfo,
                                       const SkSurfaceProps* props = nullptr) {
        return MakeRaster(imageInfo, 0, props);
    }

    /**
     *  Helper version of NewRaster. It creates a SkImageInfo with the
     *  specified width and height, and populates the rest of info to match
     *  pixels in SkPMColor format.
     */
    static sk_sp<SkSurface> MakeRasterN32Premul(int width, int height,
                                                const SkSurfaceProps* surfaceProps = nullptr) {
        return MakeRaster(SkImageInfo::MakeN32Premul(width, height), surfaceProps);
    }

    /**
     *  Used to wrap a pre-existing backend 3D API texture as a SkSurface. Skia will not assume
     *  ownership of the texture and the client must ensure the texture is valid for the lifetime
     *  of the SkSurface. If sampleCnt > 1, then we will create an intermediate mssa surface which
     *  we will use for rendering. We then resolve into the passed in texture.
     */
    static sk_sp<SkSurface> MakeFromBackendTexture(GrContext* context,
                                                   const GrBackendTexture& backendTexture,
                                                   GrSurfaceOrigin origin, int sampleCnt,
                                                   sk_sp<SkColorSpace> colorSpace,
                                                   const SkSurfaceProps* surfaceProps);

    /**
     *  Used to wrap a pre-existing backend 3D API texture as a SkSurface. Skia will not assume
     *  ownership of the texture and the client must ensure the texture is valid for the lifetime
     *  of the SkSurface. If sampleCnt > 1, then we will create an intermediate mssa surface which
     *  we will use for rendering. We then resolve into the passed in texture.
     *
     *  The GrBackendTexture must have a valid backend format supplied (GrGLTextureInfo::fFormat,
     *  GrVkImageInfo::fFormat, etc.) in it. The passed in SkColorType informs skia how it should
     *  interpret the backend format supplied by the GrBackendTexture. If the format in the
     *  GrBackendTexture is not compatible with the sampleCnt, SkColorType, and SkColorSpace we
     *  will return nullptr.
     */
    static sk_sp<SkSurface> MakeFromBackendTexture(GrContext* context,
                                                   const GrBackendTexture& backendTexture,
                                                   GrSurfaceOrigin origin, int sampleCnt,
                                                   SkColorType colorType,
                                                   sk_sp<SkColorSpace> colorSpace,
                                                   const SkSurfaceProps* surfaceProps);

    static sk_sp<SkSurface> MakeFromBackendRenderTarget(GrContext* context,
                                                const GrBackendRenderTarget& backendRenderTarget,
                                                GrSurfaceOrigin origin,
                                                sk_sp<SkColorSpace> colorSpace,
                                                const SkSurfaceProps* surfaceProps);

    /**
     *  The GrBackendRenderTarget must have a valid backend format set (GrGLTextureInfo::fFormat,
     *  GrVkImageInfo::fFormat, etc.) in it. The passed in SkColorType informs skia how it should
     *  interpret the backend format supplied by the GrBackendRenderTarget. If the format in the
     *  GrBackendRenderTarget is not compatible with the sampleCnt, SkColorType, and SkColorSpace
     *  we will return nullptr.
     */
    static sk_sp<SkSurface> MakeFromBackendRenderTarget(GrContext* context,
                                                const GrBackendRenderTarget& backendRenderTarget,
                                                GrSurfaceOrigin origin,
                                                SkColorType colorType,
                                                sk_sp<SkColorSpace> colorSpace,
                                                const SkSurfaceProps* surfaceProps);

    /**
     *  Used to wrap a pre-existing 3D API texture as a SkSurface. Skia will treat the texture as
     *  a rendering target only, but unlike NewFromBackendRenderTarget, Skia will manage and own
     *  the associated render target objects (but not the provided texture). Skia will not assume
     *  ownership of the texture and the client must ensure the texture is valid for the lifetime
     *  of the SkSurface.
     */
    static sk_sp<SkSurface> MakeFromBackendTextureAsRenderTarget(GrContext* context,
                                                            const GrBackendTexture& backendTexture,
                                                            GrSurfaceOrigin origin,
                                                            int sampleCnt,
                                                            sk_sp<SkColorSpace> colorSpace,
                                                            const SkSurfaceProps* surfaceProps);

    /**
     *  Used to wrap a pre-existing 3D API texture as a SkSurface. Skia will treat the texture as
     *  a rendering target only, but unlike NewFromBackendRenderTarget, Skia will manage and own
     *  the associated render target objects (but not the provided texture). Skia will not assume
     *  ownership of the texture and the client must ensure the texture is valid for the lifetime
     *  of the SkSurface.
     *
     *  The GrBackendTexture must have a valid backend format supplied (GrGLTextureInfo::fFormat,
     *  GrVkImageInfo::fFormat, etc.) in it. The passed in SkColorType informs skia how it should
     *  interpret the backend format supplied by the GrBackendTexture. If the format in the
     *  GrBackendTexture is not compatible with the sampleCnt, SkColorType, and SkColorSpace we
     *  will return nullptr.
     */
    static sk_sp<SkSurface> MakeFromBackendTextureAsRenderTarget(GrContext* context,
                                                            const GrBackendTexture& backendTexture,
                                                            GrSurfaceOrigin origin,
                                                            int sampleCnt,
                                                            SkColorType colorType,
                                                            sk_sp<SkColorSpace> colorSpace,
                                                            const SkSurfaceProps* surfaceProps);

    /**
     *  Return a new surface whose contents will be drawn to an offscreen
     *  render target, allocated by the surface. The optional shouldCreateWithMips flag is a hint
     *  that this surface may be snapped to an SkImage which will be used with mip maps so we should
     *  create the backend gpu RenderTarget with mips to avoid a copy later on.
     */
    static sk_sp<SkSurface> MakeRenderTarget(GrContext* context, SkBudgeted budgeted,
                                             const SkImageInfo& imageInfo,
                                             int sampleCount, GrSurfaceOrigin surfaceOrigin,
                                             const SkSurfaceProps* surfaceProps,
                                             bool shouldCreateWithMips = false);

    static sk_sp<SkSurface> MakeRenderTarget(GrContext* context, SkBudgeted budgeted,
                                             const SkImageInfo& imageInfo, int sampleCount,
                                             const SkSurfaceProps* props) {
        return MakeRenderTarget(context, budgeted, imageInfo, sampleCount,
                                kBottomLeft_GrSurfaceOrigin, props);
    }

    static sk_sp<SkSurface> MakeRenderTarget(GrContext* context, SkBudgeted budgeted,
                                             const SkImageInfo& imageInfo) {
        if (!imageInfo.width() || !imageInfo.height()) {
            return nullptr;
        }
        return MakeRenderTarget(context, budgeted, imageInfo, 0, kBottomLeft_GrSurfaceOrigin,
                                nullptr);
    }

    /**
     *  Returns a surface that stores no pixels. It can be drawn to via its canvas, but that
     *  canvas does not draw anything. Calling makeImageSnapshot() will return nullptr.
     */
    static sk_sp<SkSurface> MakeNull(int width, int height);

    int width() const { return fWidth; }
    int height() const { return fHeight; }

    /**
     *  Returns a unique non-zero, unique value identifying the content of this
     *  surface. Each time the content is changed changed, either by drawing
     *  into this surface, or explicitly calling notifyContentChanged()) this
     *  method will return a new value.
     *
     *  If this surface is empty (i.e. has a zero-dimention), this will return
     *  0.
     */
    uint32_t generationID();

    /**
     *  Modes that can be passed to notifyContentWillChange
     */
    enum ContentChangeMode {
        /**
         *  Use this mode if it is known that the upcoming content changes will
         *  clear or overwrite prior contents, thus making them discardable.
         */
        kDiscard_ContentChangeMode,
        /**
         *  Use this mode if prior surface contents need to be preserved or
         *  if in doubt.
         */
        kRetain_ContentChangeMode,
    };

    /**
     *  Call this if the contents are about to change. This will (lazily) force a new
     *  value to be returned from generationID() when it is called next.
     *
     *  CAN WE DEPRECATE THIS?
     */
    void notifyContentWillChange(ContentChangeMode mode);

    enum BackendHandleAccess {
        kFlushRead_BackendHandleAccess,     //!< caller may read from the backend object
        kFlushWrite_BackendHandleAccess,    //!< caller may write to the backend object
        kDiscardWrite_BackendHandleAccess,  //!< caller must over-write the entire backend object
    };

    /*
     * These are legacy aliases which will be removed soon
     */
    static const BackendHandleAccess kFlushRead_TextureHandleAccess =
            kFlushRead_BackendHandleAccess;
    static const BackendHandleAccess kFlushWrite_TextureHandleAccess =
            kFlushWrite_BackendHandleAccess;
    static const BackendHandleAccess kDiscardWrite_TextureHandleAccess =
            kDiscardWrite_BackendHandleAccess;


    /**
     *  Retrieves the backend API handle of the texture used by this surface, or 0 if the surface
     *  is not backed by a GPU texture.
     *
     *  The returned texture-handle is only valid until the next draw-call into the surface,
     *  or the surface is deleted.
     */
    GrBackendObject getTextureHandle(BackendHandleAccess backendHandleAccess);

    /**
     *  Retrieves the backend API handle of the RenderTarget backing this surface.  Callers must
     *  ensure this function returns 'true' or else the GrBackendObject will be invalid
     *
     *  In OpenGL this will return the FramebufferObject ID.
     */
    bool getRenderTargetHandle(GrBackendObject* backendObject,
                               BackendHandleAccess backendHandleAccess);

    /**
     *  Return a canvas that will draw into this surface. This will always
     *  return the same canvas for a given surface, and is manged/owned by the
     *  surface. It should not be used when its parent surface has gone out of
     *  scope.
     */
    SkCanvas* getCanvas();

    /**
     *  Return a new surface that is "compatible" with this one, in that it will
     *  efficiently be able to be drawn into this surface. Typical calling
     *  pattern:
     *
     *  SkSurface* A = SkSurface::New...();
     *  SkCanvas* canvasA = surfaceA->newCanvas();
     *  ...
     *  SkSurface* surfaceB = surfaceA->newSurface(...);
     *  SkCanvas* canvasB = surfaceB->newCanvas();
     *  ... // draw using canvasB
     *  canvasA->drawSurface(surfaceB); // <--- this will always be optimal!
     */
    sk_sp<SkSurface> makeSurface(const SkImageInfo& imageInfo);

    /**
     *  Returns an image of the current state of the surface pixels up to this
     *  point. Subsequent changes to the surface (by drawing into its canvas)
     *  will not be reflected in this image. For the GPU-backend, the budgeting
     *  decision for the snapped image will match that of the surface.
     */
    sk_sp<SkImage> makeImageSnapshot();

    /**
     *  Though the caller could get a snapshot image explicitly, and draw that,
     *  it seems that directly drawing a surface into another canvas might be
     *  a common pattern, and that we could possibly be more efficient, since
     *  we'd know that the "snapshot" need only live until we've handed it off
     *  to the canvas.
     */
    void draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint);

    /**
     *  If the surface has direct access to its pixels (i.e. they are in local
     *  RAM) return true, and if not null, set the pixmap parameter to point to the information
     *  about the surface's pixels. The pixel address in the pixmap is only valid while
     *  the surface object is in scope, and no API call is made on the surface
     *  or its canvas.
     *
     *  On failure, returns false and the pixmap parameter is ignored.
     */
    bool peekPixels(SkPixmap* pixmap);

    /**
     *  Copy the pixels from the surface into the specified pixmap,
     *  converting them into the pixmap's format. The surface pixels are read
     *  starting at the specified (srcX,srcY) location.
     *
     *  The pixmap and (srcX,srcY) offset specifies a source rectangle
     *
     *      srcR.setXYWH(srcX, srcY, pixmap.width(), pixmap.height());
     *
     *  srcR is intersected with the bounds of the base-layer. If this intersection is not empty,
     *  then we have two sets of pixels (of equal size). Replace the dst pixels with the
     *  corresponding src pixels, performing any colortype/alphatype transformations needed
     *  (in the case where the src and dst have different colortypes or alphatypes).
     *
     *  This call can fail, returning false, for several reasons:
     *  - If srcR does not intersect the surface bounds.
     *  - If the requested colortype/alphatype cannot be converted from the surface's types.
     */
    bool readPixels(const SkPixmap& dst, int srcX, int srcY);
    bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
                    int srcX, int srcY);
    bool readPixels(const SkBitmap& dst, int srcX, int srcY);

    const SkSurfaceProps& props() const { return fProps; }

    /**
     * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
     *
     * The flush calls below are the new preferred way to flush calls to a surface, and this call
     * will eventually be removed.
     */
    void prepareForExternalIO();

    /**
     * Issue any pending surface IO to the current backend 3D API
     */
    void flush();

    /**
     * Issue any pending surface IO to the current backend 3D API. After issuing all commands,
     * numSemaphore semaphores will be signaled by the gpu. The client passes in an array of
     * numSemaphores GrBackendSemaphores. In general these GrBackendSemaphore's can be either
     * initialized or not. If they are initialized, the backend uses the passed in semaphore.
     * If it is not initialized, a new semaphore is created and the GrBackendSemaphore object
     * is initialized with that semaphore.
     *
     * The client will own and be responsible for deleting the underlying semaphores that are stored
     * and returned in initialized GrBackendSemaphore objects. The GrBackendSemaphore objects
     * themselves can be deleted as soon as this function returns.
     *
     * If the backend API is OpenGL only uninitialized GrBackendSemaphores are supported.
     * If the backend API is Vulkan either initialized or unitialized semaphores are supported.
     * If unitialized, the semaphores which are created will be valid for use only with the VkDevice
     * with which they were created.
     *
     * If this call returns GrSemaphoresSubmited::kNo, the GPU backend will not have created or
     * added any semaphores to signal on the GPU. Thus the client should not have the GPU wait on
     * any of the semaphores. However, any pending surface IO will still be flushed.
     */
    GrSemaphoresSubmitted flushAndSignalSemaphores(int numSemaphores,
                                                   GrBackendSemaphore signalSemaphores[]);

    /**
     * Inserts a list of GPU semaphores that the current backend 3D API must wait on before
     * executing any more commands on the GPU for this surface. Skia will take ownership of the
     * underlying semaphores and delete them once they have been signaled and waited on.
     *
     * If this call returns false, then the GPU backend will not wait on any passed in semaphores,
     * and the client will still own the semaphores.
     */
    bool wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores);

    /**
     * This creates a characterization of this SkSurface's properties that can
     * be used to perform gpu-backend preprocessing in a separate thread (via
     * the SkDeferredDisplayListRecorder).
     * It will return false on failure (e.g., if the SkSurface is cpu-backed).
     */
    bool characterize(SkSurfaceCharacterization* characterization) const;

    /**
     * Draw a deferred display list (created via SkDeferredDisplayListRecorder).
     * The draw will be skipped if the characterization stored in the display list
     * isn't compatible with this surface.
     */
    bool draw(SkDeferredDisplayList* deferredDisplayList);

protected:
    SkSurface(int width, int height, const SkSurfaceProps* surfaceProps);
    SkSurface(const SkImageInfo& imageInfo, const SkSurfaceProps* surfaceProps);

    // called by subclass if their contents have changed
    void dirtyGenerationID() {
        fGenerationID = 0;
    }

private:
    const SkSurfaceProps fProps;
    const int            fWidth;
    const int            fHeight;
    uint32_t             fGenerationID;

    typedef SkRefCnt INHERITED;
};

#endif