fix(thumbnails): Fassung in der Adresse, Sofortanzeige, kein Flackern
Gleicher Dateiname erzeugt keine neue Datei, sondern eine neue Fassung - gleiche ID, gleiche Vorschaubild-Adresse. Die darf einen Tag im Browser liegen, also zeigte die Kachel nach dem erneuten Hochladen bis zu 24 Stunden das alte Bild. Das erklaert, warum eine Drehung erst nach mehreren Versuchen "ankam". Die Adresse traegt jetzt ?v=<Fassung>. Vorschaubilder entstehen im Hintergrund; die Kachel fragt jetzt bis zu fuenfmal nach, statt bis zum naechsten Neuladen ihr Ersatzsymbol zu zeigen. Nur bei Bildern juenger als zwei Minuten - sonst fragte jede dauerhaft fehlgeschlagene Datei bei jedem Oeffnen erneut. Gemessen: 608 ms bis zur Anzeige, ohne Neuladen. Das Anheben der Kachel beim Ueberfahren ist raus: Steht der Zeiger auf der Unterkante, rutscht sie unter ihm weg, mouseleave, sie faellt zurueck, mouseenter - im Kreis, sichtbar als Flackern. Dazu liest die EXIF-Auswertung jetzt 256 statt 64 KB und gibt bei einem abgeschnittenen Block nicht mehr auf: Die Drehung steht vorn, das eingebettete Vorschaubild dahinter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b3658640e
commit
5ea66ea884
9 files changed
+145
-20
No files matched your search
@@ -8,9 +8,12 @@ import (
|
||||
)
|
||||
|
||||
// HeadBytes ist die Menge, die zum Finden der Drehung gelesen werden muss.
|
||||
// Der EXIF-Block steht unmittelbar hinter dem Dateianfang; 64 KB decken auch
|
||||
// Kameras ab, die ein Vorschaubild mit hineinlegen.
|
||||
const HeadBytes = 64 << 10
|
||||
//
|
||||
// 256 KB, nicht 64: Ein APP1-Segment darf selbst schon 65 535 Byte groß sein –
|
||||
// Telefone legen ihr eigenes Vorschaubild dort hinein –, und davor können
|
||||
// weitere Segmente stehen. Bei 64 KB reichte der Puffer im Grenzfall nicht
|
||||
// einmal für das eine Segment.
|
||||
const HeadBytes = 256 << 10
|
||||
|
||||
/*
|
||||
Orientation liest die EXIF-Drehung (1–8) aus dem Anfang einer JPEG-Datei.
|
||||
@@ -43,15 +46,27 @@ func Orientation(head []byte) int {
|
||||
return 1
|
||||
}
|
||||
size := int(binary.BigEndian.Uint16(head[i+2 : i+4]))
|
||||
if size < 2 || i+2+size > len(head) {
|
||||
if size < 2 {
|
||||
return 1
|
||||
}
|
||||
if marker == 0xE1 {
|
||||
if o, ok := orientationInAPP1(head[i+4 : i+2+size]); ok {
|
||||
end := i + 2 + size
|
||||
truncated := end > len(head)
|
||||
if truncated {
|
||||
// Der Puffer endet mitten im Segment. Trotzdem nachsehen: Das
|
||||
// Verzeichnis mit der Drehung steht ganz am Anfang des Blocks,
|
||||
// das eingebettete Vorschaubild dahinter. Aufzugeben hieße, wegen
|
||||
// eines Bildes im Bild die Drehung zu verlieren.
|
||||
end = len(head)
|
||||
}
|
||||
if marker == 0xE1 && i+4 < end {
|
||||
if o, ok := orientationInAPP1(head[i+4 : end]); ok {
|
||||
return o
|
||||
}
|
||||
}
|
||||
i += 2 + size
|
||||
if truncated {
|
||||
return 1
|
||||
}
|
||||
i = end
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -146,3 +146,19 @@ func TestApplyLaesstEinsUnberuehrt(t *testing.T) {
|
||||
t.Error("unbekannte Drehung hat kopiert")
|
||||
}
|
||||
}
|
||||
|
||||
// Telefone legen ihr eigenes Vorschaubild in den EXIF-Block; der wird dadurch
|
||||
// zehntausende Byte groß. Reicht der gelesene Anfang nicht bis zum Ende des
|
||||
// Blocks, muss die Drehung trotzdem gefunden werden – sie steht vorn.
|
||||
func TestOrientationBeiAbgeschnittenemBlock(t *testing.T) {
|
||||
voll := jpegMitDrehung(t, 6, false)
|
||||
// Absichtlich hinter dem Verzeichnis abschneiden, aber vor dem Dateiende.
|
||||
for _, laenge := range []int{40, 60, 80} {
|
||||
if laenge > len(voll) {
|
||||
continue
|
||||
}
|
||||
if got := Orientation(voll[:laenge]); got != 6 {
|
||||
t.Errorf("bei %d Byte Kopf: Drehung %d statt 6", laenge, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user