From 296f99900a9b8a74848d3c7c866b95a25d644eff Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Mon, 13 Apr 2026 00:07:58 -0600 Subject: [PATCH] Fix profile image squish and rotate isusse --- .../controllers/MainLayoutController.java | 93 ++++++++++++++++--- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 9f14ec3f..61e7fa4e 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -14,7 +14,6 @@ import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; -import javafx.scene.paint.ImagePattern; import javafx.scene.shape.Circle; import javafx.stage.Stage; import org.example.petshopdesktop.api.endpoints.ChatApi; @@ -31,6 +30,8 @@ import java.io.ByteArrayInputStream; public class MainLayoutController { + private int avatarExifOrientation = 1; + private static final String NAV_BASE_STYLE = "-fx-background-color: transparent; " + "-fx-text-fill: #cbd5e1; " + "-fx-background-radius: 10; " + @@ -331,9 +332,11 @@ public class MainLayoutController { try { byte[] imageBytes = AuthApi.getInstance().getMyAvatarFile(); - Image image = new Image(new ByteArrayInputStream(imageBytes), 52, 52, true, true); + avatarExifOrientation = readExifOrientation(imageBytes); + Image image = new Image(new ByteArrayInputStream(imageBytes), 200, 200, true, true); return image.isError() ? null : image; } catch (Exception e) { + avatarExifOrientation = 1; return null; } } @@ -342,19 +345,31 @@ public class MainLayoutController { Circle border = new Circle(29); border.setFill(Color.web("#dbe4ee")); - Circle circle = new Circle(26); - Label initials = new Label(initials(displayName)); - initials.setStyle("-fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 16px;"); - if (avatarImage != null) { - circle.setFill(new ImagePattern(avatarImage)); - initials.setVisible(false); - } else { - circle.setFill(Color.web("#4ECDC4")); - initials.setVisible(true); - } + double imgW = avatarImage.getWidth(); + double imgH = avatarImage.getHeight(); + double cropSize = Math.min(imgW, imgH); + double cropX = (imgW - cropSize) / 2; + double cropY = (imgH - cropSize) / 2; - avatarPreview.getChildren().setAll(border, circle, initials); + javafx.scene.image.ImageView imageView = new javafx.scene.image.ImageView(avatarImage); + imageView.setViewport(new javafx.geometry.Rectangle2D(cropX, cropY, cropSize, cropSize)); + imageView.setFitWidth(52); + imageView.setFitHeight(52); + imageView.setPreserveRatio(false); + imageView.setRotate(exifOrientationToAngle(avatarExifOrientation)); + + Circle clip = new Circle(26, 26, 26); + imageView.setClip(clip); + + avatarPreview.getChildren().setAll(border, imageView); + } else { + Circle circle = new Circle(26); + circle.setFill(Color.web("#4ECDC4")); + Label initials = new Label(initials(displayName)); + initials.setStyle("-fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: 16px;"); + avatarPreview.getChildren().setAll(border, circle, initials); + } } private String initials(String displayName) { @@ -495,4 +510,56 @@ public class MainLayoutController { } } + private double exifOrientationToAngle(int orientation) { + return switch (orientation) { + case 3 -> 180; + case 6 -> 90; + case 8 -> -90; + default -> 0; + }; + } + + private int readExifOrientation(byte[] data) { + try { + if (data.length < 2 || (data[0] & 0xFF) != 0xFF || (data[1] & 0xFF) != 0xD8) return 1; + int offset = 2; + while (offset + 4 < data.length) { + if ((data[offset] & 0xFF) != 0xFF) break; + int marker = data[offset + 1] & 0xFF; + int segLen = ((data[offset + 2] & 0xFF) << 8) | (data[offset + 3] & 0xFF); + if (marker == 0xE1 && offset + 9 < data.length + && data[offset + 4] == 'E' && data[offset + 5] == 'x' + && data[offset + 6] == 'i' && data[offset + 7] == 'f' + && data[offset + 8] == 0 && data[offset + 9] == 0) { + int tiff = offset + 10; + boolean le = data[tiff] == 'I'; + int ifdOffset = readInt(data, tiff + 4, le); + int ifd = tiff + ifdOffset; + int entries = readShort(data, ifd, le); + for (int i = 0; i < entries; i++) { + int e = ifd + 2 + i * 12; + if (e + 9 < data.length && readShort(data, e, le) == 0x0112) { + return readShort(data, e + 8, le); + } + } + } + if (marker == 0xDA) break; + offset += 2 + segLen; + } + } catch (Exception ignored) {} + return 1; + } + + private int readShort(byte[] data, int offset, boolean le) { + return le + ? (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8) + : ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); + } + + private int readInt(byte[] data, int offset, boolean le) { + return le + ? (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8) | ((data[offset + 2] & 0xFF) << 16) | ((data[offset + 3] & 0xFF) << 24) + : ((data[offset] & 0xFF) << 24) | ((data[offset + 1] & 0xFF) << 16) | ((data[offset + 2] & 0xFF) << 8) | (data[offset + 3] & 0xFF); + } + }