summaryrefslogtreecommitdiffstats
path: root/libs/surfaceflinger/BlurFilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/surfaceflinger/BlurFilter.cpp')
-rw-r--r--libs/surfaceflinger/BlurFilter.cpp326
1 files changed, 326 insertions, 0 deletions
diff --git a/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp
new file mode 100644
index 0000000..5dc0ba0
--- /dev/null
+++ b/libs/surfaceflinger/BlurFilter.cpp
@@ -0,0 +1,326 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <utils/Errors.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "clz.h"
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+namespace android {
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) {
+ return v;
+}
+inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) {
+ return v;
+}
+#else
+inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) {
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) {
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+#endif
+
+const int BLUR_DITHER_BITS = 6; // dither weights stored on 6 bits
+const int BLUR_DITHER_ORDER_SHIFT= 3;
+const int BLUR_DITHER_ORDER = (1<<BLUR_DITHER_ORDER_SHIFT);
+const int BLUR_DITHER_SIZE = BLUR_DITHER_ORDER * BLUR_DITHER_ORDER;
+const int BLUR_DITHER_MASK = BLUR_DITHER_ORDER-1;
+
+static const uint8_t gDitherMatrix[BLUR_DITHER_SIZE] = {
+ 0, 32, 8, 40, 2, 34, 10, 42,
+ 48, 16, 56, 24, 50, 18, 58, 26,
+ 12, 44, 4, 36, 14, 46, 6, 38,
+ 60, 28, 52, 20, 62, 30, 54, 22,
+ 3, 35, 11, 43, 1, 33, 9, 41,
+ 51, 19, 59, 27, 49, 17, 57, 25,
+ 15, 47, 7, 39, 13, 45, 5, 37,
+ 63, 31, 55, 23, 61, 29, 53, 21
+};
+
+
+template <int FACTOR = 0>
+struct BlurColor565
+{
+ typedef uint16_t type;
+ int r, g, b;
+ inline BlurColor565() { }
+ inline BlurColor565(uint16_t v) {
+ r = v >> 11;
+ g = (v >> 5) & 0x3E;
+ b = v & 0x1F;
+ }
+ inline void clear() { r=g=b=0; }
+ inline uint16_t to(int shift, int last, int dither) const {
+ int R = r;
+ int G = g;
+ int B = b;
+ if (UNLIKELY(last)) {
+ if (FACTOR>0) {
+ int L = (R+G+B)>>1;
+ R += (((L>>1) - R) * FACTOR) >> 8;
+ G += (((L ) - G) * FACTOR) >> 8;
+ B += (((L>>1) - B) * FACTOR) >> 8;
+ }
+ R += (dither << shift) >> BLUR_DITHER_BITS;
+ G += (dither << shift) >> BLUR_DITHER_BITS;
+ B += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ R >>= shift;
+ G >>= shift;
+ B >>= shift;
+ return (R<<11) | (G<<5) | B;
+ }
+ inline BlurColor565& operator += (const BlurColor565& rhs) {
+ r += rhs.r;
+ g += rhs.g;
+ b += rhs.b;
+ return *this;
+ }
+ inline BlurColor565& operator -= (const BlurColor565& rhs) {
+ r -= rhs.r;
+ g -= rhs.g;
+ b -= rhs.b;
+ return *this;
+ }
+};
+
+struct BlurGray565
+{
+ typedef uint16_t type;
+ int l;
+ inline BlurGray565() { }
+ inline BlurGray565(uint16_t v) {
+ int r = v >> 11;
+ int g = (v >> 5) & 0x3F;
+ int b = v & 0x1F;
+ l = (r + g + b + 1)>>1;
+ }
+ inline void clear() { l=0; }
+ inline uint16_t to(int shift, int last, int dither) const {
+ int L = l;
+ if (UNLIKELY(last)) {
+ L += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ L >>= shift;
+ return ((L>>1)<<11) | (L<<5) | (L>>1);
+ }
+ inline BlurGray565& operator += (const BlurGray565& rhs) {
+ l += rhs.l;
+ return *this;
+ }
+ inline BlurGray565& operator -= (const BlurGray565& rhs) {
+ l -= rhs.l;
+ return *this;
+ }
+};
+
+struct BlurGray8888
+{
+ typedef uint32_t type;
+ int l, a;
+ inline BlurGray8888() { }
+ inline BlurGray8888(uint32_t v) {
+ v = BLUR_RGBA_TO_HOST(v);
+ int r = v & 0xFF;
+ int g = (v >> 8) & 0xFF;
+ int b = (v >> 16) & 0xFF;
+ a = v >> 24;
+ l = r + g + g + b;
+ }
+ inline void clear() { l=a=0; }
+ inline uint32_t to(int shift, int last, int dither) const {
+ int L = l;
+ int A = a;
+ if (UNLIKELY(last)) {
+ L += (dither << (shift+2)) >> BLUR_DITHER_BITS;
+ A += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ L >>= (shift+2);
+ A >>= shift;
+ return BLUR_HOST_TO_RGBA((A<<24) | (L<<16) | (L<<8) | L);
+ }
+ inline BlurGray8888& operator += (const BlurGray8888& rhs) {
+ l += rhs.l;
+ a += rhs.a;
+ return *this;
+ }
+ inline BlurGray8888& operator -= (const BlurGray8888& rhs) {
+ l -= rhs.l;
+ a -= rhs.a;
+ return *this;
+ }
+};
+
+
+template<typename PIXEL>
+static status_t blurFilter(
+ GGLSurface const* dst,
+ GGLSurface const* src,
+ int kernelSizeUser,
+ int repeat)
+{
+ typedef typename PIXEL::type TYPE;
+
+ const int shift = 31 - clz(kernelSizeUser);
+ const int areaShift = shift*2;
+ const int kernelSize = 1<<shift;
+ const int kernelHalfSize = kernelSize/2;
+ const int mask = kernelSize-1;
+ const int w = src->width;
+ const int h = src->height;
+ const uint8_t* ditherMatrix = gDitherMatrix;
+
+ // we need a temporary buffer to store one line of blurred columns
+ // as well as kernelSize lines of source pixels organized as a ring buffer.
+ void* const temporary_buffer = malloc(
+ (w + kernelSize) * sizeof(PIXEL) +
+ (src->stride * kernelSize) * sizeof(TYPE));
+ if (!temporary_buffer)
+ return NO_MEMORY;
+
+ PIXEL* const sums = (PIXEL*)temporary_buffer;
+ TYPE* const scratch = (TYPE*)(sums + w + kernelSize);
+
+ // Apply the blur 'repeat' times, this is used to approximate
+ // gaussian blurs. 3 times gives good results.
+ for (int k=0 ; k<repeat ; k++) {
+
+ // Clear the columns sums for this round
+ memset(sums, 0, (w + kernelSize) * sizeof(PIXEL));
+ TYPE* head;
+ TYPE pixel;
+ PIXEL current;
+
+ // Since we're going to override the source data we need
+ // to copy it in a temporary buffer. Only kernelSize lines are
+ // required. But since we start in the center of the kernel,
+ // we only copy half of the data, and fill the rest with zeros
+ // (assuming black/transparent pixels).
+ memcpy( scratch + src->stride*kernelHalfSize,
+ src->data,
+ src->stride*kernelHalfSize*sizeof(TYPE));
+
+ // sum half of each column, because we assume the first half is
+ // zeros (black/transparent).
+ for (int y=0 ; y<kernelHalfSize ; y++) {
+ head = (TYPE*)src->data + y*src->stride;
+ for (int x=0 ; x<w ; x++)
+ sums[x] += PIXEL( *head++ );
+ }
+
+ for (int y=0 ; y<h ; y++) {
+ TYPE* fb = (TYPE*)dst->data + y*dst->stride;
+
+ // compute the dither matrix line
+ uint8_t const * ditherY = ditherMatrix
+ + (y & BLUR_DITHER_MASK)*BLUR_DITHER_ORDER;
+
+ // Horizontal blur pass on the columns sums
+ int count, dither, x=0;
+ PIXEL const * out= sums;
+ PIXEL const * in = sums;
+ current.clear();
+
+ count = kernelHalfSize;
+ do {
+ current += *in;
+ in++;
+ } while (--count);
+
+ count = kernelHalfSize;
+ do {
+ current += *in;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ in++;
+ } while (--count);
+
+ count = w-kernelSize;
+ do {
+ current += *in;
+ current -= *out;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ in++, out++;
+ } while (--count);
+
+ count = kernelHalfSize;
+ do {
+ current -= *out;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ out++;
+ } while (--count);
+
+ // vertical blur pass, subtract the oldest line from each columns
+ // and add a new line. Subtract or add zeros at the top
+ // and bottom edges.
+ TYPE* const tail = scratch + (y & mask) * src->stride;
+ if (y >= kernelHalfSize) {
+ for (int x=0 ; x<w ; x++)
+ sums[x] -= PIXEL( tail[x] );
+ }
+ if (y < h-kernelSize) {
+ memcpy( tail,
+ (TYPE*)src->data + (y+kernelHalfSize)*src->stride,
+ src->stride*sizeof(TYPE));
+ for (int x=0 ; x<w ; x++)
+ sums[x] += PIXEL( tail[x] );
+ }
+ }
+
+ // The subsequent passes are always done in-place.
+ src = dst;
+ }
+
+ free(temporary_buffer);
+
+ return NO_ERROR;
+}
+
+template status_t blurFilter< BlurColor565<0x80> >(
+ GGLSurface const* dst,
+ GGLSurface const* src,
+ int kernelSizeUser,
+ int repeat);
+
+status_t blurFilter(
+ GGLSurface const* image,
+ int kernelSizeUser,
+ int repeat)
+{
+ return blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat);
+}
+
+} // namespace android
+
+//err = blur< BlurColor565<0x80> >(dst, src, kernelSizeUser, repeat);
+//err = blur<BlurGray565>(dst, src, kernelSizeUser, repeat);
+//err = blur<BlurGray8888>(dst, src, kernelSizeUser, repeat);