Make it more obvious when using an unaccelerated image buffer, and fix a few callers...
[WebKit-https.git] / Source / WebCore / platform / graphics / ShadowBlur.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Sencha, Inc. All rights reserved.
4  * Copyright (C) 2010 Igalia S.L. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6  * Copyright (C) 2013 Digia Plc. and/or its subsidiary(-ies).
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
28  */
29
30 #include "config.h"
31 #include "ShadowBlur.h"
32
33 #include "AffineTransform.h"
34 #include "FloatQuad.h"
35 #include "GraphicsContext.h"
36 #include "ImageBuffer.h"
37 #include "Timer.h"
38 #include <wtf/MathExtras.h>
39 #include <wtf/Noncopyable.h>
40
41 namespace WebCore {
42
43 enum {
44     leftLobe = 0,
45     rightLobe = 1
46 };
47
48 static inline int roundUpToMultipleOf32(int d)
49 {
50     return (1 + (d >> 5)) << 5;
51 }
52
53 // ShadowBlur needs a scratch image as the buffer for the blur filter.
54 // Instead of creating and destroying the buffer for every operation,
55 // we create a buffer which will be automatically purged via a timer.
56 class ScratchBuffer {
57     WTF_MAKE_FAST_ALLOCATED;
58 public:
59     ScratchBuffer()
60         : m_purgeTimer(*this, &ScratchBuffer::clearScratchBuffer)
61         , m_lastWasInset(false)
62 #if !ASSERT_DISABLED
63         , m_bufferInUse(false)
64 #endif
65     {
66     }
67     
68     ImageBuffer* getScratchBuffer(const IntSize& size)
69     {
70         ASSERT(!m_bufferInUse);
71 #if !ASSERT_DISABLED
72         m_bufferInUse = true;
73 #endif
74         // We do not need to recreate the buffer if the current buffer is large enough.
75         if (m_imageBuffer && m_imageBuffer->logicalSize().width() >= size.width() && m_imageBuffer->logicalSize().height() >= size.height())
76             return m_imageBuffer.get();
77
78         // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
79         IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
80
81         clearScratchBuffer();
82
83         // ShadowBlur is not used with accelerated drawing, so it's OK to make an unconditionally unaccelerated buffer.
84         m_imageBuffer = ImageBuffer::create(roundedSize, Unaccelerated, 1);
85         return m_imageBuffer.get();
86     }
87
88     bool setCachedShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii, const FloatSize& layerSize)
89     {
90         if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastShadowRect == shadowRect &&  m_lastRadii == radii && m_lastLayerSize == layerSize)
91             return false;
92
93         m_lastWasInset = false;
94         m_lastRadius = radius;
95         m_lastColor = color;
96         m_lastColorSpace = colorSpace;
97         m_lastShadowRect = shadowRect;
98         m_lastRadii = radii;
99         m_lastLayerSize = layerSize;
100
101         return true;
102     }
103
104     bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii)
105     {
106         if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii)
107             return false;
108
109         m_lastWasInset = true;
110         m_lastInsetBounds = bounds;
111         m_lastRadius = radius;
112         m_lastColor = color;
113         m_lastColorSpace = colorSpace;
114         m_lastShadowRect = shadowRect;
115         m_lastRadii = radii;
116
117         return true;
118     }
119
120     void scheduleScratchBufferPurge()
121     {
122 #if !ASSERT_DISABLED
123         m_bufferInUse = false;
124 #endif
125         if (m_purgeTimer.isActive())
126             m_purgeTimer.stop();
127
128         const double scratchBufferPurgeInterval = 2;
129         m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
130     }
131     
132     static ScratchBuffer& singleton();
133
134 private:
135     void clearScratchBuffer()
136     {
137         m_imageBuffer = nullptr;
138         m_lastRadius = FloatSize();
139         m_lastLayerSize = FloatSize();
140     }
141
142     std::unique_ptr<ImageBuffer> m_imageBuffer;
143     Timer m_purgeTimer;
144     
145     FloatRect m_lastInsetBounds;
146     FloatRect m_lastShadowRect;
147     FloatRoundedRect::Radii m_lastRadii;
148     Color m_lastColor;
149     ColorSpace m_lastColorSpace;
150     FloatSize m_lastRadius;
151     bool m_lastWasInset;
152     FloatSize m_lastLayerSize;
153     
154 #if !ASSERT_DISABLED
155     bool m_bufferInUse;
156 #endif
157 };
158
159 ScratchBuffer& ScratchBuffer::singleton()
160 {
161     DEPRECATED_DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
162     return scratchBuffer;
163 }
164
165 static const int templateSideLength = 1;
166
167 #if USE(CG)
168 static float radiusToLegacyRadius(float radius)
169 {
170     return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
171 }
172 #endif
173
174 ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
175     : m_color(color)
176     , m_colorSpace(colorSpace)
177     , m_blurRadius(radius)
178     , m_offset(offset)
179     , m_layerImage(0)
180     , m_shadowsIgnoreTransforms(false)
181 {
182     updateShadowBlurValues();
183 }
184
185 ShadowBlur::ShadowBlur(const GraphicsContextState& state)
186     : m_color(state.shadowColor)
187     , m_colorSpace(state.shadowColorSpace)
188     , m_blurRadius(state.shadowBlur, state.shadowBlur)
189     , m_offset(state.shadowOffset)
190     , m_layerImage(0)
191     , m_shadowsIgnoreTransforms(state.shadowsIgnoreTransforms)
192 {
193 #if USE(CG)
194     if (state.shadowsUseLegacyRadius) {
195         float shadowBlur = radiusToLegacyRadius(state.shadowBlur);
196         m_blurRadius = FloatSize(shadowBlur, shadowBlur);
197     }
198 #endif
199     updateShadowBlurValues();
200 }
201
202 ShadowBlur::ShadowBlur()
203     : m_type(NoShadow)
204     , m_blurRadius(0, 0)
205     , m_shadowsIgnoreTransforms(false)
206 {
207 }
208
209 void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace, bool ignoreTransforms)
210 {
211     m_blurRadius = radius;
212     m_offset = offset;
213     m_color = color;
214     m_colorSpace = colorSpace;
215     m_shadowsIgnoreTransforms = ignoreTransforms;
216
217     updateShadowBlurValues();
218 }
219
220 void ShadowBlur::updateShadowBlurValues()
221 {
222     // Limit blur radius to 128 to avoid lots of very expensive blurring.
223     m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128));
224
225     // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
226     if (!m_color.isValid() || !m_color.alpha()) {
227         // Can't paint the shadow with invalid or invisible color.
228         m_type = NoShadow;
229     } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) {
230         // Shadow is always blurred, even the offset is zero.
231         m_type = BlurShadow;
232     } else if (!m_offset.width() && !m_offset.height()) {
233         // Without blur and zero offset means the shadow is fully hidden.
234         m_type = NoShadow;
235     } else
236         m_type = SolidShadow;
237 }
238
239 // Instead of integer division, we use 17.15 for fixed-point division.
240 static const int blurSumShift = 15;
241
242 // Takes a two dimensional array with three rows and two columns for the lobes.
243 static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms)
244 {
245     int diameter;
246     if (shadowsIgnoreTransforms)
247         diameter = std::max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
248     else {
249         // http://dev.w3.org/csswg/css3-background/#box-shadow
250         // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
251         // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
252         // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
253         // so we apply a fudge factor to bring the radius down slightly.
254         float stdDev = blurRadius / 2;
255         const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
256         const float fudgeFactor = 0.88f;
257         diameter = std::max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
258     }
259
260     if (diameter & 1) {
261         // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
262         int lobeSize = (diameter - 1) / 2;
263         lobes[0][leftLobe] = lobeSize;
264         lobes[0][rightLobe] = lobeSize;
265         lobes[1][leftLobe] = lobeSize;
266         lobes[1][rightLobe] = lobeSize;
267         lobes[2][leftLobe] = lobeSize;
268         lobes[2][rightLobe] = lobeSize;
269     } else {
270         // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
271         // between the output pixel and the one to the left, the second one centered on the pixel
272         // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
273         int lobeSize = diameter / 2;
274         lobes[0][leftLobe] = lobeSize;
275         lobes[0][rightLobe] = lobeSize - 1;
276         lobes[1][leftLobe] = lobeSize - 1;
277         lobes[1][rightLobe] = lobeSize;
278         lobes[2][leftLobe] = lobeSize;
279         lobes[2][rightLobe] = lobeSize;
280     }
281 }
282
283 void ShadowBlur::clear()
284 {
285     m_type = NoShadow;
286     m_color = Color();
287     m_blurRadius = FloatSize();
288     m_offset = FloatSize();
289 }
290
291 void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
292 {
293     const int channels[4] = { 3, 0, 1, 3 };
294
295     int lobes[3][2]; // indexed by pass, and left/right lobe
296     calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms);
297
298     // First pass is horizontal.
299     int stride = 4;
300     int delta = rowStride;
301     int final = size.height();
302     int dim = size.width();
303
304     // Two stages: horizontal and vertical
305     for (int pass = 0; pass < 2; ++pass) {
306         unsigned char* pixels = imageData;
307         
308         if (!pass && !m_blurRadius.width())
309             final = 0; // Do no work if horizonal blur is zero.
310
311         for (int j = 0; j < final; ++j, pixels += delta) {
312             // For each step, we blur the alpha in a channel and store the result
313             // in another channel for the subsequent step.
314             // We use sliding window algorithm to accumulate the alpha values.
315             // This is much more efficient than computing the sum of each pixels
316             // covered by the box kernel size for each x.
317             for (int step = 0; step < 3; ++step) {
318                 int side1 = lobes[step][leftLobe];
319                 int side2 = lobes[step][rightLobe];
320                 int pixelCount = side1 + 1 + side2;
321                 int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
322                 int ofs = 1 + side2;
323                 int alpha1 = pixels[channels[step]];
324                 int alpha2 = pixels[(dim - 1) * stride + channels[step]];
325
326                 unsigned char* ptr = pixels + channels[step + 1];
327                 unsigned char* prev = pixels + stride + channels[step];
328                 unsigned char* next = pixels + ofs * stride + channels[step];
329
330                 int i;
331                 int sum = side1 * alpha1 + alpha1;
332                 int limit = (dim < side2 + 1) ? dim : side2 + 1;
333
334                 for (i = 1; i < limit; ++i, prev += stride)
335                     sum += *prev;
336
337                 if (limit <= side2)
338                     sum += (side2 - limit + 1) * alpha2;
339
340                 limit = (side1 < dim) ? side1 : dim;
341                 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
342                     *ptr = (sum * invCount) >> blurSumShift;
343                     sum += ((ofs < dim) ? *next : alpha2) - alpha1;
344                 }
345                 
346                 prev = pixels + channels[step];
347                 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
348                     *ptr = (sum * invCount) >> blurSumShift;
349                     sum += (*next) - (*prev);
350                 }
351                 
352                 for (; i < dim; ptr += stride, prev += stride, ++i) {
353                     *ptr = (sum * invCount) >> blurSumShift;
354                     sum += alpha2 - (*prev);
355                 }
356             }
357         }
358
359         // Last pass is vertical.
360         stride = rowStride;
361         delta = 4;
362         final = size.width();
363         dim = size.height();
364
365         if (!m_blurRadius.height())
366             break;
367
368         if (m_blurRadius.width() != m_blurRadius.height())
369             calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms);
370     }
371 }
372
373 void ShadowBlur::adjustBlurRadius(GraphicsContext& context)
374 {
375     if (!m_shadowsIgnoreTransforms)
376         return;
377
378     AffineTransform transform = context.getCTM();
379     m_blurRadius.scale(1 / static_cast<float>(transform.xScale()), 1 / static_cast<float>(transform.yScale()));
380 }
381
382 IntSize ShadowBlur::blurredEdgeSize() const
383 {
384     IntSize edgeSize = expandedIntSize(m_blurRadius);
385
386     // To avoid slowing down blurLayerImage() for radius == 1, we give it two empty pixels on each side.
387     if (edgeSize.width() == 1)
388         edgeSize.setWidth(2);
389
390     if (edgeSize.height() == 1)
391         edgeSize.setHeight(2);
392
393     return edgeSize;
394 }
395
396 IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext& context, const FloatRect& shadowedRect, const IntRect& clipRect)
397 {
398     IntSize edgeSize = blurredEdgeSize();
399
400     // Calculate the destination of the blurred and/or transformed layer.
401     FloatRect layerRect;
402     IntSize inflation;
403
404     const AffineTransform transform = context.getCTM();
405     if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
406         FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
407         transformedPolygon.move(m_offset);
408         layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
409     } else {
410         layerRect = shadowedRect;
411         layerRect.move(m_offset);
412     }
413
414     // We expand the area by the blur radius to give extra space for the blur transition.
415     if (m_type == BlurShadow) {
416         layerRect.inflateX(edgeSize.width());
417         layerRect.inflateY(edgeSize.height());
418         inflation = edgeSize;
419     }
420
421     FloatRect unclippedLayerRect = layerRect;
422
423     if (!clipRect.contains(enclosingIntRect(layerRect))) {
424         // If we are totally outside the clip region, we aren't painting at all.
425         if (intersection(layerRect, clipRect).isEmpty())
426             return IntRect();
427
428         IntRect inflatedClip = clipRect;
429         // Pixels at the edges can be affected by pixels outside the buffer,
430         // so intersect with the clip inflated by the blur.
431         if (m_type == BlurShadow) {
432             inflatedClip.inflateX(edgeSize.width());
433             inflatedClip.inflateY(edgeSize.height());
434         } else {
435             // Enlarge the clipping area 1 pixel so that the fill does not
436             // bleed (due to antialiasing) even if the unaligned clip rect occurred
437             inflatedClip.inflateX(1);
438             inflatedClip.inflateY(1);
439         }
440         
441         layerRect.intersect(inflatedClip);
442     }
443
444     IntSize frameSize = inflation;
445     frameSize.scale(2);
446     m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize.width(), shadowedRect.height() + frameSize.height());
447     m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
448     m_layerSize = layerRect.size();
449
450     const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
451     const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
452
453     // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
454     // out region, set the origin accordingly to the full bounding rect's top-left corner.
455     float translationX = -shadowedRect.x() + inflation.width() - fabsf(clippedOut.width());
456     float translationY = -shadowedRect.y() + inflation.height() - fabsf(clippedOut.height());
457     m_layerContextTranslation = FloatSize(translationX, translationY);
458
459     return enclosingIntRect(layerRect);
460 }
461
462 void ShadowBlur::drawShadowBuffer(GraphicsContext& graphicsContext)
463 {
464     if (!m_layerImage)
465         return;
466
467     GraphicsContextStateSaver stateSaver(graphicsContext);
468
469     IntSize bufferSize = m_layerImage->internalSize();
470     if (bufferSize != m_layerSize) {
471         // The rect passed to clipToImageBuffer() has to be the size of the entire buffer,
472         // but we may not have cleared it all, so clip to the filled part first.
473         graphicsContext.clip(FloatRect(m_layerOrigin, m_layerSize));
474     }
475     graphicsContext.clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize));
476     graphicsContext.setFillColor(m_color, m_colorSpace);
477
478     graphicsContext.clearShadow();
479     graphicsContext.fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
480 }
481
482 static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const FloatRoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
483 {
484     leftSlice = twiceRadius.width() + std::max(radii.topLeft().width(), radii.bottomLeft().width());
485     rightSlice = twiceRadius.width() + std::max(radii.topRight().width(), radii.bottomRight().width());
486
487     topSlice = twiceRadius.height() + std::max(radii.topLeft().height(), radii.topRight().height());
488     bottomSlice = twiceRadius.height() + std::max(radii.bottomLeft().height(), radii.bottomRight().height());
489 }
490
491 IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const FloatRoundedRect::Radii& radii) const
492 {
493     const int templateSideLength = 1;
494
495     int leftSlice;
496     int rightSlice;
497     int topSlice;
498     int bottomSlice;
499     
500     IntSize blurExpansion = radiusPadding;
501     blurExpansion.scale(2);
502
503     computeSliceSizesFromRadii(blurExpansion, radii, leftSlice, rightSlice, topSlice, bottomSlice);
504     
505     return IntSize(templateSideLength + leftSlice + rightSlice,
506                    templateSideLength + topSlice + bottomSlice);
507 }
508
509 void ShadowBlur::drawRectShadow(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect)
510 {
511     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect.rect(), graphicsContext.clipBounds());
512     if (layerRect.isEmpty())
513         return;
514
515     adjustBlurRadius(graphicsContext);
516
517     // drawRectShadowWithTiling does not work with rotations.
518     // https://bugs.webkit.org/show_bug.cgi?id=45042
519     if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
520         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerRect);
521         return;
522     }
523
524     IntSize edgeSize = blurredEdgeSize();
525     IntSize templateSize = this->templateSize(edgeSize, shadowedRect.radii());
526     const FloatRect& rect = shadowedRect.rect();
527
528     if (templateSize.width() > rect.width() || templateSize.height() > rect.height()
529         || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
530         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerRect);
531         return;
532     }
533
534     drawRectShadowWithTiling(graphicsContext, shadowedRect, templateSize, edgeSize);
535 }
536
537 void ShadowBlur::drawInsetShadow(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect)
538 {
539     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext.clipBounds());
540     if (layerRect.isEmpty())
541         return;
542
543     adjustBlurRadius(graphicsContext);
544
545     // drawInsetShadowWithTiling does not work with rotations.
546     // https://bugs.webkit.org/show_bug.cgi?id=45042
547     if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
548         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerRect);
549         return;
550     }
551
552     IntSize edgeSize = blurredEdgeSize();
553     IntSize templateSize = this->templateSize(edgeSize, holeRect.radii());
554     const FloatRect& hRect = holeRect.rect();
555
556     if (templateSize.width() > hRect.width() || templateSize.height() > hRect.height()
557         || (templateSize.width() * templateSize.height() > hRect.width() * hRect.height())) {
558         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerRect);
559         return;
560     }
561
562     drawInsetShadowWithTiling(graphicsContext, rect, holeRect, templateSize, edgeSize);
563 }
564
565 void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntRect& layerRect)
566 {
567     m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerRect.size());
568     if (!m_layerImage)
569         return;
570
571     FloatRect bufferRelativeShadowedRect = shadowedRect.rect();
572     bufferRelativeShadowedRect.move(m_layerContextTranslation);
573
574     // Only redraw in the scratch buffer if its cached contents don't match our needs
575     bool redrawNeeded = ScratchBuffer::singleton().setCachedShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, shadowedRect.radii(), m_layerSize);
576     if (redrawNeeded) {
577         GraphicsContext& shadowContext = m_layerImage->context();
578         GraphicsContextStateSaver stateSaver(shadowContext);
579
580         // Add a pixel to avoid later edge aliasing when rotated.
581         shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
582         shadowContext.translate(m_layerContextTranslation);
583         shadowContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
584         if (shadowedRect.radii().isZero())
585             shadowContext.fillRect(shadowedRect.rect());
586         else {
587             Path path;
588             path.addRoundedRect(shadowedRect);
589             shadowContext.fillPath(path);
590         }
591
592         blurShadowBuffer(expandedIntSize(m_layerSize));
593     }
594     
595     drawShadowBuffer(graphicsContext);
596     m_layerImage = 0;
597     ScratchBuffer::singleton().scheduleScratchBufferPurge();
598 }
599
600 void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntRect& layerRect)
601 {
602     m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerRect.size());
603     if (!m_layerImage)
604         return;
605
606     FloatRect bufferRelativeRect = rect;
607     bufferRelativeRect.move(m_layerContextTranslation);
608
609     FloatRect bufferRelativeHoleRect = holeRect.rect();
610     bufferRelativeHoleRect.move(m_layerContextTranslation);
611
612     // Only redraw in the scratch buffer if its cached contents don't match our needs
613     bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRect.radii());
614     if (redrawNeeded) {
615         GraphicsContext& shadowContext = m_layerImage->context();
616         GraphicsContextStateSaver stateSaver(shadowContext);
617
618         // Add a pixel to avoid later edge aliasing when rotated.
619         shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
620         shadowContext.translate(m_layerContextTranslation);
621
622         Path path;
623         path.addRect(rect);
624         if (holeRect.radii().isZero())
625             path.addRect(holeRect.rect());
626         else
627             path.addRoundedRect(holeRect);
628
629         shadowContext.setFillRule(RULE_EVENODD);
630         shadowContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
631         shadowContext.fillPath(path);
632
633         blurShadowBuffer(expandedIntSize(m_layerSize));
634     }
635     
636     drawShadowBuffer(graphicsContext);
637     m_layerImage = 0;
638     ScratchBuffer::singleton().scheduleScratchBufferPurge();
639 }
640
641 /*
642   These functions use tiling to improve the performance of the shadow
643   drawing of rounded rectangles. The code basically does the following
644   steps:
645
646      1. Calculate the size of the shadow template, a rectangle that
647      contains all the necessary tiles to draw the complete shadow.
648
649      2. If that size is smaller than the real rectangle render the new
650      template rectangle and its shadow in a new surface, in other case
651      render the shadow of the real rectangle in the destination
652      surface.
653
654      3. Calculate the sizes and positions of the tiles and their
655      destinations and use drawPattern to render the final shadow. The
656      code divides the rendering in 8 tiles:
657
658         1 | 2 | 3
659        -----------
660         4 |   | 5
661        -----------
662         6 | 7 | 8
663
664      The corners are directly copied from the template rectangle to the
665      real one and the side tiles are 1 pixel width, we use them as
666      tiles to cover the destination side. The corner tiles are bigger
667      than just the side of the rounded corner, we need to increase it
668      because the modifications caused by the corner over the blur
669      effect. We fill the central or outer part with solid color to complete
670      the shadow.
671  */
672
673 void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& templateSize, const IntSize& edgeSize)
674 {
675     m_layerImage = ScratchBuffer::singleton().getScratchBuffer(templateSize);
676     if (!m_layerImage)
677         return;
678
679     // Draw the rectangle with hole.
680     FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
681     FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
682
683     // Only redraw in the scratch buffer if its cached contents don't match our needs
684     bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, holeRect.radii());
685     if (redrawNeeded) {
686         // Draw shadow into a new ImageBuffer.
687         GraphicsContext& shadowContext = m_layerImage->context();
688         GraphicsContextStateSaver shadowStateSaver(shadowContext);
689         shadowContext.clearRect(templateBounds);
690         shadowContext.setFillRule(RULE_EVENODD);
691         shadowContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
692
693         Path path;
694         path.addRect(templateBounds);
695         if (holeRect.radii().isZero())
696             path.addRect(templateHole);
697         else
698             path.addRoundedRect(FloatRoundedRect(templateHole, holeRect.radii()));
699
700         shadowContext.fillPath(path);
701
702         blurAndColorShadowBuffer(templateSize);
703     }
704     FloatSize offset = m_offset;
705     if (shadowsIgnoreTransforms()) {
706         AffineTransform transform = graphicsContext.getCTM();
707         offset.scale(1 / transform.xScale(), 1 / transform.yScale());
708     }
709
710     FloatRect boundingRect = rect;
711     boundingRect.move(offset);
712
713     FloatRect destHoleRect = holeRect.rect();
714     destHoleRect.move(offset);
715     FloatRect destHoleBounds = destHoleRect;
716     destHoleBounds.inflateX(edgeSize.width());
717     destHoleBounds.inflateY(edgeSize.height());
718
719     // Fill the external part of the shadow (which may be visible because of offset).
720     Path exteriorPath;
721     exteriorPath.addRect(boundingRect);
722     exteriorPath.addRect(destHoleBounds);
723
724     {
725         GraphicsContextStateSaver fillStateSaver(graphicsContext);
726         graphicsContext.setFillRule(RULE_EVENODD);
727         graphicsContext.setFillColor(m_color, m_colorSpace);
728         graphicsContext.clearShadow();
729         graphicsContext.fillPath(exteriorPath);
730     }
731     
732     drawLayerPieces(graphicsContext, destHoleBounds, holeRect.radii(), edgeSize, templateSize, InnerShadow);
733
734     m_layerImage = 0;
735     ScratchBuffer::singleton().scheduleScratchBufferPurge();
736 }
737
738 void ShadowBlur::drawRectShadowWithTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& templateSize, const IntSize& edgeSize)
739 {
740     auto& scratchBuffer = ScratchBuffer::singleton();
741     m_layerImage = scratchBuffer.getScratchBuffer(templateSize);
742     if (!m_layerImage)
743         return;
744
745     FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
746
747     // Only redraw in the scratch buffer if its cached contents don't match our needs
748     bool redrawNeeded = scratchBuffer.setCachedShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, shadowedRect.radii(), m_layerSize);
749     if (redrawNeeded) {
750         // Draw shadow into the ImageBuffer.
751         GraphicsContext& shadowContext = m_layerImage->context();
752         GraphicsContextStateSaver shadowStateSaver(shadowContext);
753
754         shadowContext.clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
755         shadowContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
756         
757         if (shadowedRect.radii().isZero())
758             shadowContext.fillRect(templateShadow);
759         else {
760             Path path;
761             path.addRoundedRect(FloatRoundedRect(templateShadow, shadowedRect.radii()));
762             shadowContext.fillPath(path);
763         }
764
765         blurAndColorShadowBuffer(templateSize);
766     }
767     FloatSize offset = m_offset;
768     if (shadowsIgnoreTransforms()) {
769         AffineTransform transform = graphicsContext.getCTM();
770         offset.scale(1 / transform.xScale(), 1 / transform.yScale());
771     }
772
773     FloatRect shadowBounds = shadowedRect.rect();
774     shadowBounds.move(offset);
775     shadowBounds.inflateX(edgeSize.width());
776     shadowBounds.inflateY(edgeSize.height());
777
778     drawLayerPieces(graphicsContext, shadowBounds, shadowedRect.radii(), edgeSize, templateSize, OuterShadow);
779
780     m_layerImage = 0;
781     ScratchBuffer::singleton().scheduleScratchBufferPurge();
782 }
783
784 void ShadowBlur::drawLayerPieces(GraphicsContext& graphicsContext, const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
785 {
786     const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
787
788     int leftSlice;
789     int rightSlice;
790     int topSlice;
791     int bottomSlice;
792     computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
793
794     int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
795     int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
796
797     if (direction == OuterShadow) {
798         FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
799         if (!shadowInterior.isEmpty()) {
800             GraphicsContextStateSaver stateSaver(graphicsContext);
801             graphicsContext.setFillColor(m_color, m_colorSpace);
802             graphicsContext.clearShadow();
803             graphicsContext.fillRect(shadowInterior);
804         }
805     }
806
807     GraphicsContextStateSaver stateSaver(graphicsContext);
808     graphicsContext.setFillColor(m_color, m_colorSpace);
809     graphicsContext.clearShadow();
810
811     // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
812     // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
813     FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
814     centerRect = graphicsContext.roundToDevicePixels(centerRect);
815     
816     // Top side.
817     FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
818     FloatRect destRect = FloatRect(centerRect.x(), centerRect.y() - topSlice, centerRect.width(), topSlice);
819     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
820
821     // Draw the bottom side.
822     tileRect.setY(templateSize.height() - bottomSlice);
823     tileRect.setHeight(bottomSlice);
824     destRect.setY(centerRect.maxY());
825     destRect.setHeight(bottomSlice);
826     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
827
828     // Left side.
829     tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
830     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y(), leftSlice, centerRect.height());
831     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
832
833     // Right side.
834     tileRect.setX(templateSize.width() - rightSlice);
835     tileRect.setWidth(rightSlice);
836     destRect.setX(centerRect.maxX());
837     destRect.setWidth(rightSlice);
838     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
839
840     // Top left corner.
841     tileRect = FloatRect(0, 0, leftSlice, topSlice);
842     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y() - topSlice, leftSlice, topSlice);
843     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
844
845     // Top right corner.
846     tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
847     destRect = FloatRect(centerRect.maxX(), centerRect.y() - topSlice, rightSlice, topSlice);
848     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
849
850     // Bottom right corner.
851     tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
852     destRect = FloatRect(centerRect.maxX(), centerRect.maxY(), rightSlice, bottomSlice);
853     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
854
855     // Bottom left corner.
856     tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
857     destRect = FloatRect(centerRect.x() - leftSlice, centerRect.maxY(), leftSlice, bottomSlice);
858     graphicsContext.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
859 }
860
861
862 void ShadowBlur::blurShadowBuffer(const IntSize& templateSize)
863 {
864     if (m_type != BlurShadow)
865         return;
866
867     IntRect blurRect(IntPoint(), templateSize);
868     RefPtr<Uint8ClampedArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
869     blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
870     m_layerImage->putByteArray(Unmultiplied, layerData.get(), blurRect.size(), blurRect, IntPoint());
871 }
872
873 void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
874 {
875     blurShadowBuffer(templateSize);
876
877     // Mask the image with the shadow color.
878     GraphicsContext& shadowContext = m_layerImage->context();
879     GraphicsContextStateSaver stateSaver(shadowContext);
880     shadowContext.setCompositeOperation(CompositeSourceIn);
881     shadowContext.setFillColor(m_color, m_colorSpace);
882     shadowContext.fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
883 }
884
885 GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext& context, const FloatRect& layerArea)
886 {
887     adjustBlurRadius(context);
888
889     IntRect layerRect = calculateLayerBoundingRect(context, layerArea, context.clipBounds());
890
891     if (layerRect.isEmpty())
892         return nullptr;
893
894     // We reset the scratch buffer values here, because the buffer will no longer contain
895     // data from any previous rectangle or inset shadows drawn via the tiling path.
896     auto& scratchBuffer = ScratchBuffer::singleton();
897     scratchBuffer.setCachedShadowValues(FloatSize(), Color::black, ColorSpaceDeviceRGB, IntRect(), FloatRoundedRect::Radii(), m_layerSize);
898     m_layerImage = scratchBuffer.getScratchBuffer(layerRect.size());
899
900     GraphicsContext& shadowContext = m_layerImage->context();
901     shadowContext.save();
902
903     // Add a pixel to avoid later edge aliasing when rotated.
904     shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
905
906     shadowContext.translate(m_layerContextTranslation);
907     return &shadowContext;
908 }
909
910 void ShadowBlur::endShadowLayer(GraphicsContext& context)
911 {
912     m_layerImage->context().restore();
913
914     blurAndColorShadowBuffer(expandedIntSize(m_layerSize));
915     GraphicsContextStateSaver stateSave(context);
916
917     context.clearShadow();
918     context.drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, FloatRect(roundedIntPoint(m_layerOrigin), m_layerSize), FloatRect(FloatPoint(), m_layerSize), context.compositeOperation());
919
920     m_layerImage = 0;
921     ScratchBuffer::singleton().scheduleScratchBufferPurge();
922 }
923
924 } // namespace WebCore