~robertgzr/corrupter

74252e37d3a1e652bc75a4a602011ec248e08b4b — Robert Günzler 6 months ago b76ec21 master
Break out stages into public functions

Signed-off-by: Robert Günzler <r@gnzler.io>
1 files changed, 61 insertions(+), 46 deletions(-)

M corrupter.go
M corrupter.go => corrupter.go +61 -46
@@ 40,108 40,123 @@ type Options struct {

func Corrupt(m image.Image, opts Options) (*image.NRGBA, error) {
	seededRand := rand.New(rand.NewSource(opts.Seed))
	println(seededRand)

	// trying to obtain raw pointers to color data, since .At(), .Set() are very slow
	m_raw_stride, m_raw_pix := 0, []uint8(nil)
	var src *image.NRGBA

	// trying to obtain raw pointers to color data, since .At(), .Set() are very slow
	switch m.(type) {
	default:
		return nil, errors.New("unknown image type")
	case *image.NRGBA:
		m_raw := m.(*image.NRGBA)
		m_raw_stride = m_raw.Stride
		m_raw_pix = m_raw.Pix
		src = m.(*image.NRGBA)
	case *image.RGBA:
		m_raw := m.(*image.RGBA)
		m_raw_stride = m_raw.Stride
		m_raw_pix = m_raw.Pix
		src = image.NewNRGBA(m.Bounds())
		src.Stride = m_raw.Stride
		src.Pix = m_raw.Pix
	}

	b := m.Bounds()

	// first stage is dissolve+block corruption
	new_img := image.NewNRGBA(b)
	new_img1 := image.NewNRGBA(b)
	DissolveAndBlockCorruption(new_img1, src, b, seededRand, opts)

	// second stage is adding per-channel scan inconsistency and brightening
	new_img2 := image.NewNRGBA(b)
	ChannelInconsistencyAndBrightening(new_img2, new_img1, b, seededRand, opts)

	// third stage is to add chromatic abberation+chromatic trails
	// (trails happen because we're changing the same image we process)
	new_img3 := new_img2
	ChromaticAbberationAndTrails(new_img3, b, seededRand, opts)

	return new_img3, nil
}

func DissolveAndBlockCorruption(dst, src *image.NRGBA, b image.Rectangle, rng *rand.Rand, opts Options) {
	line_off := 0
	stride := 0.
	yset := 0
	for y := 0; y < b.Max.Y; y++ {
		for x := 0; x < b.Max.X; x++ {
			// Every BHEIGHT lines in average a new distorted block begins
			if seededRand.Intn(opts.Bheight*b.Max.X) == 0 {
				line_off = offset(seededRand, opts.Boffset)
				stride = seededRand.NormFloat64() * opts.Stride
			if rng.Intn(opts.Bheight*b.Max.X) == 0 {
				line_off = offset(rng, opts.Boffset)
				stride = rng.NormFloat64() * opts.Stride
				yset = y
			}

			// at the line where the block has begun, we don't want to offset the image
			// so stride_off is 0 on the block's line
			stride_off := int(stride * float64(y-yset))

			// offset is composed of the blur, block offset, and skew offset (stride)
			offx := offset(seededRand, opts.Mag) + line_off + stride_off
			offy := offset(seededRand, opts.Mag)
			offx := offset(rng, opts.Mag) + line_off + stride_off
			offy := offset(rng, opts.Mag)

			// copy the corresponding pixel (4 bytes) to the new image
			src_idx := m_raw_stride*wrap(y+offy, b.Max.Y) + 4*wrap(x+offx, b.Max.X)
			dst_idx := new_img.Stride*y + 4*x
			src_idx := src.Stride*wrap(y+offy, b.Max.Y) + 4*wrap(x+offx, b.Max.X)
			dst_idx := dst.Stride*y + 4*x

			copy(new_img.Pix[dst_idx:dst_idx+4], m_raw_pix[src_idx:src_idx+4])
			copy(dst.Pix[dst_idx:dst_idx+4], src.Pix[src_idx:src_idx+4])
		}
	}
}

	// second stage is adding per-channel scan inconsistency and brightening
	new_img1 := image.NewNRGBA(b)

// per-channel scan inconsistency and brightening
func ChannelInconsistencyAndBrightening(dst, src *image.NRGBA, b image.Rectangle, rng *rand.Rand, opts Options) {
	lr, lg, lb := opts.Lr, opts.Lg, opts.Lb
	for y := 0; y < b.Max.Y; y++ {
		for x := 0; x < b.Max.X; x++ {
			lr += seededRand.NormFloat64() * opts.Lag
			lg += seededRand.NormFloat64() * opts.Lag
			lb += seededRand.NormFloat64() * opts.Lag
			offx := offset(seededRand, opts.StdOffset)
			lr += rng.NormFloat64() * opts.Lag
			lg += rng.NormFloat64() * opts.Lag
			lb += rng.NormFloat64() * opts.Lag
			offx := offset(rng, opts.StdOffset)

			// obtain source pixel base offsets. red/blue border is also smoothed by offx
			ra_idx := new_img.Stride*y + 4*wrap(x+int(lr)-offx, b.Max.X)
			g_idx := new_img.Stride*y + 4*wrap(x+int(lg), b.Max.X)
			b_idx := new_img.Stride*y + 4*wrap(x+int(lb)+offx, b.Max.X)
			ra_idx := src.Stride*y + 4*wrap(x+int(lr)-offx, b.Max.X)
			g_idx := src.Stride*y + 4*wrap(x+int(lg), b.Max.X)
			b_idx := src.Stride*y + 4*wrap(x+int(lb)+offx, b.Max.X)

			// pixels are stored in (r, g, b, a) order in memory
			r := new_img.Pix[ra_idx]
			a := new_img.Pix[ra_idx+3]
			g := new_img.Pix[g_idx+1]
			b := new_img.Pix[b_idx+2]
			r := src.Pix[ra_idx]
			a := src.Pix[ra_idx+3]
			g := src.Pix[g_idx+1]
			b := src.Pix[b_idx+2]

			r, g, b = brighten(r, uint8(opts.Add)), brighten(g, uint8(opts.Add)), brighten(b, uint8(opts.Add))

			// copy the corresponding pixel (4 bytes) to the new image
			dst_idx := new_img1.Stride*y + 4*x
			copy(new_img1.Pix[dst_idx:dst_idx+4], []uint8{r, g, b, a})
			dst_idx := dst.Stride*y + 4*x
			copy(dst.Pix[dst_idx:dst_idx+4], []uint8{r, g, b, a})
		}
	}
}

	// third stage is to add chromatic abberation+chromatic trails
	// (trails happen because we're changing the same image we process)
// chromatic abberation+chromatic trails
func ChromaticAbberationAndTrails(dst *image.NRGBA, b image.Rectangle, rng *rand.Rand, opts Options) {
	for y := 0; y < b.Max.Y; y++ {
		for x := 0; x < b.Max.X; x++ {
			offx := int(opts.MeanAbber) + offset(seededRand, opts.StdAbber) // lower offset arg = longer trails
			offx := int(opts.MeanAbber) + offset(rng, opts.StdAbber) // lower offset arg = longer trails

			// obtain source pixel base offsets. only red and blue are distorted
			ra_idx := new_img1.Stride*y + 4*wrap(x+offx, b.Max.X)
			g_idx := new_img1.Stride*y + 4*x
			b_idx := new_img1.Stride*y + 4*wrap(x-offx, b.Max.X)
			ra_idx := dst.Stride*y + 4*wrap(x+offx, b.Max.X)
			g_idx := dst.Stride*y + 4*x
			b_idx := dst.Stride*y + 4*wrap(x-offx, b.Max.X)

			// pixels are stored in (r, g, b, a) order in memory
			r := new_img1.Pix[ra_idx]
			a := new_img1.Pix[ra_idx+3]
			g := new_img1.Pix[g_idx+1]
			b := new_img1.Pix[b_idx+2]
			r := dst.Pix[ra_idx]
			a := dst.Pix[ra_idx+3]
			g := dst.Pix[g_idx+1]
			b := dst.Pix[b_idx+2]

			// copy the corresponding pixel (4 bytes) to the SAME image. this gets us nice colorful trails
			dst_idx := new_img1.Stride*y + 4*x
			copy(new_img1.Pix[dst_idx:dst_idx+4], []uint8{r, g, b, a})
			dst_idx := dst.Stride*y + 4*x
			copy(dst.Pix[dst_idx:dst_idx+4], []uint8{r, g, b, a})
		}
	}

	return new_img1, nil
}

// force x to stay in [0, b) range. x is assumed to be in [-b,2*b) range