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