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