2 * Copyright (C) 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "platform/DragImage.h"
29 #include "platform/RuntimeEnabledFeatures.h"
30 #include "platform/fonts/Font.h"
31 #include "platform/fonts/FontCache.h"
32 #include "platform/fonts/FontDescription.h"
33 #include "platform/fonts/FontMetrics.h"
34 #include "platform/geometry/FloatPoint.h"
35 #include "platform/geometry/FloatRect.h"
36 #include "platform/geometry/IntPoint.h"
37 #include "platform/graphics/BitmapImage.h"
38 #include "platform/graphics/Color.h"
39 #include "platform/graphics/GraphicsContext.h"
40 #include "platform/graphics/Image.h"
41 #include "platform/graphics/ImageBuffer.h"
42 #include "platform/graphics/paint/DrawingRecorder.h"
43 #include "platform/graphics/paint/SkPictureBuilder.h"
44 #include "platform/text/BidiTextRun.h"
45 #include "platform/text/StringTruncator.h"
46 #include "platform/text/TextRun.h"
47 #include "platform/transforms/AffineTransform.h"
48 #include "platform/weborigin/KURL.h"
49 #include "skia/ext/image_operations.h"
50 #include "third_party/skia/include/core/SkCanvas.h"
51 #include "third_party/skia/include/core/SkImage.h"
52 #include "third_party/skia/include/core/SkMatrix.h"
53 #include "third_party/skia/include/core/SkSurface.h"
54 #include "wtf/PassOwnPtr.h"
55 #include "wtf/RefPtr.h"
56 #include "wtf/text/WTFString.h"
64 const float kDragLabelBorderX
= 4;
65 // Keep border_y in synch with DragController::LinkDragBorderInset.
66 const float kDragLabelBorderY
= 2;
67 const float kLabelBorderYOffset
= 2;
69 const float kMaxDragLabelWidth
= 300;
70 const float kMaxDragLabelStringWidth
= (kMaxDragLabelWidth
- 2 * kDragLabelBorderX
);
72 const float kDragLinkLabelFontSize
= 11;
73 const float kDragLinkUrlFontSize
= 10;
75 PassRefPtr
<SkImage
> adjustedImage(PassRefPtr
<SkImage
> image
, const IntSize
& size
,
76 const AffineTransform
& transform
, float opacity
, InterpolationQuality interpolationQuality
)
78 if (transform
.isIdentity() && opacity
== 1) {
79 // Nothing to adjust, just use the original.
80 ASSERT(image
->width() == size
.width());
81 ASSERT(image
->height() == size
.height());
85 RefPtr
<SkSurface
> surface
= adoptRef(SkSurface::NewRasterN32Premul(size
.width(), size
.height()));
90 ASSERT(opacity
>= 0 && opacity
<= 1);
91 paint
.setAlpha(opacity
* 255);
92 paint
.setFilterQuality(interpolationQuality
== InterpolationNone
93 ? kNone_SkFilterQuality
: kHigh_SkFilterQuality
);
95 SkCanvas
* canvas
= surface
->getCanvas();
96 canvas
->clear(SK_ColorTRANSPARENT
);
97 canvas
->concat(affineTransformToSkMatrix(transform
));
98 canvas
->drawImage(image
.get(), 0, 0, &paint
);
100 return adoptRef(surface
->newImageSnapshot());
103 } // anonymous namespace
105 FloatSize
DragImage::clampedImageScale(const Image
& image
, const IntSize
& size
,
106 const IntSize
& maxSize
)
108 // Non-uniform scaling for size mapping.
109 FloatSize
imageScale(
110 static_cast<float>(size
.width()) / image
.width(),
111 static_cast<float>(size
.height()) / image
.height());
113 // Uniform scaling for clamping.
114 const float clampScaleX
= size
.width() > maxSize
.width()
115 ? static_cast<float>(maxSize
.width()) / size
.width() : 1;
116 const float clampScaleY
= size
.height() > maxSize
.height()
117 ? static_cast<float>(maxSize
.height()) / size
.height() : 1;
118 imageScale
.scale(std::min(clampScaleX
, clampScaleY
));
123 PassOwnPtr
<DragImage
> DragImage::create(Image
* image
,
124 RespectImageOrientationEnum shouldRespectImageOrientation
, float deviceScaleFactor
,
125 InterpolationQuality interpolationQuality
, float opacity
, const FloatSize
& imageScale
)
130 RefPtr
<SkImage
> skImage
= image
->imageForCurrentFrame();
134 IntSize size
= image
->size();
135 size
.scale(imageScale
.width(), imageScale
.height());
139 AffineTransform transform
;
140 transform
.scaleNonUniform(imageScale
.width(), imageScale
.height());
142 if (shouldRespectImageOrientation
== RespectImageOrientation
&& image
->isBitmapImage()) {
143 BitmapImage
* bitmapImage
= toBitmapImage(image
);
144 ImageOrientation orientation
= bitmapImage
->currentFrameOrientation();
146 if (orientation
!= DefaultImageOrientation
) {
147 size
= bitmapImage
->sizeRespectingOrientation();
148 if (orientation
.usesWidthAsHeight())
149 size
.scale(imageScale
.height(), imageScale
.width());
151 size
.scale(imageScale
.width(), imageScale
.height());
153 transform
*= orientation
.transformFromDefault(size
);
158 RefPtr
<SkImage
> resizedImage
=
159 adjustedImage(skImage
.release(), size
, transform
, opacity
, interpolationQuality
);
160 if (!resizedImage
|| !resizedImage
->asLegacyBitmap(&bm
, SkImage::kRO_LegacyBitmapMode
))
163 return adoptPtr(new DragImage(bm
, deviceScaleFactor
, interpolationQuality
));
166 static Font
deriveDragLabelFont(int size
, FontWeight fontWeight
, const FontDescription
& systemFont
)
168 FontDescription description
= systemFont
;
169 description
.setWeight(fontWeight
);
170 description
.setSpecifiedSize(size
);
171 description
.setComputedSize(size
);
172 Font
result(description
);
173 result
.update(nullptr);
177 PassOwnPtr
<DragImage
> DragImage::create(const KURL
& url
, const String
& inLabel
, const FontDescription
& systemFont
, float deviceScaleFactor
)
179 const Font labelFont
= deriveDragLabelFont(kDragLinkLabelFontSize
, FontWeightBold
, systemFont
);
180 const Font urlFont
= deriveDragLabelFont(kDragLinkUrlFontSize
, FontWeightNormal
, systemFont
);
181 FontCachePurgePreventer fontCachePurgePreventer
;
183 bool drawURLString
= true;
184 bool clipURLString
= false;
185 bool clipLabelString
= false;
187 String urlString
= url
.string();
188 String label
= inLabel
.stripWhiteSpace();
189 if (label
.isEmpty()) {
190 drawURLString
= false;
194 // First step is drawing the link drag image width.
195 TextRun
labelRun(label
.impl());
196 TextRun
urlRun(urlString
.impl());
197 IntSize
labelSize(labelFont
.width(labelRun
), labelFont
.fontMetrics().ascent() + labelFont
.fontMetrics().descent());
199 if (labelSize
.width() > kMaxDragLabelStringWidth
) {
200 labelSize
.setWidth(kMaxDragLabelStringWidth
);
201 clipLabelString
= true;
204 IntSize urlStringSize
;
205 IntSize
imageSize(labelSize
.width() + kDragLabelBorderX
* 2, labelSize
.height() + kDragLabelBorderY
* 2);
208 urlStringSize
.setWidth(urlFont
.width(urlRun
));
209 urlStringSize
.setHeight(urlFont
.fontMetrics().ascent() + urlFont
.fontMetrics().descent());
210 imageSize
.setHeight(imageSize
.height() + urlStringSize
.height());
211 if (urlStringSize
.width() > kMaxDragLabelStringWidth
) {
212 imageSize
.setWidth(kMaxDragLabelWidth
);
213 clipURLString
= true;
215 imageSize
.setWidth(std::max(labelSize
.width(), urlStringSize
.width()) + kDragLabelBorderX
* 2);
218 // We now know how big the image needs to be, so we create and
219 // fill the background
220 IntSize scaledImageSize
= imageSize
;
221 scaledImageSize
.scale(deviceScaleFactor
);
222 OwnPtr
<ImageBuffer
> buffer(ImageBuffer::create(scaledImageSize
));
226 buffer
->canvas()->scale(deviceScaleFactor
, deviceScaleFactor
);
228 const float DragLabelRadius
= 5;
230 IntRect
rect(IntPoint(), imageSize
);
231 SkPaint backgroundPaint
;
232 backgroundPaint
.setColor(SkColorSetRGB(140, 140, 140));
234 rrect
.setRectXY(SkRect::MakeWH(imageSize
.width(), imageSize
.height()), DragLabelRadius
, DragLabelRadius
);
235 buffer
->canvas()->drawRRect(rrect
, backgroundPaint
);
241 urlString
= StringTruncator::centerTruncate(urlString
, imageSize
.width() - (kDragLabelBorderX
* 2.0f
), urlFont
);
242 IntPoint
textPos(kDragLabelBorderX
, imageSize
.height() - (kLabelBorderYOffset
+ urlFont
.fontMetrics().descent()));
243 TextRun
textRun(urlString
);
244 urlFont
.drawText(buffer
->canvas(), TextRunPaintInfo(textRun
), textPos
, deviceScaleFactor
, textPaint
);
248 label
= StringTruncator::rightTruncate(label
, imageSize
.width() - (kDragLabelBorderX
* 2.0f
), labelFont
);
250 bool hasStrongDirectionality
;
251 TextRun textRun
= textRunWithDirectionality(label
, &hasStrongDirectionality
);
252 IntPoint
textPos(kDragLabelBorderX
, kDragLabelBorderY
+ labelFont
.fontDescription().computedPixelSize());
253 if (hasStrongDirectionality
&& textRun
.direction() == RTL
) {
254 float textWidth
= labelFont
.width(textRun
);
255 int availableWidth
= imageSize
.width() - kDragLabelBorderX
* 2;
256 textPos
.setX(availableWidth
- ceilf(textWidth
));
258 labelFont
.drawBidiText(buffer
->canvas(), TextRunPaintInfo(textRun
), FloatPoint(textPos
), Font::DoNotPaintIfFontNotReady
, deviceScaleFactor
, textPaint
);
260 RefPtr
<Image
> image
= buffer
->newImageSnapshot();
261 return DragImage::create(image
.get(), DoNotRespectImageOrientation
, deviceScaleFactor
);
264 DragImage::DragImage(const SkBitmap
& bitmap
, float resolutionScale
, InterpolationQuality interpolationQuality
)
266 , m_resolutionScale(resolutionScale
)
267 , m_interpolationQuality(interpolationQuality
)
271 DragImage::~DragImage()
275 void DragImage::scale(float scaleX
, float scaleY
)
277 skia::ImageOperations::ResizeMethod resizeMethod
= m_interpolationQuality
== InterpolationNone
? skia::ImageOperations::RESIZE_BOX
: skia::ImageOperations::RESIZE_LANCZOS3
;
278 int imageWidth
= scaleX
* m_bitmap
.width();
279 int imageHeight
= scaleY
* m_bitmap
.height();
280 m_bitmap
= skia::ImageOperations::Resize(m_bitmap
, resizeMethod
, imageWidth
, imageHeight
);