Example #1
0
inline void FEGaussianBlur::platformApplyGeneric(ByteArray* srcPixelArray, ByteArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
{
    int stride = 4 * paintSize.width();
    int dxLeft = 0;
    int dxRight = 0;
    int dyLeft = 0;
    int dyRight = 0;
    for (int i = 0; i < 3; ++i) {
        if (kernelSizeX) {
            kernelPosition(i, kernelSizeX, dxLeft, dxRight);
            boxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage());
        } else {
            ByteArray* auxPixelArray = tmpPixelArray;
            tmpPixelArray = srcPixelArray;
            srcPixelArray = auxPixelArray;
        }

        if (kernelSizeY) {
            kernelPosition(i, kernelSizeY, dyLeft, dyRight);
            boxBlur(tmpPixelArray, srcPixelArray, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage());
        } else {
            ByteArray* auxPixelArray = tmpPixelArray;
            tmpPixelArray = srcPixelArray;
            srcPixelArray = auxPixelArray;
        }
    }
}
Example #2
0
inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
{
    int stride = 4 * paintSize.width();
    int dxLeft = 0;
    int dxRight = 0;
    int dyLeft = 0;
    int dyRight = 0;
    Uint8ClampedArray* src = srcPixelArray;
    Uint8ClampedArray* dst = tmpPixelArray;

    for (int i = 0; i < 3; ++i) {
        if (kernelSizeX) {
            kernelPosition(i, kernelSizeX, dxLeft, dxRight);
            boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage());
            swap(src, dst);
        }

        if (kernelSizeY) {
            kernelPosition(i, kernelSizeY, dyLeft, dyRight);
            boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage());
            swap(src, dst);
        }
    }

    // The final result should be stored in srcPixelArray.
    if (dst == srcPixelArray) {
        ASSERT(src->length() == dst->length());
        memcpy(dst->data(), src->data(), src->length());
    }

}
Example #3
0
void FEGaussianBlur::apply(Filter* filter)
{
    m_in->apply(filter);
    if (!m_in->resultImage())
        return;

    if (!getEffectContext())
        return;

    setIsAlphaImage(m_in->isAlphaImage());

    if (m_x == 0 || m_y == 0)
        return;

    unsigned sdx = static_cast<unsigned>(floor(m_x * 3 * sqrt(2 * M_PI) / 4.f + 0.5f));
    unsigned sdy = static_cast<unsigned>(floor(m_y * 3 * sqrt(2 * M_PI) / 4.f + 0.5f));

    IntRect effectDrawingRect = calculateDrawingIntRect(m_in->subRegion());
    RefPtr<ImageData> srcImageData(m_in->resultImage()->getPremultipliedImageData(effectDrawingRect));
    CanvasPixelArray* srcPixelArray(srcImageData->data());

    IntRect imageRect(IntPoint(), resultImage()->size());
    RefPtr<ImageData> tmpImageData = ImageData::create(imageRect.width(), imageRect.height());
    CanvasPixelArray* tmpPixelArray(tmpImageData->data());

    int stride = 4 * imageRect.width();
    for (int i = 0; i < 3; ++i) {
        boxBlur(srcPixelArray, tmpPixelArray, sdx, 4, stride, imageRect.width(), imageRect.height(), isAlphaImage());
        boxBlur(tmpPixelArray, srcPixelArray, sdy, stride, 4, imageRect.height(), imageRect.width(), isAlphaImage());
    }

    resultImage()->putPremultipliedImageData(srcImageData.get(), imageRect, IntPoint());
}
void GaussianBlur::gaussBlur(IntensityMap &input, IntensityMap &result, double radius, bool tileable) {
    std::vector<double> boxes = boxesForGauss(radius, 3);

    boxBlur(input, result, ((boxes.at(0) - 1) / 2), tileable);
    boxBlur(result, input, ((boxes.at(1) - 1) / 2), tileable);
    boxBlur(input, result, ((boxes.at(2) - 1) / 2), tileable);
}
dtStatus dtBuildTileCacheDistanceField(dtTileCacheAlloc* alloc, dtTileCacheLayer& layer, dtTileCacheDistanceField& dfield)
{
    dtAssert(alloc);

    const int w = (int)layer.header->width;
    const int h = (int)layer.header->height;

    dfield.data = (unsigned short*)alloc->alloc(w*h*sizeof(unsigned short));
    if (!dfield.data)
    {
        return DT_FAILURE | DT_OUT_OF_MEMORY;
    }

    dtTileCacheDistanceField tmpField;
    tmpField.data = (unsigned short*)alloc->alloc(w*h*sizeof(unsigned short));
    if (!tmpField.data)
    {
        return DT_FAILURE | DT_OUT_OF_MEMORY;
    }

    calculateDistanceField(layer, dfield.data, dfield.maxDist);
    if (boxBlur(layer, 1, dfield.data, tmpField.data) != dfield.data)
    {
        dtSwap(dfield.data, tmpField.data);
    }

    alloc->free(tmpField.data);
    return DT_SUCCESS;
}
Example #6
0
inline void standardBoxBlur(Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSizeX, unsigned kernelSizeY, int stride, IntSize& paintSize, bool isAlphaImage, EdgeModeType edgeMode)
{
    int dxLeft = 0;
    int dxRight = 0;
    int dyLeft = 0;
    int dyRight = 0;

    for (int i = 0; i < 3; ++i) {
        if (kernelSizeX) {
            kernelPosition(i, kernelSizeX, dxLeft, dxRight);
#if HAVE(ARM_NEON_INTRINSICS)
            if (!isAlphaImage)
                boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height());
            else
                boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, edgeMode);
#else
            boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage, edgeMode);
