diff options
Diffstat (limited to 'libs/surfaceflinger/BlurFilter.cpp')
-rw-r--r-- | libs/surfaceflinger/BlurFilter.cpp | 326 |
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); |