git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/imaging/utils.go (about)

     1  package imaging
     2  
     3  import (
     4  	"image"
     5  	"math"
     6  	"runtime"
     7  	"sync"
     8  	"sync/atomic"
     9  )
    10  
    11  var maxProcs int64
    12  
    13  // SetMaxProcs limits the number of concurrent processing goroutines to the given value.
    14  // A value <= 0 clears the limit.
    15  func SetMaxProcs(value int) {
    16  	atomic.StoreInt64(&maxProcs, int64(value))
    17  }
    18  
    19  // parallel processes the data in separate goroutines.
    20  func parallel(start, stop int, fn func(<-chan int)) {
    21  	count := stop - start
    22  	if count < 1 {
    23  		return
    24  	}
    25  
    26  	procs := runtime.GOMAXPROCS(0)
    27  	limit := int(atomic.LoadInt64(&maxProcs))
    28  	if procs > limit && limit > 0 {
    29  		procs = limit
    30  	}
    31  	if procs > count {
    32  		procs = count
    33  	}
    34  
    35  	c := make(chan int, count)
    36  	for i := start; i < stop; i++ {
    37  		c <- i
    38  	}
    39  	close(c)
    40  
    41  	var wg sync.WaitGroup
    42  	for i := 0; i < procs; i++ {
    43  		wg.Add(1)
    44  		go func() {
    45  			defer wg.Done()
    46  			fn(c)
    47  		}()
    48  	}
    49  	wg.Wait()
    50  }
    51  
    52  // absint returns the absolute value of i.
    53  func absint(i int) int {
    54  	if i < 0 {
    55  		return -i
    56  	}
    57  	return i
    58  }
    59  
    60  // clamp rounds and clamps float64 value to fit into uint8.
    61  func clamp(x float64) uint8 {
    62  	v := int64(x + 0.5)
    63  	if v > 255 {
    64  		return 255
    65  	}
    66  	if v > 0 {
    67  		return uint8(v)
    68  	}
    69  	return 0
    70  }
    71  
    72  func reverse(pix []uint8) {
    73  	if len(pix) <= 4 {
    74  		return
    75  	}
    76  	i := 0
    77  	j := len(pix) - 4
    78  	for i < j {
    79  		pi := pix[i : i+4 : i+4]
    80  		pj := pix[j : j+4 : j+4]
    81  		pi[0], pj[0] = pj[0], pi[0]
    82  		pi[1], pj[1] = pj[1], pi[1]
    83  		pi[2], pj[2] = pj[2], pi[2]
    84  		pi[3], pj[3] = pj[3], pi[3]
    85  		i += 4
    86  		j -= 4
    87  	}
    88  }
    89  
    90  func toNRGBA(img image.Image) *image.NRGBA {
    91  	if img, ok := img.(*image.NRGBA); ok {
    92  		return &image.NRGBA{
    93  			Pix:    img.Pix,
    94  			Stride: img.Stride,
    95  			Rect:   img.Rect.Sub(img.Rect.Min),
    96  		}
    97  	}
    98  	return Clone(img)
    99  }
   100  
   101  // rgbToHSL converts a color from RGB to HSL.
   102  func rgbToHSL(r, g, b uint8) (float64, float64, float64) {
   103  	rr := float64(r) / 255
   104  	gg := float64(g) / 255
   105  	bb := float64(b) / 255
   106  
   107  	max := math.Max(rr, math.Max(gg, bb))
   108  	min := math.Min(rr, math.Min(gg, bb))
   109  
   110  	l := (max + min) / 2
   111  
   112  	if max == min {
   113  		return 0, 0, l
   114  	}
   115  
   116  	var h, s float64
   117  	d := max - min
   118  	if l > 0.5 {
   119  		s = d / (2 - max - min)
   120  	} else {
   121  		s = d / (max + min)
   122  	}
   123  
   124  	switch max {
   125  	case rr:
   126  		h = (gg - bb) / d
   127  		if g < b {
   128  			h += 6
   129  		}
   130  	case gg:
   131  		h = (bb-rr)/d + 2
   132  	case bb:
   133  		h = (rr-gg)/d + 4
   134  	}
   135  	h /= 6
   136  
   137  	return h, s, l
   138  }
   139  
   140  // hslToRGB converts a color from HSL to RGB.
   141  func hslToRGB(h, s, l float64) (uint8, uint8, uint8) {
   142  	var r, g, b float64
   143  	if s == 0 {
   144  		v := clamp(l * 255)
   145  		return v, v, v
   146  	}
   147  
   148  	var q float64
   149  	if l < 0.5 {
   150  		q = l * (1 + s)
   151  	} else {
   152  		q = l + s - l*s
   153  	}
   154  	p := 2*l - q
   155  
   156  	r = hueToRGB(p, q, h+1/3.0)
   157  	g = hueToRGB(p, q, h)
   158  	b = hueToRGB(p, q, h-1/3.0)
   159  
   160  	return clamp(r * 255), clamp(g * 255), clamp(b * 255)
   161  }
   162  
   163  func hueToRGB(p, q, t float64) float64 {
   164  	if t < 0 {
   165  		t++
   166  	}
   167  	if t > 1 {
   168  		t--
   169  	}
   170  	if t < 1/6.0 {
   171  		return p + (q-p)*6*t
   172  	}
   173  	if t < 1/2.0 {
   174  		return q
   175  	}
   176  	if t < 2/3.0 {
   177  		return p + (q-p)*(2/3.0-t)*6
   178  	}
   179  	return p
   180  }