@@ -61,17 +61,23 @@ static void run (const gchar *name,
61
61
gint * nreturn_vals ,
62
62
GimpParam * * return_vals );
63
63
64
- static void blur_line (const gdouble * cmatrix ,
64
+ static void gaussian_blur_line (const gdouble * cmatrix ,
65
65
const gint cmatrix_length ,
66
66
const guchar * src ,
67
67
guchar * dest ,
68
68
const gint len ,
69
- const gint bytes );
69
+ const gint bpp );
70
+ static void box_blur_line (const gint box_width ,
71
+ const gint even_offset ,
72
+ const guchar * src ,
73
+ guchar * dest ,
74
+ const gint len ,
75
+ const gint bpp );
70
76
static gint gen_convolve_matrix (gdouble std_dev ,
71
77
gdouble * * cmatrix );
72
78
static void unsharp_region (GimpPixelRgn * srcPTR ,
73
79
GimpPixelRgn * dstPTR ,
74
- gint bytes ,
80
+ gint bpp ,
75
81
gdouble radius ,
76
82
gdouble amount ,
77
83
gint x1 ,
@@ -235,17 +241,114 @@ run (const gchar *name,
235
241
#endif
236
242
}
237
243
244
+ /* This function is written as if it is blurring a row of pixels,
245
+ * even though it can operate on colums, too. There is no difference
246
+ * in the processing of the lines, at least to the blur_line function.
247
+ */
248
+ static void
249
+ box_blur_line (const gint box_width , /* Width of the kernel */
250
+ const gint even_offset ,/* If even width,
251
+ offset to left or right */
252
+ const guchar * src , /* Pointer to source buffer */
253
+ guchar * dest , /* Pointer to destination buffer */
254
+ const gint len , /* Length of buffer, in pixels */
255
+ const gint bpp ) /* Bytes per pixel */
256
+ {
257
+ gint i ;
258
+ gint lead ; /* This marks the leading edge of the kernel */
259
+ gint output ; /* This marks the center of ther kernel */
260
+ gint trail ; /* This marks the pixel BEHIND the last 1 in the
261
+ kernel; it's the pixel to remove from the accumulator. */
262
+ gint ac [bpp ]; /* Accumulator for each channel */
263
+
264
+
265
+ /* The algorithm differs for even and odd-sized kernels.
266
+ * With the output at the center,
267
+ * If odd, the kernel might look like this: 0011100
268
+ * If even, the kernel will either be centered on the boundary between
269
+ * the output and its left neighbor, or on the boundary between the
270
+ * output and its right neighbor, depending on even_lr.
271
+ * So it might be 0111100 or 0011110, where output is on the center
272
+ * of these arrays.
273
+ */
274
+ lead = 0 ;
275
+
276
+ if (box_width % 2 )
277
+ /* Odd-width kernel */
278
+ {
279
+ output = lead - (box_width - 1 ) / 2 ;
280
+ trail = lead - box_width ;
281
+ }
282
+ else
283
+ /* Even-width kernel. */
284
+ {
285
+ /* Right offset */
286
+ if (even_offset == 1 )
287
+ {
288
+ output = lead + 1 - box_width / 2 ;
289
+ trail = lead - box_width ;
290
+ }
291
+ /* Left offset */
292
+ else if (even_offset == -1 )
293
+ {
294
+ output = lead - box_width / 2 ;
295
+ trail = lead - box_width ;
296
+ }
297
+ /* If even_offset isn't 1 or -1, there's some error. */
298
+ else
299
+ g_assert_not_reached ();
300
+ }
301
+
302
+ /* Initialize accumulator */
303
+ for (i = 0 ; i < bpp ; i ++ )
304
+ ac [i ] = 0 ;
305
+
306
+ while (output < len )
307
+ {
308
+ /* The number of pixels that are both in the image and
309
+ * currently covered by the kernel. This is necessary to
310
+ * handle edge cases. */
311
+ guint coverage = (lead < len ? lead : len - 1 ) - (trail >=0 ? trail : -1 );
312
+
313
+ for (i = 0 ; i < bpp ; i ++ )
314
+ {
315
+ /* If the leading edge of the kernel is still on the image,
316
+ * add the value there to the accumulator. */
317
+ if (lead < len )
318
+ ac [i ] += src [bpp * lead + i ];
319
+
320
+ /* If the trailing edge of the kernel is on the image,
321
+ * subtract the value there from the accumulator. */
322
+ if (trail >= 0 )
323
+ ac [i ] -= src [bpp * trail + i ];
324
+
325
+ /* Take the averaged value in the accumulator and store
326
+ * that value in the output. The number of pixels currently
327
+ * stored in the accumulator can be less than the nominal
328
+ * width of the kernel because the kernel can go "over the edge"
329
+ * of the image. */
330
+ if (output >= 0 )
331
+ dest [bpp * output + i ] = (ac [i ] + (coverage >> 1 )) / coverage ;
332
+ }
333
+
334
+ lead ++ ;
335
+ output ++ ;
336
+ trail ++ ;
337
+ }
338
+ }
339
+
340
+
238
341
/* This function is written as if it is blurring a column at a time,
239
342
* even though it can operate on rows, too. There is no difference
240
343
* in the processing of the lines, at least to the blur_line function.
241
344
*/
242
345
static void
243
- blur_line (const gdouble * cmatrix ,
244
- const gint cmatrix_length ,
245
- const guchar * src ,
246
- guchar * dest ,
247
- const gint len ,
248
- const gint bytes )
346
+ gaussian_blur_line (const gdouble * cmatrix ,
347
+ const gint cmatrix_length ,
348
+ const guchar * src ,
349
+ guchar * dest ,
350
+ const gint len ,
351
+ const gint bpp )
249
352
{
250
353
const gdouble * cmatrix_p ;
251
354
const guchar * src_p ;
@@ -275,7 +378,7 @@ blur_line (const gdouble *cmatrix,
275
378
276
379
src_p = src ;
277
380
278
- for (i = 0 ; i < bytes ; i ++ )
381
+ for (i = 0 ; i < bpp ; i ++ )
279
382
{
280
383
gdouble sum = 0 ;
281
384
@@ -287,7 +390,7 @@ blur_line (const gdouble *cmatrix,
287
390
j + cmatrix_middle - row < cmatrix_length )
288
391
sum += * src_p1 * cmatrix [j ];
289
392
290
- src_p1 += bytes ;
393
+ src_p1 += bpp ;
291
394
}
292
395
293
396
* dest ++ = (guchar ) ROUND (sum / scale );
@@ -307,7 +410,7 @@ blur_line (const gdouble *cmatrix,
307
410
308
411
src_p = src ;
309
412
310
- for (i = 0 ; i < bytes ; i ++ )
413
+ for (i = 0 ; i < bpp ; i ++ )
311
414
{
312
415
gdouble sum = 0 ;
313
416
@@ -316,7 +419,7 @@ blur_line (const gdouble *cmatrix,
316
419
for (j = cmatrix_middle - row ; j < cmatrix_length ; j ++ )
317
420
{
318
421
sum += * src_p1 * cmatrix [j ];
319
- src_p1 += bytes ;
422
+ src_p1 += bpp ;
320
423
}
321
424
322
425
* dest ++ = (guchar ) ROUND (sum / scale );
@@ -326,9 +429,9 @@ blur_line (const gdouble *cmatrix,
326
429
/* go through each pixel in each col */
327
430
for (; row < len - cmatrix_middle ; row ++ )
328
431
{
329
- src_p = src + (row - cmatrix_middle ) * bytes ;
432
+ src_p = src + (row - cmatrix_middle ) * bpp ;
330
433
331
- for (i = 0 ; i < bytes ; i ++ )
434
+ for (i = 0 ; i < bpp ; i ++ )
332
435
{
333
436
gdouble sum = 0 ;
334
437
@@ -338,7 +441,7 @@ blur_line (const gdouble *cmatrix,
338
441
for (j = 0 ; j < cmatrix_length ; j ++ )
339
442
{
340
443
sum += cmatrix [j ] * * src_p1 ;
341
- src_p1 += bytes ;
444
+ src_p1 += bpp ;
342
445
}
343
446
344
447
src_p ++ ;
@@ -355,9 +458,9 @@ blur_line (const gdouble *cmatrix,
355
458
for (j = 0 ; j < len - row + cmatrix_middle ; j ++ )
356
459
scale += cmatrix [j ];
357
460
358
- src_p = src + (row - cmatrix_middle ) * bytes ;
461
+ src_p = src + (row - cmatrix_middle ) * bpp ;
359
462
360
- for (i = 0 ; i < bytes ; i ++ )
463
+ for (i = 0 ; i < bpp ; i ++ )
361
464
{
362
465
gdouble sum = 0 ;
363
466
@@ -366,7 +469,7 @@ blur_line (const gdouble *cmatrix,
366
469
for (j = 0 ; j < len - row + cmatrix_middle ; j ++ )
367
470
{
368
471
sum += * src_p1 * cmatrix [j ];
369
- src_p1 += bytes ;
472
+ src_p1 += bpp ;
370
473
}
371
474
372
475
* dest ++ = (guchar ) ROUND (sum / scale );
@@ -409,51 +512,124 @@ unsharp_mask (GimpDrawable *drawable,
409
512
static void
410
513
unsharp_region (GimpPixelRgn * srcPR ,
411
514
GimpPixelRgn * destPR ,
412
- gint bytes ,
413
- gdouble radius ,
515
+ gint bpp ,
516
+ gdouble radius , /* Radius, AKA standard deviation */
414
517
gdouble amount ,
415
518
gint x1 ,
416
519
gint x2 ,
417
520
gint y1 ,
418
521
gint y2 ,
419
522
gboolean show_progress )
420
523
{
421
- guchar * src ;
422
- guchar * dest ;
423
- gint width = x2 - x1 ;
424
- gint height = y2 - y1 ;
425
- gdouble * cmatrix = NULL ;
426
- gint cmatrix_length ;
427
- gint row , col ;
428
- gint threshold = unsharp_params .threshold ;
524
+ guchar * src ; /* Temporary copy of source row/col */
525
+ guchar * dest ; /* Temporary copy of destination row/col */
526
+ const gint width = x2 - x1 ;
527
+ const gint height = y2 - y1 ;
528
+ gdouble * cmatrix = NULL ; /* Convolution matrix (for gaussian) */
529
+ gint cmatrix_length = 0 ;
530
+ gint row , col ; /* Row,column counter */
531
+ const gint threshold = unsharp_params .threshold ;
532
+ gboolean box_blur ; /* If we want to use a three pass box blur
533
+ * instead of a gaussian blur
534
+ */
535
+ gint box_width = 0 ;
429
536
430
537
if (show_progress )
431
538
gimp_progress_init (_ ("Blurring" ));
432
539
433
- /* generate convolution matrix
434
- and make sure it's smaller than each dimension */
435
- cmatrix_length = gen_convolve_matrix (radius , & cmatrix );
540
+ /* If the radius is less than 10, use a true gaussian kernel. This
541
+ * is slower, but more accurate and allows for finer adjustments.
542
+ * Otherwise use a three-pass box blur; this is much faster but it
543
+ * isn't a perfect approximation, and it only allows radius
544
+ * increments of about 0.42.
545
+ */
546
+ if (radius < 10 )
547
+ {
548
+ box_blur = FALSE;
549
+ /* If true gaussian, generate convolution matrix
550
+ and make sure it's smaller than each dimension */
551
+ cmatrix_length = gen_convolve_matrix (radius , & cmatrix );
552
+ }
553
+ else
554
+ {
555
+ box_blur = TRUE;
556
+ /* Three box blurs of this width approximate a gaussian */
557
+ box_width = ROUND (radius * 3 * sqrt (2 * G_PI ) / 4 );
558
+ }
436
559
437
- /* allocate buffers */
438
- src = g_new (guchar , MAX (width , height ) * bytes );
439
- dest = g_new (guchar , MAX (width , height ) * bytes );
560
+ /* Allocate buffers temporary copies of a row/column */
561
+ src = g_new (guchar , MAX (width , height ) * bpp );
562
+ dest = g_new (guchar , MAX (width , height ) * bpp );
440
563
441
- /* blur the rows */
564
+ /* Blur the rows */
442
565
for (row = 0 ; row < height ; row ++ )
443
566
{
444
567
gimp_pixel_rgn_get_row (srcPR , src , x1 , y1 + row , width );
445
- blur_line (cmatrix , cmatrix_length , src , dest , width , bytes );
568
+
569
+ if (box_blur )
570
+ {
571
+ /* Odd-width box blur: repeat 3 times, centered on output pixel.
572
+ * Swap back and forth between the buffers. */
573
+ if (box_width % 2 )
574
+ {
575
+ box_blur_line (box_width , 0 , src , dest , width , bpp );
576
+ box_blur_line (box_width , 0 , dest , src , width , bpp );
577
+ box_blur_line (box_width , 0 , src , dest , width , bpp );
578
+ }
579
+ /* Even-width box blur:
580
+ * This method is suggested by the specification for SVG.
581
+ * One pass with width n, centered between output and right pixel
582
+ * One pass with width n, centered between output and left pixel
583
+ * One pass with width n+1, centered on output pixel
584
+ * Swap back and forth between buffers.
585
+ */
586
+ else
587
+ {
588
+ box_blur_line (box_width , -1 , src , dest , width , bpp );
589
+ box_blur_line (box_width , 1 , dest , src , width , bpp );
590
+ box_blur_line (box_width + 1 , 0 , src , dest , width , bpp );
591
+ }
592
+ }
593
+ else
594
+ {
595
+ /* Gaussian blur */
596
+ gaussian_blur_line (cmatrix , cmatrix_length , src , dest , width , bpp );
597
+ }
598
+
446
599
gimp_pixel_rgn_set_row (destPR , dest , x1 , y1 + row , width );
447
600
448
601
if (show_progress && row % 16 == 0 )
449
602
gimp_progress_update ((gdouble ) row / (3 * height ));
450
603
}
451
604
452
- /* blur the cols */
605
+ /* Blur the cols. Essentially same as above. */
453
606
for (col = 0 ; col < width ; col ++ )
454
607
{
455
608
gimp_pixel_rgn_get_col (destPR , src , x1 + col , y1 , height );
456
- blur_line (cmatrix , cmatrix_length , src , dest , height , bytes );
609
+
610
+ if (box_blur )
611
+ {
612
+ /* Odd-width box blur */
613
+ if (box_width % 2 )
614
+ {
615
+ box_blur_line (box_width , 0 , src , dest , height , bpp );
616
+ box_blur_line (box_width , 0 , dest , src , height , bpp );
617
+ box_blur_line (box_width , 0 , src , dest , height , bpp );
618
+ }
619
+ /* Even-width box blur */
620
+ else
621
+ {
622
+ box_blur_line (box_width , -1 , src , dest , height , bpp );
623
+ box_blur_line (box_width , 1 , dest , src , height , bpp );
624
+ box_blur_line (box_width + 1 , 0 , src , dest , height , bpp );
625
+ }
626
+ }
627
+ else
628
+ {
629
+ /* Gaussian blur */
630
+ gaussian_blur_line (cmatrix , cmatrix_length , src , dest ,height , bpp );
631
+ }
632
+
457
633
gimp_pixel_rgn_set_col (destPR , dest , x1 + col , y1 , height );
458
634
459
635
if (show_progress && col % 16 == 0 )
@@ -480,7 +656,7 @@ unsharp_region (GimpPixelRgn *srcPR,
480
656
/* combine the two */
481
657
for (u = 0 ; u < width ; u ++ )
482
658
{
483
- for (v = 0 ; v < bytes ; v ++ )
659
+ for (v = 0 ; v < bpp ; v ++ )
484
660
{
485
661
gint value ;
486
662
gint diff = * s - * d ;
0 commit comments