func (a *Application) Store(filepath string, i *image.ImageFile) error { con := a.KVStore.Connection() defer con.Close() err := i.Save() if err != nil { a.Logger.Fatal(err) return err } a.Logger.Infof("Save thumbnail %s to storage", i.Filepath) key := a.WithPrefix(i.Key) err = con.Set(key, i.Filepath) if err != nil { a.Logger.Fatal(err) return err } a.Logger.Infof("Save key %s => %s to kvstore", key, i.Filepath) // Write children info only when we actually want to be able to delete things. if a.EnableDelete { err = con.SetAdd(filepath+":children", key) if err != nil { a.Logger.Fatal(err) return err } a.Logger.Infof("Put key into set %s:children => %s in kvstore", filepath, key) } return nil }
func (e *GoImageEngine) TransformGIF(img *imagefile.ImageFile, width int, height int, options *Options, trans Transformation) ([]byte, error) { first, err := gif.Decode(bytes.NewReader(img.Source)) if err != nil { return nil, err } factor := scalingFactorImage(first, width, height) if factor > 1 && !options.Upscale { return img.Source, nil } g, err := gif.DecodeAll(bytes.NewReader(img.Source)) if err != nil { return nil, err } length := len(g.Image) done := make(chan *Result) images := make([]*image.Paletted, length) processed := 0 for i := range g.Image { go func(paletted *image.Paletted, width int, height int, position int, trans Transformation, options *Options) { img := e.Scale(paletted, width, height, options.Upscale, trans) bounds := img.Bounds() done <- &Result{ Image: img, Position: position, Paletted: image.NewPaletted(image.Rect(0, 0, bounds.Max.X, bounds.Max.Y), paletted.Palette), } }(g.Image[i], width, height, i, trans, options) } for { select { case result := <-done: bounds := result.Image.Bounds() draw.Draw(result.Paletted, bounds, result.Image, bounds.Min, draw.Src) images[result.Position] = result.Paletted processed++ case <-time.After(time.Second * 5): break } if processed == length { break } } close(done) g.Image = images buf := &bytes.Buffer{} err = gif.EncodeAll(buf, g) if err != nil { return nil, err } return buf.Bytes(), nil }
func (e *GoImageEngine) Transform(img *imagefile.ImageFile, operation *Operation, qs map[string]string) (*imagefile.ImageFile, error) { params := map[string]string{ "upscale": "1", "h": "0", "w": "0", "deg": "90", } err := mergo.Merge(&qs, params) if err != nil { return nil, err } var quality int var format string q, ok := qs["q"] if ok { quality, err := strconv.Atoi(q) if err != nil { return nil, err } if quality > 100 { return nil, fmt.Errorf("Quality should be <= 100") } } else { quality = e.DefaultQuality } format, ok = qs["fmt"] filepath := img.Filepath if ok { if _, ok := ContentTypes[format]; !ok { return nil, fmt.Errorf("Unknown format %s", format) } } if format == "" && e.Format != "" { format = e.Format } if format == "" { format = img.Format() } if format == "" { format = e.DefaultFormat } if format != img.Format() { index := len(filepath) - len(img.Format()) filepath = filepath[:index] + format } file := &imagefile.ImageFile{ Source: img.Source, Key: img.Key, Headers: img.Headers, Filepath: filepath, } options := &Options{ Quality: quality, Format: Formats[format], } switch operation { case Flip: pos, ok := qs["pos"] if !ok { return nil, fmt.Errorf("Parameter \"pos\" not found in query string") } content, err := e.Flip(img, pos, options) if err != nil { return nil, err } file.Processed = content return file, err case Rotate: deg, err := strconv.Atoi(qs["deg"]) if err != nil { return nil, err } content, err := e.Rotate(img, deg, options) if err != nil { return nil, err } file.Processed = content return file, err case Thumbnail, Resize, Fit: var upscale bool var w int var h int if upscale, err = strconv.ParseBool(qs["upscale"]); err != nil { return nil, err } if w, err = strconv.Atoi(qs["w"]); err != nil { return nil, err } if h, err = strconv.Atoi(qs["h"]); err != nil { return nil, err } options.Upscale = upscale switch operation { case Resize: content, err := e.Resize(img, w, h, options) if err != nil { return nil, err } file.Processed = content return file, err case Thumbnail: content, err := e.Thumbnail(img, w, h, options) if err != nil { return nil, err } file.Processed = content return file, err case Fit: content, err := e.Fit(img, w, h, options) if err != nil { return nil, err } file.Processed = content return file, err } case Original: file.Processed = img.Source return file, err } return nil, fmt.Errorf("Operation not found for %s", operation) }
func (a *Application) ImageFileFromRequest(req *Request, async bool, load bool) (*image.ImageFile, error) { var file *image.ImageFile = &image.ImageFile{ Key: req.Key, Storage: a.DestStorage, Headers: map[string]string{}, } var err error key := a.WithPrefix(req.Key) // Image from the KVStore found stored, err := gokvstores.String(req.Connection.Get(key)) file.Filepath = stored if stored != "" { a.Logger.Infof("Key %s found in kvstore: %s", key, stored) if load { file, err = image.FromStorage(a.DestStorage, stored) if err != nil { return nil, err } } } else { a.Logger.Infof("Key %s not found in kvstore", key) // Image not found from the KVStore, we need to process it // URL available in Query String if req.URL != nil { file, err = image.FromURL(req.URL) } else { // URL provided we use http protocol to retrieve it file, err = image.FromStorage(a.SourceStorage, req.Filepath) } if err != nil { return nil, err } file, err = a.Engine.Transform(file, req.Operation, req.QueryString) if err != nil { return nil, err } file.Filepath = fmt.Sprintf("%s.%s", a.ShardFilename(req.Key), file.Format()) } file.Key = req.Key file.Storage = a.DestStorage file.Headers["Content-Type"] = file.ContentType() file.Headers["ETag"] = req.Key if stored == "" { if async == true { go a.Store(req.Filepath, file) } else { err = a.Store(req.Filepath, file) } } return file, err }