pallet

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Dec 27, 2023 License: MIT Imports: 14 Imported by: 1

README

pallet

import "github.com/KEINOS/go-pallet/pallet"

Package pallet is the core package of the Pallet to use it as a library. Which simply returns the colors (RGBA) used in an image.

For the actual application see ../cmd/main.go.

Index

Variables

JSONMarshal is a copy of json.Marshal() to ease mock during test. Temporary replace the function to mock its behavior.

var JSONMarshal = json.Marshal

JSONMarshalIndent is a copy of json.MarshalIndent() to ease mock during test. Temporary replace the function to mock its behavior.

var JSONMarshalIndent = json.MarshalIndent

func ColorToString

func ColorToString(c color.Color) string

ColorToString returns color.RGBA object's RGBA value as a RRRGGGBBBAAA formatted string. Mostly used for the key of a map.

func Diff

func Diff(img1, img2 *image.RGBA) (diff *image.RGBA, err error)

Diff returns an image.RGBA object whose pixels are the absolute difference values between two images. The two input images must have the same bounds.

Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
	"log"
)

func main() {
	// Get image1 (3x3pix)
	pathFileImg1 := "../testdata/rgbacmykw.png"

	imgRGBA1, err := pallet.Load(pathFileImg1)
	if err != nil {
		log.Fatal(err)
	}

	// Get image2 (3x3pix)
	pathFileImg2 := "../testdata/rgbacmykw.png"

	imgRGBA2, err := pallet.Load(pathFileImg2)
	if err != nil {
		log.Fatal(err)
	}

	// Get the absolute diff between two images
	imgDiff, err := pallet.Diff(imgRGBA1, imgRGBA2)
	if err != nil {
		log.Fatal(err)
	}

	// It should be all zero since it's the same image
	fmt.Printf("%v", imgDiff.Pix)

}
Output
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

func Load

func Load(pathFileImg string) (*image.RGBA, error)

Load returns the image.RGBA object pointer read image from pathFileImg.

func Open

func Open(filename string) (image.Image, error)

Open loads and decodes an image from a file and returns it.

Usage example: // Decodes an image from a file with the given filename // returns an error if something went wrong img, err := Open("exampleName")

func Save

func Save(filename string, img image.Image, encoder Encoder) error

Save creates a file and writes to it an image using the provided encoder.

Usage example: // Save an image to a file in PNG format, // returns an error if something went wrong err := Save("exampleName", img, imgio.JPEGEncoder(100))

type Encoder

Encoder encodes the provided image and writes it.

type Encoder func(io.Writer, image.Image) error
func BMPEncoder
func BMPEncoder() Encoder

BMPEncoder returns an encoder to BMP.

func JPEGEncoder
func JPEGEncoder(quality int) Encoder

JPEGEncoder returns an encoder to JPEG given the argument 'quality'.

func PNGEncoder
func PNGEncoder() Encoder

PNGEncoder returns an encoder to PNG.

type Histogram

Histogram holds the total occurrence of each RGBA channel.

type Histogram struct {
    R   []int `json:"r"`
    G   []int `json:"g"`
    B   []int `json:"b"`
    A   []int `json:"a"`
}
func AsHistogram
func AsHistogram(imgRGBA *image.RGBA) Histogram

AsHistogram returns a Histogram object from an image.

Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
	"log"
)

func main() {
	// 2x2 pixel image with each RGBA color of 1-pixel
	pathFile := "../testdata/r1g1b1a1.png"

	imgRGBA, err := pallet.Load(pathFile)
	if err != nil {
		log.Fatal(err)
	}

	hist := pallet.AsHistogram(imgRGBA)

	// Print the occurrences of each color channel's shade level.
	//   <channel>[<shade level>] = <occurrence>
	// If a red pixel with max-opacity (R,G,B,A=255,0,0,255) appeared twice in
	// an image then it will be:
	//   r[255]=2, g[0]=2, b[0]=2, a[255]=2
	fmt.Printf("r[0]=%v, r[255]=%v\n", hist.R[0], hist.R[255])
	fmt.Printf("g[0]=%v, g[255]=%v\n", hist.G[0], hist.G[255])
	fmt.Printf("b[0]=%v, b[255]=%v\n", hist.B[0], hist.B[255])
	fmt.Printf("a[0]=%v, a[255]=%v\n", hist.A[0], hist.A[255])

}
Output
r[0]=3, r[255]=1
g[0]=3, g[255]=1
b[0]=3, b[255]=1
a[0]=1, a[255]=3

func NewHistogram
func NewHistogram() *Histogram

