initial import
[vuplus_webkit] / 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 RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::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     RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::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 RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
668 {
669     m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
670     if (!m_layerImage)
671         return;
672
673     // Draw the rectangle with hole.
674     FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
675     FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
676
677     if (!ScratchBuffer::shared().matchesLastInsetShadow(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii)) {
678         // Draw shadow into a new ImageBuffer.
679         GraphicsContext* shadowContext = m_layerImage->context();
680         GraphicsContextStateSaver shadowStateSaver(*shadowContext);
681         shadowContext->clearRect(templateBounds);
682         shadowContext->setFillRule(RULE_EVENODD);
683         shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
684
685         Path path;
686         path.addRect(templateBounds);
687         if (radii.isZero())
688             path.addRect(templateHole);
689         else
690             path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
691
692         shadowContext->fillPath(path);
693
694         blurAndColorShadowBuffer(templateSize);
695     
696         ScratchBuffer::shared().setLastInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii);
697     }
698
699     FloatRect boundingRect = rect;
700     boundingRect.move(m_offset);
701
702     FloatRect destHoleRect = holeRect;
703     destHoleRect.move(m_offset);
704     FloatRect destHoleBounds = destHoleRect;
705     destHoleBounds.inflateX(edgeSize.width());
706     destHoleBounds.inflateY(edgeSize.height());
707
708     // Fill the external part of the shadow (which may be visible because of offset).
709     Path exteriorPath;
710     exteriorPath.addRect(boundingRect);
711     exteriorPath.addRect(destHoleBounds);
712
713     {
714         GraphicsContextStateSaver fillStateSaver(*graphicsContext);
715         graphicsContext->clearShadow();
716         graphicsContext->setFillRule(RULE_EVENODD);
717         graphicsContext->setFillColor(m_color, m_colorSpace);
718         graphicsContext->fillPath(exteriorPath);
719     }
720     
721     drawLayerPieces(graphicsContext, destHoleBounds, radii, edgeSize, templateSize, InnerShadow);
722
723     m_layerImage = 0;
724     ScratchBuffer::shared().scheduleScratchBufferPurge();
725 }
726
727 void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
728 {
729     m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
730     if (!m_layerImage)
731         return;
732
733     FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
734
735     if (!ScratchBuffer::shared().matchesLastShadow(m_blurRadius, m_color, m_colorSpace, templateShadow, radii)) {
736         // Draw shadow into the ImageBuffer.
737         GraphicsContext* shadowContext = m_layerImage->context();
738         GraphicsContextStateSaver shadowStateSaver(*shadowContext);
739
740         shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
741         shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
742         
743         if (radii.isZero())
744             shadowContext->fillRect(templateShadow);
745         else {
746             Path path;
747             path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
748             shadowContext->fillPath(path);
749         }
750
751         blurAndColorShadowBuffer(templateSize);
752
753         ScratchBuffer::shared().setLastShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii);
754     }
755
756     FloatRect shadowBounds = shadowedRect;
757     shadowBounds.move(m_offset.width(), m_offset.height());
758     shadowBounds.inflateX(edgeSize.width());
759     shadowBounds.inflateY(edgeSize.height());
760
761     drawLayerPieces(graphicsContext, shadowBounds, radii, edgeSize, templateSize, OuterShadow);
762
763     m_layerImage = 0;
764     ScratchBuffer::shared().scheduleScratchBufferPurge();
765 }
766
767 void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
768 {
769     const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
770
771     int leftSlice;
772     int rightSlice;
773     int topSlice;
774     int bottomSlice;
775     computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
776
777     int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
778     int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
779
780     if (direction == OuterShadow) {
781         FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
782         if (!shadowInterior.isEmpty()) {
783             GraphicsContextStateSaver stateSaver(*graphicsContext);
784             graphicsContext->setFillColor(m_color, m_colorSpace);
785             graphicsContext->clearShadow();
786             graphicsContext->fillRect(shadowInterior);
787         }
788     }
789
790     GraphicsContextStateSaver stateSaver(*graphicsContext);
791     graphicsContext->clearShadow();
792     graphicsContext->setFillColor(m_color, m_colorSpace);
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     // We reset the scratch buffer values here, because the buffer will no longer contain
878     // data from any previous rectangle or inset shadows drawn via the tiling path.
879     ScratchBuffer::shared().setLastShadowValues(FloatSize(), Color::black, ColorSpaceDeviceRGB, IntRect(), RoundedRect::Radii());
880     m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
881
882     GraphicsContext* shadowContext = m_layerImage->context();
883     shadowContext->save();
884
885     // Add a pixel to avoid later edge aliasing when rotated.
886     shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
887
888     shadowContext->translate(m_layerContextTranslation);
889     return shadowContext;
890 }
891
892 void ShadowBlur::endShadowLayer(GraphicsContext* context)
893 {
894     m_layerImage->context()->restore();
895
896     blurAndColorShadowBuffer(expandedIntSize(m_layerSize));
897     GraphicsContextStateSaver stateSave(*context);
898
899     context->clearShadow();
900     context->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, roundedIntPoint(m_layerOrigin), IntRect(0, 0, m_layerSize.width(), m_layerSize.height()), context->compositeOperation());
901
902     m_layerImage = 0;
903     ScratchBuffer::shared().scheduleScratchBufferPurge();
904 }
905
906 #if PLATFORM(QT) || USE(CAIRO)
907 bool ShadowBlur::mustUseShadowBlur(GraphicsContext* context) const
908 {
909     // We can't avoid ShadowBlur, since the shadow has blur.
910     if (type() == BlurShadow)
911         return true;
912     // We can avoid ShadowBlur and optimize, since we're not drawing on a
913     // canvas and box shadows are affected by the transformation matrix.
914     if (!shadowsIgnoreTransforms())
915         return false;
916     // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
917     if (context->getCTM().isIdentity())
918         return false;
919     // Otherwise, no chance avoiding ShadowBlur.
920     return true;
921 }
922 #endif
923
924 } // namespace WebCore