tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebCore / platform / graphics / ContextShadow.cpp
1 /*
2  * Copyright (C) 2010 Sencha, Inc.
3  * Copyright (C) 2010 Igalia S.L.
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 "ContextShadow.h"
31
32 #include "AffineTransform.h"
33 #include "FloatQuad.h"
34 #include "GraphicsContext.h"
35 #include <cmath>
36 #include <wtf/MathExtras.h>
37 #include <wtf/Noncopyable.h>
38
39 #if ENABLE(TIZEN_CONTEXTSHADOW_BLUR_NEON)
40 #include "ShadowBlurNEON.h"
41 #endif
42
43 using WTF::min;
44 using WTF::max;
45
46 namespace WebCore {
47
48 ContextShadow::ContextShadow()
49     : m_type(NoShadow)
50     , m_blurDistance(0)
51     , m_layerContext(0)
52     , m_shadowsIgnoreTransforms(false)
53 {
54 }
55
56 ContextShadow::ContextShadow(const Color& color, float radius, const FloatSize& offset)
57     : m_color(color)
58     , m_blurDistance(round(radius))
59     , m_offset(offset)
60     , m_layerContext(0)
61     , m_shadowsIgnoreTransforms(false)
62 {
63     // See comments in http://webkit.org/b/40793, it seems sensible
64     // to follow Skia's limit of 128 pixels of blur radius
65     m_blurDistance = min(m_blurDistance, 128);
66
67     // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
68     if (!m_color.isValid() || !color.alpha()) {
69         // Can't paint the shadow with invalid or invisible color.
70         m_type = NoShadow;
71     } else if (radius > 0) {
72         // Shadow is always blurred, even the offset is zero.
73         m_type = BlurShadow;
74     } else if (!m_offset.width() && !m_offset.height()) {
75         // Without blur and zero offset means the shadow is fully hidden.
76         m_type = NoShadow;
77     } else {
78         m_type = SolidShadow;
79     }
80 }
81
82 void ContextShadow::clear()
83 {
84     m_type = NoShadow;
85     m_color = Color();
86     m_blurDistance = 0;
87     m_offset = FloatSize();
88 }
89
90 bool ContextShadow::mustUseContextShadow(GraphicsContext* context)
91 {
92     // We can't avoid ContextShadow, since the shadow has blur.
93     if (m_type == ContextShadow::BlurShadow)
94         return true;
95     // We can avoid ContextShadow and optimize, since we're not drawing on a
96     // canvas and box shadows are affected by the transformation matrix.
97     if (!shadowsIgnoreTransforms())
98         return false;
99     // We can avoid ContextShadow, since there are no transformations to apply to the canvas.
100     if (context->getCTM().isIdentity())
101         return false;
102     // Otherwise, no chance avoiding ContextShadow.
103     return true;
104 }
105
106 // Instead of integer division, we use 17.15 for fixed-point division.
107 static const int BlurSumShift = 15;
108
109 #if ENABLE(TIZEN_CONTEXTSHADOW_BLUR_NEON)
110 #if CPU(BIG_ENDIAN)
111 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
112     // Load
113     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16,
114     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16,
115     2, 16, 16, 16,    6, 16, 16, 16,    10, 16, 16, 16,    14, 16, 16, 16,
116     // Store
117     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28,
118     0, 1, 16, 3,    4, 5, 20, 7,    8, 9, 24, 11,    12, 13, 28, 15,
119     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15
120 };
121 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
122     // Load
123     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16,
124     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16, // don't care
125     2, 16, 16, 16,    6, 16, 16, 16,    10, 16, 16, 16,    14, 16, 16, 16, // don't care
126     // Store
127     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28,
128     0, 1, 16, 3,    4, 5, 20, 7,    8, 9, 24, 11,    12, 13, 28, 15, // don't caree
129     16, 1, 2 , 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15 // don't care
130 };
131 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
132     // Load
133     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16,
134     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16, // don't care
135     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16, // don't care
136     // Store
137     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15,
138     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28, // don't caree
139     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15 // don't care
140 };
141 #elif CPU(MIDDLE_ENDIAN)
142 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
143     // Load
144     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16,
145     2, 16, 16, 16,    6, 16, 16, 16,    10, 16, 16, 16,    14, 16, 16, 16,
146     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16
147     // Store
148     0, 1, 16, 3,    4, 5, 20, 7,    8, 9, 24, 11,    12, 13, 28, 15,
149     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28,
150     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15
151 };
152 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
153     // Load
154     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16,
155     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16, // don't care
156     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16, // don't care
157     // Store
158     0, 1, 16, 3,    4, 5, 20, 7,    8, 9, 24, 11,    12, 13, 28, 15,
159     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15, // don't care
160     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28 // don't care
161 };
162 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
163     // Load
164     2, 16, 16, 16,    6, 16, 16, 16,    10, 16, 16, 16,    14, 16, 16, 16,
165     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16, // don't care
166     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16, // don't care
167     // Store
168     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15,
169     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15, // don't care
170     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15 // don't care
171 };
172 #else
173 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
174     // Load
175     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16,
176     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16,
177     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16,
178     // Store
179     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15,
180     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15,
181     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28
182 };
183 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
184     // Load
185     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16,
186     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16, // don't care
187     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16, // don't care
188     // Store
189     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15,
190     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15, // don't care
191     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28 // don't care
192 };
193 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
194     // Load
195     0, 16, 16, 16,    4, 16, 16, 16,    8, 16, 16, 16,    12, 16, 16, 16,
196     3, 16, 16, 16,    7, 16, 16, 16,    11, 16, 16, 16,    15, 16, 16, 16, // don't care
197     1, 16, 16, 16,    5, 16, 16, 16,    9, 16, 16, 16,    13, 16, 16, 16, // don't care
198     // Store
199     0, 1, 2, 16,    4, 5, 6, 20,    8, 9, 10, 24,    12, 13, 14, 28,
200     16, 1, 2, 3,    20, 5, 6, 7,    24, 9, 10, 11,    28, 13, 14, 15, // don't care
201     0, 16, 2, 3,    4, 20, 6, 7,    8, 24, 10, 11,    12, 28, 14, 15 // don't care
202 };
203 #endif
204
205 struct BoxBlurData {
206     int dmax;
207     int dmin;
208     int stride;
209     int strideWidth;
210     int delta;
211     int jfinal;
212     int dim;
213     int remainingStrides;
214     int stepCount;
215     unsigned char* channels;
216 };
217 #endif
218
219 // Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlur.
220 // As noted in the SVG filter specification, running box blur 3x
221 // approximates a real gaussian blur nicely.
222 void ContextShadow::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
223 {
224 #if ENABLE(TIZEN_CONTEXTSHADOW_BLUR_NEON)
225     int stepCount = (m_blurDistance < 3) ? 1 : 3; // apply only 1-step box blur for small radius cases (radius < 3) instead of full 3-step box blurs
226     int d = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurDistance)));
227     int dmax = d >> 1;
228     int dmin = dmax - 1 + (d & 1);
229     if (dmin < 0)
230         dmin = 0;
231
232     // Two stages: horizontal and vertical
233     for (int k = 0; k < 2; ++k) {
234         unsigned char* pixels = imageData;
235         int stride = (!k) ? 4 : rowStride;
236         int delta = (!k) ? rowStride : 4;
237         int jfinal = (!k) ? size.height() : size.width();
238         int dim = (!k) ? size.width() : size.height();
239         int remainingStrides = jfinal % 4;
240         jfinal = (jfinal >> 2) << 2;
241         int strideWidth = (!k) ? dim * 4 : stride * dim * 4;
242
243         unsigned char* remapConstantsForStepLoadStore = remapConstantsForStepLoadStoreNomal;
244         if (stepCount == 1) {
245             remapConstantsForStepLoadStore = (!k) ?
246                     remapConstantsForStepLoadStore1StepOnlyForHorizontal : remapConstantsForStepLoadStore1StepOnlyForVertical;
247         }
248         BoxBlurData arguments = {
249             dmax,
250             dmin,
251             stride,
252             strideWidth,
253             delta,
254             jfinal,
255             dim,
256             remainingStrides,
257             stepCount,
258             remapConstantsForStepLoadStore
259         };
260         boxBlurNeon(pixels, &arguments);
261     }
262 #else
263 #if CPU(BIG_ENDIAN)
264     int channelsNormalMode[4] = { 0, 3, 2, 0 };
265     int channelsOnestepModeX[4] = { 0, 3, 3, 3 };
266     int channelsOnestepModeY[4] = { 3, 0, 0, 0 };
267 #elif CPU(MIDDLE_ENDIAN)
268     int channelsNormalMode[4] = { 1, 2, 3, 1 };
269     int channelsOnestepModeX[4] = { 1, 2, 2, 2 };
270     int channelsOnestepModeY[4] = { 2, 1, 1, 1 };
271 #else
272     int channelsNormalMode[4] = { 3, 0, 1, 3 };
273     int channelsOnestepModeX[4] = { 3, 0, 0, 0 };
274     int channelsOnestepModeY[4] = { 0, 3, 3, 3 };
275 #endif
276     int* channels = channelsNormalMode;
277     int stepCount = (m_blurDistance < 3) ? 1 : 3; // apply only 1-step box blur for small radius cases (radius < 3) instead of full 3-step box blurs
278     int d = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurDistance)));
279     int dmax = d >> 1;
280     int dmin = dmax - 1 + (d & 1);
281     if (dmin < 0)
282         dmin = 0;
283
284     // Two stages: horizontal and vertical
285     for (int k = 0; k < 2; ++k) {
286
287         unsigned char* pixels = imageData;
288         int stride = (!k) ? 4 : rowStride;
289         int delta = (!k) ? rowStride : 4;
290         int jfinal = (!k) ? size.height() : size.width();
291         int dim = (!k) ? size.width() : size.height();
292
293         if (stepCount == 1)
294             channels = (!k) ? channelsOnestepModeX : channelsOnestepModeY;
295
296         for (int j = 0; j < jfinal; ++j, pixels += delta) {
297
298             // For each step, we blur the alpha in a channel and store the result
299             // in another channel for the subsequent step.
300             // We use sliding window algorithm to accumulate the alpha values.
301             // This is much more efficient than computing the sum of each pixels
302             // covered by the box kernel size for each x.
303
304             for (int step = 0; step < stepCount; ++step) {
305                 int side1 = (!step) ? dmin : dmax;
306                 int side2 = (step == 1) ? dmin : dmax;
307                 int pixelCount = side1 + 1 + side2;
308                 int invCount = ((1 << BlurSumShift) + pixelCount - 1) / pixelCount;
309                 int ofs = 1 + side2;
310                 int alpha1 = pixels[channels[step]];
311                 int alpha2 = pixels[(dim - 1) * stride + channels[step]];
312                 unsigned char* ptr = pixels + channels[step + 1];
313                 unsigned char* prev = pixels + stride + channels[step];
314                 unsigned char* next = pixels + ofs * stride + channels[step];
315
316                 int i;
317                 int sum = side1 * alpha1 + alpha1;
318                 int limit = (dim < side2 + 1) ? dim : side2 + 1;
319                 for (i = 1; i < limit; ++i, prev += stride)
320                     sum += *prev;
321                 if (limit <= side2)
322                     sum += (side2 - limit + 1) * alpha2;
323
324                 limit = (side1 < dim) ? side1 : dim;
325                 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
326                     *ptr = (sum * invCount) >> BlurSumShift;
327                     sum += ((ofs < dim) ? *next : alpha2) - alpha1;
328                 }
329                 prev = pixels + channels[step];
330                 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
331                     *ptr = (sum * invCount) >> BlurSumShift;
332                     sum += (*next) - (*prev);
333                 }
334                 for (; i < dim; ptr += stride, prev += stride, ++i) {
335                     *ptr = (sum * invCount) >> BlurSumShift;
336                     sum += alpha2 - (*prev);
337                 }
338             }
339         }
340     }
341 #endif
342 }
343
344 void ContextShadow::adjustBlurDistance(GraphicsContext* context)
345 {
346     const AffineTransform transform = context->getCTM();
347
348     // Adjust blur if we're scaling, since the radius must not be affected by transformations.
349     if (transform.isIdentity())
350         return;
351
352     // Calculate transformed unit vectors.
353     const FloatQuad unitQuad(FloatPoint(0, 0), FloatPoint(1, 0),
354                              FloatPoint(0, 1), FloatPoint(1, 1));
355     const FloatQuad transformedUnitQuad = transform.mapQuad(unitQuad);
356
357     // Calculate X axis scale factor.
358     const FloatSize xUnitChange = transformedUnitQuad.p2() - transformedUnitQuad.p1();
359     const float xAxisScale = sqrtf(xUnitChange.width() * xUnitChange.width()
360                                    + xUnitChange.height() * xUnitChange.height());
361
362     // Calculate Y axis scale factor.
363     const FloatSize yUnitChange = transformedUnitQuad.p3() - transformedUnitQuad.p1();
364     const float yAxisScale = sqrtf(yUnitChange.width() * yUnitChange.width()
365                                    + yUnitChange.height() * yUnitChange.height());
366
367     // blurLayerImage() does not support per-axis blurring, so calculate a balanced scaling.
368     const float scale = sqrtf(xAxisScale * yAxisScale);
369     m_blurDistance = roundf(static_cast<float>(m_blurDistance) / scale);
370 }
371
372 IntRect ContextShadow::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& layerArea, const IntRect& clipRect)
373 {
374     // Calculate the destination of the blurred and/or transformed layer.
375     FloatRect layerFloatRect;
376     float inflation = 0;
377
378     const AffineTransform transform = context->getCTM();
379     if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
380         FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(layerArea));
381         transformedPolygon.move(m_offset);
382         layerFloatRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
383     } else {
384         layerFloatRect = layerArea;
385         layerFloatRect.move(m_offset);
386     }
387
388     // We expand the area by the blur radius to give extra space for the blur transition.
389     if (m_type == BlurShadow) {
390         layerFloatRect.inflate(m_blurDistance);
391         inflation += m_blurDistance;
392     }
393
394     FloatRect unclippedLayerRect = layerFloatRect;
395
396     if (!clipRect.contains(enclosingIntRect(layerFloatRect))) {
397         // No need to have the buffer larger than the clip.
398         layerFloatRect.intersect(clipRect);
399
400         // If we are totally outside the clip region, we aren't painting at all.
401         if (layerFloatRect.isEmpty())
402             return IntRect(0, 0, 0, 0);
403
404         // We adjust again because the pixels at the borders are still
405         // potentially affected by the pixels outside the buffer.
406         if (m_type == BlurShadow) {
407             layerFloatRect.inflate(m_blurDistance);
408             unclippedLayerRect.inflate(m_blurDistance);
409             inflation += m_blurDistance;
410         }
411     }
412
413     const int frameSize = inflation * 2;
414     m_sourceRect = IntRect(0, 0, layerArea.width() + frameSize, layerArea.height() + frameSize);
415     m_layerOrigin = FloatPoint(layerFloatRect.x(), layerFloatRect.y());
416
417     const FloatPoint m_unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
418     const FloatSize clippedOut = m_unclippedLayerOrigin - m_layerOrigin;
419
420     // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
421     // out region, set the origin accordingly to the full bounding rect's top-left corner.
422     const float translationX = -layerArea.x() + inflation - fabsf(clippedOut.width());
423     const float translationY = -layerArea.y() + inflation - fabsf(clippedOut.height());
424     m_layerContextTranslation = FloatPoint(translationX, translationY);
425
426     return enclosingIntRect(layerFloatRect);
427 }
428
429 } // namespace WebCore