NewHistogram returns an initialized object pointer of Histogram.

func (*Histogram) InJSON
func (h *Histogram) InJSON(perLine bool) (string, error)

InJSON returns the histogram of the image in JSON string.

{
  "r": [...],
  "g": [...],
  "g": [...],
  "a": [...],
}

Each channel contains a matrix consisting of 256 elements. The index of the matrix represents the shadow level, and the value represents the number of occurrence of that level.

type PixInfo

PixInfo holds the color (RGBA) and it's number of occurrences.

type PixInfo struct {
    R     int `json:"r"`
    G     int `json:"g"`
    B     int `json:"b"`
    A     int `json:"a"`
    Count int `json:"count"`
}
func (PixInfo) GetKey
func (p PixInfo) GetKey() string

GetKey returns the RGBA values in RRRGGGBBBAAA format string for ID key.

Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pixInfo := pallet.PixInfo{
		R: 12, // Red   --> 012
		G: 34, // Green --> 034
		B: 56, // Blue  --> 056
		A: 0,  // Alpha --> 000
	}

	key := pixInfo.GetKey()

	// Note that each RGBA values are filled with zero
	fmt.Println(key)

}
Output
012034056000

func (PixInfo) MarshalJSON
func (p PixInfo) MarshalJSON() ([]byte, error)

MarshalJSON is an implementation of Marshaler which returns the elements in a single line.

type PixInfoList

PixInfoList is a slice of PixInfo which is sortable.

type PixInfoList []PixInfo
func ByOccurrence
func ByOccurrence(imgRGBA *image.RGBA) PixInfoList

ByOccurrence returns PixInfoList which is a slice of PixInfo sorted by occurrence of color.

Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
	"log"
)

func main() {
	pathFile := "../testdata/gopher.png"

	imgRGBA, err := pallet.Load(pathFile)
	if err != nil {
		log.Fatal(err)
	}

	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print the first 2 most used colors
	fmt.Println(pixInfoList[0:2])

}
Output
[{0 0 0 0 46618} {208 182 152 255 32505}]

func (PixInfoList) InJSON
func (p PixInfoList) InJSON(perLine bool) (string, error)

InJSON returns a JSON formatted string of the color map. If perLine is true then it will output each element per line.

Example (Element_per_line)

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
	"log"
)

func main() {
	pathFileImg := "../testdata/r1g2b4a2.png"

	// Load image
	imgRGBA, err := pallet.Load(pathFileImg)
	if err != nil {
		log.Fatal(err)
	}

	// Count by occurrence
	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print in JSON (each element per line)
	outputPerLine := true

	result, err := pixInfoList.InJSON(outputPerLine)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result)

}
Output
[
{"r":0,"g":0,"b":0,"a":0,"count":12},
{"r":255,"g":255,"b":255,"a":255,"count":6},
{"r":0,"g":0,"b":255,"a":255,"count":4},
{"r":0,"g":255,"b":0,"a":255,"count":2},
{"r":255,"g":0,"b":0,"a":255,"count":1}
]

Example (Single_line)

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
	"log"
)

func main() {
	pathFileImg := "../testdata/r1g2b4a2.png"

	// Load image
	imgRGBA, err := pallet.Load(pathFileImg)
	if err != nil {
		log.Fatal(err)
	}

	// Count by occurrence
	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print in JSON as a single line
	outputPerLine := false

	result, err := pixInfoList.InJSON(outputPerLine)
	if err != nil {
		log.Fatal(err)
	}

	// Print-out in fixed width
	width := 70
	for i, r := range result {
		if i%width == 0 {
			fmt.Println()
		}

		fmt.Print(string(r))
	}

}
Output
[{"r":0,"g":0,"b":0,"a":0,"count":12},{"r":255,"g":255,"b":255,"a":255
,"count":6},{"r":0,"g":0,"b":255,"a":255,"count":4},{"r":0,"g":255,"b"
:0,"a":255,"count":2},{"r":255,"g":0,"b":0,"a":255,"count":1}]

func (PixInfoList) Len
func (p PixInfoList) Len() int

Len is an implementation of Len() for sort function. Which returns the current object's slice length.

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

Less is an implementation of Less() for sort function. Which returns true if the current value of Count in "i" is less than "j".

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

Swap is an implementation of Swap() for sort function. It will swap the elements between "i" and "j".

type PixKey

PixKey is a string type for RRRGGGBBBAAA formatted string.

RRRGGGBBBAAA formatted string is the key of the map during count of the occurrence of colors in an image.

