github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/gift/rank.go (about)

     1  package gift
     2  
     3  import (
     4  	"image"
     5  	"image/draw"
     6  )
     7  
     8  type rankMode int
     9  
    10  const (
    11  	rankMedian rankMode = iota
    12  	rankMin
    13  	rankMax
    14  )
    15  
    16  type rankFilter struct {
    17  	ksize int
    18  	disk  bool
    19  	mode  rankMode
    20  }
    21  
    22  func (p *rankFilter) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangle) {
    23  	dstBounds = image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
    24  	return
    25  }
    26  
    27  func (p *rankFilter) Draw(dst draw.Image, src image.Image, options *Options) {
    28  	if options == nil {
    29  		options = &defaultOptions
    30  	}
    31  
    32  	srcb := src.Bounds()
    33  	dstb := dst.Bounds()
    34  
    35  	if srcb.Dx() <= 0 || srcb.Dy() <= 0 {
    36  		return
    37  	}
    38  
    39  	ksize := p.ksize
    40  	if ksize%2 == 0 {
    41  		ksize--
    42  	}
    43  
    44  	if ksize <= 1 {
    45  		copyimage(dst, src, options)
    46  		return
    47  	}
    48  	kradius := ksize / 2
    49  
    50  	opaque := isOpaque(src)
    51  
    52  	var disk []float32
    53  	if p.disk {
    54  		disk = genDisk(ksize)
    55  	}
    56  
    57  	pixGetter := newPixelGetter(src)
    58  	pixSetter := newPixelSetter(dst)
    59  
    60  	parallelize(options.Parallelization, srcb.Min.Y, srcb.Max.Y, func(pmin, pmax int) {
    61  		pxbuf := []pixel{}
    62  
    63  		var rbuf, gbuf, bbuf, abuf []float32
    64  		if p.mode == rankMedian {
    65  			rbuf = make([]float32, 0, ksize*ksize)
    66  			gbuf = make([]float32, 0, ksize*ksize)
    67  			bbuf = make([]float32, 0, ksize*ksize)
    68  			if !opaque {
    69  				abuf = make([]float32, 0, ksize*ksize)
    70  			}
    71  		}
    72  
    73  		for y := pmin; y < pmax; y++ {
    74  			// init buffer
    75  			pxbuf = pxbuf[0:0]
    76  			for i := srcb.Min.X - kradius; i <= srcb.Min.X+kradius; i++ {
    77  				for j := y - kradius; j <= y+kradius; j++ {
    78  					kx, ky := i, j
    79  					if kx < srcb.Min.X {
    80  						kx = srcb.Min.X
    81  					} else if kx > srcb.Max.X-1 {
    82  						kx = srcb.Max.X - 1
    83  					}
    84  					if ky < srcb.Min.Y {
    85  						ky = srcb.Min.Y
    86  					} else if ky > srcb.Max.Y-1 {
    87  						ky = srcb.Max.Y - 1
    88  					}
    89  					pxbuf = append(pxbuf, pixGetter.getPixel(kx, ky))
    90  				}
    91  			}
    92  
    93  			for x := srcb.Min.X; x < srcb.Max.X; x++ {
    94  				var r, g, b, a float32
    95  				if p.mode == rankMedian {
    96  					rbuf = rbuf[0:0]
    97  					gbuf = gbuf[0:0]
    98  					bbuf = bbuf[0:0]
    99  					if !opaque {
   100  						abuf = abuf[0:0]
   101  					}
   102  				} else if p.mode == rankMin {
   103  					r, g, b, a = 1.0, 1.0, 1.0, 1.0
   104  				} else if p.mode == rankMax {
   105  					r, g, b, a = 0.0, 0.0, 0.0, 0.0
   106  				}
   107  
   108  				sz := 0
   109  				for i := 0; i < ksize; i++ {
   110  					for j := 0; j < ksize; j++ {
   111  
   112  						if p.disk {
   113  							if disk[i*ksize+j] == 0.0 {
   114  								continue
   115  							}
   116  						}
   117  
   118  						px := pxbuf[i*ksize+j]
   119  						if p.mode == rankMedian {
   120  							rbuf = append(rbuf, px.R)
   121  							gbuf = append(gbuf, px.G)
   122  							bbuf = append(bbuf, px.B)
   123  							if !opaque {
   124  								abuf = append(abuf, px.A)
   125  							}
   126  						} else if p.mode == rankMin {
   127  							r = minf32(r, px.R)
   128  							g = minf32(g, px.G)
   129  							b = minf32(b, px.B)
   130  							if !opaque {
   131  								a = minf32(a, px.A)
   132  							}
   133  						} else if p.mode == rankMax {
   134  							r = maxf32(r, px.R)
   135  							g = maxf32(g, px.G)
   136  							b = maxf32(b, px.B)
   137  							if !opaque {
   138  								a = maxf32(a, px.A)
   139  							}
   140  						}
   141  						sz++
   142  					}
   143  				}
   144  
   145  				if p.mode == rankMedian {
   146  					qsortf32(rbuf)
   147  					qsortf32(gbuf)
   148  					qsortf32(bbuf)
   149  					if !opaque {
   150  						qsortf32(abuf)
   151  					}
   152  
   153  					idx := sz / 2
   154  					r, g, b = rbuf[idx], gbuf[idx], bbuf[idx]
   155  					if !opaque {
   156  						a = abuf[idx]
   157  					}
   158  				}
   159  
   160  				if opaque {
   161  					a = 1.0
   162  				}
   163  
   164  				pixSetter.setPixel(dstb.Min.X+x-srcb.Min.X, dstb.Min.Y+y-srcb.Min.Y, pixel{r, g, b, a})
   165  
   166  				// rotate buffer columns
   167  				if x < srcb.Max.X-1 {
   168  					copy(pxbuf[0:], pxbuf[ksize:])
   169  					pxbuf = pxbuf[0 : ksize*(ksize-1)]
   170  					kx := x + 1 + kradius
   171  					if kx > srcb.Max.X-1 {
   172  						kx = srcb.Max.X - 1
   173  					}
   174  					for j := y - kradius; j <= y+kradius; j++ {
   175  						ky := j
   176  						if ky < srcb.Min.Y {
   177  							ky = srcb.Min.Y
   178  						} else if ky > srcb.Max.Y-1 {
   179  							ky = srcb.Max.Y - 1
   180  						}
   181  						pxbuf = append(pxbuf, pixGetter.getPixel(kx, ky))
   182  					}
   183  				}
   184  			}
   185  		}
   186  	})
   187  }
   188  
   189  // Median creates a median image filter.
   190  // Picks a median value per channel in neighborhood for each pixel.
   191  // The ksize parameter is the kernel size. It must be an odd positive integer (for example: 3, 5, 7).
   192  // If the disk parameter is true, a disk-shaped neighborhood will be used instead of a square neighborhood.
   193  func Median(ksize int, disk bool) Filter {
   194  	return &rankFilter{
   195  		ksize: ksize,
   196  		disk:  disk,
   197  		mode:  rankMedian,
   198  	}
   199  }
   200  
   201  // Minimum creates a local minimum image filter.
   202  // Picks a minimum value per channel in neighborhood for each pixel.
   203  // The ksize parameter is the kernel size. It must be an odd positive integer (for example: 3, 5, 7).
   204  // If the disk parameter is true, a disk-shaped neighborhood will be used instead of a square neighborhood.
   205  func Minimum(ksize int, disk bool) Filter {
   206  	return &rankFilter{
   207  		ksize: ksize,
   208  		disk:  disk,
   209  		mode:  rankMin,
   210  	}
   211  }
   212  
   213  // Maximum creates a local maximum image filter.
   214  // Picks a maximum value per channel in neighborhood for each pixel.
   215  // The ksize parameter is the kernel size. It must be an odd positive integer (for example: 3, 5, 7).
   216  // If the disk parameter is true, a disk-shaped neighborhood will be used instead of a square neighborhood.
   217  func Maximum(ksize int, disk bool) Filter {
   218  	return &rankFilter{
   219  		ksize: ksize,
   220  		disk:  disk,
   221  		mode:  rankMax,
   222  	}
   223  }