/* Sadly, clang is picky about get_box_filter_size(2) not being a *constantexpression,thuswehavetouseprecomputedvalues.
*/ #define BOX_FILTER_SIZE_2 3 #define BOX_FILTER_SIZE_3 5 #define BOX_FILTER_SIZE_4 7 #define BOX_FILTER_SIZE_5 9 #define BOX_FILTER_SIZE_6 11 #define BOX_FILTER_SIZE_7 13 #define BOX_FILTER_SIZE_8 15 #define BOX_FILTER_SIZE_9 16 #define BOX_FILTER_SIZE_10 18
/* This applies a single box blur pass to a horizontal range of pixels; *sincetheboxblurhasthesameweightforallpixels,wecan *implementanefficientslidingwindowalgorithmwhereweadd *inpixelscomingintothewindowfromtherightandremove *themwhentheyleavethewindwtotheleft. * *disthefilterwidth;forevendshiftindicateshowtheblurred *resultisalignedwiththeoriginal-does'x'goto'yy'(shift=1) *or'yy'(shift=-1)
*/ staticvoid
blur_xspan (guchar *row,
guchar *tmp_buffer, int row_width, int d, int shift)
{ int offset; int sum = 0; int i;
if (d % 2 == 1)
offset = d / 2; else
offset = (d - shift) / 2;
/* All the conditionals in here look slow, but the branches will *bewellpredictedandthereareenoughdifferentpossibilities *thattryingtowritethisasaseriesofunconditionalloops *ishardandnotanobviouswin.Themainslowdownhereseems *tobetheintegerdivisionperpixel;onepossibleoptimization *wouldbetoaccumulateintotwo16-bitintegerbuffersand *onlydividedownafterallthreepasses.(SSEparallelimplementation *ofthedividestepispossible.)
*/
#define BLUR_ROW_KERNEL(D) \ for (i = -(D) + offset; i < row_width + offset; i++) \
{ \ if (i >= 0 && i < row_width) \
sum += row[i]; \
\ if (i >= offset) \
{ \ if (i >= (D)) \
sum -= row[i - (D)]; \
\
tmp_buffer[i - offset] = (sum + (D) / 2) / (D); \
} \
} \ break;
/* We unroll the values for d for radius 2-10 to avoid a generic
* divide operation (not radius 1, because its a no-op) */ switch (d)
{ case BOX_FILTER_SIZE_2: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_2); case BOX_FILTER_SIZE_3: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_3); case BOX_FILTER_SIZE_4: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_4); case BOX_FILTER_SIZE_5: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_5); case BOX_FILTER_SIZE_6: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_6); case BOX_FILTER_SIZE_7: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_7); case BOX_FILTER_SIZE_8: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_8); case BOX_FILTER_SIZE_9: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_9); case BOX_FILTER_SIZE_10: BLUR_ROW_KERNEL (BOX_FILTER_SIZE_10); default: BLUR_ROW_KERNEL (d);
}
memcpy (row, tmp_buffer, row_width);
}
staticvoid
blur_rows (guchar *dst_buffer,
guchar *tmp_buffer, int buffer_width, int buffer_height, int d)
{ int i;
for (i = 0; i < buffer_height; i++)
{
guchar *row = dst_buffer + i * buffer_width;
/* We want to produce a symmetric blur that spreads a pixel *equallyfartotheleftandright.Ifdisoddthathappens *naturally,butfordeven,weapproximatebyusingablur *oneithersideandthenacenteredblurofsized+1. *(techniquealsofromtheSVGspecification)
*/ if (d % 2 == 1)
{
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
} else
{
blur_xspan (row, tmp_buffer, buffer_width, d, 1);
blur_xspan (row, tmp_buffer, buffer_width, d, -1);
blur_xspan (row, tmp_buffer, buffer_width, d + 1, 0);
}
}
}
/* Swaps width and height.
*/ staticvoid
flip_buffer (guchar *dst_buffer,
guchar *src_buffer, int width, int height)
{ /* Working in blocks increases cache efficiency, compared to reading *orwritinganentirecolumnatonce
*/ #define BLOCK_SIZE 16
int i0, j0;
for (i0 = 0; i0 < width; i0 += BLOCK_SIZE) for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
{ int max_j = MIN(j0 + BLOCK_SIZE, height); int max_i = MIN(i0 + BLOCK_SIZE, width); int i, j;
for (i = i0; i < max_i; i++) for (j = j0; j < max_j; j++)
dst_buffer[i * height + j] = src_buffer[j * width + i];
} #undef BLOCK_SIZE
}
staticvoid
_boxblur (guchar *buffer, int width, int height, int radius,
GskBlurFlags flags)
{
guchar *flipped_buffer; int d = get_box_filter_size (radius);
flipped_buffer = g_malloc (width * height);
if (flags & GSK_BLUR_Y)
{ /* Step 1: swap rows and columns */
flip_buffer (flipped_buffer, buffer, width, height);
static gboolean
needs_blur (float radius,
GskBlurFlags blur_flags)
{ /* Neither blurring horizontal nor vertical means no blurring at all. */ if ((blur_flags & (GSK_BLUR_X | GSK_BLUR_Y)) == 0) returnFALSE;
/* The code doesn't actually do any blurring for radius 1, as it
* ends up with box filter size 1 */ if (radius <= 1.0) returnFALSE;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.