See: ColorToString() at ./pallet.go as well.

type PixKey string
Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pix := pallet.PixKey("123456789255")

	fmt.Println("Red:", pix.GetRed())
	fmt.Println("Green:", pix.GetGreen())
	fmt.Println("Blue:", pix.GetBlue())
	fmt.Println("Alpha:", pix.GetAlpha())

}
Output
Red: 123
Green: 456
Blue: 789
Alpha: 255

Example (Direct)

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	r := pallet.PixKey("123456789255").GetRed()
	g := pallet.PixKey("123456789255").GetGreen()
	b := pallet.PixKey("123456789255").GetBlue()
	a := pallet.PixKey("123456789255").GetAlpha()

	fmt.Println("Red:", r)
	fmt.Println("Green:", g)
	fmt.Println("Blue:", b)
	fmt.Println("Alpha:", a)

}
Output
Red: 123
Green: 456
Blue: 789
Alpha: 255

func (PixKey) GetAlpha
func (k PixKey) GetAlpha() int

GetAlpha returns the alpha value from the RRRGGGBBBAAA format key string.

a := GetAlpha("255255255100") // --> 100
func (PixKey) GetBlue
func (k PixKey) GetBlue() int

GetBlue returns the blue value from the RRRGGGBBBAAA format key string.

a := GetBlue("255255100255") // --> 100
func (PixKey) GetGreen
func (k PixKey) GetGreen() int

GetGreen returns the green value from the RRRGGGBBBAAA format key string.

a := GetGreen("255100255255") // --> 100
func (PixKey) GetRed
func (k PixKey) GetRed() int

GetRed returns the red value from the RRRGGGBBBAAA format key string.

a := GetRed("100255255255") // --> 100
func (PixKey) NewPixInfo
func (k PixKey) NewPixInfo(count int) PixInfo

NewPixInfo creates PixInfo object from PixKey.

Example

package main

import (
	"fmt"
	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pixKey := pallet.PixKey("123456789255")

	// Create new PixInfo object from pixKey
	count := 0
	pixInfo := pixKey.NewPixInfo(count)

	fmt.Println(pixInfo)
}
Output
{123 456 789 255 0}

Generated by gomarkdoc

Documentation

Overview

Package pallet is the core package of the Pallet to use it as a library. Which simply returns the colors (RGBA) used in an image.

For the actual application see ../cmd/main.go.

Index

Examples

Constants

This section is empty.

Variables

View Source
var JSONMarshal = json.Marshal

JSONMarshal is a copy of json.Marshal() to ease mock during test. Temporary replace the function to mock its behavior.

View Source
var JSONMarshalIndent = json.MarshalIndent

JSONMarshalIndent is a copy of json.MarshalIndent() to ease mock during test. Temporary replace the function to mock its behavior.

Functions

func ColorToString

func ColorToString(c color.Color) string

ColorToString returns color.RGBA object's RGBA value as a RRRGGGBBBAAA formatted string. Mostly used for the key of a map.

func Diff

func Diff(img1, img2 *image.RGBA) (*image.RGBA, error)

Diff returns an image.RGBA object whose pixels are the absolute difference values between two images. The two input images must have the same bounds.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	// Get image1 (3x3pix)
	const pathFileImg1 = "../testdata/rgbacmykw.png"

	imgRGBA1, err := pallet.Load(pathFileImg1)
	if err != nil {
		log.Fatal(err)
	}

	// Get image2 (3x3pix)
	const pathFileImg2 = "../testdata/rgbacmykw.png"

	imgRGBA2, err := pallet.Load(pathFileImg2)
	if err != nil {
		log.Fatal(err)
	}

	// Get the absolute diff between two images
	imgDiff, err := pallet.Diff(imgRGBA1, imgRGBA2)
	if err != nil {
		log.Fatal(err)
	}

	// It should be all zero since it's the same image
	fmt.Printf("%v", imgDiff.Pix)

}
Output:

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

func Load

func Load(pathFileImg string) (*image.RGBA, error)

Load returns the image.RGBA object pointer read image from pathFileImg.

func Open

func Open(filename string) (image.Image, error)

Open loads and decodes an image from a file and returns it.

Usage example:

// Decodes an image from a file with the given filename
// returns an error if something went wrong
img, err := Open("exampleName")

func Save

func Save(filename string, img image.Image, encoder Encoder) error

Save creates a file and writes to it an image using the provided encoder.

Usage example:

// Save an image to a file in PNG format,
// returns an error if something went wrong
err := Save("exampleName", img, imgio.JPEGEncoder(100))

