327 lines
9.8 KiB
C++
327 lines
9.8 KiB
C++
/*
|
|
**
|
|
** 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);
|