gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

adjust.go (7414B)


      1 package imaging
      2 
      3 import (
      4 	"image"
      5 	"image/color"
      6 	"math"
      7 )
      8 
      9 // Grayscale produces a grayscale version of the image.
     10 func Grayscale(img image.Image) *image.NRGBA {
     11 	src := newScanner(img)
     12 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
     13 	parallel(0, src.h, func(ys <-chan int) {
     14 		for y := range ys {
     15 			i := y * dst.Stride
     16 			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
     17 			for x := 0; x < src.w; x++ {
     18 				d := dst.Pix[i : i+3 : i+3]
     19 				r := d[0]
     20 				g := d[1]
     21 				b := d[2]
     22 				f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
     23 				y := uint8(f + 0.5)
     24 				d[0] = y
     25 				d[1] = y
     26 				d[2] = y
     27 				i += 4
     28 			}
     29 		}
     30 	})
     31 	return dst
     32 }
     33 
     34 // Invert produces an inverted (negated) version of the image.
     35 func Invert(img image.Image) *image.NRGBA {
     36 	src := newScanner(img)
     37 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
     38 	parallel(0, src.h, func(ys <-chan int) {
     39 		for y := range ys {
     40 			i := y * dst.Stride
     41 			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
     42 			for x := 0; x < src.w; x++ {
     43 				d := dst.Pix[i : i+3 : i+3]
     44 				d[0] = 255 - d[0]
     45 				d[1] = 255 - d[1]
     46 				d[2] = 255 - d[2]
     47 				i += 4
     48 			}
     49 		}
     50 	})
     51 	return dst
     52 }
     53 
     54 // AdjustSaturation changes the saturation of the image using the percentage parameter and returns the adjusted image.
     55 // The percentage must be in the range (-100, 100).
     56 // The percentage = 0 gives the original image.
     57 // The percentage = 100 gives the image with the saturation value doubled for each pixel.
     58 // The percentage = -100 gives the image with the saturation value zeroed for each pixel (grayscale).
     59 //
     60 // Examples:
     61 //  dstImage = imaging.AdjustSaturation(srcImage, 25) // Increase image saturation by 25%.
     62 //  dstImage = imaging.AdjustSaturation(srcImage, -10) // Decrease image saturation by 10%.
     63 //
     64 func AdjustSaturation(img image.Image, percentage float64) *image.NRGBA {
     65 	percentage = math.Min(math.Max(percentage, -100), 100)
     66 	multiplier := 1 + percentage/100
     67 
     68 	return AdjustFunc(img, func(c color.NRGBA) color.NRGBA {
     69 		h, s, l := rgbToHSL(c.R, c.G, c.B)
     70 		s *= multiplier
     71 		if s > 1 {
     72 			s = 1
     73 		}
     74 		r, g, b := hslToRGB(h, s, l)
     75 		return color.NRGBA{r, g, b, c.A}
     76 	})
     77 }
     78 
     79 // AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
     80 // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
     81 // The percentage = -100 gives solid gray image.
     82 //
     83 // Examples:
     84 //
     85 //	dstImage = imaging.AdjustContrast(srcImage, -10) // Decrease image contrast by 10%.
     86 //	dstImage = imaging.AdjustContrast(srcImage, 20) // Increase image contrast by 20%.
     87 //
     88 func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
     89 	percentage = math.Min(math.Max(percentage, -100.0), 100.0)
     90 	lut := make([]uint8, 256)
     91 
     92 	v := (100.0 + percentage) / 100.0
     93 	for i := 0; i < 256; i++ {
     94 		switch {
     95 		case 0 <= v && v <= 1:
     96 			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
     97 		case 1 < v && v < 2:
     98 			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
     99 		default:
    100 			lut[i] = uint8(float64(i)/255.0+0.5) * 255
    101 		}
    102 	}
    103 
    104 	return adjustLUT(img, lut)
    105 }
    106 
    107 // AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
    108 // The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
    109 // The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
    110 //
    111 // Examples:
    112 //
    113 //	dstImage = imaging.AdjustBrightness(srcImage, -15) // Decrease image brightness by 15%.
    114 //	dstImage = imaging.AdjustBrightness(srcImage, 10) // Increase image brightness by 10%.
    115 //
    116 func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
    117 	percentage = math.Min(math.Max(percentage, -100.0), 100.0)
    118 	lut := make([]uint8, 256)
    119 
    120 	shift := 255.0 * percentage / 100.0
    121 	for i := 0; i < 256; i++ {
    122 		lut[i] = clamp(float64(i) + shift)
    123 	}
    124 
    125 	return adjustLUT(img, lut)
    126 }
    127 
    128 // AdjustGamma performs a gamma correction on the image and returns the adjusted image.
    129 // Gamma parameter must be positive. Gamma = 1.0 gives the original image.
    130 // Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
    131 //
    132 // Example:
    133 //
    134 //	dstImage = imaging.AdjustGamma(srcImage, 0.7)
    135 //
    136 func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
    137 	e := 1.0 / math.Max(gamma, 0.0001)
    138 	lut := make([]uint8, 256)
    139 
    140 	for i := 0; i < 256; i++ {
    141 		lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
    142 	}
    143 
    144 	return adjustLUT(img, lut)
    145 }
    146 
    147 // AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
    148 // It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
    149 // The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
    150 // The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
    151 // If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
    152 //
    153 // Examples:
    154 //
    155 //	dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // Increase the contrast.
    156 //	dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // Decrease the contrast.
    157 //
    158 func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
    159 	if factor == 0 {
    160 		return Clone(img)
    161 	}
    162 
    163 	lut := make([]uint8, 256)
    164 	a := math.Min(math.Max(midpoint, 0.0), 1.0)
    165 	b := math.Abs(factor)
    166 	sig0 := sigmoid(a, b, 0)
    167 	sig1 := sigmoid(a, b, 1)
    168 	e := 1.0e-6
    169 
    170 	if factor > 0 {
    171 		for i := 0; i < 256; i++ {
    172 			x := float64(i) / 255.0
    173 			sigX := sigmoid(a, b, x)
    174 			f := (sigX - sig0) / (sig1 - sig0)
    175 			lut[i] = clamp(f * 255.0)
    176 		}
    177 	} else {
    178 		for i := 0; i < 256; i++ {
    179 			x := float64(i) / 255.0
    180 			arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
    181 			f := a - math.Log(1.0/arg-1.0)/b
    182 			lut[i] = clamp(f * 255.0)
    183 		}
    184 	}
    185 
    186 	return adjustLUT(img, lut)
    187 }
    188 
    189 func sigmoid(a, b, x float64) float64 {
    190 	return 1 / (1 + math.Exp(b*(a-x)))
    191 }
    192 
    193 // adjustLUT applies the given lookup table to the colors of the image.
    194 func adjustLUT(img image.Image, lut []uint8) *image.NRGBA {
    195 	src := newScanner(img)
    196 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
    197 	lut = lut[0:256]
    198 	parallel(0, src.h, func(ys <-chan int) {
    199 		for y := range ys {
    200 			i := y * dst.Stride
    201 			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
    202 			for x := 0; x < src.w; x++ {
    203 				d := dst.Pix[i : i+3 : i+3]
    204 				d[0] = lut[d[0]]
    205 				d[1] = lut[d[1]]
    206 				d[2] = lut[d[2]]
    207 				i += 4
    208 			}
    209 		}
    210 	})
    211 	return dst
    212 }
    213 
    214 // AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
    215 //
    216 // Example:
    217 //
    218 //	dstImage = imaging.AdjustFunc(
    219 //		srcImage,
    220 //		func(c color.NRGBA) color.NRGBA {
    221 //			// Shift the red channel by 16.
    222 //			r := int(c.R) + 16
    223 //			if r > 255 {
    224 //				r = 255
    225 //			}
    226 //			return color.NRGBA{uint8(r), c.G, c.B, c.A}
    227 //		}
    228 //	)
    229 //
    230 func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
    231 	src := newScanner(img)
    232 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
    233 	parallel(0, src.h, func(ys <-chan int) {
    234 		for y := range ys {
    235 			i := y * dst.Stride
    236 			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
    237 			for x := 0; x < src.w; x++ {
    238 				d := dst.Pix[i : i+4 : i+4]
    239 				r := d[0]
    240 				g := d[1]
    241 				b := d[2]
    242 				a := d[3]
    243 				c := fn(color.NRGBA{r, g, b, a})
    244 				d[0] = c.R
    245 				d[1] = c.G
    246 				d[2] = c.B
    247 				d[3] = c.A
    248 				i += 4
    249 			}
    250 		}
    251 	})
    252 	return dst
    253 }