blob: a0dafa146181cec93e4721b7a5e946628f27ed84 [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 "third_party/blink/renderer/core/page/drag_image.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "skia/ext/image_operations.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/layout/layout_theme_font_provider.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/fonts/font_metrics.h"
#include "third_party/blink/renderer/platform/fonts/string_truncator.h"
#include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
#include "third_party/blink/renderer/platform/text/text_run.h"
#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
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
gfx::Vector2dF DragImage::ClampedImageScale(const gfx::Size& image_size,
const gfx::Size& size,
const gfx::Size& max_size) {
// Non-uniform scaling for size mapping.
gfx::Vector2dF image_scale(
static_cast<float>(size.width()) / image_size.width(),
static_cast<float>(size.height()) / image_size.height());
// Uniform scaling for clamping.
const float clamp_scale_x =
size.width() > max_size.width()
? static_cast<float>(max_size.width()) / size.width()
: 1;
const float clamp_scale_y =
size.height() > max_size.height()
? static_cast<float>(max_size.height()) / size.height()
: 1;
image_scale.Scale(std::min(clamp_scale_x, clamp_scale_y));
return image_scale;
}
std::unique_ptr<DragImage> DragImage::Create(
Image* image,
RespectImageOrientationEnum should_respect_image_orientation,
InterpolationQuality interpolation_quality,
float opacity,
gfx::Vector2dF image_scale) {
if (!image)
return nullptr;
PaintImage paint_image = image->PaintImageForCurrentFrame();
if (!paint_image)
return nullptr;
ImageOrientation orientation;
auto* bitmap_image = DynamicTo<BitmapImage>(image);
if (should_respect_image_orientation == kRespectImageOrientation &&
bitmap_image)
orientation = bitmap_image->CurrentFrameOrientation();
SkBitmap bm;
paint_image = Image::ResizeAndOrientImage(
paint_image, orientation, image_scale, opacity, interpolation_quality,
SkColorSpace::MakeSRGB());
if (!paint_image || !paint_image.GetSwSkImage()->asLegacyBitmap(&bm))
return nullptr;
return base::WrapUnique(new DragImage(bm, interpolation_quality));
}
static Font DeriveDragLabelFont(int size, FontSelectionValue font_weight) {
const AtomicString& family =
LayoutThemeFontProvider::SystemFontFamily(CSSValueID::kNone);
FontDescription description;
description.SetFamily(
FontFamily(family, FontFamily::InferredTypeFor(family)));
description.SetWeight(font_weight);
description.SetSpecifiedSize(size);
description.SetComputedSize(size);
Font result(description);
return result;
}
// static
std::unique_ptr<DragImage> DragImage::Create(const KURL& url,
const String& in_label,
float device_scale_factor) {
const Font label_font =
DeriveDragLabelFont(kDragLinkLabelFontSize, kBoldWeightValue);
const SimpleFontData* label_font_data = label_font.PrimaryFont();
DCHECK(label_font_data);
const Font url_font =
DeriveDragLabelFont(kDragLinkUrlFontSize, kNormalWeightValue);
const SimpleFontData* url_font_data = url_font.PrimaryFont();
DCHECK(url_font_data);
if (!label_font_data || !url_font_data)
return nullptr;
FontCachePurgePreventer font_cache_purge_preventer;
bool draw_url_string = true;
bool clip_url_string = false;
bool clip_label_string = false;
float max_drag_label_string_width_dip =
kMaxDragLabelStringWidth / device_scale_factor;
String url_string = url.GetString();
String label = in_label.StripWhiteSpace();
if (label.empty()) {
draw_url_string = false;
label = url_string;
}
// First step is drawing the link drag image width.
TextRun label_run(label.Impl());
TextRun url_run(url_string.Impl());
gfx::Size label_size(label_font.Width(label_run),
label_font_data->GetFontMetrics().Ascent() +
label_font_data->GetFontMetrics().Descent());
if (label_size.width() > max_drag_label_string_width_dip) {
label_size.set_width(max_drag_label_string_width_dip);
clip_label_string = true;
}
gfx::Size url_string_size;
gfx::Size image_size(label_size.width() + kDragLabelBorderX * 2,
label_size.height() + kDragLabelBorderY * 2);
if (draw_url_string) {
url_string_size.set_width(url_font.Width(url_run));
url_string_size.set_height(url_font_data->GetFontMetrics().Ascent() +
url_font_data->GetFontMetrics().Descent());
image_size.set_height(image_size.height() + url_string_size.height());
if (url_string_size.width() > max_drag_label_string_width_dip) {
image_size.set_width(max_drag_label_string_width_dip);
clip_url_string = true;
} else {
image_size.set_width(
std::max(label_size.width(), url_string_size.width()) +
kDragLabelBorderX * 2);
}
}
// We now know how big the image needs to be, so we create and
// fill the background
gfx::Size scaled_image_size =
gfx::ScaleToFlooredSize(image_size, device_scale_factor);
// TODO(fserb): are we sure this should be software?
std::unique_ptr<CanvasResourceProvider> resource_provider(
CanvasResourceProvider::CreateBitmapProvider(
SkImageInfo::MakeN32Premul(scaled_image_size.width(),
scaled_image_size.height()),
cc::PaintFlags::FilterQuality::kLow,
CanvasResourceProvider::ShouldInitialize::kNo));
if (!resource_provider)
return nullptr;
resource_provider->Canvas().scale(device_scale_factor, device_scale_factor);
const float kDragLabelRadius = 5;
gfx::Rect rect(image_size);
cc::PaintFlags background_paint;
background_paint.setColor(SkColorSetRGB(140, 140, 140));
background_paint.setAntiAlias(true);
SkRRect rrect;
rrect.setRectXY(SkRect::MakeWH(image_size.width(), image_size.height()),
kDragLabelRadius, kDragLabelRadius);
resource_provider->Canvas().drawRRect(rrect, background_paint);
// Draw the text
cc::PaintFlags text_paint;
if (draw_url_string) {
if (clip_url_string)
url_string = StringTruncator::CenterTruncate(
url_string, image_size.width() - (kDragLabelBorderX * 2.0f),
url_font);
gfx::PointF text_pos(
kDragLabelBorderX,
image_size.height() -
(kLabelBorderYOffset + url_font_data->GetFontMetrics().Descent()));
TextRun text_run(url_string);
url_font.DrawText(&resource_provider->Canvas(), TextRunPaintInfo(text_run),
text_pos, device_scale_factor, text_paint);
}
if (clip_label_string) {
label = StringTruncator::RightTruncate(
label, image_size.width() - (kDragLabelBorderX * 2.0f), label_font);
}
TextRun text_run(label);
text_run.SetDirectionFromText();
gfx::Point text_pos(
kDragLabelBorderX,
kDragLabelBorderY + label_font.GetFontDescription().ComputedPixelSize());
if (text_run.Direction() == TextDirection::kRtl) {
float text_width = label_font.Width(text_run);
int available_width = image_size.width() - kDragLabelBorderX * 2;
text_pos.set_x(available_width - ceilf(text_width));
}
label_font.DrawBidiText(&resource_provider->Canvas(),
TextRunPaintInfo(text_run), gfx::PointF(text_pos),
Font::kDoNotPaintIfFontNotReady, text_paint);
scoped_refptr<StaticBitmapImage> image =
resource_provider->Snapshot(FlushReason::kNon2DCanvas);
return DragImage::Create(image.get(), kRespectImageOrientation);
}
DragImage::DragImage(const SkBitmap& bitmap,
InterpolationQuality interpolation_quality)
: bitmap_(bitmap), interpolation_quality_(interpolation_quality) {}
DragImage::~DragImage() = default;
void DragImage::Scale(float scale_x, float scale_y) {
skia::ImageOperations::ResizeMethod resize_method =
interpolation_quality_ == kInterpolationNone
? skia::ImageOperations::RESIZE_BOX
: skia::ImageOperations::RESIZE_LANCZOS3;
int image_width = scale_x * bitmap_.width();
int image_height = scale_y * bitmap_.height();
bitmap_ = skia::ImageOperations::Resize(bitmap_, resize_method, image_width,
image_height);
}
} // namespace blink