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