Skip to content
Permalink
Browse files
Drawing the PDF snapshot should take place in WebProcess
https://bugs.webkit.org/show_bug.cgi?id=240368
rdar://91660159

Reviewed by Simon Fraser.

The ImageBufferBackends should act like a drawing surface only. It has
a GraphicsContext. It can get and set pixels. And it can get a NativeImage
out of the pixels. But it should never draw itself to a GraphicsContext.
The ImageBuffer can act like an image. It can draw itself as an image or
as a pattern into a GraphicsContext.

So draw(), drawPattern() and drawConsuming() will be removed from the
ImageBufferBackend super classes and the implementation will be unified
for all platforms and moved to ConcreteImageBuffer.

By doing that, a canvas can be drawn to the PDF context in WebProcess
even if the GPUProcess for DOM rendering is enabled.

Canonical link: https://commits.webkit.org/250791@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294536 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
shallawa committed May 20, 2022
1 parent ba0fcb4 commit 45e917961a35547686a7d00dbdc55a363e7e8d1e
Showing 15 changed files with 55 additions and 131 deletions.
@@ -28,6 +28,7 @@
#include "Filter.h"
#include "FilterImage.h"
#include "FilterResults.h"
#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "PixelBuffer.h"

@@ -169,17 +170,24 @@ class ConcreteImageBuffer : public ImageBuffer {

void draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options) override
{
FloatRect srcRectScaled = srcRect;
srcRectScaled.scale(resolutionScale());

if (auto* backend = ensureBackendCreated()) {
flushDrawingContext();
backend->draw(destContext, destRect, srcRect, options);
if (auto image = copyNativeImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
destContext.drawNativeImage(*image, backendSize(), destRect, srcRectScaled, options);
backend->finalizeDrawIntoContext(destContext);
}
}

void drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options) override
{
FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(resolutionScale());

if (auto* backend = ensureBackendCreated()) {
flushDrawingContext();
backend->drawPattern(destContext, destRect, srcRect, patternTransform, phase, spacing, options);
if (auto image = copyImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
image->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, options);
}
}

