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