Author SHA1 Message Date
schreifuchs 2db36e2ada fieat(gallery): recursive image search 2026-06-01 13:45:41 +02:00
schreifuchs 9ca81d7f13 fix(gallery): config 2026-06-01 13:23:04 +02:00
schreifuchs c55e6bc43f feat(gallery): reactivity 2026-06-01 13:07:32 +02:00
schreifuchs efcc32b522 feat(gallery): caching 2026-06-01 12:38:09 +02:00
schreifuchs 8a72b96e66 feat(gallery): packing 2026-05-31 15:48:18 +02:00
schreifuchs 9282f1f634 feat(gallery): routes 2026-05-31 10:46:07 +02:00
3 changed files with 17 additions and 63 deletions
+1
View File
@@ -91,3 +91,4 @@ To optimize images before uploading to Nextcloud:
```bash ```bash
convert input.png -resize 3000 -quality 80 output.webp convert input.png -resize 3000 -quality 80 output.webp
``` ```
+13 -52
View File
@@ -2,7 +2,6 @@ package gallery
import ( import (
"context" "context"
"log/slog"
"math" "math"
"github.com/InfinityTools/go-binpack2d" "github.com/InfinityTools/go-binpack2d"
@@ -43,17 +42,12 @@ func (s *Service) GetGallery(ctx context.Context, width int) (gallery Gallery, e
return return
} }
packer := binpack2d.Create(width, math.MaxInt) packer := binpack2d.Create(width, 999999)
for len(imgs) > 0 { for len(imgs) > 0 {
unused := make([]Image, 0, len(imgs)) unused := make([]Image, 0, len(imgs))
for _, img := range imgs { for _, img := range imgs {
adjustImageSizeStrictRatio(&img, 200) adjustImageSize(&img, 200)
if img.Height == 0 || img.Width == 0 {
slog.Warn("image has invalid size", "img", img)
continue
}
rect, ok := packer.Insert(img.Width, img.Height, binpack2d.RULE_BEST_SHORT_SIDE_FIT) rect, ok := packer.Insert(img.Width, img.Height, binpack2d.RULE_BEST_SHORT_SIDE_FIT)
if !ok { if !ok {
unused = append(unused, img) unused = append(unused, img)
@@ -76,50 +70,17 @@ func (s *Service) GetGallery(ctx context.Context, width int) (gallery Gallery, e
return return
} }
// getCleanRatio returns the simplified aspect ratio as (numerator, denominator). // adjustImageSize adjusts the image size so that the aspect ratio stays the same
// It searches for the best fit within a denominator limit to account for rounding errors. // but the image area is the given parameter.
func getCleanRatio(width, height int) (int, int) { func adjustImageSize(img *Image, area int) {
targetRatio := float64(width) / float64(height) // Calculate the original aspect ratio (W / H)
const maxDenominator = 100 aspectRatio := float64(img.Width) / float64(img.Height)
bestN, bestD := 0, 1 // Calculate new height and width using the derived formulas
minDiff := math.MaxFloat64 newHeight := math.Sqrt(float64(area) / aspectRatio)
newWidth := newHeight * aspectRatio
for d := 1; d <= maxDenominator; d++ { // Round to the nearest integer to minimize precision loss
// Calculate the closest numerator for this denominator img.Width = int(math.Round(newWidth))
n := int(math.Round(float64(d) * targetRatio)) img.Height = int(math.Round(newHeight))
// Calculate the difference
currentRatio := float64(n) / float64(d)
diff := math.Abs(currentRatio - targetRatio)
// Keep track of the one with the smallest error
if diff < minDiff {
minDiff = diff
bestN, bestD = n, d
}
}
return bestN, bestD
}
func adjustImageSizeStrictRatio(img *Image, targetArea int) {
baseW, baseH := getCleanRatio(img.Width, img.Height)
baseArea := baseW * baseH
idealK := math.Sqrt(float64(targetArea) / float64(baseArea))
k := int(math.Floor(idealK))
if math.Abs(float64(k*k*baseArea-targetArea)) >
math.Abs(float64(int(math.Ceil(idealK))*int(math.Ceil(idealK))*baseArea-targetArea)) {
k = int(math.Ceil(idealK))
}
slog.Debug("adslkfj", "k", k, "idealK", idealK, "baseH", baseH, "baseW", baseW, "imgW", img.Width, "imgH", img.Height)
// 5. Apply the scaled dimensions (guarantees perfect aspect ratio)
img.Width = baseW * k
img.Height = baseH * k
} }
+3 -11
View File
@@ -45,6 +45,7 @@ func (s *Service) assembleImageList(ctx context.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("could not read contents of %s: %w", s.cfg.Path, err) return fmt.Errorf("could not read contents of %s: %w", s.cfg.Path, err)
} }
slog.Debug("loaded files", "files", files)
savedHash, _ := s.cache.Do(ctx, s.cache.B().Get().Key(hashKey).Build()).AsBytes() savedHash, _ := s.cache.Do(ctx, s.cache.B().Get().Key(hashKey).Build()).AsBytes()
@@ -76,19 +77,10 @@ func (s *Service) assembleImageList(ctx context.Context) error {
continue continue
} }
w := img.Bounds().Dx()
h := img.Bounds().Dy()
// FIX 2: Defensive check to guard against unreadable/corrupted image boundaries
if w <= 0 || h <= 0 {
slog.Warn("skipping image with zero or negative dimensions", "path", imagePath, "w", w, "h", h)
continue
}
imgs = append(imgs, Image{ imgs = append(imgs, Image{
UID: uid, UID: uid,
Width: w, Width: img.Bounds().Dx(),
Height: h, Height: img.Bounds().Dy(),
}) })
} }