@@ -203,13 +211,17 @@ class ConcreteImageBuffer : public ImageBuffer {

void drawConsuming(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options) override
{
FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(resolutionScale());

ASSERT(&destContext != &context());
if (auto* backend = ensureBackendCreated()) {
flushDrawingContext();
backend->drawConsuming(destContext, destRect, srcRect, options);
auto backendSize = backend->backendSize();
if (auto image = sinkIntoNativeImage())
destContext.drawNativeImage(*image, backendSize, destRect, adjustedSrcRect, options);
}
}

void clipToMask(GraphicsContext& destContext, const FloatRect& destRect) override
{
if (auto* backend = ensureBackendCreated()) {
@@ -65,11 +65,6 @@ RefPtr<Image> ImageBufferBackend::sinkIntoImage(PreserveResolution preserveResol
return copyImage(DontCopyBackingStore, preserveResolution);
}

void ImageBufferBackend::drawConsuming(GraphicsContext& destinationContext, const FloatRect& destinationRect, const FloatRect& sourceRect, const ImagePaintingOptions& options)
{
draw(destinationContext, destinationRect, sourceRect, options);
}

void ImageBufferBackend::convertToLuminanceMask()
{
PixelBufferFormat format { AlphaPremultiplication::Unpremultiplied, PixelFormat::RGBA8, colorSpace() };
@@ -108,15 +108,12 @@ class ImageBufferBackend {

virtual IntSize backendSize() const { return { }; }

virtual void finalizeDrawIntoContext(GraphicsContext&) { }
virtual RefPtr<NativeImage> copyNativeImage(BackingStoreCopy) const = 0;
virtual RefPtr<Image> copyImage(BackingStoreCopy, PreserveResolution) const = 0;

WEBCORE_EXPORT virtual void draw(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) = 0;
WEBCORE_EXPORT virtual void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions&) = 0;

WEBCORE_EXPORT virtual RefPtr<NativeImage> sinkIntoNativeImage();
WEBCORE_EXPORT virtual RefPtr<Image> sinkIntoImage(PreserveResolution);
WEBCORE_EXPORT virtual void drawConsuming(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&);

virtual void clipToMask(GraphicsContext&, const FloatRect&) { }

@@ -51,37 +51,6 @@ RefPtr<Image> ImageBufferCairoBackend::copyImage(BackingStoreCopy copyBehavior,
return BitmapImage::create(copyNativeImage(copyBehavior));
}

void ImageBufferCairoBackend::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
{
if (!destContext.hasPlatformContext()) {
// If there's no platformContext, we're using threaded rendering, and all the operations must be done
// through the GraphicsContext.
auto image = copyImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore);
destContext.drawImage(*image, destRect, srcRect, options);
return;
}

InterpolationQualityMaintainer interpolationQualityForThisScope(destContext, options.interpolationQuality());
const auto& destinationContextState = destContext.state();

if (auto image = copyNativeImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
drawPlatformImage(*destContext.platformContext(), image->platformImage().get(), destRect, srcRect, { options, destinationContextState.imageInterpolationQuality() }, destinationContextState.alpha(), WebCore::Cairo::ShadowState(destinationContextState));
}

void ImageBufferCairoBackend::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options)
{
if (!destContext.hasPlatformContext()) {
// If there's no platformContext, we're using threaded rendering, and all the operations must be done
// through the GraphicsContext.
auto image = copyImage(DontCopyBackingStore);
image->drawPattern(destContext, destRect, srcRect, patternTransform, phase, spacing, options);
return;
}

if (auto image = copyNativeImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
Cairo::drawPattern(*destContext.platformContext(), image->platformImage().get(), logicalSize(), destRect, srcRect, patternTransform, phase, options);
}

void ImageBufferCairoBackend::clipToMask(GraphicsContext& destContext, const FloatRect& destRect)
{
if (auto image = copyNativeImage(DontCopyBackingStore))
@@ -39,9 +39,6 @@ class ImageBufferCairoBackend : public ImageBufferBackend {
public:
RefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, PreserveResolution = PreserveResolution::No) const override;

void draw(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) override;
void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions&) override;

void clipToMask(GraphicsContext&, const FloatRect& destRect) override;

void transformToColorSpace(const DestinationColorSpace&) override;
@@ -131,28 +131,6 @@ RefPtr<Image> ImageBufferCGBackend::sinkIntoImage(PreserveResolution preserveRes
return createBitmapImageAfterScalingIfNeeded(sinkIntoNativeImage(), logicalSize(), backendSize, resolutionScale(), preserveResolution);
}

void ImageBufferCGBackend::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
{
prepareToDrawIntoContext(destContext);

FloatRect srcRectScaled = srcRect;
srcRectScaled.scale(resolutionScale());

if (auto image = copyNativeImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
destContext.drawNativeImage(*image, backendSize(), destRect, srcRectScaled, options);
}

void ImageBufferCGBackend::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options)
{
prepareToDrawIntoContext(destContext);

FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(resolutionScale());

if (auto image = copyImage(&destContext == &context() ? CopyBackingStore : DontCopyBackingStore))
image->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, options);
}

void ImageBufferCGBackend::clipToMask(GraphicsContext& destContext, const FloatRect& destRect)
{
auto nativeImage = copyNativeImage(DontCopyBackingStore);
@@ -248,10 +226,6 @@ std::unique_ptr<ThreadSafeImageBufferFlusher> ImageBufferCGBackend::createFlushe
return makeUnique<ThreadSafeImageBufferFlusherCG>(context().platformContext());
}

void ImageBufferCGBackend::prepareToDrawIntoContext(GraphicsContext&)
{
}

bool ImageBufferCGBackend::originAtBottomLeftCorner() const
{
return isOriginAtBottomLeftCorner;
@@ -41,12 +41,11 @@ class WEBCORE_EXPORT ImageBufferCGBackend : public ImageBufferBackend {
static constexpr bool isOriginAtBottomLeftCorner = true;

protected:
using ImageBufferBackend::ImageBufferBackend;

RefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, PreserveResolution = PreserveResolution::No) const override;
RefPtr<Image> sinkIntoImage(PreserveResolution) override;

void draw(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) override;
void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions&) override;

void clipToMask(GraphicsContext&, const FloatRect& destRect) override;

String toDataURL(const String& mimeType, std::optional<double> quality, PreserveResolution) const override;
@@ -56,12 +55,8 @@ class WEBCORE_EXPORT ImageBufferCGBackend : public ImageBufferBackend {

bool originAtBottomLeftCorner() const override;

using ImageBufferBackend::ImageBufferBackend;

static RetainPtr<CGColorSpaceRef> contextColorSpace(const GraphicsContext&);

virtual void prepareToDrawIntoContext(GraphicsContext&);

virtual RetainPtr<CGImageRef> copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const;
};

@@ -153,16 +153,6 @@ unsigned ImageBufferIOSurfaceBackend::bytesPerRow() const
return m_surface->bytesPerRow();
}

void ImageBufferIOSurfaceBackend::prepareToDrawIntoContext(GraphicsContext& destContext)
{
auto currentSeed = m_surface->seed();
if (std::exchange(m_lastSeedWhenDrawingImage, currentSeed) == currentSeed)
return;

if (destContext.renderingMode() == RenderingMode::Unaccelerated)
invalidateCachedNativeImage();
}

void ImageBufferIOSurfaceBackend::invalidateCachedNativeImage() const
{
// Force QuartzCore to invalidate its cached CGImageRef for this IOSurface.
@@ -183,25 +173,13 @@ RefPtr<NativeImage> ImageBufferIOSurfaceBackend::sinkIntoNativeImage()
return NativeImage::create(IOSurface::sinkIntoImage(WTFMove(m_surface)));
}

void ImageBufferIOSurfaceBackend::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
void ImageBufferIOSurfaceBackend::finalizeDrawIntoContext(GraphicsContext& destinationContext)
{
ImageBufferCGBackend::draw(destContext, destRect, srcRect, options);
// Accelerated to/from unaccelerated image buffers need complex caching. We trust that
// Accelerated to unaccelerated image buffers need complex caching. We trust that
// this is a one-off draw, and as such we clear the caches of the source image after each draw.
if (destContext.renderingMode() != context().renderingMode())
if (destinationContext.renderingMode() == RenderingMode::Unaccelerated)
invalidateCachedNativeImage();
}
void ImageBufferIOSurfaceBackend::drawConsuming(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
{
prepareToDrawIntoContext(destContext);

FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(resolutionScale());

auto backendSize = this->backendSize();
if (auto image = sinkIntoNativeImage())
destContext.drawNativeImage(*image, backendSize, destRect, adjustedSrcRect, options);
}

RetainPtr<CGImageRef> ImageBufferIOSurfaceBackend::copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution preserveResolution) const
{
@@ -66,9 +66,6 @@ class WEBCORE_EXPORT ImageBufferIOSurfaceBackend : public ImageBufferCGBackend {
RefPtr<NativeImage> copyNativeImage(BackingStoreCopy = CopyBackingStore) const override;
RefPtr<NativeImage> sinkIntoNativeImage() override;

void draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) override;
void drawConsuming(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) override;

std::optional<PixelBuffer> getPixelBuffer(const PixelBufferFormat& outputFormat, const IntRect&) const override;
void putPixelBuffer(const PixelBuffer&, const IntRect& srcRect, const IntPoint& destPoint, AlphaPremultiplication destFormat) override;

@@ -88,11 +85,10 @@ class WEBCORE_EXPORT ImageBufferIOSurfaceBackend : public ImageBufferCGBackend {
// ImageBufferCGBackend overrides.
RetainPtr<CGImageRef> copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const final;

void prepareToDrawIntoContext(GraphicsContext& destinationContext) override;
void finalizeDrawIntoContext(GraphicsContext& destinationContext) override;
void invalidateCachedNativeImage() const;

std::unique_ptr<IOSurface> m_surface;
IOSurfaceSeed m_lastSeedWhenDrawingImage { 0 };
mutable bool m_requiresDrawAfterPutPixelBuffer { false };

mutable std::optional<GraphicsContextCG> m_context;
@@ -147,7 +147,7 @@ class IOSurface final {
WEBCORE_EXPORT IOSurfaceID surfaceID() const;
size_t bytesPerRow() const;

IOSurfaceSeed seed() const;
WEBCORE_EXPORT IOSurfaceSeed seed() const;

WEBCORE_EXPORT bool isInUse() const;

@@ -50,6 +50,7 @@ template<typename BackendType>
class RemoteImageBufferProxy : public WebCore::ConcreteImageBuffer<BackendType> {
using BaseConcreteImageBuffer = WebCore::ConcreteImageBuffer<BackendType>;
using BaseConcreteImageBuffer::m_backend;
using BaseConcreteImageBuffer::canMapBackingStore;
using BaseConcreteImageBuffer::m_renderingResourceIdentifier;
using BaseConcreteImageBuffer::resolutionScale;

@@ -174,21 +175,29 @@ class RemoteImageBufferProxy : public WebCore::ConcreteImageBuffer<BackendType>
return m_remoteRenderingBackendProxy->getDataForImageBuffer(mimeType, quality, m_renderingResourceIdentifier);
}

RefPtr<WebCore::NativeImage> copyNativeImage(WebCore::BackingStoreCopy = WebCore::CopyBackingStore) const final
RefPtr<WebCore::NativeImage> copyNativeImage(WebCore::BackingStoreCopy copyBehavior = WebCore::CopyBackingStore) const final
{
if (UNLIKELY(!m_remoteRenderingBackendProxy))
return { };

if (canMapBackingStore())
return BaseConcreteImageBuffer::copyNativeImage(copyBehavior);

const_cast<RemoteImageBufferProxy*>(this)->flushDrawingContext();
auto bitmap = m_remoteRenderingBackendProxy->getShareableBitmap(m_renderingResourceIdentifier, WebCore::PreserveResolution::Yes);
if (!bitmap)
return { };
return WebCore::NativeImage::create(bitmap->createPlatformImage(WebCore::DontCopyBackingStore));
}

RefPtr<WebCore::Image> copyImage(WebCore::BackingStoreCopy = WebCore::CopyBackingStore, WebCore::PreserveResolution preserveResolution = WebCore::PreserveResolution::No) const final
RefPtr<WebCore::Image> copyImage(WebCore::BackingStoreCopy copyBehavior = WebCore::CopyBackingStore, WebCore::PreserveResolution preserveResolution = WebCore::PreserveResolution::No) const final
{
if (UNLIKELY(!m_remoteRenderingBackendProxy))
return { };

if (canMapBackingStore())
return BaseConcreteImageBuffer::copyImage(copyBehavior, preserveResolution);

const_cast<RemoteImageBufferProxy*>(this)->flushDrawingContext();
auto bitmap = m_remoteRenderingBackendProxy->getShareableBitmap(m_renderingResourceIdentifier, preserveResolution);
if (!bitmap)
@@ -118,16 +118,6 @@ RefPtr<Image> ImageBufferRemoteIOSurfaceBackend::copyImage(BackingStoreCopy, Pre
return { };
}

void ImageBufferRemoteIOSurfaceBackend::draw(GraphicsContext&, const FloatRect&, const FloatRect&, const ImagePaintingOptions&)
{
RELEASE_ASSERT_NOT_REACHED();
}

void ImageBufferRemoteIOSurfaceBackend::drawPattern(GraphicsContext&, const FloatRect&, const FloatRect&, const AffineTransform&, const FloatPoint&, const FloatSize&, const ImagePaintingOptions&)
{
RELEASE_ASSERT_NOT_REACHED();
}

String ImageBufferRemoteIOSurfaceBackend::toDataURL(const String&, std::optional<double>, PreserveResolution) const
{
RELEASE_ASSERT_NOT_REACHED();
@@ -61,8 +61,6 @@ class ImageBufferRemoteIOSurfaceBackend final : public WebCore::ImageBufferBacke
WebCore::IntSize backendSize() const final;
RefPtr<WebCore::NativeImage> copyNativeImage(WebCore::BackingStoreCopy) const final;
RefPtr<WebCore::Image> copyImage(WebCore::BackingStoreCopy, WebCore::PreserveResolution) const final;
void draw(WebCore::GraphicsContext&, const WebCore::FloatRect& destRect, const WebCore::FloatRect& srcRect, const WebCore::ImagePaintingOptions&) final;
void drawPattern(WebCore::GraphicsContext&, const WebCore::FloatRect& destRect, const WebCore::FloatRect& srcRect, const WebCore::AffineTransform& patternTransform, const WebCore::FloatPoint& phase, const WebCore::FloatSize& spacing, const WebCore::ImagePaintingOptions&) final;
String toDataURL(const String& mimeType, std::optional<double> quality, WebCore::PreserveResolution) const final;
Vector<uint8_t> toData(const String& mimeType, std::optional<double> quality) const final;
std::optional<WebCore::PixelBuffer> getPixelBuffer(const WebCore::PixelBufferFormat& outputFormat, const WebCore::IntRect&) const final;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Apple Inc. All rights reserved.
* Copyright (C) 2020-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -83,6 +83,18 @@ void ImageBufferShareableMappedIOSurfaceBackend::setOwnershipIdentity(const WebC
m_surface->setOwnershipIdentity(resourceOwner);
}

RefPtr<NativeImage> ImageBufferShareableMappedIOSurfaceBackend::copyNativeImage(BackingStoreCopy copyBehavior) const
{
auto currentSeed = m_surface->seed();

// Invalidate the cached image before getting a new one because GPUProcess might
// have drawn something to the IOSurface since last time this function was called.
if (std::exchange(m_lastSeedWhenCopyingImage, currentSeed) != currentSeed)
invalidateCachedNativeImage();

return ImageBufferIOSurfaceBackend::copyNativeImage(copyBehavior);
}

} // namespace WebKit

#endif // ENABLE(GPU_PROCESS) && HAVE(IOSURFACE)

0 comments on commit 45e9179

Please sign in to comment.