fix(ili9341): rotation 3 was mirrored — use explicit per-rotation map

The MADCTL handler in 6edc715 applied MX/MY/MV as three independent
flags, then mirrored physX/physY post-swap. That double-applies the
mirror for setRotation(3) (which Adafruit sends as MX|MY|MV|BGR=0xE8):
expected formula for rotation 3 is

  physX = 239 - curY
  physY = curX

but the flag-by-flag approach computed

  physX = 239 - curY      (correct by coincidence)
  physY = 319 - curX      (mirrored — should be just curX)

so every landscape-rot-3 sketch rendered horizontally flipped. The
user's Pico Doom title screen looked mirrored even after the previous
fix landed.

Replaced with an explicit per-rotation table derived from
Adafruit_ILI9341's setRotation() source:

  rot 0  MX|BGR        : (curX, curY)
  rot 1  MV|BGR        : (curY, 319 - curX)
  rot 2  MY|BGR        : (239 - curX, 319 - curY)
  rot 3  MX|MY|MV|BGR  : (239 - curY, curX)

Selects the case based on (madMV, madMX, madMY) bits, which is
straightforward because Adafruit only emits these 4 specific values.
Other drivers that set arbitrary MADCTL combinations (e.g. with the ML
or MH bits) still fall through to the closest of the four — good
enough for the screens we actually run.

Build verified (vite OSS+pro, 285 SEO pages).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Montero Crespo 2026-05-15 01:32:16 -03:00
parent d79f2923d9
commit a91b857d8c
1 changed files with 28 additions and 6 deletions

View File

@ -902,18 +902,40 @@ const ili9341Simulation = {
// In rotation 1/3 (MV set) the driver iterates X in 0..319 and Y in
// 0..239; we swap them at the last possible moment before touching
// the imageData buffer (which is always physically 240 wide × 320 tall).
//
// The mapping is rotation-specific because applying MX/MY/MV as three
// independent flags double-mirrors the output (we tried that in
// commit 6edc715 and the user saw "espejada" text). The four
// Adafruit_ILI9341 setRotation() values map cleanly to four explicit
// (curX, curY) → (physX, physY) formulae taken from the chip's
// datasheet section 8.2.29 (Memory Access Control):
//
// rot 0 M=0x48 (MX|BGR) : (curX, curY) [portrait]
// rot 1 M=0x28 (MV|BGR) : (curY, (319 - curX)) [landscape]
// rot 2 M=0x88 (MY|BGR) : ((239 - curX), (319 - curY)) [portrait flipped]
// rot 3 M=0xE8 (MX|MY|MV|BGR) : ((239 - curY), curX) [landscape flipped]
//
// The Adafruit driver computes the rotation register value, sends it
// once via MADCTL, then writes pixels in the rotated framebuffer's
// coordinate space — we mirror that on the receive side.
const writePixel = (hi: number, lo: number) => {
if (curX > colEnd || curY > rowEnd) return;
// Map logical → physical via MADCTL.
let physX = curX;
let physY = curY;
if (madMV) {
// Map logical → physical via the (MV, MX, MY) rotation signature.
let physX: number, physY: number;
if (!madMV) {
// Portrait (rotations 0 or 2)
physX = madMY ? (SCREEN_W - 1) - curX : curX;
physY = madMY ? (SCREEN_H - 1) - curY : curY;
} else if (!madMX && !madMY) {
// Landscape rotation 1: m = MV | BGR. (curY, 319 - curX)
physX = curY;
physY = (SCREEN_H - 1) - curX;
} else {
// Landscape rotation 3: m = MX | MY | MV | BGR. (239 - curY, curX)
physX = (SCREEN_W - 1) - curY;
physY = curX;
}
if (madMX) physX = (SCREEN_W - 1) - physX;
if (madMY) physY = (SCREEN_H - 1) - physY;
if (physX < 0 || physX >= SCREEN_W || physY < 0 || physY >= SCREEN_H) {
curX++;