| /* |
| ** |
| ** 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); |