Confused about supersampling in tgpl

I am working my way through “The Go Programming Language” by Alan Donovan and Brian Kernighan. I am on exercise 3.6 which reads as

“Supersampling is a technique to reduce the effect of pixelation by computing the color value at several points within each pixel and taking the average. The simplest method is to divide each pixel into four “subpixels.” Implement it.”

Here is the code I am supposed to add this to

// This version has color.
// Uses supersampling
package main

import (
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
)

func main() {
	const (
		xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height = 1024, 1024
	)

	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py++ {
		y := float64(py)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px++ {
			x := float64(px)/width*(xmax-xmin) + xmin
			z := complex(x, y)
			// image point (px, py) represents complex value z.
			img.Set(px, py, mandelbrot(z))
		}
	}
	png.Encode(os.Stdout, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.RGBA {
	const iterations = 255
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			return color.RGBA{contrast*(n+2), contrast*(n+1), contrast*n, 255}
		}
	}
	return color.RGBA{0, 0, 0, 255}
}

Now I have several questions.

  1. Everything I read says a pixel is one solid color so how does breaking it into subpixels and sampling them help wouldn’t they all just be the color of the pixel that you broke up into subpixels ?

  2. Do I need to wait until after png.Encode() runs and open the image and then run this on it or can I do this immediately after coloring the pixel ? I am wondering this because I am thinking perhaps that the encoding causes more then one color to be present in the pixel since my program only places one color in them.

  3. I have been looking through the packages and there documentation and I can figure out how to extract individual bit or parts of the RGBA value ( Example: I could extract hex value of G ). I can break it into sections of its underlying code but none of those things give me a complete color. Could someone point me in the right direction here ?

Any help would be greatly appreciated.

Pixels indeed only have one, solid color each. Supersampling is a process used to determine what that color should be, when there is higher resolution information available; https://en.m.wikipedia.org/wiki/Supersampling

Your task here is probably to render the fractal at higher resolution and use supersampling to determine the pixel colors, before encoding.

1 Like

@calmh @rot13 This is what I did based on your advice.

// Exercise3.6 emits a PNG image of the Mandelbrot fractal.
// This version has color.
// Uses supersampling
package main

import (
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
)

func main() {
	const (
		xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height          = 1024, 1024
	)
        
    // creating image at *2 resolution.
	img2 := image.NewRGBA(image.Rect(0, 0, width * 2, height * 2))
	for py := 0; py < height * 2; py++ {
		y := float64(py)/(height*2)*(ymax-ymin) + ymin
		for px := 0; px < width * 2; px++ {
			x := float64(px)/(width*2)*(xmax-xmin) + xmin
			z := complex(x, y)
			// image point (px, py) represents complex value z.
			img2.SetRGBA(px, py, mandelbrot(z))
		}
	}
    // decreasing image size.
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height*2; py = py + 2 {
		for px := 0; px < width*2; px = px + 2 {
			var c = []color.RGBA{
				img2.RGBAAt(px, py),
				img2.RGBAAt(px+1, py),
				img2.RGBAAt(px, py+1),
				img2.RGBAAt(px+1, py+1),
			}
			var pr, pg, pb, pa int
			for n := 0; n < 4; n++ {
				pr += int(c[n].R)
				pg += int(c[n].G)
				pb += int(c[n].B)
				pa += int(c[n].A)
			}
			img.SetRGBA(px/2, py/2, color.RGBA{uint8(pr / 4), uint8(pg / 4), uint8(pb / 4), uint8(pa / 4)})
		}
	}
	png.Encode(os.Stdout, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.RGBA {
	const iterations = 255
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			return color.RGBA{contrast * (n + 2), contrast * (n + 1), contrast * n, 255}
		}
	}
	return color.RGBA{0, 0, 0, 255}
}

I believe it is working. Since I kept all colors at alpha 255 averaging worked fine but if I wanted to work with more variation your alpha hacks would be useful and they where interesting reading. If you all could look over this code and let me know if you think I’ve got it right or you see any problems let me know. Thanks for the help so far.

@calmh Thanks for the help. I was able to work out the solution based on your advise here.

@rot13 Thanks for the help it looks like these tips will be useful on my next assignment.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.