feat: blurred letterbox, portrait pairs, overlay metadata, button fixes
- Letterbox: extend edge pixels outward with blur + fade to solid color - Portrait: detect and pair portraits side-by-side, fallback to solo - Metadata: add overlay mode (corner badge), time field, custom date/time format strings (strftime), font size 1.5x, region-aware for pairs - Buttons: swap G9/G10 to match physical layout, double-press random button for deep sleep, SLP indicator drawn before sleep - Boot: remove redundant 'Loading photos' screen (saves 17s refresh) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
79
src/main.cpp
79
src/main.cpp
@@ -106,7 +106,7 @@ void displayTask(void* param) {
|
||||
// Fetch asset info for portrait detection and metadata
|
||||
AssetInfo info = immichClient.fetchAssetInfo(assetId);
|
||||
|
||||
// Download photo
|
||||
// Download primary photo
|
||||
uint8_t* jpegBuf = nullptr;
|
||||
size_t jpegSize = 0;
|
||||
bool downloaded = immichClient.downloadAsset(
|
||||
@@ -121,20 +121,77 @@ void displayTask(void* param) {
|
||||
(unsigned)(jpegSize / 1024),
|
||||
(unsigned)(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024));
|
||||
|
||||
ProcessedImage img = imagePipeline.process(jpegBuf, jpegSize);
|
||||
ProcessedImage img = {nullptr, 0, 0, false};
|
||||
|
||||
if (info.isPortrait) {
|
||||
// Try to find a second portrait for side-by-side display
|
||||
Serial.println("[display_task] Portrait detected, searching for pair");
|
||||
String pairId = photoQueue.findNextPortrait();
|
||||
|
||||
if (pairId.length() > 0) {
|
||||
// Fetch info for second portrait (for metadata)
|
||||
AssetInfo info2 = immichClient.fetchAssetInfo(pairId);
|
||||
|
||||
// Download the second portrait
|
||||
uint8_t* jpeg2Buf = nullptr;
|
||||
size_t jpeg2Size = 0;
|
||||
bool dl2 = immichClient.downloadAsset(
|
||||
pairId, s.img_quality, &jpeg2Buf, &jpeg2Size);
|
||||
|
||||
if (dl2 && jpeg2Buf != nullptr) {
|
||||
Serial.printf("[display_task] Portrait pair: %u KB + %u KB\n",
|
||||
(unsigned)(jpegSize / 1024),
|
||||
(unsigned)(jpeg2Size / 1024));
|
||||
img = imagePipeline.processPortraitPair(
|
||||
jpegBuf, jpegSize, jpeg2Buf, jpeg2Size);
|
||||
free(jpeg2Buf);
|
||||
|
||||
if (img.valid) {
|
||||
uint16_t halfW = (DISPLAY_WIDTH - PORTRAIT_GAP_PX) / 2;
|
||||
uint16_t rightX = halfW + PORTRAIT_GAP_PX;
|
||||
displayManager.showImage(img);
|
||||
if (s.meta_flags != 0) {
|
||||
displayManager.showMetadataInRegion(
|
||||
info, s.meta_flags, s.meta_pos,
|
||||
0, 0, halfW, DISPLAY_HEIGHT,
|
||||
s.date_fmt, s.time_fmt);
|
||||
displayManager.showMetadataInRegion(
|
||||
info2, s.meta_flags, s.meta_pos,
|
||||
rightX, 0, halfW, DISPLAY_HEIGHT,
|
||||
s.date_fmt, s.time_fmt);
|
||||
}
|
||||
displayManager.refresh();
|
||||
imagePipeline.freeImage(img);
|
||||
displayed = true;
|
||||
}
|
||||
} else {
|
||||
Serial.println("[display_task] Pair download failed, showing solo");
|
||||
img = imagePipeline.process(jpegBuf, jpegSize);
|
||||
}
|
||||
} else {
|
||||
// No pair found — show solo portrait (will get L/R letterbox)
|
||||
img = imagePipeline.process(jpegBuf, jpegSize);
|
||||
}
|
||||
} else {
|
||||
// Normal landscape processing
|
||||
img = imagePipeline.process(jpegBuf, jpegSize);
|
||||
}
|
||||
|
||||
free(jpegBuf);
|
||||
|
||||
Serial.printf("[display_task] Post-process PSRAM free: %u KB\n",
|
||||
(unsigned)(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024));
|
||||
|
||||
if (img.valid) {
|
||||
if (img.valid && !displayed) {
|
||||
displayManager.showImage(img);
|
||||
if (s.meta_flags != 0) {
|
||||
displayManager.showMetadata(info, s.meta_flags, s.meta_pos);
|
||||
displayManager.showMetadata(info, s.meta_flags, s.meta_pos,
|
||||
s.date_fmt, s.time_fmt);
|
||||
}
|
||||
displayManager.refresh();
|
||||
imagePipeline.freeImage(img);
|
||||
displayed = true;
|
||||
} else {
|
||||
} else if (!img.valid && !displayed) {
|
||||
Serial.println("[display_task] Processing failed, trying next");
|
||||
}
|
||||
}
|
||||
@@ -206,7 +263,8 @@ void setup() {
|
||||
if (wifiManager.isAPMode()) {
|
||||
displayManager.showSetupScreen();
|
||||
} else {
|
||||
displayManager.showMessage("Immich Frame", "Loading photos...");
|
||||
// Skip "Loading photos" screen — the display task will show the first photo
|
||||
// directly, avoiding a redundant 17s e-ink refresh cycle on boot.
|
||||
|
||||
// Create display task on Core 1
|
||||
xTaskCreatePinnedToCore(displayTask, "display", 32768, nullptr, 1,
|
||||
@@ -245,7 +303,14 @@ void loop() {
|
||||
xSemaphoreGive(stateMutex);
|
||||
}
|
||||
break;
|
||||
case ButtonEvent::DeepSleep:
|
||||
case ButtonEvent::DeepSleep: {
|
||||
Serial.println("[main] Entering deep sleep...");
|
||||
Settings sleepS = settingsManager.get();
|
||||
displayManager.showSleepIndicator(sleepS.meta_pos);
|
||||
displayManager.refresh();
|
||||
powerManager.enterDeepSleep();
|
||||
break;
|
||||
}
|
||||
case ButtonEvent::FactoryReset:
|
||||
break;
|
||||
case ButtonEvent::None:
|
||||
|
||||
Reference in New Issue
Block a user