1/* 2 * Copyright (C) 2010 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 INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "FindIndicator.h" 28 29#include "ShareableBitmap.h" 30#include <WebCore/GeometryUtilities.h> 31#include <WebCore/Gradient.h> 32#include <WebCore/GraphicsContext.h> 33#include <WebCore/IntRect.h> 34#include <WebCore/Path.h> 35 36#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 37#define ENABLE_LEGACY_FIND_INDICATOR_STYLE 1 38#else 39#define ENABLE_LEGACY_FIND_INDICATOR_STYLE 0 40#endif 41 42using namespace WebCore; 43 44#if ENABLE(LEGACY_FIND_INDICATOR_STYLE) 45static const float cornerRadius = 3.0; 46 47static const float shadowOffsetX = 0.0; 48static const float shadowOffsetY = 1.0; 49static const float shadowBlurRadius = 3.0; 50 51static const int shadowRed = 0; 52static const int shadowGreen = 0; 53static const int shadowBlue = 0; 54static const int shadowAlpha = 204; 55 56static const float lightBorderThickness = 1.0; 57static const float horizontalPaddingInsideLightBorder = 3.0; 58static const float verticalPaddingInsideLightBorder = 1.0; 59 60static const float horizontalBorderInsideShadow = lightBorderThickness + horizontalPaddingInsideLightBorder; 61static const float verticalBorderInsideShadow = lightBorderThickness + verticalPaddingInsideLightBorder; 62 63static const float leftBorderThickness = horizontalBorderInsideShadow + shadowOffsetX + shadowBlurRadius / 2.0; 64static const float topBorderThickness = verticalBorderInsideShadow - shadowOffsetY + shadowBlurRadius / 2.0; 65static const float rightBorderThickness = horizontalBorderInsideShadow - shadowOffsetX + shadowBlurRadius / 2.0; 66static const float bottomBorderThickness = verticalBorderInsideShadow + shadowOffsetY + shadowBlurRadius / 2.0; 67 68static const float horizontalOutsetToCenterOfLightBorder = horizontalBorderInsideShadow - lightBorderThickness / 2.0; 69static const float verticalOutsetToCenterOfLightBorder = verticalBorderInsideShadow - lightBorderThickness / 2.0; 70 71static const int lightBorderRed = 245; 72static const int lightBorderGreen = 230; 73static const int lightBorderBlue = 0; 74static const int lightBorderAlpha = 255; 75 76static const int gradientDarkRed = 237; 77static const int gradientDarkGreen = 204; 78static const int gradientDarkBlue = 0; 79static const int gradientDarkAlpha = 255; 80 81static const int gradientLightRed = 242; 82static const int gradientLightGreen = 239; 83static const int gradientLightBlue = 0; 84static const int gradientLightAlpha = 255; 85#else 86const float flatStyleHorizontalBorder = 2; 87const float flatStyleVerticalBorder = 1; 88const float flatShadowOffsetX = 0; 89const float flatShadowOffsetY = 5; 90const float flatShadowBlurRadius = 25; 91const float flatRimShadowBlurRadius = 2; 92#endif 93 94namespace WebKit { 95 96PassRefPtr<FindIndicator> FindIndicator::create(const FloatRect& selectionRectInWindowCoordinates, const Vector<FloatRect>& textRectsInSelectionRectCoordinates, float contentImageScaleFactor, const ShareableBitmap::Handle& contentImageHandle) 97{ 98 RefPtr<ShareableBitmap> contentImage = ShareableBitmap::create(contentImageHandle); 99 if (!contentImage) 100 return 0; 101 ASSERT(contentImageScaleFactor != 1 || contentImage->size() == enclosingIntRect(selectionRectInWindowCoordinates).size()); 102 103 return adoptRef(new FindIndicator(selectionRectInWindowCoordinates, textRectsInSelectionRectCoordinates, contentImageScaleFactor, contentImage.release())); 104} 105 106static FloatRect inflateRect(const FloatRect& rect, float inflateX, float inflateY) 107{ 108 FloatRect inflatedRect = rect; 109 inflatedRect.inflateX(inflateX); 110 inflatedRect.inflateY(inflateY); 111 return inflatedRect; 112} 113 114static FloatRect outsetIndicatorRectIncludingShadow(const FloatRect rect) 115{ 116#if ENABLE(LEGACY_FIND_INDICATOR_STYLE) 117 FloatRect outsetRect = rect; 118 outsetRect.move(-leftBorderThickness, -topBorderThickness); 119 outsetRect.expand(leftBorderThickness + rightBorderThickness, topBorderThickness + bottomBorderThickness); 120 return outsetRect; 121#else 122 return inflateRect(rect, flatShadowBlurRadius + flatStyleHorizontalBorder, flatShadowBlurRadius + flatStyleVerticalBorder); 123#endif 124} 125 126static bool findIndicatorsForTextRectsOverlap(const Vector<FloatRect>& textRects) 127{ 128 size_t count = textRects.size(); 129 if (count <= 1) 130 return false; 131 132 Vector<FloatRect> indicatorRects; 133 indicatorRects.reserveInitialCapacity(count); 134 135 for (size_t i = 0; i < count; ++i) { 136 FloatRect indicatorRect = outsetIndicatorRectIncludingShadow(textRects[i]); 137 138 for (size_t j = indicatorRects.size(); j; ) { 139 --j; 140 if (indicatorRect.intersects(indicatorRects[j])) 141 return true; 142 } 143 144 indicatorRects.uncheckedAppend(indicatorRect); 145 } 146 147 return false; 148} 149 150FindIndicator::FindIndicator(const WebCore::FloatRect& selectionRectInWindowCoordinates, const Vector<WebCore::FloatRect>& textRectsInSelectionRectCoordinates, float contentImageScaleFactor, PassRefPtr<ShareableBitmap> contentImage) 151 : m_selectionRectInWindowCoordinates(selectionRectInWindowCoordinates) 152 , m_textRectsInSelectionRectCoordinates(textRectsInSelectionRectCoordinates) 153 , m_contentImageScaleFactor(contentImageScaleFactor) 154 , m_contentImage(contentImage) 155{ 156 if (findIndicatorsForTextRectsOverlap(m_textRectsInSelectionRectCoordinates)) { 157 m_textRectsInSelectionRectCoordinates[0] = unionRect(m_textRectsInSelectionRectCoordinates); 158 m_textRectsInSelectionRectCoordinates.shrink(1); 159 } 160} 161 162FindIndicator::~FindIndicator() 163{ 164} 165 166FloatRect FindIndicator::frameRect() const 167{ 168 return outsetIndicatorRectIncludingShadow(m_selectionRectInWindowCoordinates); 169} 170 171#if ENABLE(LEGACY_FIND_INDICATOR_STYLE) 172static inline Color lightBorderColor() 173{ 174 return Color(lightBorderRed, lightBorderGreen, lightBorderBlue, lightBorderAlpha); 175} 176 177static inline Color shadowColor() 178{ 179 return Color(shadowRed, shadowGreen, shadowBlue, shadowAlpha); 180} 181 182static inline Color gradientLightColor() 183{ 184 return Color(gradientLightRed, gradientLightGreen, gradientLightBlue, gradientLightAlpha); 185} 186 187static inline Color gradientDarkColor() 188{ 189 return Color(gradientDarkRed, gradientDarkGreen, gradientDarkBlue, gradientDarkAlpha); 190} 191 192static Path pathWithRoundedRect(const FloatRect& pathRect, float radius) 193{ 194 Path path; 195 path.addRoundedRect(pathRect, FloatSize(radius, radius)); 196 197 return path; 198} 199#else 200static inline Color flatHighlightColor() 201{ 202 return Color(255, 255, 0, 255); 203} 204 205static inline Color flatRimShadowColor() 206{ 207 return Color(0, 0, 0, 38); 208} 209 210static inline Color flatDropShadowColor() 211{ 212 return Color(0, 0, 0, 51); 213} 214#endif 215 216void FindIndicator::draw(GraphicsContext& graphicsContext, const IntRect& /*dirtyRect*/) 217{ 218#if ENABLE(LEGACY_FIND_INDICATOR_STYLE) 219 for (size_t i = 0; i < m_textRectsInSelectionRectCoordinates.size(); ++i) { 220 FloatRect textRect = m_textRectsInSelectionRectCoordinates[i]; 221 textRect.move(leftBorderThickness, topBorderThickness); 222 223 FloatRect outerPathRect = inflateRect(textRect, horizontalOutsetToCenterOfLightBorder, verticalOutsetToCenterOfLightBorder); 224 FloatRect innerPathRect = inflateRect(textRect, horizontalPaddingInsideLightBorder, verticalPaddingInsideLightBorder); 225 226 { 227 GraphicsContextStateSaver stateSaver(graphicsContext); 228 graphicsContext.setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, shadowColor(), ColorSpaceSRGB); 229 graphicsContext.setFillColor(lightBorderColor(), ColorSpaceDeviceRGB); 230 graphicsContext.fillPath(pathWithRoundedRect(outerPathRect, cornerRadius)); 231 } 232 233 { 234 GraphicsContextStateSaver stateSaver(graphicsContext); 235 graphicsContext.clip(pathWithRoundedRect(innerPathRect, cornerRadius)); 236 RefPtr<Gradient> gradient = Gradient::create(FloatPoint(innerPathRect.x(), innerPathRect.y()), FloatPoint(innerPathRect.x(), innerPathRect.maxY())); 237 gradient->addColorStop(0, gradientLightColor()); 238 gradient->addColorStop(1, gradientDarkColor()); 239 graphicsContext.setFillGradient(gradient); 240 graphicsContext.fillRect(outerPathRect); 241 } 242 243 { 244 GraphicsContextStateSaver stateSaver(graphicsContext); 245 graphicsContext.translate(FloatSize(roundf(leftBorderThickness), roundf(topBorderThickness))); 246 247 IntRect contentImageRect = enclosingIntRect(m_textRectsInSelectionRectCoordinates[i]); 248 m_contentImage->paint(graphicsContext, m_contentImageScaleFactor, contentImageRect.location(), contentImageRect); 249 } 250 } 251#else 252 for (auto& textRect : m_textRectsInSelectionRectCoordinates) { 253 FloatRect blurRect = textRect; 254 blurRect.move(flatShadowBlurRadius + flatStyleHorizontalBorder, flatShadowBlurRadius + flatStyleVerticalBorder); 255 FloatRect outerPathRect = inflateRect(blurRect, flatStyleHorizontalBorder, flatStyleVerticalBorder); 256 257 { 258 GraphicsContextStateSaver stateSaver(graphicsContext); 259 graphicsContext.setShadow(FloatSize(), flatRimShadowBlurRadius, flatRimShadowColor(), ColorSpaceSRGB); 260 graphicsContext.setFillColor(flatHighlightColor(), ColorSpaceSRGB); 261 graphicsContext.fillRect(outerPathRect); 262 graphicsContext.setShadow(FloatSize(flatShadowOffsetX, flatShadowOffsetY), flatShadowBlurRadius, flatDropShadowColor(), ColorSpaceSRGB); 263 graphicsContext.fillRect(outerPathRect); 264 } 265 266 { 267 GraphicsContextStateSaver stateSaver(graphicsContext); 268 graphicsContext.translate(FloatSize(flatShadowBlurRadius + flatStyleHorizontalBorder, flatShadowBlurRadius + flatStyleVerticalBorder)); 269 270 IntRect contentImageRect = enclosingIntRect(textRect); 271 m_contentImage->paint(graphicsContext, m_contentImageScaleFactor, contentImageRect.location(), contentImageRect); 272 } 273 } 274#endif 275} 276 277} // namespace WebKit 278