multirender

package module
v0.0.0-...-b09d0e8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2021 License: MIT Imports: 11 Imported by: 0

README

multirender

Build Status GoDoc License Go Report Card

Fast concurrent software rendering, triangle rasterization and pixel buffer manipulation.

screencap triangles

butterflies glitch effect

software rendered duck software rendered duck with glitch effect

Features and limitations

  • Can draw software-rendered triangles concurrently, using goroutines. The work of drawing the triangles is divided on the available CPU cores.
  • Provides flat-shaded triangles.
  • Everything is drawn to a []uint32 pixel buffer (containing "red", "green", "blue" and "alpha").
  • Tested together with SDL2, but can be used with any graphics library that can output pixels from a pixel buffer.
  • Everything you need for creating an oldschool game or demoscene demo that will run on Linux, macOS and Windows, while using all of the cores.
  • Does not support palette cycling, yet.
  • The software rendering of 3D graphics in the screenshot above is provided by fauxgl. The outputs from this can be combined with effects from multirender.

Requires Go 1.3 or later, and SDL2.

Example, using multirender and SDL2:

package main

import (
	"fmt"
	"image/color"
	"math/rand"
	"os"
	"runtime"
	"time"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/xyproto/multirender"
	"github.com/xyproto/sdl2utils"
)

const (
	// Size of "worldspace pixels", measured in "screenspace pixels"
	pixelscale = 4

	// The resolution (worldspace)
	width  = 320
	height = 200

	// The width of the pixel buffer, used when calculating where to place pixels (y*pitch+x)
	pitch = width

	// Target framerate
	frameRate = 60

	// Alpha value for opaque colors
	opaque = 255
)

// rb returns a random byte
func rb() uint8 {
	return uint8(rand.Intn(255))
}

// rw returns a random int32 in the range [0,width)
func rw() int32 {
	return rand.Int31n(width)
}

// rh returns a random int32 in the range [0,height)
func rh() int32 {
	return rand.Int31n(height)
}

// DrawAll fills the pixel buffer with pixels.
// "cores" is how many CPU cores should be targeted when drawing triangles,
// by launching the same number of goroutines.
func DrawAll(pixels []uint32, cores int) {

	// Draw a triangle, concurrently
	multirender.Triangle(cores, pixels, rw(), rh(), rw(), rh(), rw(), rh(), color.RGBA{rb(), rb(), rb(), opaque}, pitch)

	// Draw a line and a red pixel, without caring about which order they appear in, or if they will complete before the next frame is drawn
	go multirender.Line(pixels, rw(), rh(), rw(), rh(), color.RGBA{0xff, 0xff, 0, opaque}, pitch)
	go multirender.Pixel(pixels, rw(), rh(), color.RGBA{0xff, 0xff, 0xff, opaque}, pitch)
}

func run() int {

	sdl.Init(sdl.INIT_VIDEO)

	var (
		window   *sdl.Window
		renderer *sdl.Renderer
		err      error
	)

	window, err = sdl.CreateWindow("Pixels!", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, int32(width*pixelscale), int32(height*pixelscale), sdl.WINDOW_SHOWN)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
		return 1
	}
	defer window.Destroy()

	renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err)
		return 1
	}
	defer renderer.Destroy()

	// Fill the render buffer with color #FF00CC
	renderer.SetDrawColor(0xff, 0, 0xcc, opaque)
	renderer.Clear()

	texture, err := renderer.CreateTexture(sdl.PIXELFORMAT_ARGB8888, sdl.TEXTUREACCESS_STREAMING, width, height)
	if err != nil {
		panic(err)
	}

	texture.SetBlendMode(sdl.BLENDMODE_BLEND) // sdl.BLENDMODE_ADD is also possible

	rand.Seed(time.Now().UnixNano())

	var (
		pixels = make([]uint32, width*height)
		cores  = runtime.NumCPU()
		event  sdl.Event
		quit   bool
		pause  bool
	)

	// Innerloop
	for !quit {

		if !pause {
			// Draw to pixel buffer
			DrawAll(pixels, cores)

			// Draw pixel buffer to screen
			texture.UpdateRGBA(nil, pixels, width)

			// Clear the render buffer between each frame
			renderer.Clear()

			renderer.Copy(texture, nil, nil)
			renderer.Present()
		}

		// Check for events
		for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch event.(type) {
			case *sdl.QuitEvent:
				quit = true
			case *sdl.KeyboardEvent:
				ke := event.(*sdl.KeyboardEvent)
				if ke.Type == sdl.KEYDOWN {
					ks := ke.Keysym
					switch ks.Sym {
					case sdl.K_ESCAPE:
						quit = true
					case sdl.K_q:
						quit = true
					case sdl.K_RETURN:
						altHeldDown := ks.Mod == sdl.KMOD_LALT || ks.Mod == sdl.KMOD_RALT
						if !altHeldDown {
							// alt+enter is not pressed
							break
						}
						// alt+enter is pressed
						fallthrough
					case sdl.K_f, sdl.K_F11:
						sdl2utils.ToggleFullscreen(window)
					case sdl.K_SPACE, sdl.K_p:
						pause = !pause
					case sdl.K_s:
						ctrlHeldDown := ks.Mod == sdl.KMOD_LCTRL || ks.Mod == sdl.KMOD_RCTRL
						if !ctrlHeldDown {
							// ctrl+s is not pressed
							break
						}
						// ctrl+s is pressed
						fallthrough
					case sdl.K_F12:
						// screenshot
						sdl2utils.Screenshot(renderer, "screenshot.png", true)
					}
				}
			}
		}
		sdl.Delay(1000 / frameRate)
	}
	return 0
}