Types

type Encoder

type Encoder func(io.Writer, image.Image) error

Encoder encodes the provided image and writes it.

func BMPEncoder

func BMPEncoder() Encoder

BMPEncoder returns an encoder to BMP.

func JPEGEncoder

func JPEGEncoder(quality int) Encoder

JPEGEncoder returns an encoder to JPEG given the argument 'quality'.

func PNGEncoder

func PNGEncoder() Encoder

PNGEncoder returns an encoder to PNG.

type Histogram

type Histogram struct {
	R []int `json:"r"`
	G []int `json:"g"`
	B []int `json:"b"`
	A []int `json:"a"`
}

Histogram holds the total occurrence of each RGBA channel.

func AsHistogram

func AsHistogram(imgRGBA *image.RGBA) Histogram

AsHistogram returns a Histogram object from an image.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	// 2x2 pixel image with each RGBA color of 1-pixel
	const pathFile = "../testdata/r1g1b1a1.png"

	imgRGBA, err := pallet.Load(pathFile)
	if err != nil {
		log.Fatal(err)
	}

	hist := pallet.AsHistogram(imgRGBA)

	// Print the occurrences of each color channel's shade level.
	//   <channel>[<shade level>] = <occurrence>
	// If a red pixel with max-opacity (R,G,B,A=255,0,0,255) appeared twice in
	// an image then it will be:
	//   r[255]=2, g[0]=2, b[0]=2, a[255]=2
	fmt.Printf("r[0]=%v, r[255]=%v\n", hist.R[0], hist.R[255])
	fmt.Printf("g[0]=%v, g[255]=%v\n", hist.G[0], hist.G[255])
	fmt.Printf("b[0]=%v, b[255]=%v\n", hist.B[0], hist.B[255])
	fmt.Printf("a[0]=%v, a[255]=%v\n", hist.A[0], hist.A[255])

}
Output:

r[0]=3, r[255]=1
g[0]=3, g[255]=1
b[0]=3, b[255]=1
a[0]=1, a[255]=3

func NewHistogram

func NewHistogram() *Histogram

NewHistogram returns an initialized object pointer of Histogram.

func (*Histogram) InJSON

func (h *Histogram) InJSON(perLine bool) (string, error)

InJSON returns the histogram of the image in JSON string.

{
  "r": [...],
  "g": [...],
  "g": [...],
  "a": [...],
}

Each channel contains a matrix consisting of 256 elements. The index of the matrix represents the shadow level, and the value represents the number of occurrence of that level.

type PixInfo

type PixInfo struct {
	R     int `json:"r"`     // R is the red channel value
	G     int `json:"g"`     // G is the green channel value
	B     int `json:"b"`     // B is the blue channel value
	A     int `json:"a"`     // A is the alpha channel value
	Count int `json:"count"` // Count is the number of occurrences
}

PixInfo holds the color (RGBA) and it's number of occurrences.

func (PixInfo) GetKey

func (p PixInfo) GetKey() string

GetKey returns the RGBA values in RRRGGGBBBAAA format string for ID key.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pixInfo := pallet.PixInfo{
		R:     12, // Red   --> 012
		G:     34, // Green --> 034
		B:     56, // Blue  --> 056
		A:     0,  // Alpha --> 000
		Count: 0,  // Not used
	}

	key := pixInfo.GetKey()

	// Print the RGBA values in RRRGGGBBBAAA format. Note that each RGBA values are filled with zero
	fmt.Println(key)

}
Output:

012034056000

func (PixInfo) MarshalJSON

func (p PixInfo) MarshalJSON() ([]byte, error)

MarshalJSON is an implementation of Marshaler which returns the elements in a single line.

type PixInfoList

type PixInfoList []PixInfo

PixInfoList is a slice of PixInfo which is sortable.

func ByOccurrence

func ByOccurrence(imgRGBA *image.RGBA) PixInfoList

ByOccurrence returns PixInfoList which is a slice of PixInfo sorted by occurrence of color.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	const pathFile = "../testdata/gopher.png"

	imgRGBA, err := pallet.Load(pathFile)
	if err != nil {
		log.Fatal(err)
	}

	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print the first 2 most used colors
	fmt.Println(pixInfoList[0:2])

}
Output:

[{0 0 0 0 46618} {208 182 152 255 32505}]

func (PixInfoList) InJSON

func (p PixInfoList) InJSON(perLine bool) (string, error)

InJSON returns a JSON formatted string of the color map. If perLine is true then it will output each element per line.

