Skip to content

Commit 322ec81

Browse files
committed
Improve documentation, code formatting
- removed manic blog post readme - stopped exporting implementation details no one cares about - miscellaneous code clean up - created Color type for more reasonable formatting logic - moved command-line util to cmd/vibrant - transformed demo app from a spaghetti nightmare into something decent
1 parent 838ba43 commit 322ec81

15 files changed

+619
-625
lines changed

.gitignore

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
*.sw*
22
*.jpg
33
*.png
4-
iris/*
5-
cmd/vibrant
6-
cmd/cmd

README.md

+76-74
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,102 @@
11
# vibrant
22

3-
go port of the [Android awesome Palette class](https://android.googlesource.com/platform/frameworks/support/+/b14fc7c/v7/palette/src/android/support/v7/graphics/)
3+
[![godoc reference](https://godoc.org/github.com/generaltso/vibrant?status.png)](https://godoc.org/github.com/generaltso/vibrant)
44

5-
which I translated from this beautifully cleaned up version:
6-
https://github.com/Infinity/Iris
75

8-
and was first made aware of by [this Google I/O 2014 presentation](https://www.youtube.com/watch?v=ctzWKRlTYHQ?t=451)
6+
Extract prominent colors from images. Go port of the [Android awesome Palette class](https://android.googlesource.com/platform/frameworks/support/+/b14fc7c/v7/palette/src/android/support/v7/graphics/) aka [Vibrant.js](https://github.com/jariz/vibrant.js).
97

10-
and of course
11-
https://github.com/jariz/vibrant.js
8+
![screenshot of app.go](https://u.teknik.io/Rv3r3.png)
129

13-
and last but not least
14-
https://github.com/akfish/node-vibrant
10+
# install
1511

16-
## Why
12+
```
13+
go get github.com/generaltso/vibrant
14+
```
15+
16+
# usage
1717

18-
I was dissatisfied with the performance of the above JavaScript ports. One day, I mocked up an HTML thumbnail view gallery thing and wanted to use "Vibrancy" to decorate the buttons, links, titles, etc. The page had about 25 wallpaper images I used just as placeholders and using Vibrant.js brought my browser to its knees.
18+
```go
19+
// example: create css stylesheet from image file
20+
checkErr := func(err error) { if err != nil { panic(err) } }
1921

20-
Yes, I could have just thumbnailed the wallpapers and carried on like a normal person, but this act of stupidity got me thinking: why do this calculation in the browser? If it kills my browser like this, imagine what it must do to mobiles. And it's not like the images are dynamic (in my use case, anyway). They're dynamic in the sense that users upload them but once they're there, they don't change and neither does their palette.
22+
f, err := os.Open("some_image.jpg")
23+
checkErr(err)
24+
defer f.Close()
2125

22-
So why not do it server-side and cache the result? As a CSS file, which I can then use with `<style scoped>` e.g.:
23-
```HTML
24-
<section class="card">
25-
<style scoped>@import "image1-vibrant.css";</style>
26-
<h1 class="darkvibrant">~Fancy Title~</h1>
27-
<div class="card-border muted">
28-
<img src="image1-thumb.jpg">
29-
</div>
30-
<div class="card-caption darkmuted">
31-
<button class="vibrant">Call to action!</button>
32-
</div>
33-
</section>
26+
img, _, err := image.Decode(f)
27+
checkErr(err)
28+
29+
palette, err := vibrant.NewPaletteFromImage(img)
30+
checkErr(err)
31+
32+
for name, swatch := range palette.ExtractAwesome() {
33+
fmt.Printf("/* %s (population: %d) */\n%s\n\n", name, swatch.Population, swatch)
34+
}
3435
```
3536

37+
###### output:
38+
```css
39+
/* LightMuted (population: 253) */
40+
.lightmuted{background-color:#cbc0a2;color:#000000;}
3641

37-
It's probably not a good idea to over-do it like that with the colors, but the point is that you *can* do it in the first place.
42+
/* DarkMuted (population: 11069) */
43+
.darkmuted{background-color:#5b553f;color:#ffffff;}
3844

39-
This approach also allows for graceful fallback to a default palette set
45+
/* Vibrant (population: 108) */
46+
.vibrant{background-color:#dfd013;color:#000000;}
4047

41-
Perhaps I should stop trying to words **here is an example of scoped css**, view source until it makes sense:
42-
http://var.abl.cl/scoped-css
48+
/* LightVibrant (population: 87) */
49+
.lightvibrant{background-color:#f4ed7d;color:#000000;}
50+
51+
/* DarkVibrant (population: 2932) */
52+
.darkvibrant{background-color:#917606;color:#ffffff;}
53+
54+
/* Muted (population: 4098) */
55+
.muted{background-color:#a58850;color:#000000;}
4356

44-
## Usage
45-
```bash
46-
$ go get github.com/generaltso/vibrant
4757
```
4858

49-
```go
50-
package main
51-
52-
import (
53-
"fmt"
54-
"image"
55-
_ "image/jpeg"
56-
"log"
57-
"os"
58-
)
59-
60-
import "github.com/generaltso/vibrant"
61-
62-
func main() {
63-
file, err := os.Open("some_image.jpg")
64-
if err != nil {
65-
log.Fatalln(err)
66-
}
67-
img, _, err := image.Decode(file)
68-
if err != nil {
69-
log.Fatalln(err)
70-
}
71-
palette, err := vibrant.NewPaletteFromImage(img)
72-
if err != nil {
73-
log.Fatalln(err)
74-
}
75-
for name, swatch := range palette.ExtractAwesome() {
76-
fmt.Printf("name: %s, color: %s, population: %d\n", name /* or swatch.Name */, swatch.RGBHex(), swatch.Population)
77-
}
78-
}
59+
See [godoc reference](https://godoc.org/github.com/generaltso/vibrant) for full API.
60+
61+
# bonus round
62+
63+
## reference implementation/command line tool
64+
```
65+
go get github.com/generaltso/vibrant/cmd/vibrant
7966
```
8067

81-
There is also a command-line tool in vibrant/:
82-
```bash
83-
$ cd vibrant && go install
84-
$ vibrant some_image.png
8568
```
86-
(it prints CSS to stdout)
69+
usage: vibrant [options] file
70+
71+
options:
72+
-compress
73+
Strip whitespace from output.
74+
-css
75+
Output results in CSS.
76+
-json
77+
Output results in JSON.
78+
-lowercase
79+
Use lowercase only for all output. (default true)
80+
-rgb
81+
Output RGB instead of HTML hex, e.g. #ffffff.
82+
```
83+
84+
## webapp
8785

88-
And there is a simple demo application which is a little more visual:
89-
```bash
90-
$ cd demo && go run app.go
91-
Listening on 0.0.0.0:8080...
9286
```
93-
![screenshot of app.go](https://u.teknik.io/G3qoka.png)
87+
go get github.com/generaltso/vibrant
88+
cd $GOPATH/github.com/generaltso/vibrant
89+
go run webapp.go
90+
# open http://localhost:8080/ in a browser
91+
```
9492

9593

96-
**This API and the tools provided are a work-in-progress proof-of-concept and certainly could use improvement, better naming, etc.**
94+
# thanks
9795

98-
Comments, feedback and pull requests are welcome!
99-
[email me](mailto:[email protected])
100-
[find this project on github](https://github.com/generaltso/vibrant)
96+
https://github.com/Infinity/Iris
97+
98+
[This Google I/O 2014 presentation](https://www.youtube.com/watch?v=ctzWKRlTYHQ?t=451)
99+
100+
https://github.com/jariz/vibrant.js
101+
102+
https://github.com/akfish/node-vibrant

bitmap.go

+16-13
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
package vibrant
22

3-
import "github.com/nfnt/resize"
4-
import "image"
5-
import "image/color"
6-
import "math"
3+
import (
4+
"image"
5+
"image/color"
6+
"math"
77

8-
// type Bitmap is a simple wrapper for an image.Image
9-
type Bitmap struct {
8+
"github.com/nfnt/resize"
9+
)
10+
11+
// type bitmap is a simple wrapper for an image.Image
12+
type bitmap struct {
1013
Width int
1114
Height int
1215
Source image.Image
1316
}
1417

15-
func NewBitmap(input image.Image) *Bitmap {
18+
func newBitmap(input image.Image) *bitmap {
1619
bounds := input.Bounds()
17-
return &Bitmap{bounds.Dx(), bounds.Dy(), input}
20+
return &bitmap{bounds.Dx(), bounds.Dy(), input}
1821
}
1922

20-
// Scales input image.Image by ratio using github.com/nfnt/resize
21-
func NewScaledBitmap(input image.Image, ratio float64) *Bitmap {
23+
// Scales input image.Image by aspect ratio using https://github.com/nfnt/resize
24+
func newScaledBitmap(input image.Image, ratio float64) *bitmap {
2225
bounds := input.Bounds()
2326
w := math.Ceil(float64(bounds.Dx()) * ratio)
2427
h := math.Ceil(float64(bounds.Dy()) * ratio)
25-
return &Bitmap{int(w), int(h), resize.Resize(uint(w), uint(h), input, resize.Bilinear)}
28+
return &bitmap{int(w), int(h), resize.Resize(uint(w), uint(h), input, resize.Bilinear)}
2629
}
2730

28-
// Returns all of the pixels of this Bitmap.Source as a 1D array of color.Color's
29-
func (b *Bitmap) Pixels() []color.Color {
31+
// Returns all of the pixels of this bitmap.Source as a 1D array of image/color.Color
32+
func (b *bitmap) Pixels() []color.Color {
3033
c := make([]color.Color, 0)
3134
for y := 0; y < b.Height; y++ {
3235
for x := 0; x < b.Width; x++ {

vibrant/vibrant.go cmd/vibrant/vibrant.go

+14-20
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import (
1515
"github.com/generaltso/vibrant"
1616
)
1717

18-
var output_json bool
19-
var output_css bool
20-
var output_compress bool
21-
var output_lowercase bool
22-
var output_rgb bool
18+
var (
19+
output_json bool
20+
output_css bool
21+
output_compress bool
22+
output_lowercase bool
23+
output_rgb bool
24+
)
2325

2426
func usage() {
2527
println("usage: vibrant [options] file")
@@ -82,10 +84,10 @@ func print_json(palette vibrant.Palette) {
8284
out := map[string]interface{}{}
8385
for name, sw := range palette.ExtractAwesome() {
8486
if output_rgb {
85-
r, g, b := sw.RGB()
87+
r, g, b := sw.Color.RGB()
8688
out[name] = map[string]int{"r": r, "g": g, "b": b}
8789
} else {
88-
out[name] = swatch{sw.RGBHex(), sw.TitleTextColor()}
90+
out[name] = swatch{sw.Color.RGBHex(), sw.Color.TitleTextColor().RGBHex()}
8991
}
9092
}
9193
var b []byte
@@ -104,14 +106,6 @@ func print_json(palette vibrant.Palette) {
104106
fmt.Println(str)
105107
}
106108

107-
func textcolor(sw *vibrant.Swatch) (r, g, b int) {
108-
c := vibrant.TextColor(sw.Color, vibrant.MIN_CONTRAST_TITLE_TEXT)
109-
r = c >> 16 & 0xff
110-
g = c >> 8 & 0xff
111-
b = c & 0xff
112-
return
113-
}
114-
115109
func rgb(r ...int) string {
116110
return fmt.Sprintf("rgb(%d,%d,%d)", r[0], r[1], r[2])
117111
}
@@ -131,11 +125,11 @@ func print_css(palette vibrant.Palette) {
131125
var bgcolor string
132126
var fgcolor string
133127
if output_rgb {
134-
bgcolor = rgb(sw.RGB())
135-
fgcolor = rgb(textcolor(sw))
128+
bgcolor = rgb(sw.Color.RGB())
129+
fgcolor = rgb(sw.Color.TitleTextColor().RGB())
136130
} else {
137-
bgcolor = sw.RGBHex()
138-
fgcolor = sw.TitleTextColor()
131+
bgcolor = sw.Color.RGBHex()
132+
fgcolor = sw.Color.TitleTextColor().RGBHex()
139133
}
140134
if output_lowercase {
141135
name = strings.ToLower(name)
@@ -162,6 +156,6 @@ func shorthex(hex string) string {
162156

163157
func print_plain(palette vibrant.Palette) {
164158
for name, sw := range palette.ExtractAwesome() {
165-
fmt.Printf("% 12s: %s, population: %d\n", name, sw.RGBHex(), sw.Population)
159+
fmt.Printf("% 12s: %s, population: %d\n", name, sw.Color.RGBHex(), sw.Population)
166160
}
167161
}

0 commit comments

Comments
 (0)