1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 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. 12 * 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. 24 */ 25 26 #include "config.h" 27 #include "platform/DragImage.h" 28 29 #include "platform/fonts/Font.h" 30 #include "platform/fonts/FontCache.h" 31 #include "platform/fonts/FontDescription.h" 32 #include "platform/fonts/FontMetrics.h" 33 #include "platform/geometry/FloatPoint.h" 34 #include "platform/geometry/FloatRect.h" 35 #include "platform/geometry/IntPoint.h" 36 #include "platform/graphics/BitmapImage.h" 37 #include "platform/graphics/Color.h" 38 #include "platform/graphics/GraphicsContext.h" 39 #include "platform/graphics/Image.h" 40 #include "platform/graphics/ImageBuffer.h" 41 #include "platform/graphics/skia/NativeImageSkia.h" 42 #include "platform/text/BidiTextRun.h" 43 #include "platform/text/StringTruncator.h" 44 #include "platform/text/TextRun.h" 45 #include "platform/transforms/AffineTransform.h" 46 #include "platform/weborigin/KURL.h" 47 #include "skia/ext/image_operations.h" 48 #include "third_party/skia/include/core/SkCanvas.h" 49 #include "third_party/skia/include/core/SkMatrix.h" 50 #include "wtf/PassOwnPtr.h" 51 #include "wtf/RefPtr.h" 52 #include "wtf/text/WTFString.h" 53 54 #include <algorithm> 55 56 namespace blink { 57 58 const float kDragLabelBorderX = 4; 59 // Keep border_y in synch with DragController::LinkDragBorderInset. 60 const float kDragLabelBorderY = 2; 61 const float kLabelBorderYOffset = 2; 62 63 const float kMaxDragLabelWidth = 300; 64 const float kMaxDragLabelStringWidth = (kMaxDragLabelWidth - 2 * kDragLabelBorderX); 65 66 const float kDragLinkLabelFontSize = 11; 67 const float kDragLinkUrlFontSize = 10; 68 69 PassOwnPtr<DragImage> DragImage::create(Image* image, RespectImageOrientationEnum shouldRespectImageOrientation, float deviceScaleFactor) 70 { 71 if (!image) 72 return nullptr; 73 74 RefPtr<NativeImageSkia> bitmap = image->nativeImageForCurrentFrame(); 75 if (!bitmap) 76 return nullptr; 77 78 if (image->isBitmapImage()) { 79 ImageOrientation orientation = DefaultImageOrientation; 80 BitmapImage* bitmapImage = toBitmapImage(image); 81 IntSize sizeRespectingOrientation = bitmapImage->sizeRespectingOrientation(); 82 83 if (shouldRespectImageOrientation == RespectImageOrientation) 84 orientation = bitmapImage->currentFrameOrientation(); 85 86 if (orientation != DefaultImageOrientation) { 87 FloatRect destRect(FloatPoint(), sizeRespectingOrientation); 88 if (orientation.usesWidthAsHeight()) 89 destRect = destRect.transposedRect(); 90 91 SkBitmap skBitmap; 92 if (!skBitmap.tryAllocN32Pixels(sizeRespectingOrientation.width(), sizeRespectingOrientation.height())) 93 return nullptr; 94 95 SkCanvas canvas(skBitmap); 96 canvas.concat(affineTransformToSkMatrix(orientation.transformFromDefault(sizeRespectingOrientation))); 97 canvas.drawBitmapRect(bitmap->bitmap(), 0, destRect); 98 99 return adoptPtr(new DragImage(skBitmap, deviceScaleFactor)); 100 } 101 } 102 103 SkBitmap skBitmap; 104 if (!bitmap->bitmap().copyTo(&skBitmap, kN32_SkColorType)) 105 return nullptr; 106 return adoptPtr(new DragImage(skBitmap, deviceScaleFactor)); 107 } 108 109 static Font deriveDragLabelFont(int size, FontWeight fontWeight, const FontDescription& systemFont) 110 { 111 FontDescription description = systemFont; 112 description.setWeight(fontWeight); 113 description.setSpecifiedSize(size); 114 description.setComputedSize(size); 115 Font result(description); 116 result.update(nullptr); 117 return result; 118 } 119 120 PassOwnPtr<DragImage> DragImage::create(const KURL& url, const String& inLabel, const FontDescription& systemFont, float deviceScaleFactor) 121 { 122 const Font labelFont = deriveDragLabelFont(kDragLinkLabelFontSize, FontWeightBold, systemFont); 123 const Font urlFont = deriveDragLabelFont(kDragLinkUrlFontSize, FontWeightNormal, systemFont); 124 FontCachePurgePreventer fontCachePurgePreventer; 125 126 bool drawURLString = true; 127 bool clipURLString = false; 128 bool clipLabelString = false; 129 130 String urlString = url.string(); 131 String label = inLabel.stripWhiteSpace(); 132 if (label.isEmpty()) { 133 drawURLString = false; 134 label = urlString; 135 } 136 137 // First step is drawing the link drag image width. 138 TextRun labelRun(label.impl()); 139 TextRun urlRun(urlString.impl()); 140 IntSize labelSize(labelFont.width(labelRun), labelFont.fontMetrics().ascent() + labelFont.fontMetrics().descent()); 141 142 if (labelSize.width() > kMaxDragLabelStringWidth) { 143 labelSize.setWidth(kMaxDragLabelStringWidth); 144 clipLabelString = true; 145 } 146 147 IntSize urlStringSize; 148 IntSize imageSize(labelSize.width() + kDragLabelBorderX * 2, labelSize.height() + kDragLabelBorderY * 2); 149 150 if (drawURLString) { 151 urlStringSize.setWidth(urlFont.width(urlRun)); 152 urlStringSize.setHeight(urlFont.fontMetrics().ascent() + urlFont.fontMetrics().descent()); 153 imageSize.setHeight(imageSize.height() + urlStringSize.height()); 154 if (urlStringSize.width() > kMaxDragLabelStringWidth) { 155 imageSize.setWidth(kMaxDragLabelWidth); 156 clipURLString = true; 157 } else 158 imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) + kDragLabelBorderX * 2); 159 } 160 161 // We now know how big the image needs to be, so we create and 162 // fill the background 163 IntSize scaledImageSize = imageSize; 164 scaledImageSize.scale(deviceScaleFactor); 165 OwnPtr<ImageBuffer> buffer(ImageBuffer::create(scaledImageSize)); 166 if (!buffer) 167 return nullptr; 168 buffer->context()->scale(deviceScaleFactor, deviceScaleFactor); 169 170 const float DragLabelRadius = 5; 171 const IntSize radii(DragLabelRadius, DragLabelRadius); 172 IntRect rect(IntPoint(), imageSize); 173 const Color backgroundColor(140, 140, 140); 174 buffer->context()->fillRoundedRect(rect, radii, radii, radii, radii, backgroundColor); 175 176 // Draw the text 177 if (drawURLString) { 178 if (clipURLString) 179 urlString = StringTruncator::centerTruncate(urlString, imageSize.width() - (kDragLabelBorderX * 2.0f), urlFont); 180 IntPoint textPos(kDragLabelBorderX, imageSize.height() - (kLabelBorderYOffset + urlFont.fontMetrics().descent())); 181 TextRun textRun(urlString); 182 buffer->context()->drawText(urlFont, TextRunPaintInfo(textRun), textPos); 183 } 184 185 if (clipLabelString) 186 label = StringTruncator::rightTruncate(label, imageSize.width() - (kDragLabelBorderX * 2.0f), labelFont); 187 188 bool hasStrongDirectionality; 189 TextRun textRun = textRunWithDirectionality(label, hasStrongDirectionality); 190 IntPoint textPos(kDragLabelBorderX, kDragLabelBorderY + labelFont.fontDescription().computedPixelSize()); 191 if (hasStrongDirectionality && textRun.direction() == RTL) { 192 float textWidth = labelFont.width(textRun); 193 int availableWidth = imageSize.width() - kDragLabelBorderX * 2; 194 textPos.setX(availableWidth - ceilf(textWidth)); 195 } 196 buffer->context()->drawBidiText(labelFont, TextRunPaintInfo(textRun), textPos); 197 198 RefPtr<Image> image = buffer->copyImage(); 199 return DragImage::create(image.get(), DoNotRespectImageOrientation, deviceScaleFactor); 200 } 201 202 DragImage::DragImage(const SkBitmap& bitmap, float resolutionScale) 203 : m_bitmap(bitmap) 204 , m_resolutionScale(resolutionScale) 205 { 206 } 207 208 DragImage::~DragImage() 209 { 210 } 211 212 void DragImage::fitToMaxSize(const IntSize& srcSize, const IntSize& maxSize) 213 { 214 float heightResizeRatio = 0.0f; 215 float widthResizeRatio = 0.0f; 216 float resizeRatio = -1.0f; 217 IntSize originalSize = size(); 218 219 if (srcSize.width() > maxSize.width()) { 220 widthResizeRatio = maxSize.width() / static_cast<float>(srcSize.width()); 221 resizeRatio = widthResizeRatio; 222 } 223 224 if (srcSize.height() > maxSize.height()) { 225 heightResizeRatio = maxSize.height() / static_cast<float>(srcSize.height()); 226 if ((resizeRatio < 0.0f) || (resizeRatio > heightResizeRatio)) 227 resizeRatio = heightResizeRatio; 228 } 229 230 if (srcSize == originalSize) { 231 if (resizeRatio > 0.0f) 232 scale(resizeRatio, resizeRatio); 233 return; 234 } 235 236 // The image was scaled in the webpage so at minimum we must account for that scaling 237 float scaleX = srcSize.width() / static_cast<float>(originalSize.width()); 238 float scaleY = srcSize.height() / static_cast<float>(originalSize.height()); 239 if (resizeRatio > 0.0f) { 240 scaleX *= resizeRatio; 241 scaleY *= resizeRatio; 242 } 243 244 scale(scaleX, scaleY); 245 } 246 247 void DragImage::scale(float scaleX, float scaleY) 248 { 249 int imageWidth = scaleX * m_bitmap.width(); 250 int imageHeight = scaleY * m_bitmap.height(); 251 m_bitmap = skia::ImageOperations::Resize( 252 m_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, imageWidth, imageHeight); 253 } 254 255 void DragImage::dissolveToFraction(float fraction) 256 { 257 m_bitmap.setAlphaType(kPremul_SkAlphaType); 258 SkAutoLockPixels lock(m_bitmap); 259 260 for (int row = 0; row < m_bitmap.height(); ++row) { 261 for (int column = 0; column < m_bitmap.width(); ++column) { 262 uint32_t* pixel = m_bitmap.getAddr32(column, row); 263 *pixel = SkPreMultiplyARGB( 264 SkColorGetA(*pixel) * fraction, 265 SkColorGetR(*pixel), 266 SkColorGetG(*pixel), 267 SkColorGetB(*pixel)); 268 } 269 } 270 } 271 272 } // namespace blink 273