2016-12-25 17:05:30 +08:00
|
|
|
package customskinloader.fake;
|
2016-08-28 19:08:59 +08:00
|
|
|
|
2020-01-24 16:56:26 +08:00
|
|
|
import java.awt.image.BufferedImage;
|
2021-05-07 17:15:45 +08:00
|
|
|
import java.util.function.Function;
|
2020-05-20 18:38:31 +08:00
|
|
|
import java.util.function.Predicate;
|
2020-01-24 16:56:26 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
import customskinloader.CustomSkinLoader;
|
|
|
|
|
import customskinloader.fake.texture.FakeBufferedImage;
|
|
|
|
|
import customskinloader.fake.texture.FakeImage;
|
2018-07-25 21:06:16 +08:00
|
|
|
import customskinloader.fake.texture.FakeNativeImage;
|
2020-01-24 16:56:26 +08:00
|
|
|
import net.minecraft.client.renderer.IImageBuffer;
|
|
|
|
|
import net.minecraft.client.renderer.texture.NativeImage;
|
2018-07-25 15:55:03 +08:00
|
|
|
|
2020-01-24 16:56:26 +08:00
|
|
|
public class FakeSkinBuffer implements IImageBuffer {
|
2018-07-25 15:55:03 +08:00
|
|
|
private int ratio = 1;
|
2021-05-07 17:15:45 +08:00
|
|
|
FakeImage image = null;
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2020-01-24 16:56:26 +08:00
|
|
|
//parseUserSkin for 1.15+
|
2021-05-07 17:15:45 +08:00
|
|
|
public static NativeImage processLegacySkin(NativeImage image, Runnable processTask, Function<NativeImage, NativeImage> processLegacySkin) {
|
2020-05-21 10:30:45 +08:00
|
|
|
if (processTask instanceof IImageBuffer) {
|
|
|
|
|
return ((IImageBuffer) processTask).func_195786_a(image);
|
|
|
|
|
}
|
2021-05-07 17:15:45 +08:00
|
|
|
return processLegacySkin.apply(image);
|
2020-01-24 16:56:26 +08:00
|
|
|
}
|
|
|
|
|
|
2018-07-25 21:06:16 +08:00
|
|
|
//parseUserSkin for 1.13+
|
2020-01-24 16:56:26 +08:00
|
|
|
public NativeImage func_195786_a(NativeImage image) {
|
2018-07-25 21:06:16 +08:00
|
|
|
if (image == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
FakeImage img = parseUserSkin(new FakeNativeImage(image));
|
|
|
|
|
if (img instanceof FakeNativeImage)
|
|
|
|
|
return ((FakeNativeImage) img).getImage();
|
|
|
|
|
|
|
|
|
|
CustomSkinLoader.logger.warning("Failed to parseUserSkin(func_195786_a).");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//parseUserSkin for 1.12.2-
|
2018-02-12 12:20:28 +08:00
|
|
|
public BufferedImage parseUserSkin(BufferedImage image) {
|
2018-07-25 15:55:03 +08:00
|
|
|
if (image == null)
|
2018-02-12 12:20:28 +08:00
|
|
|
return null;
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
FakeImage img = parseUserSkin(new FakeBufferedImage(image));
|
|
|
|
|
if (img instanceof FakeBufferedImage)
|
|
|
|
|
return ((FakeBufferedImage) img).getImage();
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
CustomSkinLoader.logger.warning("Failed to parseUserSkin.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
public FakeImage parseUserSkin(FakeImage image) {
|
|
|
|
|
if (image == null) return null;
|
|
|
|
|
this.ratio = image.getWidth() / 64;
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
if (image.getHeight() != image.getWidth()) {//Single Layer
|
|
|
|
|
//Create a new image and copy origin image data
|
|
|
|
|
FakeImage img = image.createImage(64 * ratio, 64 * ratio);
|
2020-05-20 18:38:31 +08:00
|
|
|
img.copyImageData(image);
|
|
|
|
|
image.close();
|
|
|
|
|
image = img;
|
2018-07-25 15:55:03 +08:00
|
|
|
image.fillArea(0 * ratio, 32 * ratio, 64 * ratio, 32 * ratio);
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
//Right Leg -> Left Leg
|
2020-05-20 18:38:31 +08:00
|
|
|
image.copyArea(4 * ratio, 16 * ratio, 16 * ratio, 32 * ratio, 4 * ratio, 4 * ratio, true, false);//Top
|
|
|
|
|
image.copyArea(8 * ratio, 16 * ratio, 16 * ratio, 32 * ratio, 4 * ratio, 4 * ratio, true, false);//Bottom
|
|
|
|
|
image.copyArea(0 * ratio, 20 * ratio, 24 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Right
|
|
|
|
|
image.copyArea(4 * ratio, 20 * ratio, 16 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Front
|
|
|
|
|
image.copyArea(8 * ratio, 20 * ratio, 8 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Left
|
2018-07-25 15:55:03 +08:00
|
|
|
image.copyArea(12 * ratio, 20 * ratio, 16 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Back
|
|
|
|
|
//Right Arm -> Left Arm
|
2020-05-20 18:38:31 +08:00
|
|
|
image.copyArea(44 * ratio, 16 * ratio, -8 * ratio, 32 * ratio, 4 * ratio, 4 * ratio, true, false);//Top
|
|
|
|
|
image.copyArea(48 * ratio, 16 * ratio, -8 * ratio, 32 * ratio, 4 * ratio, 4 * ratio, true, false);//Bottom
|
|
|
|
|
image.copyArea(40 * ratio, 20 * ratio, 0 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Right
|
2018-07-25 15:55:03 +08:00
|
|
|
image.copyArea(44 * ratio, 20 * ratio, -8 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Front
|
2020-05-20 18:38:31 +08:00
|
|
|
image.copyArea(48 * ratio, 20 * ratio, -16 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Left
|
2018-07-25 15:55:03 +08:00
|
|
|
image.copyArea(52 * ratio, 20 * ratio, -8 * ratio, 32 * ratio, 4 * ratio, 12 * ratio, true, false);//Back
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.image = image;
|
2020-05-20 18:38:31 +08:00
|
|
|
setAreaDueToConfig(0 * ratio, 0 * ratio, 32 * ratio, 16 * ratio);//Head - 1
|
|
|
|
|
setAreaTransparent(32 * ratio, 0 * ratio, 64 * ratio, 16 * ratio);//Head - 2
|
2018-07-25 15:55:03 +08:00
|
|
|
setAreaDueToConfig(16 * ratio, 16 * ratio, 40 * ratio, 32 * ratio);//Body - 1
|
|
|
|
|
setAreaTransparent(16 * ratio, 32 * ratio, 40 * ratio, 48 * ratio);//Body - 2
|
|
|
|
|
|
|
|
|
|
setAreaDueToConfig(40 * ratio, 16 * ratio, 56 * ratio, 32 * ratio);//Right Arm - 1
|
|
|
|
|
setAreaTransparent(40 * ratio, 32 * ratio, 56 * ratio, 48 * ratio);//Right Arm - 2
|
2020-05-20 18:38:31 +08:00
|
|
|
setAreaDueToConfig(0 * ratio, 16 * ratio, 16 * ratio, 32 * ratio);//Right Leg - 1
|
|
|
|
|
setAreaTransparent(0 * ratio, 32 * ratio, 16 * ratio, 48 * ratio);//Right Leg - 2
|
2018-07-25 15:55:03 +08:00
|
|
|
|
|
|
|
|
setAreaDueToConfig(32 * ratio, 48 * ratio, 48 * ratio, 64 * ratio);//Left Arm - 1
|
|
|
|
|
setAreaTransparent(48 * ratio, 48 * ratio, 64 * ratio, 64 * ratio);//Left Arm - 2
|
|
|
|
|
setAreaDueToConfig(16 * ratio, 48 * ratio, 32 * ratio, 64 * ratio);//Left Leg - 1
|
2020-05-20 18:38:31 +08:00
|
|
|
setAreaTransparent(0 * ratio, 48 * ratio, 16 * ratio, 64 * ratio);//Left Leg - 2
|
2018-07-25 15:55:03 +08:00
|
|
|
return image;
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2021-05-07 17:15:45 +08:00
|
|
|
/** Judge if the color is transparent or same with background */
|
|
|
|
|
static final Function<Integer, Predicate<Integer>> EQU_BG = bgColor -> getA(bgColor) == 0 ? (c) -> getA(c) == 0 : (c) -> c.equals(bgColor);
|
|
|
|
|
|
2018-05-18 21:06:28 +08:00
|
|
|
/**
|
|
|
|
|
* Judge the type of skin
|
|
|
|
|
* Must be called after parseUserSkin
|
2018-07-25 15:55:03 +08:00
|
|
|
*
|
2018-05-18 21:06:28 +08:00
|
|
|
* @return type of skin (slim / default)
|
|
|
|
|
* @since 14.9
|
|
|
|
|
*/
|
2018-07-25 15:55:03 +08:00
|
|
|
public String judgeType() {
|
|
|
|
|
if (this.image == null)
|
2020-05-21 02:37:51 +08:00
|
|
|
return "default";
|
2020-05-20 18:38:31 +08:00
|
|
|
int bgColor = image.getRGBA(63 * ratio, 20 * ratio);
|
|
|
|
|
/*
|
|
|
|
|
* If background is transparent, all the pixels in ((54, 20), (55, 31)) areas is transparent,
|
|
|
|
|
* which means it is Alex model.
|
|
|
|
|
* If background is opaque, all the pixels in ((54, 20), (55, 31)) areas is same with background,
|
|
|
|
|
* which means it is Alex model.
|
|
|
|
|
* Otherwise, it is Steve model.
|
|
|
|
|
* */
|
2021-05-07 17:15:45 +08:00
|
|
|
Predicate<Integer> predicate = EQU_BG.apply(bgColor);
|
2020-05-20 18:38:31 +08:00
|
|
|
for (int x = 54 * ratio; x <= 55 * ratio; ++x) {
|
|
|
|
|
for (int y = 20 * ratio; y <= 31 * ratio; ++y) {
|
2020-05-21 02:37:51 +08:00
|
|
|
int color = image.getRGBA(x, y);
|
|
|
|
|
if (!predicate.test(color)) {
|
2020-05-20 18:38:31 +08:00
|
|
|
return "default";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "slim";
|
2018-05-18 21:06:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2^24-1
|
2018-02-12 12:20:28 +08:00
|
|
|
* 00000000 11111111 11111111 11111111 */
|
2018-07-25 15:55:03 +08:00
|
|
|
private static final int A = 16777215;
|
|
|
|
|
private static final int WHITE = getARGB(255, 255, 255, 255);
|
|
|
|
|
private static final int BLACK = getARGB(255, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
private boolean isFilled(int x0, int y0, int x1, int y1) {
|
|
|
|
|
int data = image.getRGBA(x0, y0);
|
|
|
|
|
if (data != WHITE && data != BLACK)
|
2018-02-12 12:20:28 +08:00
|
|
|
return false;
|
2018-07-25 15:55:03 +08:00
|
|
|
for (int x = x0; x < x1; ++x)
|
|
|
|
|
for (int y = y0; y < y1; ++y)
|
|
|
|
|
if (image.getRGBA(x, y) != data)
|
2018-02-12 12:20:28 +08:00
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-07-25 15:55:03 +08:00
|
|
|
|
|
|
|
|
private void setAreaTransparent(int x0, int y0, int x1, int y1) {
|
|
|
|
|
if (!isFilled(x0, y0, x1, y1))
|
2018-02-12 12:20:28 +08:00
|
|
|
return;
|
2018-07-25 15:55:03 +08:00
|
|
|
for (int x = x0; x < x1; ++x)
|
|
|
|
|
for (int y = y0; y < y1; ++y)
|
2020-05-20 18:38:31 +08:00
|
|
|
image.setRGBA(x, y, image.getRGBA(x, y) & A);
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-02-12 12:20:28 +08:00
|
|
|
/* -2^24
|
|
|
|
|
* 00000001 00000000 00000000 00000000 ->
|
|
|
|
|
* 11111110 11111111 11111111 11111111 ->
|
|
|
|
|
* 11111111 00000000 00000000 00000000 */
|
2018-07-25 15:55:03 +08:00
|
|
|
private static final int B = -16777216;
|
|
|
|
|
|
|
|
|
|
private void setAreaOpaque(int x0, int y0, int x1, int y1) {
|
|
|
|
|
for (int x = x0; x < x1; ++x)
|
|
|
|
|
for (int y = y0; y < y1; ++y)
|
2020-05-20 18:38:31 +08:00
|
|
|
image.setRGBA(x, y, image.getRGBA(x, y) | B);
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
private void setAreaDueToConfig(int x0, int y0, int x1, int y1) {
|
|
|
|
|
if (customskinloader.CustomSkinLoader.config.enableTransparentSkin)
|
|
|
|
|
setAreaTransparent(x0, y0, x1, y1);
|
2018-02-12 12:20:28 +08:00
|
|
|
else
|
2018-07-25 15:55:03 +08:00
|
|
|
setAreaOpaque(x0, y0, x1, y1);
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2016-08-28 19:08:59 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
public void skinAvailable() {
|
|
|
|
|
//A callback when skin loaded, nothing to do
|
2018-02-12 12:20:28 +08:00
|
|
|
}
|
2018-05-18 21:06:28 +08:00
|
|
|
|
2018-07-25 15:55:03 +08:00
|
|
|
private static int getARGB(int a, int r, int g, int b) {
|
2018-02-12 12:20:28 +08:00
|
|
|
return (a << 24) | (r << 16) | (g << 8) | b;
|
|
|
|
|
}
|
2020-05-20 18:38:31 +08:00
|
|
|
|
|
|
|
|
private static int getA(int argb) {
|
|
|
|
|
return (argb & B) >>> 24;
|
|
|
|
|
}
|
2016-08-28 19:08:59 +08:00
|
|
|
}
|