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