func main() {
	// This is to allow the deferred functions in run() to kick in at exit
	os.Exit(run())
}

General information

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Abs

func Abs(a int32) int32

func Add

func Add(c1, c2 uint32) uint32

Add adds one color value on top of another. Only considers the alpha value of the second color value. Returns an opaque color.

func Alpha

func Alpha(cv uint32) uint8

Extract the alpha component from a ARGB uint32 color value

func Blend

func Blend(c1, c2 uint32) uint32

Blend blends two color values, based on the alpha value

func BlitImage

func BlitImage(pixels []uint32, pitch int32, img *image.RGBA) error

BlitImage blits an image on top of a pixel buffer, while blending

func BlitImageOnTop

func BlitImageOnTop(pixels []uint32, pitch int32, img *image.RGBA) error

BlitImageOnTop blits an image on top of a pixel buffer, disregarding any previous pixels. The resulting pixels are opaque.

func Blue

func Blue(cv uint32) uint8

Extract the blue component from a ARGB uint32 color value

func Clamp

func Clamp(x, a, b int32) int32

Clamp makes sure x is between the two given numbers by simply cutting it off

func ClampByte

func ClampByte(x, a, b uint8) uint8

Clamp makes sure x is between the two given numbers by simply cutting it off

func Clear

func Clear(pixels []uint32, c color.RGBA)

Clear changes all pixels to the given color

func ColorToColorValue

func ColorToColorValue(c color.RGBA) uint32

ColorToColorValue converts from color.RGBA to an ARGB uint32 color value

func ColorValueToRGBA

func ColorValueToRGBA(cv uint32) (uint8, uint8, uint8, uint8)

ColorValueToRGBA converts from an ARGB uint32 color value to four bytes

func FastClear

func FastClear(pixels []uint32, colorValue uint32)

FastClear changes all pixels to the given uint32 color value, like 0xff0000ff for: 0xff red, 0x00 green, 0x00 blue and 0xff alpha.

func FastPixel

func FastPixel(pixels []uint32, x, y int32, colorValue uint32, pitch int32)

FastPixel draws a pixel to the pixel buffer, given a uint32 ARGB value

func GetWrap

func GetWrap(pixels []uint32, pos, size int32) uint32

Return a pixel, with position wraparound instead of overflow

func GetXYWrap

func GetXYWrap(pixels []uint32, x, y, w, h, pitch int32) uint32

Return a pixel, with position wraparound instead of overflow

func GlitchyStretchContrast

func GlitchyStretchContrast(cores int, pixels []uint32, pitch int32, discardRatio float32)

GlitchyStretchContrast stretches the contrast of the pixels in the given "pixels" slice (of width "pitch"), discarding the discardRatio ratio of the most unpopular pixel values, then scaling the remaining pixels to cover the full 0..255 range.

func Green

func Green(cv uint32) uint8

Extract the green component from a ARGB uint32 color value

func HorizontalLine

func HorizontalLine(pixels []uint32, y, x1, x2 int32, c color.RGBA, pitch int32)

HorizontalLine draws a line from (x1, y) to (x2, y)

