2011-02-09 Simon Fraser <simon.fraser@apple.com>
[WebKit-https.git] / Source / WebCore / platform / graphics / ShadowBlur.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc.
3  * Copyright (C) 2010 Sencha, Inc.
4  * Copyright (C) 2010 Igalia S.L.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
27  */
28
29 #include "config.h"
30 #include "ShadowBlur.h"
31
32 #include "AffineTransform.h"
33 #include "FloatQuad.h"
34 #include "GraphicsContext.h"
35 #include "ImageBuffer.h"
36 #include "Timer.h"
37 #include <wtf/MathExtras.h>
38 #include <wtf/Noncopyable.h>
39 #include <wtf/UnusedParam.h>
40
41 using namespace std;
42
43 namespace WebCore {
44
45 static inline int roundUpToMultipleOf32(int d)
46 {
47     return (1 + (d >> 5)) << 5;
48 }
49
50 // ShadowBlur needs a scratch image as the buffer for the blur filter.
51 // Instead of creating and destroying the buffer for every operation,
52 // we create a buffer which will be automatically purged via a timer.
53 class ScratchBuffer {
54 public:
55     ScratchBuffer()
56         : m_purgeTimer(this, &ScratchBuffer::timerFired)
57 #if !ASSERT_DISABLED
58         , m_bufferInUse(false)
59 #endif
60     {
61     }
62     
63     ImageBuffer* getScratchBuffer(const IntSize& size)
64     {
65         ASSERT(!m_bufferInUse);
66 #if !ASSERT_DISABLED
67         m_bufferInUse = true;
68 #endif
69         // We do not need to recreate the buffer if the current buffer is large enough.
70         if (m_imageBuffer && m_imageBuffer->width() >= size.width() && m_imageBuffer->height() >= size.height())
71             return m_imageBuffer.get();
72
73         // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
74         IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
75
76         m_imageBuffer = ImageBuffer::create(roundedSize);
77         return m_imageBuffer.get();
78     }
79
80     void scheduleScratchBufferPurge()
81     {
82 #if !ASSERT_DISABLED
83         m_bufferInUse = false;
84 #endif
85         if (m_purgeTimer.isActive())
86             m_purgeTimer.stop();
87
88         const double scratchBufferPurgeInterval = 2;
89         m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
90     }
91     
92     static ScratchBuffer& shared();
93
94 private:
95     void timerFired(Timer<ScratchBuffer>*)
96     {
97         clearScratchBuffer();
98     }
99     
100     void clearScratchBuffer()
101     {
102         m_imageBuffer = 0;
103     }
104
105     OwnPtr<ImageBuffer> m_imageBuffer;
106     Timer<ScratchBuffer> m_purgeTimer;
107 #if !ASSERT_DISABLED
108     bool m_bufferInUse;
109 #endif
110 };
111
112 ScratchBuffer& ScratchBuffer::shared()
113 {
114     DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
115     return scratchBuffer;
116 }
117
118 static const int templateSideLength = 1;
119
120 ShadowBlur::ShadowBlur(float radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
121     : m_color(color)
122     , m_colorSpace(colorSpace)
123     , m_blurRadius(radius)
124     , m_offset(offset)
125     , m_layerImage(0)
126     , m_shadowsIgnoreTransforms(false)
127 {
128     // Limit blur radius to 128 to avoid lots of very expensive blurring.
129     m_blurRadius = min<float>(m_blurRadius, 128);
130
131     // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
132     if (!m_color.isValid() || !color.alpha()) {
133         // Can't paint the shadow with invalid or invisible color.
134         m_type = NoShadow;
135     } else if (m_blurRadius > 0) {
136         // Shadow is always blurred, even the offset is zero.
137         m_type = BlurShadow;
138     } else if (!m_offset.width() && !m_offset.height()) {
139         // Without blur and zero offset means the shadow is fully hidden.
140         m_type = NoShadow;
141     } else
142         m_type = SolidShadow;
143 }
144
145 // Instead of integer division, we use 17.15 for fixed-point division.
146 static const int blurSumShift = 15;
147 static const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
148
149 void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
150 {
151     const int channels[4] =
152 #if CPU(BIG_ENDIAN)
153         { 0, 3, 2, 0 };
154 #elif CPU(MIDDLE_ENDIAN)
155         { 1, 2, 3, 1 };
156 #else
157         { 3, 0, 1, 3 };
158 #endif
159
160     int diameter;
161     if (m_shadowsIgnoreTransforms)
162         diameter = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
163     else {
164         // http://dev.w3.org/csswg/css3-background/#box-shadow
165         // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
166         // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
167         // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
168         // so we apply a fudge factor to bring the radius down slightly.
169         float stdDev = m_blurRadius / 2;
170         const float fudgeFactor = 0.88f;
171         diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
172     }
173
174     enum {
175         leftLobe = 0,
176         rightLobe = 1
177     };
178
179     int lobes[3][2]; // indexed by pass, and left/right lobe
180     
181     if (diameter & 1) {
182         // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
183         int lobeSize = (diameter - 1) / 2;
184         lobes[0][leftLobe] = lobeSize;
185         lobes[0][rightLobe] = lobeSize;
186         lobes[1][leftLobe] = lobeSize;
187         lobes[1][rightLobe] = lobeSize;
188         lobes[2][leftLobe] = lobeSize;
189         lobes[2][rightLobe] = lobeSize;
190     } else {
191         // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
192         // between the output pixel and the one to the left, the second one centered on the pixel
193         // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
194         int lobeSize = diameter / 2;
195         lobes[0][leftLobe] = lobeSize;
196         lobes[0][rightLobe] = lobeSize - 1;
197         lobes[1][leftLobe] = lobeSize - 1;
198         lobes[1][rightLobe] = lobeSize;
199         lobes[2][leftLobe] = lobeSize;
200         lobes[2][rightLobe] = lobeSize;
201     }
202
203     // First pass is horizontal.
204     int stride = 4;
205     int delta = rowStride;
206     int final = size.height();
207     int dim = size.width();
208
209     // Two stages: horizontal and vertical
210     for (int pass = 0; pass < 2; ++pass) {
211         unsigned char* pixels = imageData;
212
213         for (int j = 0; j < final; ++j, pixels += delta) {
214             // For each step, we blur the alpha in a channel and store the result
215             // in another channel for the subsequent step.
216             // We use sliding window algorithm to accumulate the alpha values.
217             // This is much more efficient than computing the sum of each pixels
218             // covered by the box kernel size for each x.
219             for (int step = 0; step < 3; ++step) {
220                 int side1 = lobes[step][leftLobe];
221                 int side2 = lobes[step][rightLobe];
222                 int pixelCount = side1 + 1 + side2;
223                 int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
224                 int ofs = 1 + side2;
225                 int alpha1 = pixels[channels[step]];
226                 int alpha2 = pixels[(dim - 1) * stride + channels[step]];
227
228                 unsigned char* ptr = pixels + channels[step + 1];
229                 unsigned char* prev = pixels + stride + channels[step];
230                 unsigned char* next = pixels + ofs * stride + channels[step];
231
232                 int i;
233                 int sum = side1 * alpha1 + alpha1;
234                 int limit = (dim < side2 + 1) ? dim : side2 + 1;
235
236                 for (i = 1; i < limit; ++i, prev += stride)
237                     sum += *prev;
238
239                 if (limit <= side2)
240                     sum += (side2 - limit + 1) * alpha2;
241
242                 limit = (side1 < dim) ? side1 : dim;
243                 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
244                     *ptr = (sum * invCount) >> blurSumShift;
245                     sum += ((ofs < dim) ? *next : alpha2) - alpha1;
246                 }
247                 
248                 prev = pixels + channels[step];
249                 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
250                     *ptr = (sum * invCount) >> blurSumShift;
251                     sum += (*next) - (*prev);
252                 }
253                 
254                 for (; i < dim; ptr += stride, prev += stride, ++i) {
255                     *ptr = (sum * invCount) >> blurSumShift;
256                     sum += alpha2 - (*prev);
257                 }
258             }
259         }
260
261         // Last pass is vertical.
262         stride = rowStride;
263         delta = 4;
264         final = size.width();
265         dim = size.height();
266     }
267 }
268
269 void ShadowBlur::adjustBlurRadius(GraphicsContext* context)
270 {
271     if (!m_shadowsIgnoreTransforms)
272         return;
273
274     const AffineTransform transform = context->getCTM();
275
276     // Adjust blur if we're scaling, since the radius must not be affected by transformations.
277     // FIXME: use AffineTransform::isIdentityOrTranslationOrFlipped()?
278     if (transform.isIdentity())
279         return;
280
281     // Calculate transformed unit vectors.
282     const FloatQuad unitQuad(FloatPoint(0, 0), FloatPoint(1, 0),
283                              FloatPoint(0, 1), FloatPoint(1, 1));
284     const FloatQuad transformedUnitQuad = transform.mapQuad(unitQuad);
285
286     // Calculate X axis scale factor.
287     const FloatSize xUnitChange = transformedUnitQuad.p2() - transformedUnitQuad.p1();
288     const float xAxisScale = sqrtf(xUnitChange.width() * xUnitChange.width()
289                                    + xUnitChange.height() * xUnitChange.height());
290
291     // Calculate Y axis scale factor.
292     const FloatSize yUnitChange = transformedUnitQuad.p3() - transformedUnitQuad.p1();
293     const float yAxisScale = sqrtf(yUnitChange.width() * yUnitChange.width()
294                                    + yUnitChange.height() * yUnitChange.height());
295
296     // blurLayerImage() does not support per-axis blurring, so calculate a balanced scaling.
297     // FIXME: does AffineTransform.xScale()/yScale() help?
298     const float scale = sqrtf(xAxisScale * yAxisScale);
299     m_blurRadius = roundf(m_blurRadius / scale);
300 }
301
302 IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
303 {
304     const float roundedRadius = ceilf(m_blurRadius);
305
306     // Calculate the destination of the blurred and/or transformed layer.
307     FloatRect layerRect;
308     float inflation = 0;
309
310     const AffineTransform transform = context->getCTM();
311     if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
312         FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
313         transformedPolygon.move(m_offset);
314         layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
315     } else {
316         layerRect = shadowedRect;
317         layerRect.move(m_offset);
318     }
319
320     // We expand the area by the blur radius to give extra space for the blur transition.
321     if (m_type == BlurShadow) {
322         layerRect.inflate(roundedRadius);
323         inflation = roundedRadius;
324     }
325
326     FloatRect unclippedLayerRect = layerRect;
327
328     if (!clipRect.contains(enclosingIntRect(layerRect))) {
329         // If we are totally outside the clip region, we aren't painting at all.
330         if (intersection(layerRect, clipRect).isEmpty())
331             return IntRect();
332
333         IntRect inflatedClip = clipRect;
334         // Pixels at the edges can be affected by pixels outside the buffer,
335         // so intersect with the clip inflated by the blur.
336         if (m_type == BlurShadow)
337             inflatedClip.inflate(roundedRadius);
338         
339         layerRect.intersect(inflatedClip);
340     }
341
342     const float frameSize = inflation * 2;
343     m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize, shadowedRect.height() + frameSize);
344     m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
345     m_layerSize = layerRect.size();
346
347     const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
348     const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
349
350     // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
351     // out region, set the origin accordingly to the full bounding rect's top-left corner.
352     float translationX = -shadowedRect.x() + inflation - fabsf(clippedOut.width());
353     float translationY = -shadowedRect.y() + inflation - fabsf(clippedOut.height());
354     m_layerContextTranslation = FloatSize(translationX, translationY);
355
356     return enclosingIntRect(layerRect);
357 }
358
359 GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext* graphicsContext, const IntRect& layerRect)
360 {
361     adjustBlurRadius(graphicsContext);
362
363     // Don't paint if we are totally outside the clip region.
364     if (layerRect.isEmpty())
365         return 0;
366
367     m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
368     GraphicsContext* layerContext = m_layerImage->context();
369
370     layerContext->save(); // Balanced by restore() in endShadowLayer().
371
372     // Always clear the surface first. FIXME: we could avoid the clear on first allocation.
373     // Add a pixel to avoid later edge aliasing when rotated.
374     layerContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
375     layerContext->translate(m_layerContextTranslation);
376
377     return layerContext;
378 }
379
380 void ShadowBlur::endShadowLayer(GraphicsContext* graphicsContext)
381 {
382     if (!m_layerImage)
383         return;
384         
385     m_layerImage->context()->restore();
386
387     if (m_type == BlurShadow) {
388         IntRect blurRect = enclosingIntRect(FloatRect(FloatPoint(), m_layerSize));
389         RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
390         blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
391         m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
392     }
393
394     graphicsContext->save();
395
396     graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, m_layerImage->size()));
397     graphicsContext->setFillColor(m_color, m_colorSpace);
398
399     graphicsContext->clearShadow();
400     graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
401     
402     graphicsContext->restore();
403
404     m_layerImage = 0;
405
406     // Schedule a purge of the scratch buffer. We do not need to destroy the surface.
407     ScratchBuffer::shared().scheduleScratchBufferPurge();
408 }
409
410 static void computeSliceSizesFromRadii(int twiceRadius, const RoundedIntRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
411 {
412     leftSlice = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width()); 
413     rightSlice = twiceRadius + max(radii.topRight().width(), radii.bottomRight().width()); 
414
415     topSlice = twiceRadius + max(radii.topLeft().height(), radii.topRight().height());
416     bottomSlice = twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height());
417 }
418
419 IntSize ShadowBlur::templateSize(const RoundedIntRect::Radii& radii) const
420 {
421     const int templateSideLength = 1;
422
423     int leftSlice;
424     int rightSlice;
425     int topSlice;
426     int bottomSlice;
427     computeSliceSizesFromRadii(2 * ceilf(m_blurRadius), radii, leftSlice, rightSlice, topSlice, bottomSlice);
428     
429     return IntSize(templateSideLength + leftSlice + rightSlice,
430                    templateSideLength + topSlice + bottomSlice);
431 }
432
433 void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii)
434 {
435     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
436     if (layerRect.isEmpty())
437         return;
438
439     // drawRectShadowWithTiling does not work with rotations.
440     // https://bugs.webkit.org/show_bug.cgi?id=45042
441     if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
442         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
443         return;
444     }
445
446     IntSize templateSize = this->templateSize(radii);
447
448     if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
449         || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
450         drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
451         return;
452     }
453
454     drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize);
455 }
456
457 void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii)
458 {
459     IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
460     if (layerRect.isEmpty())
461         return;
462
463     // drawInsetShadowWithTiling does not work with rotations.
464     // https://bugs.webkit.org/show_bug.cgi?id=45042
465     if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
466         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
467         return;
468     }
469
470     IntSize templateSize = this->templateSize(holeRadii);
471
472     if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
473         || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
474         drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
475         return;
476     }
477
478     drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize);
479 }
480
481 void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect)
482 {
483     GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect);
484     if (!shadowContext)
485         return;
486
487     Path path;
488     path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
489
490     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
491     shadowContext->fillPath(path);
492     
493     endShadowLayer(graphicsContext);
494 }
495
496 void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii, const IntRect& layerRect)
497 {
498     GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect);
499     if (!shadowContext)
500         return;
501
502     Path path;
503     path.addRect(rect);
504     path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
505
506     shadowContext->setFillRule(RULE_EVENODD);
507     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
508     shadowContext->fillPath(path);
509
510     endShadowLayer(graphicsContext);
511 }
512
513 /*
514   These functions use tiling to improve the performance of the shadow
515   drawing of rounded rectangles. The code basically does the following
516   steps:
517
518      1. Calculate the size of the shadow template, a rectangle that
519      contains all the necessary tiles to draw the complete shadow.
520
521      2. If that size is smaller than the real rectangle render the new
522      template rectangle and its shadow in a new surface, in other case
523      render the shadow of the real rectangle in the destination
524      surface.
525
526      3. Calculate the sizes and positions of the tiles and their
527      destinations and use drawPattern to render the final shadow. The
528      code divides the rendering in 8 tiles:
529
530         1 | 2 | 3
531        -----------
532         4 |   | 5
533        -----------
534         6 | 7 | 8
535
536      The corners are directly copied from the template rectangle to the
537      real one and the side tiles are 1 pixel width, we use them as
538      tiles to cover the destination side. The corner tiles are bigger
539      than just the side of the rounded corner, we need to increase it
540      because the modifications caused by the corner over the blur
541      effect. We fill the central or outer part with solid color to complete
542      the shadow.
543  */
544
545 void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
546 {
547     graphicsContext->save();
548     graphicsContext->clearShadow();
549
550     const float roundedRadius = ceilf(m_blurRadius);
551     const float twiceRadius = roundedRadius * 2;
552
553     m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
554
555     // Draw the rectangle with hole.
556     FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
557     FloatRect templateHole = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
558     Path path;
559     path.addRect(templateBounds);
560     path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
561
562     // Draw shadow into a new ImageBuffer.
563     GraphicsContext* shadowContext = m_layerImage->context();
564     shadowContext->save();
565     shadowContext->clearRect(templateBounds);
566     shadowContext->setFillRule(RULE_EVENODD);
567     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
568     shadowContext->fillPath(path);
569     blurAndColorShadowBuffer(templateSize);
570     shadowContext->restore();
571
572     FloatRect boundingRect = rect;
573     boundingRect.move(m_offset);
574
575     FloatRect destHoleRect = holeRect;
576     destHoleRect.move(m_offset);
577     FloatRect destHoleBounds = destHoleRect;
578     destHoleBounds.inflate(roundedRadius);
579
580     // Fill the external part of the shadow (which may be visible because of offset).
581     Path exteriorPath;
582     exteriorPath.addRect(boundingRect);
583     exteriorPath.addRect(destHoleBounds);
584
585     graphicsContext->save();
586     graphicsContext->setFillRule(RULE_EVENODD);
587     graphicsContext->setFillColor(m_color, m_colorSpace);
588     graphicsContext->fillPath(exteriorPath);
589     graphicsContext->restore();
590     
591     drawLayerPieces(graphicsContext, destHoleBounds, radii, roundedRadius, templateSize, InnerShadow);
592
593     graphicsContext->restore();
594
595     m_layerImage = 0;
596     // Schedule a purge of the scratch buffer.
597     ScratchBuffer::shared().scheduleScratchBufferPurge();
598 }
599
600 void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
601 {
602     graphicsContext->save();
603     graphicsContext->clearShadow();
604
605     const float roundedRadius = ceilf(m_blurRadius);
606     const float twiceRadius = roundedRadius * 2;
607
608     m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
609
610     // Draw the rectangle.
611     FloatRect templateShadow = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
612     Path path;
613     path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
614
615     // Draw shadow into the ImageBuffer.
616     GraphicsContext* shadowContext = m_layerImage->context();
617     shadowContext->save();
618     shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
619     shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
620     shadowContext->fillPath(path);
621     blurAndColorShadowBuffer(templateSize);
622     shadowContext->restore();
623
624     FloatRect shadowBounds = shadowedRect;
625     shadowBounds.move(m_offset.width(), m_offset.height());
626     shadowBounds.inflate(roundedRadius);
627
628     drawLayerPieces(graphicsContext, shadowBounds, radii, roundedRadius, templateSize, OuterShadow);
629
630     graphicsContext->restore();
631
632     m_layerImage = 0;
633     // Schedule a purge of the scratch buffer.
634     ScratchBuffer::shared().scheduleScratchBufferPurge();
635 }
636
637 void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedIntRect::Radii& radii, float roundedRadius, const IntSize& templateSize, ShadowDirection direction)
638 {
639     const float twiceRadius = roundedRadius * 2;
640
641     int leftSlice;
642     int rightSlice;
643     int topSlice;
644     int bottomSlice;
645     computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
646
647     int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
648     int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
649
650     if (direction == OuterShadow) {
651         FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
652         if (!shadowInterior.isEmpty()) {
653             graphicsContext->save();
654             
655             graphicsContext->setFillColor(m_color, m_colorSpace);
656             graphicsContext->fillRect(shadowInterior);
657             
658             graphicsContext->restore();
659         }
660     }
661
662     // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
663     // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
664     
665     // Top side.
666     FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
667     FloatRect destRect = FloatRect(shadowBounds.x() + leftSlice, shadowBounds.y(), centerWidth, topSlice);
668     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
669
670     // Draw the bottom side.
671     tileRect.setY(templateSize.height() - bottomSlice);
672     tileRect.setHeight(bottomSlice);
673     destRect.setY(shadowBounds.maxY() - bottomSlice);
674     destRect.setHeight(bottomSlice);
675     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
676
677     // Left side.
678     tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
679     destRect = FloatRect(shadowBounds.x(), shadowBounds.y() + topSlice, leftSlice, centerHeight);
680     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
681
682     // Right side.
683     tileRect.setX(templateSize.width() - rightSlice);
684     tileRect.setWidth(rightSlice);
685     destRect.setX(shadowBounds.maxX() - rightSlice);
686     destRect.setWidth(rightSlice);
687     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
688
689     // Top left corner.
690     tileRect = FloatRect(0, 0, leftSlice, topSlice);
691     destRect = FloatRect(shadowBounds.x(), shadowBounds.y(), leftSlice, topSlice);
692     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
693
694     // Top right corner.
695     tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
696     destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.y(), rightSlice, topSlice);
697     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
698
699     // Bottom right corner.
700     tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
701     destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.maxY() - bottomSlice, rightSlice, bottomSlice);
702     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
703
704     // Bottom left corner.
705     tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
706     destRect = FloatRect(shadowBounds.x(), shadowBounds.maxY() - bottomSlice, leftSlice, bottomSlice);
707     graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
708 }
709
710
711 void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
712 {
713     {
714         IntRect blurRect(IntPoint(), templateSize);
715         RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
716         blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
717         m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
718     }
719
720     // Mask the image with the shadow color.
721     GraphicsContext* shadowContext = m_layerImage->context();
722     shadowContext->setCompositeOperation(CompositeSourceIn);
723     shadowContext->setFillColor(m_color, m_colorSpace);
724     shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
725 }
726
727 } // namespace WebCore