package photos import ( "bytes" "image" "github.com/disintegration/imaging" "github.com/gen2brain/webp" ) // Object roles holding the non-destructive edited version of a photo. They // mirror the base roles and are bound in addition to them. const ( RoleEdit = "edit" RoleEditThumb = "edit_thumb" RoleEditMedium = "edit_medium" RoleEditPreview = "edit_preview" ) // applyLight renders the light adjustments into a copy of the image. The // original pixels are never modified in place. func applyLight(src image.Image, p LightParams) *image.NRGBA { // Clone returns a fresh NRGBA copy, so the source stays untouched. dst := imaging.Clone(src) master := curveLUT(p.Curve.RGB) red := curveLUT(p.Curve.Red) green := curveLUT(p.Curve.Green) blue := curveLUT(p.Curve.Blue) width := dst.Rect.Dx() for y := 0; y < dst.Rect.Dy(); y++ { row := dst.Pix[y*dst.Stride : y*dst.Stride+width*4] for i := 0; i+3 < len(row); i += 4 { if row[i+3] == 0 { continue } row[i] = uint8(adjustChannel(float64(row[i]), p, &master, &red)) row[i+1] = uint8(adjustChannel(float64(row[i+1]), p, &master, &green)) row[i+2] = uint8(adjustChannel(float64(row[i+2]), p, &master, &blue)) } } return dst } // buildEditedVariants renders the edited version of a photo: the full-size // image plus the thumb, medium and preview variants. The variants mirror the // base ones, so the grid and the viewer can switch to them transparently. func buildEditedVariants(data []byte, p EditParams) (thumb, medium, preview, full []byte, err error) { img, err := imaging.Decode(bytes.NewReader(data), imaging.AutoOrientation(true)) if err != nil { return nil, nil, nil, nil, err } edited := applyLight(img, p.Light) thumbImg := imaging.Fill(edited, 200, 200, imaging.Center, imaging.Lanczos) mediumImg := imaging.Fill(edited, 800, 800, imaging.Center, imaging.Lanczos) previewImg := imaging.Fit(edited, 1200, 1200, imaging.Lanczos) opts := webp.Options{Quality: 82} var buf bytes.Buffer if err := webp.Encode(&buf, thumbImg, opts); err != nil { return nil, nil, nil, nil, err } thumb = buf.Bytes() buf.Reset() if err := webp.Encode(&buf, mediumImg, opts); err != nil { return nil, nil, nil, nil, err } medium = buf.Bytes() buf.Reset() if err := webp.Encode(&buf, previewImg, opts); err != nil { return nil, nil, nil, nil, err } preview = buf.Bytes() buf.Reset() if err := webp.Encode(&buf, edited, opts); err != nil { // Полноразмерная версия не обязательна: превью и миниатюры уже // готовы, поэтому ошибку кодирования полной версии не считаем // фатальной. full = nil } else { full = buf.Bytes() } return thumb, medium, preview, full, nil }