From 4586079fae34e848216083d2a85d010ab32881f9 Mon Sep 17 00:00:00 2001 From: Matt Edholm Date: Wed, 6 May 2026 16:10:25 -0400 Subject: [PATCH] fix: flip portrait rotation direction so the EPD shows the photo upright MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first rotation pass picked CW server-side / CCW preview-side based on "ribbon on left" → user rotates frame 90° CCW. On hardware the photo came out upside down, which means the user's physical rotation is the opposite of what was assumed: 90° CW from landscape native, putting the ribbon to the left from the user's POV but to the right from the EPD's reference frame. The two rotation signs always need to stay opposite — flipping both keeps the webapp preview upright while fixing the device. Also drops the temporary upload debug log; the cropOrientation persistence issue resolved on its own once Doctrine's metadata cache was cleared. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Controller/DeviceApiController.php | 4 ++-- src/Controller/ImageApiController.php | 15 --------------- src/MessageHandler/RenderImageMessageHandler.php | 11 ++++++----- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/Controller/DeviceApiController.php b/src/Controller/DeviceApiController.php index 4cfb9b0..a60ac33 100644 --- a/src/Controller/DeviceApiController.php +++ b/src/Controller/DeviceApiController.php @@ -253,10 +253,10 @@ class DeviceApiController extends AbstractController $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 + // the renderer pre-rotated the photo 90° CCW; rotate 90° here so the // browser-side preview shows the photo upright. if ($orientation === Orientation::Portrait) { - $im->rotateImage(new \ImagickPixel('white'), -90); + $im->rotateImage(new \ImagickPixel('white'), 90); } $im->setImageFormat('png'); diff --git a/src/Controller/ImageApiController.php b/src/Controller/ImageApiController.php index f922a49..5d262ee 100644 --- a/src/Controller/ImageApiController.php +++ b/src/Controller/ImageApiController.php @@ -15,7 +15,6 @@ use App\Message\RenderImageMessage; use App\Service\ShareService; use App\Service\TokenService; use Doctrine\ORM\EntityManagerInterface; -use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -55,7 +54,6 @@ class ImageApiController extends AbstractController Request $request, EntityManagerInterface $em, MessageBusInterface $bus, - LoggerInterface $logger, ): JsonResponse { $file = $request->files->get('file'); if (!$file) { @@ -110,19 +108,6 @@ class ImageApiController extends AbstractController ); } - // TEMP debug: log what arrived in the upload form so we can diagnose - // why crop_orientation is landing as NULL despite the frontend - // claiming to send it. Remove once stable. - $logger->info('image.upload.fields', [ - 'image_id' => $image->getId(), - 'has_cropParams' => $request->request->has('cropParams'), - 'has_stickerState' => $request->request->has('stickerState'), - 'has_cropOrient' => $request->request->has('cropOrientation'), - 'cropOrient_raw' => $request->request->get('cropOrientation'), - 'all_keys' => $request->request->keys(), - 'persisted_orient' => $image->getCropOrientation()?->value, - ]); - // Generate thumbnail from composited if available, otherwise from original $thumbSrc = file_exists($storageDir . '/composited.jpg') ? $storageDir . '/composited.jpg' diff --git a/src/MessageHandler/RenderImageMessageHandler.php b/src/MessageHandler/RenderImageMessageHandler.php index 670947b..7818cd2 100644 --- a/src/MessageHandler/RenderImageMessageHandler.php +++ b/src/MessageHandler/RenderImageMessageHandler.php @@ -96,13 +96,14 @@ final class RenderImageMessageHandler $imagick->autoOrient(); $imagick->cropThumbnailImage($width, $height); - // Portrait: rotate the cropped photo 90° CW so the packed .bin's row + // Portrait: rotate the cropped photo 90° CCW 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. + // physically rotated 90° CW for portrait (ribbon on right from EPD's + // POV → on left from user's view), so the photo's top 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->rotateImage(new \ImagickPixel('white'), -90); } $imagick->setImageColorspace(\Imagick::COLORSPACE_SRGB);