Add skin cropping
This commit is contained in:
parent
41c6a9bf38
commit
193974dda9
67
src/main/java/me/braydon/mc/common/PlayerUtils.java
Normal file
67
src/main/java/me/braydon/mc/common/PlayerUtils.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package me.braydon.mc.common;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import me.braydon.mc.model.Skin;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.image.AffineTransformOp;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public final class PlayerUtils {
|
||||||
|
private static final int SKIN_TEXTURE_SIZE = 64; // The skin of a skin texture
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the head texture of a skin.
|
||||||
|
*
|
||||||
|
* @param skin the skin to get the head texture from
|
||||||
|
* @param size the size to scale the head texture to
|
||||||
|
* @return the head texture of the skin
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static byte[] getHeadTexture(@NonNull Skin skin, int size) {
|
||||||
|
return getSkinPartTexture(skin, 8, 8, SKIN_TEXTURE_SIZE / 8, SKIN_TEXTURE_SIZE / 8, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the texture of a specific part of a skin.
|
||||||
|
*
|
||||||
|
* @param skin the skin to get the part from
|
||||||
|
* @param x the x position of the part
|
||||||
|
* @param y the y position of the part
|
||||||
|
* @param width the width of the part
|
||||||
|
* @param height the height of the part
|
||||||
|
* @param size the size to scale the part to
|
||||||
|
* @return the texture of the skin part
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static byte[] getSkinPartTexture(@NonNull Skin skin, int x, int y, int width, int height, int size) {
|
||||||
|
BufferedImage skinImage = ImageIO.read(new URL(skin.getUrl())); // The skin texture
|
||||||
|
|
||||||
|
// Create a new BufferedImage for the part of the skin texture
|
||||||
|
BufferedImage headTexture = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
|
||||||
|
// Crop just the part we want based on our x, y, width, and height
|
||||||
|
headTexture.getGraphics().drawImage(skinImage, 0, 0, width, height, x, y, x + width, y + height, null);
|
||||||
|
|
||||||
|
// Scale the skin part texture
|
||||||
|
double scale = (double) size / width;
|
||||||
|
AffineTransform transform = AffineTransform.getScaleInstance(scale, scale);
|
||||||
|
headTexture = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR).filter(headTexture, null);
|
||||||
|
|
||||||
|
// Convert BufferedImage to byte array
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
ImageIO.write(headTexture, "png", outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ import me.braydon.mc.model.Skin;
|
|||||||
import me.braydon.mc.model.cache.CachedPlayer;
|
import me.braydon.mc.model.cache.CachedPlayer;
|
||||||
import me.braydon.mc.service.MojangService;
|
import me.braydon.mc.service.MojangService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@ -25,6 +24,9 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
@RequestMapping(value = "/player", produces = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(value = "/player", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@Log4j2(topic = "Player Controller")
|
@Log4j2(topic = "Player Controller")
|
||||||
public final class PlayerController {
|
public final class PlayerController {
|
||||||
|
private static final int DEFAULT_HEAD_SIZE = 128;
|
||||||
|
private static final int MAX_HEAD_SIZE = 512;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Mojang service to use for player information.
|
* The Mojang service to use for player information.
|
||||||
*/
|
*/
|
||||||
@ -60,15 +62,23 @@ public final class PlayerController {
|
|||||||
*
|
*
|
||||||
* @param query the query to search for the player by
|
* @param query the query to search for the player by
|
||||||
* @param extension the head image extension
|
* @param extension the head image extension
|
||||||
|
* @param size the size of the head image
|
||||||
* @return the head texture
|
* @return the head texture
|
||||||
* @throws BadRequestException if the extension is invalid
|
* @throws BadRequestException if the extension is invalid
|
||||||
*/
|
*/
|
||||||
@GetMapping("/head/{query}.{extension}")
|
@GetMapping("/head/{query}.{extension}")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<ByteArrayResource> getHead(@PathVariable @NonNull String query, @PathVariable @NonNull String extension) throws BadRequestException {
|
public ResponseEntity<byte[]> getHead(@PathVariable @NonNull String query, @PathVariable @NonNull String extension,
|
||||||
|
@RequestParam(required = false) Integer size
|
||||||
|
) throws BadRequestException {
|
||||||
if ((extension = extension.trim()).isBlank()) { // Invalid extension
|
if ((extension = extension.trim()).isBlank()) { // Invalid extension
|
||||||
throw new BadRequestException("Invalid extension");
|
throw new BadRequestException("Invalid extension");
|
||||||
}
|
}
|
||||||
|
if (size == null || size <= 0) { // Invalid size
|
||||||
|
size = DEFAULT_HEAD_SIZE;
|
||||||
|
}
|
||||||
|
size = Math.min(size, MAX_HEAD_SIZE); // Limit the size to 512
|
||||||
|
|
||||||
Skin target = null; // The target skin to get the head of
|
Skin target = null; // The target skin to get the head of
|
||||||
try {
|
try {
|
||||||
CachedPlayer player = mojangService.getPlayer(query, false); // Retrieve the player
|
CachedPlayer player = mojangService.getPlayer(query, false); // Retrieve the player
|
||||||
@ -82,6 +92,6 @@ public final class PlayerController {
|
|||||||
// Return the head texture
|
// Return the head texture
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(extension.equalsIgnoreCase("png") ? MediaType.IMAGE_PNG : MediaType.IMAGE_JPEG)
|
.contentType(extension.equalsIgnoreCase("png") ? MediaType.IMAGE_PNG : MediaType.IMAGE_JPEG)
|
||||||
.body(new ByteArrayResource(PlayerUtils.getSkinPartTexture(target)));
|
.body(PlayerUtils.getHeadTexture(target, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user