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