func HorizontalLineFast

func HorizontalLineFast(pixels []uint32, y, x1, x2 int32, c color.RGBA, pitch int32)

HorizontalLineFast draws a line from (x1, y) to (x2, y), but x1 must be smaller than x2!

func Lengths

func Lengths(p1, p2 *Pos) (int32, int32)

Lengths returns the x direction and y direction distance between the two points

func Line

func Line(pixels []uint32, x1, y1, x2, y2 int32, c color.RGBA, pitch int32)

Line draws a line in a completely wrong way to the pixel buffer. pixels are the pixels, pitch is the width of the pixel buffer.

func Max2

func Max2(a, b int32) int32

Max2 returns the largest of two numbers

func Max3

func Max3(a, b, c int32) int32

Max3 finds the largest of three numbers

func Min2

func Min2(a, b int32) int32

Min2 returns the smallest of two numbers

func Min3

func Min3(a, b, c int32) int32

Min3 finds the smallest of three numbers

func MinMax

func MinMax(a, b int32) (int32, int32)

MinMax find the smallest and greatest of two given numbers

func MinMax3

func MinMax3(a, b, c int32) (int32, int32)

MinMax3 finds the smallest and largest of three numbers

func MinMax3Byte

func MinMax3Byte(a, b, c uint8) (uint8, uint8)

MinMax3Byte finds the smallest and largest of three bytes

func OrAlpha

func OrAlpha(cores int, pixels []uint32)

Turn on all the alpha bits

func Pixel

func Pixel(pixels []uint32, x, y int32, c color.RGBA, pitch int32)

Pixel draws a pixel to the pixel buffer

func PixelRGB

func PixelRGB(pixels []uint32, x, y int32, r, g, b uint8, pitch int32)

PixelRGB draws an opaque pixel to the pixel buffer, given red, green and blue

func PixelsToImage

func PixelsToImage(pixels []uint32, pitch int32) *image.RGBA

PixelsToImage converts a pixel buffer to an image.RGBA image

func RGBAToColorValue

func RGBAToColorValue(r, g, b, a uint8) uint32

RGBAToColorValue converts from four bytes to an ARGB uint32 color value

func Red

func Red(cv uint32) uint8

Extract the red component from a ARGB uint32 color value

func RemoveBlue

func RemoveBlue(cores int, pixels []uint32)

RemoveBlue removes all blue color.

func RemoveGreen

func RemoveGreen(cores int, pixels []uint32)

RemoveGreen removes all green color.

func RemoveRed

func RemoveRed(cores int, pixels []uint32)

RemoveRed removes all red color.

func SaveImageToPNG

func SaveImageToPNG(img *image.RGBA, filename string, overwrite bool) error

SaveImageToPNG saves an image.RGBA image to a PNG file. Set overwrite to true to allow overwriting files.

func SavePixelsToPNG

func SavePixelsToPNG(pixels []uint32, pitch int32, filename string, overwrite bool) error

Save pixels in uint32 ARGB format to PNG with alpha. pitch is the width of the pixel buffer. Set overwrite to true to allow overwriting files.

func Scale

func Scale(x, fromA, toA, fromB, toB int32) int32

Scale an int on the scale from fromA to toA, to a scale from fromB to toB.

Example
fmt.Println(ScaleByte(0, 0, 255, 0, 255))
fmt.Println(ScaleByte(20, 20, 80, 0, 255))
fmt.Println(ScaleByte(80, 20, 80, 0, 255))
fmt.Println(ScaleByte(255, 255, 255, 0, 255))
fmt.Println(ScaleByte(0, 0, 0, 0, 255))
Output:

0
0
255
255
0

func ScaleByte

func ScaleByte(x, fromA, toA, fromB, toB uint8) uint8

ScaleByte scales a byte on the scale from fromA to toA, to a scale from fromB to toB.

func SetBlueBits

func SetBlueBits(cores int, pixels []uint32)

Turn on all the blue bits

func SetGreenBits

func SetGreenBits(cores int, pixels []uint32)

Turn on all the green bits

func SetRedBits

func SetRedBits(cores int, pixels []uint32)

Turn on all the red bits

func SetWrap

func SetWrap(pixels []uint32, pos, size int32, colorValue uint32)

Set a pixel, with position wraparound instead of overflow

func SetXYWrap

func SetXYWrap(pixels []uint32, x, y, w, h int32, colorValue uint32, pitch int32)

