diff --git a/src/Controller/DeviceApiController.php b/src/Controller/DeviceApiController.php index d15cb51..4cfb9b0 100644 --- a/src/Controller/DeviceApiController.php +++ b/src/Controller/DeviceApiController.php @@ -215,8 +215,9 @@ class DeviceApiController extends AbstractController $this->renderBinToPng( $binPath, $pngPath, - $device->getModel()->width($device->getOrientation()), - $device->getModel()->height($device->getOrientation()), + $device->getModel()->nativeWidth(), + $device->getModel()->nativeHeight(), + $device->getOrientation(), ); } @@ -229,7 +230,7 @@ class DeviceApiController extends AbstractController return $response; } - private function renderBinToPng(string $binPath, string $pngPath, int $width, int $height): void + private function renderBinToPng(string $binPath, string $pngPath, int $width, int $height, Orientation $orientation): void { $bin = (string) file_get_contents($binPath); $len = strlen($bin); @@ -250,6 +251,14 @@ class DeviceApiController extends AbstractController $im = new \Imagick(); $im->readImageBlob($ppm); + + // The .bin is always laid out in EPD-native scan order. For portrait, + // the renderer pre-rotated the photo 90° CW; rotate -90° here so the + // browser-side preview shows the photo upright. + if ($orientation === Orientation::Portrait) { + $im->rotateImage(new \ImagickPixel('white'), -90); + } + $im->setImageFormat('png'); $im->writeImage($pngPath); $im->destroy(); diff --git a/src/Enum/DeviceModel.php b/src/Enum/DeviceModel.php index 9e4452f..194cc3b 100644 --- a/src/Enum/DeviceModel.php +++ b/src/Enum/DeviceModel.php @@ -17,4 +17,16 @@ enum DeviceModel: string { return $orientation === Orientation::Portrait ? 800 : 480; } + + /** EPD's hardware scan-row width — independent of user orientation. */ + public function nativeWidth(): int + { + return 800; + } + + /** EPD's hardware scan-row count — independent of user orientation. */ + public function nativeHeight(): int + { + return 480; + } } diff --git a/src/MessageHandler/RenderImageMessageHandler.php b/src/MessageHandler/RenderImageMessageHandler.php index d297403..670947b 100644 --- a/src/MessageHandler/RenderImageMessageHandler.php +++ b/src/MessageHandler/RenderImageMessageHandler.php @@ -70,7 +70,7 @@ final class RenderImageMessageHandler : $this->projectDir . '/' . $image->getStoragePath(); $width = $model->width($orientation); $height = $model->height($orientation); - $bin = $this->renderToBin($originalPath, $width, $height); + $bin = $this->renderToBin($originalPath, $width, $height, $orientation); $relPath = 'var/storage/images/' . $image->getId() . '/' . $model->value . '_' . $orientation->value . '.bin'; @@ -87,7 +87,7 @@ final class RenderImageMessageHandler $this->em->flush(); } - private function renderToBin(string $path, int $width, int $height): string + private function renderToBin(string $path, int $width, int $height, Orientation $orientation): string { $imagick = new \Imagick($path); $imagick->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE); @@ -95,6 +95,16 @@ final class RenderImageMessageHandler $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN); $imagick->autoOrient(); $imagick->cropThumbnailImage($width, $height); + + // Portrait: rotate the cropped photo 90° CW so the packed .bin's row + // layout matches the EPD's native 800-pixel scan order. The frame is + // physically rotated 90° CCW for portrait (ribbon on left), so the + // photo's bottom edge maps to the EPD's left column. Firmware streams + // bytes raw — no orientation awareness on-device. + if ($orientation === Orientation::Portrait) { + $imagick->rotateImage(new \ImagickPixel('white'), 90); + } + $imagick->setImageColorspace(\Imagick::COLORSPACE_SRGB); // Auto-levels: stretch the tonal range, clipping 1% at each end.