-
Notifications
You must be signed in to change notification settings - Fork 38
feat: halfblocks support #387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
1ba2778
to
f543d5c
Compare
|
||
// EncoderOptions contains all configurable settings | ||
type EncoderOptions struct { | ||
ColorMode int // 0=none, 1=8colors, 2=256colors, 3=truecolor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we probably don't need this since downsampling colors is handled by colorprofile
DitherLevel float64 // Dithering amount (0.0-1.0) | ||
UseFgBgOnly bool // Use only foreground/background colors (no block symbols) | ||
InvertColors bool // Invert colors | ||
ScaleMode string // fit, stretch, center |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's define a type for this one, maybe use draw.Scaler
https://pkg.go.dev/golang.org/x/image/draw#Scaler
btw we use the term enconding but in halfblocks isn't reaaaallly encoding. I think makes more sense to rename to render term |
talked with @aymanbagabas and we think it makes more sense to live in lipgloss. Will be moving it to there and address the comments |
} | ||
|
||
// getPixelSafe returns the color at (x,y) or black if out of bounds | ||
func (r *Renderer) getPixelSafe(img image.Image, x, y int) color.RGBA { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use color.Color
instead of color.RGBA
all over
func (r *Renderer) getPixelSafe(img image.Image, x, y int) color.RGBA { | ||
bounds := img.Bounds() | ||
if x < bounds.Min.X || x >= bounds.Max.X || y < bounds.Min.Y || y >= bounds.Max.Y { | ||
return color.RGBA{0, 0, 0, 255} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be
return color.RGBA{0, 0, 0, 255} | |
return color.Black |
r8, g8, b8, a8 := img.At(x, y).RGBA() | ||
return color.RGBA{ | ||
R: uint8(r8 >> 8), | ||
G: uint8(g8 >> 8), | ||
B: uint8(b8 >> 8), | ||
A: uint8(a8 >> 8), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r8, g8, b8, a8 := img.At(x, y).RGBA() | |
return color.RGBA{ | |
R: uint8(r8 >> 8), | |
G: uint8(g8 >> 8), | |
B: uint8(b8 >> 8), | |
A: uint8(a8 >> 8), | |
} | |
return img.At(x, y) |
} | ||
|
||
// scaleImage resizes an image to the specified dimensions | ||
func (r *Renderer) scaleImage(img image.Image, width, height int) image.Image { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All scale functions should adhere to draw.Scaler
var fgStr, bgStr string | ||
|
||
switch r.Options.ColorMode { | ||
case 1: // 8 colors | ||
fgCode := nearestAnsi8Color(fg.R, fg.G, fg.B) | ||
bgCode := nearestAnsi8Color(bg.R, bg.G, bg.B) | ||
fgStr = fmt.Sprintf("\033[%dm", 30+fgCode) | ||
bgStr = fmt.Sprintf("\033[%dm", 40+bgCode) | ||
|
||
case 2: // 256 colors | ||
fgCode := nearestAnsi256Color(fg.R, fg.G, fg.B) | ||
bgCode := nearestAnsi256Color(bg.R, bg.G, bg.B) | ||
fgStr = fmt.Sprintf("\033[38;5;%dm", fgCode) | ||
bgStr = fmt.Sprintf("\033[48;5;%dm", bgCode) | ||
|
||
case 3: // True color | ||
fgStr = fmt.Sprintf("\033[38;2;%d;%d;%dm", fg.R, fg.G, fg.B) | ||
bgStr = fmt.Sprintf("\033[48;2;%d;%d;%dm", bg.R, bg.G, bg.B) | ||
} | ||
|
||
return fgStr + bgStr + string(char) + "\033[0m" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use ansi.Style
with color.Color
instead. Downgrading colors can should be handled by colorprofile. We have types for 16-bit and indexed in ansi
, specifically ansi.BasicColor
, ansi.IndexedColor
, any other color.Color
type will be treated as a true color.
The only optimization I can think of is checking if the RGB value corresponds to a 256 terminal color and using that to encode the color saving some SGR bytes i.e. if the color is #5f0000
we can use the defined XTerm dark red (indexed 52) color instead of to save bytes \x1b[48;2;95;0;0m
vs `\x1b[48;5;52m'.
Anyway, we should use ansi.Style{}.ForegroundColor(fg).BackgroundColor(bg).Styled(string(char))
instead
Moving it to charmbracelet/lipgloss#482 |
Convert images to strings using half blocks symbols
API
Result:
Other examples