#endif
            std::swap(src, dst);
        }

        if (kernelSizeY) {
            kernelPosition(i, kernelSizeY, dyLeft, dyRight);
#if HAVE(ARM_NEON_INTRINSICS)
            if (!isAlphaImage)
                boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width());
            else
                boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, edgeMode);
#else
            boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage, edgeMode);
#endif
            std::swap(src, dst);
        }
    }

    // The final result should be stored in src.
    if (dst == src) {
        ASSERT(src->length() == dst->length());
        memcpy(dst->data(), src->data(), src->length());
    }
}
// ######################################################################
Image<float> ContourBoundaryDetector::getLabStandardDeviation
(Image<PixRGB<byte> > ima, int r)
{
  // convert to CIElab color space
  Image<float> lImg; 
  Image<float> aImg;
  Image<float> bImg;
  getLAB(ima, lImg, aImg, bImg);

  // compute squares of each channel
  Image<float> lSqImg = lImg * lImg; 
  Image<float> aSqImg = aImg * aImg;
  Image<float> bSqImg = bImg * bImg;

  // compute box blur for each channel
  Image<float> lBbImg = boxBlur(lImg, r);
  Image<float> aBbImg = boxBlur(aImg, r); 
  Image<float> bBbImg = boxBlur(bImg, r); 

  Image<float> lSqBbImg = boxBlur(lSqImg, r);
  Image<float> aSqBbImg = boxBlur(aSqImg, r); 
  Image<float> bSqBbImg = boxBlur(bSqImg, r); 

  //calculate standard deviation of each l a b channel
  Image<float> vl = lBbImg*lBbImg - lSqBbImg;
  Image<float> va = aBbImg*aBbImg - aSqBbImg;
  Image<float> vb = bBbImg*bBbImg - bSqBbImg;
  Image<float> varImg = sqCombine(vl, va, vb);

  return varImg;
}
/// @par
/// 
/// This is usually the second to the last step in creating a fully built
/// compact heightfield.  This step is required before regions are built
/// using #rcBuildRegions or #rcBuildRegionsMonotone.
/// 
/// After this step, the distance data is available via the rcCompactHeightfield::maxDistance
/// and rcCompactHeightfield::dist fields.
///
/// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
{
    rcAssert(ctx);
    
    ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
    
    if (chf.dist)
    {
        rcFree(chf.dist);
        chf.dist = 0;
    }
    
    unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
    if (!src)
    {
        ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
        return false;
    }
    unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
    if (!dst)
    {
        ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
        rcFree(src);
        return false;
    }
    
    unsigned short maxDist = 0;

    ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
    
    calculateDistanceField(chf, src, maxDist);
    chf.maxDistance = maxDist;
    
    ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
    
    ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
    
    // Blur
    if (boxBlur(chf, 1, src, dst) != src)
        rcSwap(src, dst);
    
    // Store distance.
    chf.dist = src;
    
    ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);

    ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
    
    rcFree(dst);
    
    return true;
}
Example #9
0
/// @par
/// 
/// This is usually the second to the last step in creating a fully built
/// compact heightfield.  This step is required before regions are built
/// using #rcBuildRegions or #rcBuildRegionsMonotone.
/// 
/// After this step, the distance data is available via the rcCompactHeightfield::maxDistance
/// and rcCompactHeightfield::dist fields.
///
/// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone
bool rcBuildDistanceField(rcCompactHeightfield& chf)
{
	if (chf.dist)
	{
		rcFree(chf.dist);
		chf.dist = 0;
	}
	
	unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
	if (!src)
	{
		return false;
	}
	unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
	if (!dst)
	{
		rcFree(src);
		return false;
	}
	
	unsigned short maxDist = 0;

	calculateDistanceField(chf, src, maxDist);
	chf.maxDistance = maxDist;
	
	// Blur
	if (boxBlur(chf, 1, src, dst) != src)
		rcSwap(src, dst);
	
	// Store distance.
	chf.dist = src;
	
	rcFree(dst);
	
	return true;
}
bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
                      SkScalar radius, Style style, Quality quality,
                      SkIPoint* margin)
{

    if (src.fFormat != SkMask::kA8_Format) {
        return false;
    }

    // Force high quality off for small radii (performance)
    if (radius < SkIntToScalar(3)) {
        quality = kLow_Quality;
    }

    // highQuality: use three box blur passes as a cheap way
    // to approximate a Gaussian blur
    int passCount = (kHigh_Quality == quality) ? 3 : 1;
    SkScalar passRadius = (kHigh_Quality == quality) ?
                          SkScalarMul( radius, kBlurRadiusFudgeFactor):
                          radius;

    int rx = SkScalarCeil(passRadius);
    int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);

    SkASSERT(rx >= 0);
    SkASSERT((unsigned)outerWeight <= 255);
    if (rx <= 0) {
        return false;
    }

    int ry = rx;    // only do square blur for now

    int padx = passCount * rx;
    int pady = passCount * ry;

    if (margin) {
        margin->set(padx, pady);
    }
    dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
        src.fBounds.fRight + padx, src.fBounds.fBottom + pady);

    dst->fRowBytes = dst->fBounds.width();
    dst->fFormat = SkMask::kA8_Format;
    dst->fImage = NULL;

    if (src.fImage) {
        size_t dstSize = dst->computeImageSize();
        if (0 == dstSize) {
            return false;   // too big to allocate, abort
        }

        int             sw = src.fBounds.width();
        int             sh = src.fBounds.height();
        const uint8_t*  sp = src.fImage;
        uint8_t*        dp = SkMask::AllocImage(dstSize);
        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);

        // build the blurry destination
        SkAutoTMalloc<uint8_t>  tmpBuffer(dstSize);
        uint8_t*                tp = tmpBuffer.get();
        int w = sw, h = sh;

        if (outerWeight == 255) {
            int loRadius, hiRadius;
            get_adjusted_radii(passRadius, &loRadius, &hiRadius);
            if (kHigh_Quality == quality) {
                // Do three X blurs, with a transpose on the final one.
                w = boxBlur(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h, false);
                w = boxBlur(tp, w,             dp, hiRadius, loRadius, w, h, false);
                w = boxBlur(dp, w,             tp, hiRadius, hiRadius, w, h, true);
                // Do three Y blurs, with a transpose on the final one.
                h = boxBlur(tp, h,             dp, loRadius, hiRadius, h, w, false);
                h = boxBlur(dp, h,             tp, hiRadius, loRadius, h, w, false);
                h = boxBlur(tp, h,             dp, hiRadius, hiRadius, h, w, true);
            } else {
                w = boxBlur(sp, src.fRowBytes, tp, rx, rx, w, h, true);
                h = boxBlur(tp, h,             dp, ry, ry, h, w, true);
            }
        } else {
            if (kHigh_Quality == quality) {
                // Do three X blurs, with a transpose on the final one.
                w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outerWeight);
                w = boxBlurInterp(tp, w,             dp, rx, w, h, false, outerWeight);
                w = boxBlurInterp(dp, w,             tp, rx, w, h, true, outerWeight);
                // Do three Y blurs, with a transpose on the final one.
                h = boxBlurInterp(tp, h,             dp, ry, h, w, false, outerWeight);
                h = boxBlurInterp(dp, h,             tp, ry, h, w, false, outerWeight);
                h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outerWeight);
            } else {
                w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outerWeight);
                h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outerWeight);
            }
        }

        dst->fImage = dp;
        // if need be, alloc the "real" dst (same size as src) and copy/merge
        // the blur into it (applying the src)
        if (style == kInner_Style) {
            // now we allocate the "real" dst, mirror the size of src
            size_t srcSize = src.computeImageSize();
            if (0 == srcSize) {
                return false;   // too big to allocate, abort
            }
            dst->fImage = SkMask::AllocImage(srcSize);
            merge_src_with_blur(dst->fImage, src.fRowBytes,
                                sp, src.fRowBytes,
                                dp + passCount * (rx + ry * dst->fRowBytes),
                                dst->fRowBytes, sw, sh);
            SkMask::FreeImage(dp);
        } else if (style != kNormal_Style) {
            clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes),
                            dst->fRowBytes, sp, src.fRowBytes, sw, sh, style);
        }
        (void)autoCall.detach();
    }

    if (style == kInner_Style) {
        dst->fBounds = src.fBounds; // restore trimmed bounds
        dst->fRowBytes = src.fRowBytes;
    }

    return true;
}
bool rcBuildDistanceField(rcCompactHeightfield& chf)
{
	rcTimeVal startTime = rcGetPerformanceTimer();
	
	unsigned short* dist0 = new unsigned short[chf.spanCount];
	if (!dist0)
	{
		if (rcGetLog())
			rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist0' (%d).", chf.spanCount);
		return false;
	}
	unsigned short* dist1 = new unsigned short[chf.spanCount];
	if (!dist1)
	{
		if (rcGetLog())
			rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist1' (%d).", chf.spanCount);
		delete [] dist0;
		return false;
	}
	
	unsigned short* src = dist0;
	unsigned short* dst = dist1;

	unsigned short maxDist = 0;

	rcTimeVal distStartTime = rcGetPerformanceTimer();
	
	if (calculateDistanceField(chf, src, dst, maxDist) != src)
		rcSwap(src, dst);
	
	chf.maxDistance = maxDist;
	
	rcTimeVal distEndTime = rcGetPerformanceTimer();
	
	rcTimeVal blurStartTime = rcGetPerformanceTimer();
	
	// Blur
	if (boxBlur(chf, 1, src, dst) != src)
		rcSwap(src, dst);
	
	// Store distance.
	for (int i = 0; i < chf.spanCount; ++i)
		chf.spans[i].dist = src[i];
	
	rcTimeVal blurEndTime = rcGetPerformanceTimer();
	
	delete [] dist0;
	delete [] dist1;
	
	rcTimeVal endTime = rcGetPerformanceTimer();
	
/*	if (rcGetLog())
	{
		rcGetLog()->log(RC_LOG_PROGRESS, "Build distance field: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
		rcGetLog()->log(RC_LOG_PROGRESS, " - dist: %.3f ms", rcGetDeltaTimeUsec(distStartTime, distEndTime)/1000.0f);
		rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.3f ms", rcGetDeltaTimeUsec(blurStartTime, blurEndTime)/1000.0f);
	}*/
	if (rcGetBuildTimes())
	{
		rcGetBuildTimes()->buildDistanceField += rcGetDeltaTimeUsec(startTime, endTime);
		rcGetBuildTimes()->buildDistanceFieldDist += rcGetDeltaTimeUsec(distStartTime, distEndTime);
		rcGetBuildTimes()->buildDistanceFieldBlur += rcGetDeltaTimeUsec(blurStartTime, blurEndTime);
	}
	
	return true;
}
Example #12
0
bool MCGBlurBox(const SkMask& p_src, SkScalar p_x_radius, SkScalar p_y_radius, SkScalar p_x_spread, SkScalar p_y_spread, SkMask& r_dst)
{
	int t_pass_count;
	t_pass_count = 3;
	
	// Maximum amount of spread is 254 pixels.
	int x_spread, y_spread;
	x_spread = SkMin32(SkScalarFloor(p_x_radius * p_x_spread), 254);
	y_spread = SkMin32(SkScalarFloor(p_y_radius * p_y_spread), 254);
	
	p_x_radius -= x_spread;
	p_y_radius -= y_spread;
	
	int rx, ry;
	rx = SkScalarCeil(p_x_radius);
	ry = SkScalarCeil(p_y_radius);
	
	SkScalar px, py;
	px = rx;
	py = ry;
	
	int wx, wy;
	wx = 255 - SkScalarRound((SkIntToScalar(rx) - px) * 255);
	wy = 255 - SkScalarRound((SkIntToScalar(ry) - py) * 255);
	
	int t_pad_x, t_pad_y;
	t_pad_x = rx + x_spread;
	t_pad_y = ry + y_spread;
	
	r_dst . fBounds . set(p_src . fBounds . fLeft - t_pad_x, p_src . fBounds . fTop - t_pad_y,
						  p_src . fBounds . fRight + t_pad_x, p_src . fBounds . fBottom + t_pad_y);
	r_dst . fRowBytes = r_dst . fBounds . width();
	r_dst . fFormat = SkMask::kA8_Format;
	r_dst . fImage = NULL;
	
	if (p_src . fImage == NULL)
		return true;
	
	size_t t_dst_size;
	t_dst_size = r_dst . computeImageSize();
	if (t_dst_size == 0)
		return false;
	
	int sw, sh;
	sw = p_src . fBounds . width();
	sh = p_src . fBounds . height();
	
	const uint8_t *sp;
	sp = p_src . fImage;
	
	uint8_t *dp;
	dp = SkMask::AllocImage(t_dst_size);
	if (dp == nil)
		return false;
	
	uint8_t *tp;
	tp = SkMask::AllocImage(t_dst_size);
	if (tp == nil)
	{
		SkMask::FreeImage(dp);
		return false;
	}
	
	int w, h;
	w = sw;
	h = sh;
	if (wx == 255)
	{
		if (t_pass_count == 3)
		{
			int r;
			r = rx;
			
			bool t_has_spread;
			t_has_spread = false;
			if (x_spread != 0 || y_spread != 0)
			{
				//w = dilateMask(sp, p_src . fRowBytes, tp, x_spread, w, h, true);
				//h = dilateMask(tp, h, dp, y_spread, h, w, true);
				dilateDistanceXY(sp, dp, x_spread, y_spread, w, h, w, h);
				t_has_spread = true;
			}
			
			int trx;
			trx = (r + 2) / 3; 
			r -= trx;
			
			int rx_lo, rx_hi;
			rx_lo = rx_hi = trx;
			
			w = boxBlur(t_has_spread ? dp : sp, t_has_spread ? w : p_src . fRowBytes, tp, rx_lo, rx_hi, w, h, false);
			
			trx = (r + 1) / 2;
			r -= trx;
			
			rx_lo = rx_hi = trx;
			w = boxBlur(tp, w, dp, rx_hi, rx_lo, w, h, false);
			
			trx = r;
			rx_lo = rx_hi = trx;
			w = boxBlur(dp, w, tp, rx_hi, rx_hi, w, h, true);
		}
		else
			w = boxBlur(sp, p_src . fRowBytes, tp, rx, rx, w, h, true);
	}
	else
	{
		if (t_pass_count == 3)
		{
			int r;
			r = rx;
			
			bool t_has_spread;
			t_has_spread = false;
			if (x_spread != 0 || y_spread != 0)
			{
				//w = dilateMask(sp, p_src . fRowBytes, tp, x_spread, w, h, true);
				//h = dilateMask(tp, h, dp, y_spread, h, w, true);
				dilateDistanceXY(sp, dp, x_spread, y_spread, w, h, w, h);
				t_has_spread = true;
			}
			
			int trx;
			trx = (r + 2) / 3; 
			r -= trx;
			
			w = boxBlurInterp(t_has_spread ? dp : sp, t_has_spread ? w : p_src . fRowBytes, tp, trx, w, h, false, wx);
			
			trx = (r + 1) / 2;
			r -= trx;
			
			w = boxBlurInterp(tp, w, dp, trx, w, h, false, wx);
			
			trx = r;
			
			w = boxBlurInterp(dp, w, tp, trx, w, h, true, wx);
		}
		else
			w = boxBlurInterp(sp, p_src . fRowBytes, tp, rx, w, h, true, wx);
	}
	
	if (wy == 255)
	{
		if (t_pass_count == 3)
		{
			int r;
			r = ry;
			
			int sry;
			sry = (r + 2) / 3;
			r -= sry;
			
			int ry_lo, ry_hi;
			ry_lo = ry_hi = sry;
			
			h = boxBlur(tp, h, dp, ry_lo, ry_hi, h, w, false);
			
			sry = (r + 1) / 2;
			r -= sry;
			ry_lo = ry_hi = sry;
			
			h = boxBlur(dp, h, tp, ry_hi, ry_lo, h, w, false);
			
			sry = r;
			ry_lo = ry_hi = sry;
			
			h = boxBlur(tp, h, dp, ry_hi, ry_hi, h, w, true);
		}
		else
			h = boxBlur(tp, h, dp, ry, ry, h, w, true);
	}
	else
	{
		if (t_pass_count == 3)
		{
			int r;
			r = ry;
			
			int sry;
			sry = (r + 2) / 3;
			r -= sry;
			
			h = boxBlurInterp(tp, h, dp, sry, h, w, false, wy);
			
			sry = (r + 1) / 2;
			r -= sry;
			
			h = boxBlurInterp(dp, h, tp, sry, h, w, false, wy);
			
			sry = r;
			
			h = boxBlurInterp(tp, h, dp, sry, h, w, true, wy);
		}
		else
			w = boxBlurInterp(tp, h, dp, rx, h, w, true, wy);
	}
	
	SkMask::FreeImage(tp);
	
	r_dst . fImage = dp;
	
	return true;
}
// ######################################################################
std::vector<Image<float> > ContourBoundaryDetector::calculateGradient
(Image<float> varImg, int r)
{  
  int w = varImg.getWidth();
  int h = varImg.getHeight();

  Image<float> gradImgX(w,h,NO_INIT);
  Image<float> gradImgY(w,h,NO_INIT);

  // smooth the variance Image using BoxBlur
  Image<float> varBbImg = boxBlur(varImg, r);

  std::vector<float> dx(NUM_GRADIENT_DIRECTIONS);
  std::vector<float> dy(NUM_GRADIENT_DIRECTIONS);    
  for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++)
    {
      dx[k] = cos(k*2*M_PI/NUM_GRADIENT_DIRECTIONS); 
      dy[k] = sin(k*2*M_PI/NUM_GRADIENT_DIRECTIONS);
      LDEBUG("%d %f %f", k, dx[k], dy[k]);
    }

  // go through each location
  for(int i = 0; i < w; i++)
    {
      for(int j = 0; j < h; j++)
	{
	  float sumX = 0.0;
	  float sumY = 0.0;
	  for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++)
	    {
	      int i1 = i + r*dx[k];
	      int j1 = j + r*dy[k];

	      int i2 = i - r*dx[k];		
	      int j2 = j - r*dy[k];
              
              // pixel mirroring at the image borders
	      if(i1 <  0) i1 = -i1;
	      if(j1 <  0) j1 = -j1;
	      if(i1 >= w) i1 = 2*w - 2 - i1;
	      if(j1 >= h) j1 = 2*h - 2 - j1;
	      
	      if(i2 <  0) i2 = -i2;
	      if(j2 <  0) j2 = -j2;
	      if(i2 >= w) i2 = 2*w - 2 - i2;
	      if(j2 >= h) j2 = 2*h - 2 - j2;
	      
	      float val = varBbImg.getVal(i1,j1) - varBbImg.getVal(i2,j2);

	      sumX +=  val * dx[k];
	      sumY +=  val * dy[k]; 

	      // LINFO("%d %d %d| val: %f, (%f %f) %f %f", 
	      // 	    i,j,k, val, val*dx[k], val*dy[k], sumX, sumY);
	    }

	  gradImgX.setVal(i,j, sumX);
	  gradImgY.setVal(i,j, sumY);
	}
    }

  std::vector<Image<float> > gradImg(2);
  gradImg[0] = gradImgX;
  gradImg[1] = gradImgY;

  return gradImg;
}