Example (Element_per_line)
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	const pathFileImg = "../testdata/r1g2b4a2.png"

	// Load image
	imgRGBA, err := pallet.Load(pathFileImg)
	if err != nil {
		log.Fatal(err)
	}

	// Count by occurrence
	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print in JSON (each element per line)
	outputPerLine := true

	result, err := pixInfoList.InJSON(outputPerLine)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result)

}
Output:

[
{"r":0,"g":0,"b":0,"a":0,"count":12},
{"r":255,"g":255,"b":255,"a":255,"count":6},
{"r":0,"g":0,"b":255,"a":255,"count":4},
{"r":0,"g":255,"b":0,"a":255,"count":2},
{"r":255,"g":0,"b":0,"a":255,"count":1}
]
Example (Single_line)
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	const pathFileImg = "../testdata/r1g2b4a2.png"

	// Load image
	imgRGBA, err := pallet.Load(pathFileImg)
	if err != nil {
		log.Fatal(err)
	}

	// Count by occurrence
	pixInfoList := pallet.ByOccurrence(imgRGBA)

	// Print in JSON as a single line
	outputPerLine := false

	result, err := pixInfoList.InJSON(outputPerLine)
	if err != nil {
		log.Fatal(err)
	}

	// Print-out in fixed width
	width := 70
	for i, r := range result {
		if i%width == 0 {
			fmt.Println()
		}

		fmt.Print(string(r))
	}

}
Output:

[{"r":0,"g":0,"b":0,"a":0,"count":12},{"r":255,"g":255,"b":255,"a":255
,"count":6},{"r":0,"g":0,"b":255,"a":255,"count":4},{"r":0,"g":255,"b"
:0,"a":255,"count":2},{"r":255,"g":0,"b":0,"a":255,"count":1}]

func (PixInfoList) Len

func (p PixInfoList) Len() int

Len is an implementation of Len() for sort function. Which returns the current object's slice length.

func (PixInfoList) Less

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

Less is an implementation of Less() for sort function. Which returns true if the current value of Count in "i" is less than "j".

func (PixInfoList) Swap

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

Swap is an implementation of Swap() for sort function. It will swap the elements between "i" and "j".

type PixKey

type PixKey string

PixKey is a string type for RRRGGGBBBAAA formatted string.

RRRGGGBBBAAA formatted string is the key of the map during count of the occurrence of colors in an image.

See: ColorToString() at ./pallet.go as well.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pix := pallet.PixKey("123456789255")

	fmt.Println("Red:", pix.GetRed())
	fmt.Println("Green:", pix.GetGreen())
	fmt.Println("Blue:", pix.GetBlue())
	fmt.Println("Alpha:", pix.GetAlpha())

}
Output:

Red: 123
Green: 456
Blue: 789
Alpha: 255
Example (Direct)
package main

import (
	"fmt"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	r := pallet.PixKey("123456789255").GetRed()
	g := pallet.PixKey("123456789255").GetGreen()
	b := pallet.PixKey("123456789255").GetBlue()
	a := pallet.PixKey("123456789255").GetAlpha()

	fmt.Println("Red:", r)
	fmt.Println("Green:", g)
	fmt.Println("Blue:", b)
	fmt.Println("Alpha:", a)

}
Output:

Red: 123
Green: 456
Blue: 789
Alpha: 255

func (PixKey) GetAlpha

func (k PixKey) GetAlpha() int

GetAlpha returns the alpha value from the RRRGGGBBBAAA format key string.

a := GetAlpha("255255255100") // --> 100

func (PixKey) GetBlue

func (k PixKey) GetBlue() int

GetBlue returns the blue value from the RRRGGGBBBAAA format key string.

a := GetBlue("255255100255") // --> 100

func (PixKey) GetGreen

func (k PixKey) GetGreen() int

GetGreen returns the green value from the RRRGGGBBBAAA format key string.

a := GetGreen("255100255255") // --> 100

func (PixKey) GetRed

func (k PixKey) GetRed() int

GetRed returns the red value from the RRRGGGBBBAAA format key string.

a := GetRed("100255255255") // --> 100

func (PixKey) NewPixInfo

func (k PixKey) NewPixInfo(count int) PixInfo

NewPixInfo creates PixInfo object from PixKey.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-pallet/pallet"
)

func main() {
	pixKey := pallet.PixKey("123456789255")

	// Create new PixInfo object from pixKey
	count := 0
	pixInfo := pixKey.NewPixInfo(count)

	fmt.Println(pixInfo)
}
Output:

{123 456 789 255 0}

Jump to

Keyboard shortcuts

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