blob: b5b993b7c143c05c240e1c97a9716eece8dfabc4 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/image/SkSurface_Base.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkCapabilities.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
#include "src/image/SkImage_Base.h"
#include "src/image/SkRescaleAndReadPixels.h"
#include <atomic>
#include <cstdint>
#include <memory>
class GrRecordingContext;
class SkColorSpace;
class SkPaint;
class SkSurfaceProps;
namespace skgpu { namespace graphite { class Recorder; } }
#if defined(SK_GANESH)
#include "include/gpu/GrBackendSurface.h"
#endif
SkSurface_Base::SkSurface_Base(int width, int height, const SkSurfaceProps* props)
: INHERITED(width, height, props) {
}
SkSurface_Base::SkSurface_Base(const SkImageInfo& info, const SkSurfaceProps* props)
: INHERITED(info, props) {
}
SkSurface_Base::~SkSurface_Base() {
// in case the canvas outsurvives us, we null the callback
if (fCachedCanvas) {
fCachedCanvas->setSurfaceBase(nullptr);
}
#if defined(SK_GANESH)
if (fCachedImage) {
as_IB(fCachedImage.get())->generatingSurfaceIsDeleted();
}
#endif
}
GrRecordingContext* SkSurface_Base::onGetRecordingContext() {
return nullptr;
}
skgpu::graphite::Recorder* SkSurface_Base::onGetRecorder() {
return nullptr;
}
#if defined(SK_GANESH)
GrBackendTexture SkSurface_Base::onGetBackendTexture(BackendHandleAccess) {
return GrBackendTexture(); // invalid
}
GrBackendRenderTarget SkSurface_Base::onGetBackendRenderTarget(BackendHandleAccess) {
return GrBackendRenderTarget(); // invalid
}
bool SkSurface_Base::onReplaceBackendTexture(const GrBackendTexture&,
GrSurfaceOrigin, ContentChangeMode,
TextureReleaseProc,
ReleaseContext) {
return false;
}
#endif
void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
const SkSamplingOptions& sampling, const SkPaint* paint) {
auto image = this->makeImageSnapshot();
if (image) {
canvas->drawImage(image.get(), x, y, sampling, paint);
}
}
void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
SkIRect origSrcRect,
SkSurface::RescaleGamma rescaleGamma,
RescaleMode rescaleMode,
SkSurface::ReadPixelsCallback callback,
SkSurface::ReadPixelsContext context) {
SkBitmap src;
SkPixmap peek;
SkIRect srcRect;
if (this->peekPixels(&peek)) {
src.installPixels(peek);
srcRect = origSrcRect;
} else {
src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
src.allocPixels();
if (!this->readPixels(src, origSrcRect.x(), origSrcRect.y())) {
callback(context, nullptr);
return;
}
srcRect = SkIRect::MakeSize(src.dimensions());
}
return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback,
context);
}
void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(
SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, SkIRect srcRect,
SkISize dstSize, RescaleGamma rescaleGamma, RescaleMode,
ReadPixelsCallback callback, ReadPixelsContext context) {
// TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
// call client's callback.
callback(context, nullptr);
}
bool SkSurface_Base::outstandingImageSnapshot() const {
return fCachedImage && !fCachedImage->unique();
}
bool SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
this->dirtyGenerationID();
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
if (fCachedImage) {
// the surface may need to fork its backend, if its sharing it with
// the cached image. Note: we only call if there is an outstanding owner
// on the image (besides us).
bool unique = fCachedImage->unique();
if (!unique) {
if (!this->onCopyOnWrite(mode)) {
return false;
}
}
// regardless of copy-on-write, we must drop our cached image now, so
// that the next request will get our new contents.
fCachedImage.reset();
if (unique) {
// Our content isn't held by any image now, so we can consider that content mutable.
// Raster surfaces need to be told it's safe to consider its pixels mutable again.
// We make this call after the ->unref() so the subclass can assert there are no images.
this->onRestoreBackingMutability();
}
} else if (kDiscard_ContentChangeMode == mode) {
this->onDiscard();
}
return true;
}
uint32_t SkSurface_Base::newGenerationID() {
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
static std::atomic<uint32_t> nextID{1};
return nextID.fetch_add(1, std::memory_order_relaxed);
}
sk_sp<const SkCapabilities> SkSurface_Base::onCapabilities() {
return SkCapabilities::RasterBackend();
}