blob: 29765519da054362594ddfdd4a2fb85acb0f24da [file] [log] [blame]
/*
* Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ImageBuffer.h"
#if USE(DIRECT2D)
#include "BitmapImage.h"
#include "COMPtr.h"
#include "GraphicsContext.h"
#include "ImageData.h"
#include "IntRect.h"
#include "MIMETypeRegistry.h"
#include "NotImplemented.h"
#include <d2d1.h>
#include <math.h>
#include <wtf/Assertions.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/MainThread.h>
#include <wtf/RetainPtr.h>
#include <wtf/text/Base64.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
{
float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
float yMagnification = static_cast<float>(backingStoreSize.height()) / internalSize.height();
return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
}
std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
{
if (size.isEmpty())
return nullptr;
RenderingMode renderingMode = context.renderingMode();
IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
bool success = false;
std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(scaledSize, 1, ColorSpaceSRGB, renderingMode, &context, success));
if (!success)
return nullptr;
// Set up a corresponding scale factor on the graphics context.
buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
return buffer;
}
ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace /*colorSpace*/, RenderingMode renderingMode, const GraphicsContext* targetContext, bool& success)
: m_logicalSize(size)
, m_resolutionScale(resolutionScale)
{
success = false; // Make early return mean failure.
float scaledWidth = std::ceil(resolutionScale * size.width());
float scaledHeight = std::ceil(resolutionScale * size.height());
// FIXME: Should we automatically use a lower resolution?
if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
return;
m_size = IntSize(scaledWidth, scaledHeight);
m_data.backingStoreSize = m_size;
bool accelerateRendering = renderingMode == Accelerated;
if (m_size.width() <= 0 || m_size.height() <= 0)
return;
// Prevent integer overflows
m_data.bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.width());
Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.height()) * m_data.bytesPerRow;
if (numBytes.hasOverflowed())
return;
auto renderTarget = targetContext ? targetContext->platformContext() : nullptr;
if (!renderTarget)
renderTarget = GraphicsContext::defaultRenderTarget();
RELEASE_ASSERT(renderTarget);
COMPtr<ID2D1BitmapRenderTarget> bitmapContext;
D2D1_SIZE_F desiredSize = FloatSize(m_logicalSize);
D2D1_SIZE_U pixelSize = IntSize(m_logicalSize);
HRESULT hr = renderTarget->CreateCompatibleRenderTarget(&desiredSize, &pixelSize, nullptr, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_GDI_COMPATIBLE, &bitmapContext);
if (!bitmapContext || !SUCCEEDED(hr))
return;
m_data.context = std::make_unique<GraphicsContext>(bitmapContext.get());
m_data.m_compatibleTarget = renderTarget;
success = true;
}
ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
: ImageBuffer(size, resolutionScale, imageColorSpace, renderingMode, nullptr, success)
{
}
ImageBuffer::~ImageBuffer() = default;
FloatSize ImageBuffer::sizeForDestinationSize(FloatSize destinationSize) const
{
return scaleSizeToUserSpace(destinationSize, m_data.backingStoreSize, internalSize());
}
GraphicsContext& ImageBuffer::context() const
{
return *m_data.context;
}
void ImageBuffer::flushContext() const
{
context().flush();
}
RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, PreserveResolution) const
{
notImplemented();
return nullptr;
}
RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, PreserveResolution)
{
IntSize internalSize = imageBuffer->internalSize();
IntSize logicalSize = imageBuffer->logicalSize();
IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
float resolutionScale = imageBuffer->m_resolutionScale;
notImplemented();
return nullptr;
}
BackingStoreCopy ImageBuffer::fastCopyImageMode()
{
return DontCopyBackingStore;
}
void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
{
imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
}
void ImageBuffer::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
{
auto bitmapTarget = reinterpret_cast<ID2D1BitmapRenderTarget*>(context().platformContext());
auto outputTarget = destContext.platformContext();
COMPtr<ID2D1Bitmap> image;
HRESULT hr = bitmapTarget->GetBitmap(&image);
if (!SUCCEEDED(hr))
return;
// If the render targets for the source and destination contexts do not match, move the image over.
if (destContext.platformContext() != m_data.m_compatibleTarget) {
COMPtr<ID2D1Bitmap> sourceImage = image;
image = nullptr;
auto bitmapProperties = D2D1::BitmapProperties();
GraphicsContext::systemFactory()->GetDesktopDpi(&bitmapProperties.dpiX, &bitmapProperties.dpiY);
hr = outputTarget->CreateSharedBitmap(__uuidof(ID2D1Bitmap), sourceImage.get(), &bitmapProperties, &image);
if (!SUCCEEDED(hr))
return;
}
FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
destContext.drawNativeImage(image, image->GetSize(), destRect, adjustedSrcRect, op, blendMode);
destContext.flush();
}
void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
{
FloatRect adjustedSrcRect = srcRect;
adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
if (!context().isAcceleratedContext()) {
if (&destContext == &context() || destContext.isAcceleratedContext()) {
if (RefPtr<Image> copy = copyImage(CopyBackingStore)) // Drawing into our own buffer, need to deep copy.
copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
} else {
if (RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore))
imageForRendering->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
}
} else {
if (RefPtr<Image> copy = copyImage(CopyBackingStore))
copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
}
}
RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
{
if (context().isAcceleratedContext())
flushContext();
IntRect srcRect = rect;
if (coordinateSystem == LogicalCoordinateSystem)
srcRect.scale(m_resolutionScale);
if (pixelArrayDimensions)
*pixelArrayDimensions = srcRect.size();
return m_data.getData(AlphaPremultiplication::Unpremultiplied, srcRect, internalSize(), context().isAcceleratedContext(), 1);
}
RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
{
if (context().isAcceleratedContext())
flushContext();
IntRect srcRect = rect;
if (coordinateSystem == LogicalCoordinateSystem)
srcRect.scale(m_resolutionScale);
if (pixelArrayDimensions)
*pixelArrayDimensions = srcRect.size();
return m_data.getData(AlphaPremultiplication::Premultiplied, srcRect, internalSize(), context().isAcceleratedContext(), 1);
}
void ImageBuffer::putByteArray(const Uint8ClampedArray& source, AlphaPremultiplication bufferFormat, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
{
if (context().isAcceleratedContext())
flushContext();
IntRect scaledSourceRect = sourceRect;
IntSize scaledSourceSize = sourceSize;
if (coordinateSystem == LogicalCoordinateSystem) {
scaledSourceRect.scale(m_resolutionScale);
scaledSourceSize.scale(m_resolutionScale);
}
m_data.putData(source, bufferFormat, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), context().isAcceleratedContext(), 1);
}
String ImageBuffer::toDataURL(const String&, std::optional<double>, PreserveResolution) const
{
notImplemented();
return ASCIILiteral("data:,");
}
Vector<uint8_t> ImageBuffer::toData(const String& mimeType, std::optional<double> quality) const
{
notImplemented();
return { };
}
String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
{
notImplemented();
return ASCIILiteral("data:,");
}
void ImageBuffer::transformColorSpace(ColorSpace, ColorSpace)
{
notImplemented();
}
} // namespace WebCore
#endif