diff --git a/README.md b/README.md index 2cbd4d23..1ceb9633 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,18 @@ content: "pos": [0, 0, 200, 200] ``` +###### 仿射变换/图像变形 + +**坐标格式枚举`posType`** + +- `ZOOM` 缩放(见上文) +- `DEFORM` 变形 + +`DEFORM` 坐标格式为 `[[x1,y1],[x2,y2],[x3,y3],[x4,y4]]`; +分别对应图片的`[[左下角],[左上角],[右上角],[右下角]]` + +目前仿射变换仅支持单帧 + #### 头像 `3.0`版本后 提供了更灵活的头像构造方法, 与之前的版本有很大差别 @@ -147,11 +159,12 @@ content: "pos": [[92, 64, 40, 40], [135, 40, 40, 40], [84, 105, 40, 40]], // 坐标 "round": true, // 值为true时, 头像裁切为圆形, 默认为false "avatarOnTop": true // 值为true时, 头像图层在背景之上, 默认为true - "angle": 90, // 初始角度, 目前仅支持整数 + "angle": 90, // 初始角度 }, { "type": "TO", - "pos": [[58, 90, 50, 50], [62, 95, 50, 50], [42, 100, 50, 50]], + "pos": [[65, 128],[60,210],[110,210],[110, 120]], + "posType": "DEFORM", //图像变形 坐标格式, 默认为ZOOM "antialias": true, // 抗锯齿, 对头像单独使用抗锯齿算法, 默认为false "rotate": false // 值为true时, GIF类型的头像会旋转, 默认为false } diff --git a/build.gradle b/build.gradle index 1896fbb9..3265c7dd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,3 @@ -import sun.font.EAttribute - plugins { id 'org.jetbrains.kotlin.jvm' version '1.6.10' id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10' @@ -13,7 +11,7 @@ apply plugin: "java" group = 'xmmt.dituon' -version = '3.0' +version = '3.1' repositories { maven { url 'https://maven.aliyun.com/repository/public' } diff --git a/src/main/java/xmmt/dituon/plugin/Petpet.java b/src/main/java/xmmt/dituon/plugin/Petpet.java index 7182e461..5c5f9abb 100644 --- a/src/main/java/xmmt/dituon/plugin/Petpet.java +++ b/src/main/java/xmmt/dituon/plugin/Petpet.java @@ -9,7 +9,6 @@ import net.mamoe.mirai.event.events.GroupMessageEvent; import net.mamoe.mirai.event.events.NudgeEvent; import net.mamoe.mirai.message.data.*; -import xmmt.dituon.share.AvatarExtraData; import xmmt.dituon.share.BaseConfigFactory; import xmmt.dituon.share.TextExtraData; @@ -19,7 +18,7 @@ public final class Petpet extends JavaPlugin { public static final Petpet INSTANCE = new Petpet(); - public static final float VERSION = 3.0F; + public static final float VERSION = 3.1F; ArrayList disabledGroup = new ArrayList<>(); PluginPetService pluginPetService; @@ -98,7 +97,8 @@ private void onGroupMessage(GroupMessageEvent e) { key = firstWord; otherText = m.contentToString().replace(key, "").trim(); - if (!pluginPetService.commandMustAt && notContainsAt(e.getMessage())) { + if (!pluginPetService.commandMustAt && + notContainsAt(e.getMessage()) && !e.getMessage().contains(Image.Key)) { pluginPetService.sendImage(e.getGroup(), e.getGroup().getBotAsMember(), e.getSender(), key, otherText); return; diff --git a/src/main/java/xmmt/dituon/share/AvatarModel.java b/src/main/java/xmmt/dituon/share/AvatarModel.java index 6d28f999..1610fd53 100644 --- a/src/main/java/xmmt/dituon/share/AvatarModel.java +++ b/src/main/java/xmmt/dituon/share/AvatarModel.java @@ -2,8 +2,6 @@ import kotlinx.serialization.json.JsonArray; import kotlinx.serialization.json.JsonElement; - -import java.awt.*; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.IOException; diff --git a/src/main/java/xmmt/dituon/share/ImageDeformer.java b/src/main/java/xmmt/dituon/share/ImageDeformer.java index 5176e31c..f18587ec 100644 --- a/src/main/java/xmmt/dituon/share/ImageDeformer.java +++ b/src/main/java/xmmt/dituon/share/ImageDeformer.java @@ -1,6 +1,5 @@ package xmmt.dituon.share; -import java.awt.*; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; diff --git a/src/main/java/xmmt/dituon/share/ImageSynthesis.java b/src/main/java/xmmt/dituon/share/ImageSynthesis.java index 9b8de82d..df9937af 100644 --- a/src/main/java/xmmt/dituon/share/ImageSynthesis.java +++ b/src/main/java/xmmt/dituon/share/ImageSynthesis.java @@ -25,9 +25,14 @@ public static BufferedImage synthesisImage(BufferedImage sticker, } // 背景 - g2d.setColor(Color.WHITE); - g2d.fillRect(0, 0, sticker.getWidth(), sticker.getHeight()); - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0F)); + output = g2d.getDeviceConfiguration().createCompatibleImage( + sticker.getWidth(), sticker.getHeight(), Transparency.TRANSLUCENT); + g2d.dispose(); + g2d = output.createGraphics(); + +// g2d.setColor(Color.WHITE); +// g2d.fillRect(0, 0, sticker.getWidth(), sticker.getHeight()); +// g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0F)); // 按照图层分类 ArrayList topAvatars = new ArrayList<>(); @@ -56,34 +61,37 @@ public static BufferedImage synthesisImage(BufferedImage sticker, private static void drawAvatar(Graphics2D g2d, AvatarModel avatar) { switch (avatar.getPosType()) { case ZOOM: - g2dDrawZoomAvatar(g2d, avatar.getImage(), avatar.nextPos(), avatar.getRotateIndex(), avatar.getAngle()); + g2dDrawZoomAvatar(g2d, avatar.getImage(), + avatar.nextPos(), avatar.getRotateIndex(), avatar.getAngle(), avatar.isRound()); break; case DEFORM: g2dDrawDeformAvatar(g2d, avatar.getImage(), avatar.getDeformPos()); } } - private static void g2dDrawZoomAvatar(Graphics2D g2d, BufferedImage avatarImage, int[] pos, int rotateIndex, int angle) { + private static void g2dDrawZoomAvatar(Graphics2D g2d, BufferedImage avatarImage, int[] pos, + int rotateIndex, int angle, boolean isRound) { if (avatarImage == null) { return; } - BufferedImage newAvatarImage = new BufferedImage(avatarImage.getWidth(), avatarImage.getHeight(), avatarImage.getType()); - - if (rotateIndex == 0 && angle == 0) { - newAvatarImage = avatarImage; - } else { - Graphics2D rotateG2d1 = newAvatarImage.createGraphics(); - //TODO 旋转时会有黑边 应当使用AffineTransform - rotateG2d1.rotate(Math.toRadians((float) ((360 / pos.length) * (rotateIndex)) + angle), - avatarImage.getWidth() / 2, avatarImage.getHeight() / 2); - rotateG2d1.drawImage(avatarImage, null, 0, 0); - } int x = pos[0]; int y = pos[1]; int w = pos[2]; int h = pos[3]; - g2d.drawImage(newAvatarImage, x, y, w, h, null); + if (rotateIndex == 0 && angle == 0) { + g2d.drawImage(avatarImage, x, y, w, h, null); + return; + } + + if (isRound || angle % 90 == 0) { + Graphics2D rotateG2d = avatarImage.createGraphics(); + rotateG2d.rotate(angle); + g2d.drawImage(avatarImage, x, y, w, h, null); + return; + } + + g2d.drawImage(rotateImage(avatarImage, angle), x, y, w, h, null); } private static void g2dDrawDeformAvatar(Graphics2D g2d, BufferedImage avatarImage, Point2D[] pos) { @@ -115,6 +123,27 @@ public static BufferedImage convertCircular(BufferedImage input, boolean antiali return output; } + public static BufferedImage rotateImage(BufferedImage avatarImage, int angle) { + double sin = Math.abs(Math.sin(Math.toRadians(angle))), + cos = Math.abs(Math.cos(Math.toRadians(angle))); + int w = avatarImage.getWidth(); + int h = avatarImage.getHeight(); + int neww = (int) Math.floor(w * cos + h * sin), + newh = (int) Math.floor(h * cos + w * sin); + BufferedImage rotated = new BufferedImage(neww, newh, avatarImage.getType()); + Graphics2D g2d = rotated.createGraphics(); + rotated = g2d.getDeviceConfiguration().createCompatibleImage( + rotated.getWidth(), rotated.getHeight(), Transparency.TRANSLUCENT); + g2d.dispose(); + g2d = rotated.createGraphics(); + + g2d.translate((neww - w) / 2, (newh - h) / 2); + g2d.rotate(Math.toRadians(angle), w / 2, h / 2); + g2d.drawRenderedImage(avatarImage, null); + g2d.dispose(); + return rotated; + } + public static BufferedImage getAvatarImage(String URL) { HttpURLConnection conn = null; BufferedImage image = null;