blob: 2521f81cedfba74a14507332f3015ae8f9ad2cf1 [file] [log] [blame]
/*
* Copyright (C) 2007 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 COMPUTER, 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 COMPUTER, 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 "platform/DragImage.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/fonts/Font.h"
#include "platform/fonts/FontCache.h"
#include "platform/fonts/FontDescription.h"
#include "platform/fonts/FontMetrics.h"
#include "platform/geometry/FloatPoint.h"
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/IntPoint.h"
#include "platform/graphics/BitmapImage.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/Image.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "platform/graphics/paint/SkPictureBuilder.h"
#include "platform/text/BidiTextRun.h"
#include "platform/text/StringTruncator.h"
#include "platform/text/TextRun.h"
#include "platform/transforms/AffineTransform.h"
#include "platform/weborigin/KURL.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "wtf/PtrUtil.h"
#include "wtf/RefPtr.h"
#include "wtf/text/WTFString.h"
#include <algorithm>
#include <memory>
namespace blink {
namespace {
const float kDragLabelBorderX = 4;
// Keep border_y in synch with DragController::LinkDragBorderInset.
const float kDragLabelBorderY = 2;
const float kLabelBorderYOffset = 2;
const float kMaxDragLabelWidth = 300;
const float kMaxDragLabelStringWidth =
(kMaxDragLabelWidth - 2 * kDragLabelBorderX);
const float kDragLinkLabelFontSize = 11;
const float kDragLinkUrlFontSize = 10;
} // anonymous namespace
sk_sp<SkImage> DragImage::resizeAndOrientImage(
sk_sp<SkImage> image,
ImageOrientation orientation,
FloatSize imageScale,
float opacity,
InterpolationQuality interpolationQuality) {
IntSize size(image->width(), image->height());
size.scale(imageScale.width(), imageScale.height());
AffineTransform transform;
if (orientation != DefaultImageOrientation) {
if (orientation.usesWidthAsHeight())
size = size.transposedSize();
transform *= orientation.transformFromDefault(FloatSize(size));
}
transform.scaleNonUniform(imageScale.width(), imageScale.height());
if (size.isEmpty())
return nullptr;
if (transform.isIdentity() && opacity == 1) {
// Nothing to adjust, just use the original.
ASSERT(image->width() == size.width());
ASSERT(image->height() == size.height());
return image;
}
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(size.width(), size.height());
if (!surface)
return nullptr;
SkPaint paint;
ASSERT(opacity >= 0 && opacity <= 1);
paint.setAlpha(opacity * 255);
paint.setFilterQuality(interpolationQuality == InterpolationNone
? kNone_SkFilterQuality
: kHigh_SkFilterQuality);
SkCanvas* canvas = surface->getCanvas();
canvas->concat(affineTransformToSkMatrix(transform));
canvas->drawImage(image, 0, 0, &paint);
return surface->makeImageSnapshot();
}
FloatSize DragImage::clampedImageScale(const IntSize& imageSize,
const IntSize& size,
const IntSize& maxSize) {
// Non-uniform scaling for size mapping.
FloatSize imageScale(static_cast<float>(size.width()) / imageSize.width(),
static_cast<float>(size.height()) / imageSize.height());
// Uniform scaling for clamping.
const float clampScaleX =
size.width() > maxSize.width()
? static_cast<float>(maxSize.width()) / size.width()
: 1;
const float clampScaleY =
size.height() > maxSize.height()
? static_cast<float>(maxSize.height()) / size.height()
: 1;
imageScale.scale(std::min(clampScaleX, clampScaleY));
return imageScale;
}
std::unique_ptr<DragImage> DragImage::create(
Image* image,
RespectImageOrientationEnum shouldRespectImageOrientation,
float deviceScaleFactor,
InterpolationQuality interpolationQuality,
float opacity,
FloatSize imageScale) {
if (!image)
return nullptr;
// TODO(ccameron): DragImage needs to be color space aware.
// https://crbug.com/672316
sk_sp<SkImage> skImage =
image->imageForCurrentFrame(ColorBehavior::transformToGlobalTarget());
if (!skImage)
return nullptr;
ImageOrientation orientation;
if (shouldRespectImageOrientation == RespectImageOrientation &&
image->isBitmapImage())
orientation = toBitmapImage(image)->currentFrameOrientation();
SkBitmap bm;
sk_sp<SkImage> resizedImage =
resizeAndOrientImage(std::move(skImage), orientation, imageScale, opacity,
interpolationQuality);
if (!resizedImage ||
!resizedImage->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode))
return nullptr;
return WTF::wrapUnique(
new DragImage(bm, deviceScaleFactor, interpolationQuality));
}
static Font deriveDragLabelFont(int size,
FontWeight fontWeight,
const FontDescription& systemFont) {
FontDescription description = systemFont;
description.setWeight(fontWeight);
description.setSpecifiedSize(size);
description.setComputedSize(size);
Font result(description);
result.update(nullptr);
return result;
}
std::unique_ptr<DragImage> DragImage::create(const KURL& url,
const String& inLabel,
const FontDescription& systemFont,
float deviceScaleFactor) {
const Font labelFont =
deriveDragLabelFont(kDragLinkLabelFontSize, FontWeightBold, systemFont);
const SimpleFontData* labelFontData = labelFont.primaryFont();
DCHECK(labelFontData);
const Font urlFont =
deriveDragLabelFont(kDragLinkUrlFontSize, FontWeightNormal, systemFont);
const SimpleFontData* urlFontData = urlFont.primaryFont();
DCHECK(urlFontData);
if (!labelFontData || !urlFontData)
return nullptr;
FontCachePurgePreventer fontCachePurgePreventer;
bool drawURLString = true;
bool clipURLString = false;
bool clipLabelString = false;
float maxDragLabelStringWidthDIP =
kMaxDragLabelStringWidth / deviceScaleFactor;
String urlString = url.getString();
String label = inLabel.stripWhiteSpace();
if (label.isEmpty()) {
drawURLString = false;
label = urlString;
}
// First step is drawing the link drag image width.
TextRun labelRun(label.impl());
TextRun urlRun(urlString.impl());
IntSize labelSize(labelFont.width(labelRun),
labelFontData->getFontMetrics().ascent() +
labelFontData->getFontMetrics().descent());
if (labelSize.width() > maxDragLabelStringWidthDIP) {
labelSize.setWidth(maxDragLabelStringWidthDIP);
clipLabelString = true;
}
IntSize urlStringSize;
IntSize imageSize(labelSize.width() + kDragLabelBorderX * 2,
labelSize.height() + kDragLabelBorderY * 2);
if (drawURLString) {
urlStringSize.setWidth(urlFont.width(urlRun));
urlStringSize.setHeight(urlFontData->getFontMetrics().ascent() +
urlFontData->getFontMetrics().descent());
imageSize.setHeight(imageSize.height() + urlStringSize.height());
if (urlStringSize.width() > maxDragLabelStringWidthDIP) {
imageSize.setWidth(maxDragLabelStringWidthDIP);
clipURLString = true;
} else
imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) +
kDragLabelBorderX * 2);
}
// We now know how big the image needs to be, so we create and
// fill the background
IntSize scaledImageSize = imageSize;
scaledImageSize.scale(deviceScaleFactor);
std::unique_ptr<ImageBuffer> buffer(ImageBuffer::create(scaledImageSize));
if (!buffer)
return nullptr;
buffer->canvas()->scale(deviceScaleFactor, deviceScaleFactor);
const float DragLabelRadius = 5;
IntRect rect(IntPoint(), imageSize);
SkPaint backgroundPaint;
backgroundPaint.setColor(SkColorSetRGB(140, 140, 140));
backgroundPaint.setAntiAlias(true);
SkRRect rrect;
rrect.setRectXY(SkRect::MakeWH(imageSize.width(), imageSize.height()),
DragLabelRadius, DragLabelRadius);
buffer->canvas()->drawRRect(rrect, backgroundPaint);
// Draw the text
SkPaint textPaint;
if (drawURLString) {
if (clipURLString)
urlString = StringTruncator::centerTruncate(
urlString, imageSize.width() - (kDragLabelBorderX * 2.0f), urlFont);
IntPoint textPos(
kDragLabelBorderX,
imageSize.height() -
(kLabelBorderYOffset + urlFontData->getFontMetrics().descent()));
TextRun textRun(urlString);
urlFont.drawText(buffer->canvas(), TextRunPaintInfo(textRun), textPos,
deviceScaleFactor, textPaint);
}
if (clipLabelString)
label = StringTruncator::rightTruncate(
label, imageSize.width() - (kDragLabelBorderX * 2.0f), labelFont);
bool hasStrongDirectionality;
TextRun textRun = textRunWithDirectionality(label, &hasStrongDirectionality);
IntPoint textPos(
kDragLabelBorderX,
kDragLabelBorderY + labelFont.getFontDescription().computedPixelSize());
if (hasStrongDirectionality && textRun.direction() == TextDirection::Rtl) {
float textWidth = labelFont.width(textRun);
int availableWidth = imageSize.width() - kDragLabelBorderX * 2;
textPos.setX(availableWidth - ceilf(textWidth));
}
labelFont.drawBidiText(buffer->canvas(), TextRunPaintInfo(textRun),
FloatPoint(textPos), Font::DoNotPaintIfFontNotReady,
deviceScaleFactor, textPaint);
RefPtr<Image> image = buffer->newImageSnapshot();
return DragImage::create(image.get(), DoNotRespectImageOrientation,
deviceScaleFactor);
}
DragImage::DragImage(const SkBitmap& bitmap,
float resolutionScale,
InterpolationQuality interpolationQuality)
: m_bitmap(bitmap),
m_resolutionScale(resolutionScale),
m_interpolationQuality(interpolationQuality) {}
DragImage::~DragImage() {}
void DragImage::scale(float scaleX, float scaleY) {
skia::ImageOperations::ResizeMethod resizeMethod =
m_interpolationQuality == InterpolationNone
? skia::ImageOperations::RESIZE_BOX
: skia::ImageOperations::RESIZE_LANCZOS3;
int imageWidth = scaleX * m_bitmap.width();
int imageHeight = scaleY * m_bitmap.height();
m_bitmap = skia::ImageOperations::Resize(m_bitmap, resizeMethod, imageWidth,
imageHeight);
}
} // namespace blink