Set a pixel, with position wraparound instead of overflow

func Sort2

func Sort2(a, b int32) (int32, int32)

Sort2 sorts two numbers

func Sort3

func Sort3(a, b, c int32) (int32, int32, int32)

Sort3 sorts three numbers

func StretchContrast

func StretchContrast(cores int, pixels []uint32, pitch int32, discardRatio float32)

StretchContrast uses "cores" CPU cores to concurrently stretch the contrast of the pixels in the given "pixels" slice (of width "pitch"), discarding the discardRatio ratio of the most unpopular pixel values, then scaling the remaining pixels to cover the full 0..255 range.

func Triangle

func Triangle(cores int, pixels []uint32, x1, y1, x2, y2, x3, y3 int32, c color.RGBA, pitch int32)

Triangle draws a triangle, concurrently. Core is the number of goroutines that will be used. pitch is the "width" of the pixel buffer.

func Value

func Value(cv uint32) uint8

Extract the color value / intensity from a ARGB uint32 color value. Ignores alpha.

func ValueWithAlpha

func ValueWithAlpha(cv uint32) uint8

Extract the color value / intensity from a ARGB uint32 color value

func VerticalLine

func VerticalLine(pixels []uint32, x, y1, y2 int32, c color.RGBA, pitch int32)

VerticalLine draws a line from (x, y1) to (x, y2)

func VerticalLineFast

func VerticalLineFast(pixels []uint32, x, y1, y2 int32, c color.RGBA, pitch int32)

VerticalLineFast draws a line from (x, y1) to (x, y2), but y1 must be smaller than y2!

func WireTriangle

func WireTriangle(cores int, pixels []uint32, x1, y1, x2, y2, x3, y3 int32, c color.RGBA, pitch int32)

WireTriangle draws a wireframe triangle, concurrently. Core is the number of goroutines that will be used. pitch is the "width" of the pixel buffer.

Types

type Pair

type Pair struct {
	Key   uint8
	Value int
}

A data structure to hold key/value pairs

type PairList

type PairList []Pair

A slice of pairs that implements sort.Interface to sort by values

func (PairList) Len

func (p PairList) Len() int

func (PairList) Less

func (p PairList) Less(i, j int) bool

func (PairList) Swap

func (p PairList) Swap(i, j int)

type Pos

type Pos struct {
	// contains filtered or unexported fields
}

Pos represents a position in 2D space

func Interpolate

func Interpolate(p1, p2 *Pos) []*Pos

Interpolate interpolates between two points, with the number of steps equal to the length of the longest stretch

func NewPos

func NewPos(x, y int32) *Pos

NewPos creates a new position

func (*Pos) String

func (p *Pos) String() string

type Vec3

type Vec3 struct {
	// contains filtered or unexported fields
}

type Vertex

type Vertex struct {
	// contains filtered or unexported fields
}

Vertex has a position and a color value

func NewVertex

func NewVertex(x, y, z float32, r, g, b, a uint8) *Vertex
Example
v := NewVertex(0, 1, 2, 3, 4, 5, 6)
fmt.Println(v)
Output:

v(0, 1, 2) color(3, 4, 5, 6)

func (*Vertex) A

func (v *Vertex) A() uint8

func (*Vertex) B

func (v *Vertex) B() uint8

func (*Vertex) G

func (v *Vertex) G() uint8

func (*Vertex) Get

func (v *Vertex) Get() (float32, float32, float32)

func (*Vertex) GetColor

func (v *Vertex) GetColor() color.RGBA

func (*Vertex) GetRGBA

func (v *Vertex) GetRGBA() (uint8, uint8, uint8, uint8)

func (*Vertex) GetVec3

func (v *Vertex) GetVec3() *Vec3

func (*Vertex) Normalize

func (v *Vertex) Normalize() error

func (*Vertex) R

func (v *Vertex) R() uint8

func (*Vertex) Set

func (v *Vertex) Set(x, y, z float32)

func (*Vertex) SetColor

func (v *Vertex) SetColor(c color.RGBA)

func (*Vertex) SetRGBA

func (v *Vertex) SetRGBA(r, g, b, a uint8)

func (*Vertex) String

func (v *Vertex) String() string

func (*Vertex) X

func (v *Vertex) X() float32

func (*Vertex) Y

func (v *Vertex) Y() float32

func (*Vertex) Z

func (v *Vertex) Z() float32

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL