github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/examples/gtxt/pattern/main.go (about)

     1  //go:build gtxt
     2  
     3  package main
     4  
     5  import "os"
     6  import "image"
     7  import "image/color"
     8  import "image/png"
     9  import "path/filepath"
    10  import "log"
    11  import "fmt"
    12  
    13  import "github.com/Kintar/etxt"
    14  
    15  import "golang.org/x/image/math/fixed"
    16  
    17  // Must be compiled with '-tags gtxt'
    18  
    19  // An example showcasing how to draw glyphs manually and applying a
    20  // specific pattern effect. The manual glyph drawing part is similar to
    21  // examples/gtxt/mirror.
    22  
    23  func main() {
    24  	// get font path
    25  	if len(os.Args) != 2 {
    26  		msg := "Usage: expects one argument with the path to the font to be used\n"
    27  		fmt.Fprint(os.Stderr, msg)
    28  		os.Exit(1)
    29  	}
    30  
    31  	// parse font
    32  	font, fontName, err := etxt.ParseFontFrom(os.Args[1])
    33  	if err != nil {
    34  		log.Fatal(err)
    35  	}
    36  	fmt.Printf("Font loaded: %s\n", fontName)
    37  
    38  	// create cache
    39  	cache := etxt.NewDefaultCache(1024 * 1024 * 1024) // 1GB cache
    40  
    41  	// create and configure renderer
    42  	renderer := etxt.NewStdRenderer()
    43  	renderer.SetCacheHandler(cache.NewHandler())
    44  	renderer.SetSizePx(64)
    45  	renderer.SetFont(font)
    46  	renderer.SetAlign(etxt.YCenter, etxt.XCenter)
    47  	renderer.SetColor(color.RGBA{255, 255, 255, 255}) // white
    48  
    49  	// create target image and fill it with black
    50  	outImage := image.NewRGBA(image.Rect(0, 0, 360, 64))
    51  	for i := 3; i < 360*64*4; i += 4 {
    52  		outImage.Pix[i] = 255
    53  	}
    54  
    55  	// set target and start drawing
    56  	renderer.SetTarget(outImage)
    57  	renderer.Traverse("PATTERN", fixed.P(180, 32),
    58  		func(dot fixed.Point26_6, _ rune, glyphIndex etxt.GlyphIndex) {
    59  			mask := renderer.LoadGlyphMask(glyphIndex, dot)
    60  			drawAsPattern(dot, mask, outImage)
    61  		})
    62  
    63  	// store result as png
    64  	filename, err := filepath.Abs("gtxt_pattern.png")
    65  	if err != nil {
    66  		log.Fatal(err)
    67  	}
    68  	fmt.Printf("Output image: %s\n", filename)
    69  	file, err := os.Create(filename)
    70  	if err != nil {
    71  		log.Fatal(err)
    72  	}
    73  	err = png.Encode(file, outImage)
    74  	if err != nil {
    75  		log.Fatal(err)
    76  	}
    77  	err = file.Close()
    78  	if err != nil {
    79  		log.Fatal(err)
    80  	}
    81  	fmt.Print("Program exited successfully.\n")
    82  }
    83  
    84  func drawAsPattern(dot fixed.Point26_6, mask etxt.GlyphMask, target *image.RGBA) {
    85  	// to draw a mask into a target, we need to displace it by the
    86  	// current dot (drawing position) and be careful with clipping
    87  	srcRect, destRect := getDrawBounds(mask.Rect, target.Bounds(), dot)
    88  	if destRect.Empty() {
    89  		return
    90  	} // nothing to draw
    91  
    92  	// we now have two rects that are the same size but identify
    93  	// different regions of the mask and target images. we can use
    94  	// them to read from one and draw on the other. yay.
    95  
    96  	// we start by creating some helper variables to make iteration
    97  	// through the rects more pleasant
    98  	width := srcRect.Dx()
    99  	height := srcRect.Dy()
   100  	srcOffX := srcRect.Min.X
   101  	srcOffY := srcRect.Min.Y
   102  	destOffX := destRect.Min.X
   103  	destOffY := destRect.Min.Y
   104  
   105  	// iterate the rects and draw!
   106  	for y := 0; y < height; y++ {
   107  		for x := 0; x < width; x++ {
   108  			// pattern filtering, edit and make your own!
   109  			// e.g:
   110  			// >> (x + y) % 2 == 0
   111  			// >> x % 3 != 2 && y % 3 != 2
   112  			// >> x % 3 == 2 || y % 3 == 2
   113  			// >> x == y
   114  			// >> (width - x) % 5 == y % 5
   115  			// >> (y > height/2) && (x + y) % 2 == 0
   116  			discard := x%2 != 0 || y%2 != 0
   117  			if discard {
   118  				continue
   119  			}
   120  
   121  			// get mask alpha level
   122  			level := mask.AlphaAt(srcOffX+x, srcOffY+y).A
   123  			if level == 0 {
   124  				continue
   125  			} // non-filled part of the glyph
   126  
   127  			// now we finally can draw to the target
   128  			target.SetRGBA(destOffX+x, destOffY+y, color.RGBA{255, 255, 255, 255})
   129  		}
   130  	}
   131  }
   132  
   133  // same as in gtxt/mirror
   134  func getDrawBounds(srcRect, targetRect image.Rectangle, dot fixed.Point26_6) (image.Rectangle, image.Rectangle) {
   135  	shift := image.Pt(dot.X.Floor(), dot.Y.Floor())
   136  	destRect := targetRect.Intersect(srcRect.Add(shift))
   137  	shift.X, shift.Y = -shift.X, -shift.Y
   138  	return destRect.Add(shift), destRect
   139  }