2 * Copyright (C) 2010 Sencha, Inc.
3 * Copyright (C) 2010 Igalia S.L.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
30 #include "ContextShadow.h"
32 #include "AffineTransform.h"
33 #include "FloatQuad.h"
34 #include "GraphicsContext.h"
36 #include <wtf/MathExtras.h>
37 #include <wtf/Noncopyable.h>
39 #if ENABLE(TIZEN_CONTEXTSHADOW_BLUR_NEON)
40 #include "ShadowBlurNEON.h"
48 ContextShadow::ContextShadow()
52 , m_shadowsIgnoreTransforms(false)
56 ContextShadow::ContextShadow(const Color& color, float radius, const FloatSize& offset)
58 , m_blurDistance(round(radius))
61 , m_shadowsIgnoreTransforms(false)
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);
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.
71 } else if (radius > 0) {
72 // Shadow is always blurred, even the offset is zero.
74 } else if (!m_offset.width() && !m_offset.height()) {
75 // Without blur and zero offset means the shadow is fully hidden.
82 void ContextShadow::clear()
87 m_offset = FloatSize();
90 bool ContextShadow::mustUseContextShadow(GraphicsContext* context)
92 // We can't avoid ContextShadow, since the shadow has blur.
93 if (m_type == ContextShadow::BlurShadow)
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())
99 // We can avoid ContextShadow, since there are no transformations to apply to the canvas.
100 if (context->getCTM().isIdentity())
102 // Otherwise, no chance avoiding ContextShadow.
106 // Instead of integer division, we use 17.15 for fixed-point division.
107 static const int BlurSumShift = 15;
109 #if ENABLE(TIZEN_CONTEXTSHADOW_BLUR_NEON)
111 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
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,
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
121 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
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
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
131 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
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
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
141 #elif CPU(MIDDLE_ENDIAN)
142 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
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
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
152 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
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
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
162 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
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
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
173 static unsigned char remapConstantsForStepLoadStoreNomal[96] = {
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,
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
183 static unsigned char remapConstantsForStepLoadStore1StepOnlyForHorizontal[96] = {
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
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
193 static unsigned char remapConstantsForStepLoadStore1StepOnlyForVertical[96] = {
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
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
213 int remainingStrides;
215 unsigned char* channels;
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)
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)));
228 int dmin = dmax - 1 + (d & 1);
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;
243 unsigned char* remapConstantsForStepLoadStore = remapConstantsForStepLoadStoreNomal;
244 if (stepCount == 1) {
245 remapConstantsForStepLoadStore = (!k) ?
246 remapConstantsForStepLoadStore1StepOnlyForHorizontal : remapConstantsForStepLoadStore1StepOnlyForVertical;
248 BoxBlurData arguments = {
258 remapConstantsForStepLoadStore
260 boxBlurNeon(pixels, &arguments);
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 };
272 int channelsNormalMode[4] = { 3, 0, 1, 3 };
273 int channelsOnestepModeX[4] = { 3, 0, 0, 0 };
274 int channelsOnestepModeY[4] = { 0, 3, 3, 3 };
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)));
280 int dmin = dmax - 1 + (d & 1);
284 // Two stages: horizontal and vertical
285 for (int k = 0; k < 2; ++k) {
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();
294 channels = (!k) ? channelsOnestepModeX : channelsOnestepModeY;
296 for (int j = 0; j < jfinal; ++j, pixels += delta) {
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.
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;
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];
317 int sum = side1 * alpha1 + alpha1;
318 int limit = (dim < side2 + 1) ? dim : side2 + 1;
319 for (i = 1; i < limit; ++i, prev += stride)
322 sum += (side2 - limit + 1) * alpha2;
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;
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);
334 for (; i < dim; ptr += stride, prev += stride, ++i) {
335 *ptr = (sum * invCount) >> BlurSumShift;
336 sum += alpha2 - (*prev);
344 void ContextShadow::adjustBlurDistance(GraphicsContext* context)
346 const AffineTransform transform = context->getCTM();
348 // Adjust blur if we're scaling, since the radius must not be affected by transformations.
349 if (transform.isIdentity())
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);
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());
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());
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);
372 IntRect ContextShadow::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& layerArea, const IntRect& clipRect)
374 // Calculate the destination of the blurred and/or transformed layer.
375 FloatRect layerFloatRect;
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();
384 layerFloatRect = layerArea;
385 layerFloatRect.move(m_offset);
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;
394 FloatRect unclippedLayerRect = layerFloatRect;
396 if (!clipRect.contains(enclosingIntRect(layerFloatRect))) {
397 // No need to have the buffer larger than the clip.
398 layerFloatRect.intersect(clipRect);
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);
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;
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());
417 const FloatPoint m_unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
418 const FloatSize clippedOut = m_unclippedLayerOrigin - m_layerOrigin;
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);
426 return enclosingIntRect(layerFloatRect);
429 } // namespace WebCore