[Qt] Create ShadowBlur on demand.
[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 COMPUTER, 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 #include <wtf/UnusedParam.h>
41
42 using namespace std;
43
44 namespace WebCore {
45
46 enum {
47     leftLobe = 0,
48     rightLobe = 1
49 };
50
51 static inline int roundUpToMultipleOf32(int d)
52 {
53     return (1 + (d >> 5)) << 5;
54 }
55
56 // ShadowBlur needs a scratch image as the buffer for the blur filter.
57 // Instead of creating and destroying the buffer for every operation,
58 // we create a buffer which will be automatically purged via a timer.
59 class ScratchBuffer {
60     WTF_MAKE_FAST_ALLOCATED;
61 public:
62     ScratchBuffer()
63         : m_purgeTimer(this, &ScratchBuffer::timerFired)
64         , m_lastWasInset(false)
65 #if !ASSERT_DISABLED
66         , m_bufferInUse(false)
67 #endif
68     {
69     }
70     
71     ImageBuffer* getScratchBuffer(const IntSize& size)
72     {
73         ASSERT(!m_bufferInUse);
74 #if !ASSERT_DISABLED
75         m_bufferInUse = true;
76 #endif
77         // We do not need to recreate the buffer if the current buffer is large enough.
78         if (m_imageBuffer && m_imageBuffer->logicalSize().width() >= size.width() && m_imageBuffer->logicalSize().height() >= size.height())
79             return m_imageBuffer.get();
80
81         // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
82         IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
83
84         clearScratchBuffer();
85         m_imageBuffer = ImageBuffer::create(roundedSize, 1);
86         return m_imageBuffer.get();
87     }
88
89     bool setCachedShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedRect::Radii& radii, const FloatSize& layerSize)
90     {
91         if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && 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_lastColorSpace = colorSpace;
98         m_lastShadowRect = shadowRect;
99         m_lastRadii = radii;
100         m_lastLayerSize = layerSize;
101
102         return true;
103     }
104
105     bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedRect::Radii& radii)
106     {
107         if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii)
108             return false;
109
110         m_lastWasInset = true;
111         m_lastInsetBounds = bounds;
112         m_lastRadius = radius;
113         m_lastColor = color;
114         m_lastColorSpace = colorSpace;
115         m_lastShadowRect = shadowRect;
116         m_lastRadii = radii;
117
118         return true;
119     }
120
121     void scheduleScratchBufferPurge()
122     {
123 #if !ASSERT_DISABLED
124         m_bufferInUse = false;
125 #endif
126         if (m_purgeTimer.isActive())
127             m_purgeTimer.stop();
128
129         const double scratchBufferPurgeInterval = 2;
130         m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
131     }
132     
133     static ScratchBuffer& shared();
134
135 private:
136     void timerFired(Timer<ScratchBuffer>*)
137     {
138         clearScratchBuffer();
139     }
140     
141     void clearScratchBuffer()
142     {
143         m_imageBuffer = nullptr;
144         m_lastRadius = FloatSize();
145     }
146
147     OwnPtr<ImageBuffer> m_imageBuffer;
148     Timer<ScratchBuffer> m_purgeTimer;
149     
150     FloatRect m_lastInsetBounds;
151     FloatRect m_lastShadowRect;
152     RoundedRect::Radii m_lastRadii;
153     Color m_lastColor;
154     ColorSpace m_lastColorSpace;
155     FloatSize m_lastRadius;
156     bool m_lastWasInset;
157     FloatSize m_lastLayerSize;
158     
159 #if !ASSERT_DISABLED
160     bool m_bufferInUse;
161 #endif
162 };
163
164 ScratchBuffer& ScratchBuffer::shared()
165 {
166     DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
167     return scratchBuffer;
168 }
169
170 static const int templateSideLength = 1;
171
172 #if USE(CG)
173 static float radiusToLegacyRadius(float radius)
174 {
175     return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
176 }
177 #endif
178
179 ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
180     : m_color(color)
181     , m_colorSpace(colorSpace)
182     , m_blurRadius(radius)
183     , m_offset(offset)
184     , m_layerImage(0)
185     , m_shadowsIgnoreTransforms(false)
186 {
187     updateShadowBlurValues();
188 }
189
190 ShadowBlur::ShadowBlur(const GraphicsContextState& state)
191     : m_color(state.shadowColor)
192     , m_colorSpace(state.shadowColorSpace)
193     , m_blurRadius(state.shadowBlur, state.shadowBlur)
194     , m_offset(state.shadowOffset)
195     , m_layerImage(0)
196     , m_shadowsIgnoreTransforms(state.shadowsIgnoreTransforms)
197 {
198 #if USE(CG)
199     if (state.shadowsUseLegacyRadius) {
200         float shadowBlur = radiusToLegacyRadius(state.shadowBlur);
201         m_blurRadius = FloatSize(shadowBlur, shadowBlur);
202     }
203 #endif
204     updateShadowBlurValues();
205 }
206
207 ShadowBlur::ShadowBlur()
208     : m_type(NoShadow)
209     , m_blurRadius(0, 0)
210     , m_shadowsIgnoreTransforms(false)
211 {
212 }
213
214 void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace, bool ignoreTransforms)
215 {
216     m_blurRadius = radius;
217     m_offset = offset;
218     m_color = color;
219     m_colorSpace = colorSpace;
220     m_shadowsIgnoreTransforms = ignoreTransforms;
221
222     updateShadowBlurValues();
223 }
224
225 void ShadowBlur::updateShadowBlurValues()
226 {
227     // Limit blur radius to 128 to avoid lots of very expensive blurring.
228     m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128));
229
230     // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
231     if (!m_color.isValid() || !m_color.alpha()) {
232         // Can't paint the shadow with invalid or invisible color.
233         m_type = NoShadow;
234     } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) {
235         // Shadow is always blurred, even the offset is zero.
236         m_type = BlurShadow;
237     } else if (!m_offset.width() && !m_offset.height()) {
238         // Without blur and zero offset means the shadow is fully hidden.
239         m_type = NoShadow;
240     } else
241         m_type = SolidShadow;
242 }
243
244 // Instead of integer division, we use 17.15 for fixed-point division.
245 static const int blurSumShift = 15;
246
247 // Takes a two dimensional array with three rows and two columns for the lobes.
248 static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms)
249 {
250     int diameter;
251     if (shadowsIgnoreTransforms)
252         diameter = max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
253     else {
254         // http://dev.w3.org/csswg/css3-background/#box-shadow
255         // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
256         // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
257         // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
258         // so we apply a fudge factor to bring the radius down slightly.
259         float stdDev = blurRadius / 2;
260         const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
261         const float fudgeFactor = 0.88f;
262         diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
263     }
264
265     if (diameter & 1) {
266         // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
267         int lobeSize = (diameter - 1) / 2;
268         lobes[0][leftLobe] = lobeSize;
269         lobes[0][rightLobe] = lobeSize;
270         lobes[1][leftLobe] = lobeSize;
271         lobes[1][rightLobe] = lobeSize;
272         lobes[2][leftLobe] = lobeSize;
273         lobes[2][rightLobe] = lobeSize;
274     } else {
275         // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
276         // between the output pixel and the one to the left, the second one centered on the pixel
277         // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
278         int lobeSize = diameter / 2;
279         lobes[0][leftLobe] = lobeSize;
280         lobes[0][rightLobe] = lobeSize - 1;
281         lobes[1][leftLobe] = lobeSize - 1;
282         lobes[1][rightLobe] = lobeSize;
283         lobes[2][leftLobe] = lobeSize;
284         lobes[2][rightLobe] = lobeSize;
285     }
286 }
287
288 void ShadowBlur::clear()
289 {
290     m_type = NoShadow;
291     m_color = Color();
292     m_blurRadius = FloatSize();
293     m_offset = FloatSize();
294 }
295
296 void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
297 {
298     const int channels[4] = { 3, 0, 1, 3 };
299
300     int lobes[3][2]; // indexed by pass, and left/right lobe
301     calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms);
302
303     // First pass is horizontal.
304     int stride = 4;
305     int delta = rowStride;
306     int final = size.height();
307     int dim = size.width();
308
309     // Two stages: horizontal and vertical
310     for (int pass = 0; pass < 2; ++pass) {
311         unsigned char* pixels = imageData;
312         
313         if (!pass && !m_blurRadius.width())
314             final = 0; // Do no work if horizonal blur is zero.
315
316         for (int j = 0; j < final; ++j, pixels += delta) {
317             // For each step, we blur the alpha in a channel and store the result
318             // in another channel for the subsequent step.
319             // We use sliding window algorithm to accumulate the alpha values.
320             // This is much more efficient than computing the sum of each pixels
321             // covered by the box kernel size for each x.
322             for (int step = 0; step < 3; ++step) {
323                 int side1 = lobes[step][leftLobe];
324                 int side2 = lobes[step][rightLobe];
325                 int pixelCount = side1 + 1 + side2;
326                 int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
327                 int ofs = 1 + side2;
328                 int alpha1 = pixels[channels[step]];
329                 int alpha2 = pixels[(dim - 1) * stride + channels[step]];
330
331                 unsigned char* ptr = pixels + channels[step + 1];
332                 unsigned char* prev = pixels + stride + channels[step];
333                 unsigned char* next = pixels + ofs * stride + channels[step];
334
335                 int i;
336                 int sum = side1 * alpha1 + alpha1;
337                 int limit = (dim < side2 + 1) ? dim : side2 + 1;
338
339                 for (i = 1; i < limit; ++i, prev += stride)
340                     sum += *prev;
341
342                 if (limit <= side2)
343                     sum += (side2 - limit + 1) * alpha2;
344
345                 limit = (side1 < dim) ? side1 : dim;
346                 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
347                     *ptr = (sum * invCount) >> blurSumShift;
348                     sum += ((ofs < dim) ? *next : alpha2) - alpha1;
349                 }
350                 
351                 prev = pixels + channels[step];
352                 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
353                     *ptr = (sum * invCount) >> blurSumShift;
354                     sum += (*next) - (*prev);
355                 }
356                 
357                 for (; i < dim; ptr += stride, prev += stride, ++i) {
358                     *ptr = (sum * invCount) >> blurSumShift;
359                     sum += alpha2 - (*prev);
360                 }
361             }
362         }
363
364         // Last pass is vertical.
365         stride = rowStride;
366         delta = 4;
367         final = size.width();
368         dim = size.height();
369
370         if (!m_blurRadius.height())
371             break;
372
373         if (m_blurRadius.width() != m_blurRadius.height())
374             calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms);
375     }
376 }
377
378 void ShadowBlur::adjustBlurRadius(GraphicsContext* context)
379 {
380     if (!m_shadowsIgnoreTransforms)
381         return;
382
383     AffineTransform transform = context->getCTM();
384     m_blurRadius.scale(1 / static_cast<float>(transform.xScale()), 1 / static_cast<float>(transform.yScale()));
385 }
386
387 IntSize ShadowBlur::blurredEdgeSize() const
388 {
389     IntSize edgeSize = expandedIntSize(m_blurRadius);
390
391     // To avoid slowing down blurLayerImage() for radius == 1, we give it two empty pixels on each side.
392     if (edgeSize.width() == 1)
393         edgeSize.setWidth(2);
394
395     if (edgeSize.height() == 1)
396         edgeSize.setHeight(2);
397
398     return edgeSize;
399 }
400
401 IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
402 {
403     IntSize edgeSize = blurredEdgeSize();
404
405     // Calculate the destination of the blurred and/or transformed layer.
406     FloatRect layerRect;
407     IntSize inflation;
408
409     const AffineTransform transform = context->getCTM();
410     if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
411         FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
412         transformedPolygon.move(m_offset);
413         layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
414     } else {
415         layerRect = shadowedRect;
416         layerRect.move(m_offset);
417     }
418
419     // We expand the area by the blur radius to give extra space for the blur transition.
420     if (m_type == BlurShadow) {
421         layerRect.inflateX(edgeSize.width());
422         layerRect.inflateY(edgeSize.height());
423         inflation = edgeSize;
424     }
425
426     FloatRect unclippedLayerRect = layerRect;
427
428     if (!clipRect.contains(enclosingIntRect(layerRect))) {
429         // If we are totally outside the clip region, we aren't painting at all.
430         if (intersection(layerRect, clipRect).isEmpty())
431             return IntRect();
432
433         IntRect inflatedClip = clipRect;
434         // Pixels at the edges can be affected by pixels outside the buffer,
435         // so intersect with the clip inflated by the blur.
436         if (m_type == BlurShadow) {
437             inflatedClip.inflateX(edgeSize.width());
438             inflatedClip.inflateY(edgeSize.height());
439         } else {
440             // Enlarge the clipping area 1 pixel so that the fill does not
441             // bleed (due to antialiasing) even if the unaligned clip rect occurred
442             inflatedClip.inflateX(1);
443             inflatedClip.inflateY(1);
444         }
445         
446         layerRect.intersect(inflatedClip);
447     }
448
449     IntSize frameSize = inflation;
450     frameSize.scale(2);
451     m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize.width(), shadowedRect.height() + frameSize.height());
452     m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
453     m_layerSize = layerRect.size();
454
455     const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
456     const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
457
458     // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
459     // out region, set the origin accordingly to the full bounding rect's top-left corner.
460     float translationX = -shadowedRect.x() + inflation.width() - fabsf(clippedOut.width());
461     float translationY = -shadowedRect.y() + inflation.height() - fabsf(clippedOut.height());
462     m_layerContextTranslation = FloatSize(translationX, translationY);
463
464     return enclosingIntRect(layerRect);
465 }
466
467 void ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext)
468 {
469     if (!m_layerImage)
470         return;
471
472     GraphicsContextStateSaver stateSaver(*graphicsContext);
473
474     IntSize bufferSize = m_layerImage->internalSize();
475     if (bufferSize != m_layerSize) {
476         // The rect passed to clipToImageBuffer() has to be the size of the entire buffer,
477         // but we may not have cleared it all, so clip to the filled part first.
478         graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize));
479     }
480     graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize));
481     graphicsContext->setFillColor(m_color, m_colorSpace);
482
483     graphicsContext->clearShadow();
484     graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
485 }
486
487 static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const RoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
488 {
489     leftSlice = twiceRadius.width() + max(radii.topLeft().width(), radii.bottomLeft().width()); 
490     rightSlice = twiceRadius.width() + max(radii.topRight().width(), radii.bottomRight().width()); 
491
492     topSlice = twiceRadius.height() + max(radii.topLeft().height(), radii.topRight().height());
493     bottomSlice = twiceRadius.height() + max(radii.bottomLeft().height(), radii.bottomRight().height());
494 }
495
496 IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect::Radii& radii) const
497 {
498     const int templateSideLength = 1;
499
500     int leftSlice;
501     int rightSlice;
502     int topSlice;
503     int bottomSlice;
504     
505     IntSize blurExpansion = radiusPadding;
506     blurExpansion.scale(2);
507
508     computeSliceSizesFromRadii(blurExpansion, radii, leftSlice, rightSlice, topSlice, bottomSlice);
509     
510     return IntSize(templateSideLength + leftSlice + rightSlice,
511                    templateSideLength + topSlice + bottomSlice);
512 }
513
514 void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii)
515 {
516     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
517     if (layerRect.isEmpty())
518         return;
519
520     adjustBlurRadius(graphicsContext);
521
522     // drawRectShadowWithTiling does not work with rotations.
523     // https://bugs.webkit.org/show_bug.cgi?id=45042
524     if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
525         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
526         return;
527     }
528
529     IntSize edgeSize = blurredEdgeSize();
530     IntSize templateSize = this->templateSize(edgeSize, radii);
531
532     if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
533         || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
534         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
535         return;
536     }
537
538     drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize, edgeSize);
539 }
540
541 void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii)
542 {
543     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
544     if (layerRect.isEmpty())
545         return;
546
547     adjustBlurRadius(graphicsContext);
548
549     // drawInsetShadowWithTiling does not work with rotations.
550     // https://bugs.webkit.org/show_bug.cgi?id=45042
551     if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
552         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
553         return;
554     }
555
556     IntSize edgeSize = blurredEdgeSize();
557     IntSize templateSize = this->templateSize(edgeSize, holeRadii);
558
559     if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
560         || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
561         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
562         return;
563     }
564
565     drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize, edgeSize);
566 }
567
568 void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntRect& layerRect)
569 {
570     m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
571     if (!m_layerImage)
572         return;
573
574     FloatRect bufferRelativeShadowedRect = shadowedRect;
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, 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 (radii.isZero())
588             shadowContext->fillRect(shadowedRect);
589         else {
590             Path path;
591             path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
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 FloatRect& holeRect, const RoundedRect::Radii& holeRadii, 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;
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, holeRadii);
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 (holeRadii.isZero())
628             path.addRect(holeRect);
629         else
630             path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
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 FloatRect& holeRect, const RoundedRect::Radii& radii, 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, 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 (radii.isZero())
699             path.addRect(templateHole);
700         else
701             path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
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;
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, radii, edgeSize, templateSize, InnerShadow);
736
737     m_layerImage = 0;
738     ScratchBuffer::shared().scheduleScratchBufferPurge();
739 }
740
741 void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, 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, 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 (radii.isZero())
760             shadowContext->fillRect(templateShadow);
761         else {
762             Path path;
763             path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
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;
776     shadowBounds.move(offset);
777     shadowBounds.inflateX(edgeSize.width());
778     shadowBounds.inflateY(edgeSize.height());
779
780     drawLayerPieces(graphicsContext, shadowBounds, 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 RoundedRect::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(), RoundedRect::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, roundedIntPoint(m_layerOrigin), IntRect(0, 0, m_layerSize.width(), m_layerSize.height()), context->compositeOperation());
920
921     m_layerImage = 0;
922     ScratchBuffer::shared().scheduleScratchBufferPurge();
923 }
924
925 } // namespace WebCore