-
Notifications
You must be signed in to change notification settings - Fork 175
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #96 from thoas/superpose-images
backend method: flat
- Loading branch information
Showing
37 changed files
with
2,431 additions
and
716 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Flat | ||
|
||
Flat is a method implemented to the engine goimage in order to draw | ||
images on a background image. | ||
|
||
This method can be used only with the multiple operation parameter `op`, | ||
in the URL. | ||
|
||
## Parameters: | ||
|
||
* `path`: the foreground image, can be multiple. | ||
* `pos`: the foreground destination as a rectangle | ||
* `color`: the foreground color in Hex (without `#`), default is transparent. | ||
|
||
|
||
## Usage | ||
|
||
The background is defined by the image transformed by the previous | ||
operation. In order to draw an image on the background a position must | ||
be given in the sub-parameter `pos` and the image path in the | ||
sub-parameter `path`. | ||
|
||
example: | ||
``` | ||
/display?path=path/to/background.png&op=resize&w=100&h=100 | ||
&op=op:flat+path:path/to/foreground.png+pos:60.10.80.30 | ||
``` | ||
|
||
|
||
The value of `pos` must be the coordinates of the rectangle defined | ||
according to the [go image package](https://blog.golang.org/go-image-package): | ||
an axis-aligned rectangle on the integer grid, defined by its top-left and | ||
bottom-right Point. | ||
|
||
![Rectangle position](https://github.com/thoas/picfit/blob/superpose-images/docs/picfit-dst-position.png) | ||
|
||
The foreground image is resized in order to fit in the given rectangle | ||
and centered inside. | ||
|
||
If several images are given in the same flat operation with the | ||
subparameters path. The rectangle is cut in equal parts, **horizontally** if | ||
the rectangle width `Dx` is superior to its height `Dy` and | ||
**vertically** if it is not the case. Each images are then resized in | ||
order to fit in each parts and centered inside. The order follow the | ||
given order of `path` parameters in the URL. | ||
|
||
![Flat multiple images](https://github.com/thoas/picfit/blob/superpose-images/docs/picfit-flat.png) | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package backend | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
"image/draw" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/disintegration/imaging" | ||
colorful "github.com/lucasb-eyer/go-colorful" | ||
|
||
imagefile "github.com/thoas/picfit/image" | ||
) | ||
|
||
func (e *GoImage) Flat(backgroundFile *imagefile.ImageFile, options *Options) ([]byte, error) { | ||
if options.Format == imaging.GIF { | ||
return e.TransformGIF(backgroundFile, options, imaging.Resize) | ||
} | ||
|
||
background, err := e.Source(backgroundFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
images := make([]image.Image, len(options.Images)) | ||
for i := range options.Images { | ||
images[i], err = e.Source(&options.Images[i]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
bg := image.NewRGBA(image.Rectangle{image.Point{}, background.Bounds().Size()}) | ||
draw.Draw(bg, background.Bounds(), background, image.Point{}, draw.Src) | ||
|
||
dst := positionForeground(bg, options.Position) | ||
fg := foregroundImage(dst, options.Color) | ||
fg = drawForeground(fg, images, options) | ||
|
||
draw.Draw(bg, dst, fg, fg.Bounds().Min, draw.Over) | ||
|
||
return e.ToBytes(bg, options.Format, options.Quality) | ||
} | ||
|
||
func positionForeground(bg image.Image, pos string) image.Rectangle { | ||
ratios := []int{100, 100, 100, 100} | ||
val := strings.Split(pos, ".") | ||
for i := range val { | ||
if i+1 > len(ratios) { | ||
break | ||
} | ||
ratios[i], _ = strconv.Atoi(val[i]) | ||
} | ||
b := bg.Bounds() | ||
return image.Rectangle{ | ||
image.Point{b.Dx() * ratios[0], b.Dy() * ratios[1]}.Div(100), | ||
image.Point{b.Dx() * ratios[2], b.Dy() * ratios[3]}.Div(100), | ||
} | ||
} | ||
|
||
func foregroundImage(rec image.Rectangle, c string) *image.RGBA { | ||
fg := image.NewRGBA(image.Rectangle{image.ZP, rec.Size()}) | ||
if c == "" { | ||
return fg | ||
} | ||
|
||
col, err := colorful.Hex(fmt.Sprintf("#%s", c)) | ||
if err != nil { | ||
return fg | ||
} | ||
|
||
draw.Draw(fg, fg.Bounds(), &image.Uniform{col}, fg.Bounds().Min, draw.Src) | ||
return fg | ||
} | ||
|
||
func drawForeground(fg *image.RGBA, images []image.Image, options *Options) *image.RGBA { | ||
n := len(images) | ||
if n == 0 { | ||
return fg | ||
} | ||
|
||
// resize images for foreground | ||
b := fg.Bounds() | ||
opts := &Options{Upscale: true} | ||
|
||
if b.Dx() > b.Dy() { | ||
opts.Width = b.Dx() / n | ||
opts.Height = b.Dy() | ||
} else { | ||
opts.Width = b.Dx() | ||
opts.Height = b.Dy() / n | ||
} | ||
|
||
for i := range images { | ||
images[i] = scale(images[i], opts, imaging.Fit) | ||
} | ||
|
||
if b.Dx() > b.Dy() { | ||
return foregroundHorizontal(fg, images, options) | ||
} else { | ||
return foregroundVertical(fg, images, options) | ||
} | ||
} | ||
|
||
func foregroundHorizontal(fg *image.RGBA, images []image.Image, options *Options) *image.RGBA { | ||
position := fg.Bounds().Min | ||
totalHeight := fg.Bounds().Dy() | ||
cellWidth := fg.Bounds().Dx() / len(images) | ||
for i := range images { | ||
bounds := images[i].Bounds() | ||
position.Y = (totalHeight - bounds.Dy()) / 2 | ||
position.X = fg.Bounds().Min.X + i*cellWidth + (cellWidth-bounds.Dx())/2 | ||
r := image.Rectangle{ | ||
position, | ||
position.Add(fg.Bounds().Size()), | ||
} | ||
draw.Draw(fg, r, images[i], bounds.Min, draw.Over) | ||
} | ||
return fg | ||
} | ||
|
||
func foregroundVertical(fg *image.RGBA, images []image.Image, options *Options) *image.RGBA { | ||
position := fg.Bounds().Min | ||
cellHeight := fg.Bounds().Dy() / len(images) | ||
totalWidth := fg.Bounds().Dx() | ||
for i := range images { | ||
bounds := images[i].Bounds() | ||
position.Y = fg.Bounds().Min.Y + i*cellHeight + (cellHeight-bounds.Dy())/2 | ||
position.X = fg.Bounds().Min.X + (totalWidth-bounds.Dx())/2 | ||
r := image.Rectangle{ | ||
position, | ||
position.Add(image.Point{bounds.Dx(), bounds.Dy()}), | ||
} | ||
draw.Draw(fg, r, images[i], bounds.Min, draw.Over) | ||
} | ||
return fg | ||
} |
Oops, something went wrong.