~dricottone/image2ascii

32c6345ebc23b02d145076b1ac7ec2f1e4c2fc3a — qeesung 6 years ago d721f2b
add reverse options
6 files changed, 53 insertions(+), 22 deletions(-)

M ascii/ascii.go
M ascii/ascii_test.go
M ascii/option.go
M convert/convert.go
M convert/convert_test.go
M image2ascii.go
M ascii/ascii.go => ascii/ascii.go +7 -7
@@ 12,11 12,11 @@ import (

// ConvertPixelToASCII converts a pixel to a ASCII char string
func ConvertPixelToASCII(pixel color.Color, options *Options) string {
	defaultOptions := NewOptions()
	defaultOptions.mergeOptions(options)
	convertOptions := NewOptions()
	convertOptions.mergeOptions(options)

	if defaultOptions.Reverse {
		defaultOptions.Pixels = reverse(defaultOptions.Pixels)
	if convertOptions.Reversed {
		convertOptions.Pixels = reverse(convertOptions.Pixels)
	}

	r := reflect.ValueOf(pixel).FieldByName("R").Uint()


@@ 26,9 26,9 @@ func ConvertPixelToASCII(pixel color.Color, options *Options) string {
	value := intensity(r, g, b, a)

	// Choose the char
	precision := float64(255 * 3 / (len(options.Pixels) - 1))
	rawChar := options.Pixels[roundValue(float64(value)/precision)]
	if options.Colored {
	precision := float64(255 * 3 / (len(convertOptions.Pixels) - 1))
	rawChar := convertOptions.Pixels[roundValue(float64(value)/precision)]
	if convertOptions.Colored {
		return decorateWithColor(r, g, b, rawChar)
	}
	return string([]byte{rawChar})

M ascii/ascii_test.go => ascii/ascii_test.go +8 -8
@@ 13,7 13,7 @@ func TestNewOptions(t *testing.T) {
	newOptions := NewOptions()
	assertions := assert.New(t)
	assertions.True(newOptions.Colored, "Default colored option should be true")
	assertions.False(newOptions.Reverse, "Default reverse option should be false")
	assertions.False(newOptions.Reversed, "Default reverse option should be false")
	assertions.Equal(" .,:;i1tfLCG08@", string(newOptions.Pixels), "Default pixels should be  .,:;i1tfLCG08@")
}



@@ 24,7 24,7 @@ func TestMergeOptions(t *testing.T) {
	options2 := NewOptions()
	options2.Colored = false
	options1.mergeOptions(&options2)
	assertions.False(options1.Reverse, "Merged reverse option should be false")
	assertions.False(options1.Reversed, "Merged reverse option should be false")
	assertions.False(options1.Colored, "Merged colored option should be false")
}



@@ 47,11 47,11 @@ func TestConvertPixelToASCIIWhiteColor(t *testing.T) {
		fmt.Sprintf("White color chould be converted to %s", string([]byte{lastPixelChar})))

	defaultOptions.Colored = false
	defaultOptions.Reverse = true
	defaultOptions.Reversed = true
	convertedChar = ConvertPixelToASCII(pixel, &defaultOptions)
	firstPixelChar := reverse(defaultOptions.Pixels)[0]
	firstPixelChar := defaultOptions.Pixels[0]
	assertions.Equal(convertedChar, string([]byte{firstPixelChar}),
		fmt.Sprintf("Reverse white color chould be converted to %s", string([]byte{firstPixelChar})))
		fmt.Sprintf("Reversed white color chould be converted to %s", string([]byte{firstPixelChar})))
}

// TestConvertPixelToASCIIBlackColor convert a white image pixel to ascii string


@@ 73,11 73,11 @@ func TestConvertPixelToASCIIBlackColor(t *testing.T) {
		fmt.Sprintf("Black color chould be converted to %s", string([]byte{firstPixelChar})))

	defaultOptions.Colored = false
	defaultOptions.Reverse = true
	defaultOptions.Reversed = true
	convertedChar = ConvertPixelToASCII(pixel, &defaultOptions)
	lastPixelChar := reverse(defaultOptions.Pixels)[len(defaultOptions.Pixels)-1]
	lastPixelChar := defaultOptions.Pixels[len(defaultOptions.Pixels)-1]
	assertions.Equal(convertedChar, string([]byte{lastPixelChar}),
		fmt.Sprintf("Reverse Black color chould be converted to %s", string([]byte{lastPixelChar})))
		fmt.Sprintf("Reversed Black color chould be converted to %s", string([]byte{lastPixelChar})))
}

func TestColoredASCIIChar(t *testing.T) {

M ascii/option.go => ascii/option.go +7 -7
@@ 2,16 2,16 @@ package ascii

// Options convert pixel to raw char
type Options struct {
	Pixels  []byte
	Reverse bool
	Colored bool
	Pixels   []byte
	Reversed bool
	Colored  bool
}

// DefaultOptions that contains the default pixels
var DefaultOptions = Options{
	Pixels:  []byte(" .,:;i1tfLCG08@"),
	Reverse: false,
	Colored: true,
	Pixels:   []byte(" .,:;i1tfLCG08@"),
	Reversed: false,
	Colored:  true,
}

// NewOptions create a new convert option


@@ 24,6 24,6 @@ func NewOptions() Options {
// mergeOptions merge two options
func (options *Options) mergeOptions(newOptions *Options) {
	options.Pixels = append([]byte{}, newOptions.Pixels...)
	options.Reverse = newOptions.Reverse
	options.Reversed = newOptions.Reversed
	options.Colored = newOptions.Colored
}

M convert/convert.go => convert/convert.go +3 -0
@@ 21,6 21,7 @@ type Options struct {
	ExpectedHeight int
	FitScreen      bool
	Colored        bool
	Reversed       bool
}

// DefaultOptions for convert image


@@ 30,6 31,7 @@ var DefaultOptions = Options{
	ExpectedHeight: -1,
	FitScreen:      true,
	Colored:        true,
	Reversed:       false,
}

// Image2ASCIIMatrix converts a image to ASCII matrix


@@ 46,6 48,7 @@ func Image2ASCIIMatrix(image image.Image, imageConvertOptions *Options) []string
			// Convert the pixel to ascii char
			pixelConvertOptions := ascii.NewOptions()
			pixelConvertOptions.Colored = imageConvertOptions.Colored
			pixelConvertOptions.Reversed = imageConvertOptions.Reversed
			rawChar := ascii.ConvertPixelToASCII(pixel, &pixelConvertOptions)
			rawCharValues = append(rawCharValues, rawChar)
		}

M convert/convert_test.go => convert/convert_test.go +25 -0
@@ 94,6 94,31 @@ func TestImageFile2ASCIIString(t *testing.T) {
	}
}

func TestImage2ReversedASCIIString(t *testing.T) {
	imageTests := []struct {
		imageFilename string
		asciiString   string
	}{
		{"testdata/3x3_white.png", "   \n   \n   \n"},
		{"testdata/3x3_black.png", "@@@\n@@@\n@@@\n"},
	}

	for _, tt := range imageTests {
		t.Run(tt.imageFilename, func(t *testing.T) {
			convertOptions := DefaultOptions
			convertOptions.FitScreen = false
			convertOptions.Colored = false
			convertOptions.Reversed = true

			charString := ImageFile2ASCIIString(tt.imageFilename, &convertOptions)
			if charString != tt.asciiString {
				t.Errorf("image %s convert expected to %+v, but get %+v",
					tt.imageFilename, tt.asciiString, charString)
			}
		})
	}
}

// BenchmarkBigImage2ASCIIMatrix benchmark convert big image to ascii
func BenchmarkBigImage2ASCIIMatrix(b *testing.B) {
	convertOptions := DefaultOptions

M image2ascii.go => image2ascii.go +3 -0
@@ 16,6 16,7 @@ var expectedWidth int
var expectedHeight int
var fitScreen bool
var colored bool
var reversed bool

func init() {
	flag.StringVar(&imageFilename, "f", "", "Image filename to be convert")


@@ 24,6 25,7 @@ func init() {
	flag.IntVar(&expectedHeight, "g", -1, "Expected image height, -1 for image default height")
	flag.BoolVar(&fitScreen, "s", true, "Fit the terminal screen, ignored when use -w, -g, -r")
	flag.BoolVar(&colored, "c", true, "Colored the ascii when output to the terminal")
	flag.BoolVar(&reversed, "i", false, "Reversed the ascii when output to the terminal")
	flag.Usage = usage
}



@@ 47,6 49,7 @@ func parseOptions() (*convert.Options, error) {
		ExpectedWidth:  expectedWidth,
		FitScreen:      fitScreen,
		Colored:        colored,
		Reversed:       reversed,
	}
	return convertOptions, nil
}