Leer imagenes desde el servidor y trasladarlas al cliente

Iniciado por Swarlog, Jun 23, 2025, 07:42 PM

Tema anterior - Siguiente tema

Swarlog

La idea de esta guía es la de crear imagenes PNG de 256 colores sin transparencias y trasladarlas al servidor y representarlas mediante un html.

Lo primero q tenemos q tener en cuenta es q para realizar esto se hace usando una libreria pero como es una tontera agregar toda una libreria por una sola clase la agregaremos directamente a nuestro source.

package com.l2jserver.util;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

public class DDSConverter
{
	public static final Logger _log = Logger.getLogger(DDSConverter.class.getName());
	
	protected static class Color
	{
		
		@Override
		public boolean equals(Object obj)
		{
			if (this == obj)
			{
				return true;
			}
			if ((obj == null) || (getClass() != obj.getClass()))
			{
				return false;
			}
			Color color = (Color) obj;
			if (b != color.b)
			{
				return false;
			}
			if (g != color.g)
			{
				return false;
			}
			return r == color.r;
		}
		
		@Override
		public int hashCode()
		{
			int i = r;
			i = (29 * i) + g;
			i = (29 * i) + b;
			return i;
		}
		
		protected int r;
		protected int g;
		protected int b;
		
		public Color()
		{
			r = g = b = 0;
		}
		
		public Color(int i, int j, int k)
		{
			r = i;
			g = j;
			b = k;
		}
	}
	
	public static ByteBuffer convertToDDS(File file) throws IOException
	{
		if (file == null)
		{
			String s = "nullValue.FileIsNull";
			_log.severe(s);
			throw new IllegalArgumentException(s);
		}
		if (!file.exists() || !file.canRead())
		{
			String s1 = "DDSConverter.NoFileOrNoPermission";
			_log.severe(s1);
			throw new IllegalArgumentException(s1);
		}
		BufferedImage bufferedimage = ImageIO.read(file);
		if (bufferedimage == null)
		{
			return null;
		}
		if (bufferedimage.getColorModel().hasAlpha())
		{
			return convertToDxt3(bufferedimage);
		}
		return convertToDxt1NoTransparency(bufferedimage);
	}
	
	public static ByteBuffer convertToDxt1NoTransparency(BufferedImage bufferedimage)
	{
		if (bufferedimage == null)
		{
			return null;
		}
		int ai[] = new int[16];
		int i = 128 + ((bufferedimage.getWidth() * bufferedimage.getHeight()) / 2);
		ByteBuffer bytebuffer = ByteBuffer.allocate(i);
		bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
		buildHeaderDxt1(bytebuffer, bufferedimage.getWidth(), bufferedimage.getHeight());
		int j = bufferedimage.getWidth() / 4;
		int k = bufferedimage.getHeight() / 4;
		for (int l = 0; l < k; l++)
		{
			for (int i1 = 0; i1 < j; i1++)
			{
				BufferedImage bufferedimage1 = bufferedimage.getSubimage(i1 * 4, l * 4, 4, 4);
				bufferedimage1.getRGB(0, 0, 4, 4, ai, 0, 4);
				Color acolor[] = getColors888(ai);
				for (int j1 = 0; j1 < ai.length; j1++)
				{
					ai[j1] = getPixel565(acolor[j1]);
					acolor[j1] = getColor565(ai[j1]);
				}
				
				int ai1[] = determineExtremeColors(acolor);
				if (ai[ai1[0]] < ai[ai1[1]])
				{
					int k1 = ai1[0];
					ai1[0] = ai1[1];
					ai1[1] = k1;
				}
				bytebuffer.putShort((short) ai[ai1[0]]);
				bytebuffer.putShort((short) ai[ai1[1]]);
				long l1 = computeBitMask(acolor, ai1);
				bytebuffer.putInt((int) l1);
			}
		}
		return bytebuffer;
	}
	
	public static ByteBuffer convertToDxt3(BufferedImage bufferedimage)
	{
		if (bufferedimage == null)
		{
			return null;
		}
		if (!bufferedimage.getColorModel().hasAlpha())
		{
			return convertToDxt1NoTransparency(bufferedimage);
		}
		int ai[] = new int[16];
		int i = 128 + (bufferedimage.getWidth() * bufferedimage.getHeight());
		ByteBuffer bytebuffer = ByteBuffer.allocate(i);
		bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
		buildHeaderDxt3(bytebuffer, bufferedimage.getWidth(), bufferedimage.getHeight());
		int j = bufferedimage.getWidth() / 4;
		int k = bufferedimage.getHeight() / 4;
		for (int l = 0; l < k; l++)
		{
			for (int i1 = 0; i1 < j; i1++)
			{
				BufferedImage bufferedimage1 = bufferedimage.getSubimage(i1 * 4, l * 4, 4, 4);
				bufferedimage1.getRGB(0, 0, 4, 4, ai, 0, 4);
				Color acolor[] = getColors888(ai);
				for (int j1 = 0; j1 < ai.length; j1 += 2)
				{
					bytebuffer.put((byte) ((ai[j1] >>> 28) | (ai[j1 + 1] >>> 24)));
				}
				
				for (int k1 = 0; k1 < ai.length; k1++)
				{
					ai[k1] = getPixel565(acolor[k1]);
					acolor[k1] = getColor565(ai[k1]);
				}
				
				int ai1[] = determineExtremeColors(acolor);
				if (ai[ai1[0]] < ai[ai1[1]])
				{
					int l1 = ai1[0];
					ai1[0] = ai1[1];
					ai1[1] = l1;
				}
				bytebuffer.putShort((short) ai[ai1[0]]);
				bytebuffer.putShort((short) ai[ai1[1]]);
				long l2 = computeBitMask(acolor, ai1);
				bytebuffer.putInt((int) l2);
			}
		}
		return bytebuffer;
	}
	
	protected static void buildHeaderDxt1(ByteBuffer bytebuffer, int i, int j)
	{
		bytebuffer.rewind();
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 83);
		bytebuffer.put((byte) 32);
		bytebuffer.putInt(124);
		int k = 0xa1007;
		bytebuffer.putInt(k);
		bytebuffer.putInt(j);
		bytebuffer.putInt(i);
		bytebuffer.putInt((i * j) / 2);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.position(bytebuffer.position() + 44);
		bytebuffer.putInt(32);
		bytebuffer.putInt(4);
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 88);
		bytebuffer.put((byte) 84);
		bytebuffer.put((byte) 49);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(4096);
		bytebuffer.putInt(0);
		bytebuffer.position(bytebuffer.position() + 12);
	}
	
	protected static void buildHeaderDxt3(ByteBuffer bytebuffer, int i, int j)
	{
		bytebuffer.rewind();
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 83);
		bytebuffer.put((byte) 32);
		bytebuffer.putInt(124);
		int k = 0xa1007;
		bytebuffer.putInt(k);
		bytebuffer.putInt(j);
		bytebuffer.putInt(i);
		bytebuffer.putInt(i * j);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.position(bytebuffer.position() + 44);
		bytebuffer.putInt(32);
		bytebuffer.putInt(4);
		bytebuffer.put((byte) 68);
		bytebuffer.put((byte) 88);
		bytebuffer.put((byte) 84);
		bytebuffer.put((byte) 51);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(0);
		bytebuffer.putInt(4096);
		bytebuffer.putInt(0);
		bytebuffer.position(bytebuffer.position() + 12);
	}
	
	protected static int[] determineExtremeColors(Color acolor[])
	{
		int i = 0x80000000;
		int ai[] = new int[2];
		for (int j = 0; j < (acolor.length - 1); j++)
		{
			for (int k = j + 1; k < acolor.length; k++)
			{
				int l = distance(acolor[j], acolor[k]);
				if (l > i)
				{
					i = l;
					ai[0] = j;
					ai[1] = k;
				}
			}
			
		}
		return ai;
	}
	
	protected static long computeBitMask(Color acolor[], int ai[])
	{
		Color acolor1[] =
		{
			null,
			null,
			new Color(),
			new Color()
		};
		acolor1[0] = acolor[ai[0]];
		acolor1[1] = acolor[ai[1]];
		if (acolor1[0].equals(acolor1[1]))
		{
			return 0L;
		}
		acolor1[2].r = ((2 * acolor1[0].r) + acolor1[1].r + 1) / 3;
		acolor1[2].g = ((2 * acolor1[0].g) + acolor1[1].g + 1) / 3;
		acolor1[2].b = ((2 * acolor1[0].b) + acolor1[1].b + 1) / 3;
		acolor1[3].r = (acolor1[0].r + (2 * acolor1[1].r) + 1) / 3;
		acolor1[3].g = (acolor1[0].g + (2 * acolor1[1].g) + 1) / 3;
		acolor1[3].b = (acolor1[0].b + (2 * acolor1[1].b) + 1) / 3;
		long l = 0L;
		for (int i = 0; i < acolor.length; i++)
		{
			int j = 0x7fffffff;
			int k = 0;
			for (int i1 = 0; i1 < acolor1.length; i1++)
			{
				int j1 = distance(acolor[i], acolor1[i1]);
				if (j1 < j)
				{
					j = j1;
					k = i1;
				}
			}
			
			l |= k << (i * 2);
		}
		return l;
	}
	
	protected static int getPixel565(Color color)
	{
		int i = color.r >> 3;
		int j = color.g >> 2;
		int k = color.b >> 3;
		return (i << 11) | (j << 5) | k;
	}
	
	protected static Color getColor565(int i)
	{
		Color color = new Color();
		color.r = (int) (i & 63488L) >> 11;
		color.g = (int) (i & 2016L) >> 5;
		color.b = (int) (i & 31L);
		return color;
	}
	
	protected static Color[] getColors888(int ai[])
	{
		Color acolor[] = new Color[ai.length];
		for (int i = 0; i < ai.length; i++)
		{
			acolor[i] = new Color();
			acolor[i].r = (int) (ai[i] & 0xff0000L) >> 16;
			acolor[i].g = (int) (ai[i] & 65280L) >> 8;
			acolor[i].b = (int) (ai[i] & 255L);
		}
		return acolor;
	}
	
	protected static int distance(Color color, Color color1)
	{
		return ((color1.r - color.r) * (color1.r - color.r)) + ((color1.g - color.g) * (color1.g - color.g)) + ((color1.b - color.b) * (color1.b - color.b));
	}
}
Esto se encarga de tomar nuestras imagenes PNG y transformarlas a DDS q es el formato q usa el cliente.

package com.l2jserver.gameserver.network.serverpackets;

public class PledgeImage extends L2GameServerPacket
{
	private final int _crestId;
	private final byte[] _data;
	
	public PledgeImage(int crestId, byte[] data)
	{
		_crestId = crestId;
		_data = data;
	}
	
	@Override
	protected final void writeImpl()
	{
		writeC(0x6a); // IL client: 0x6c, H5 client: 0x6a
		writeD(_crestId);
		
		if (_data != null)
		{
			writeD(_data.length);
			writeB(_data);
		}
		else
		{
			writeD(0);
		}
	}
}
Esto es un paquete nuevo, en realidad es creado en base al paquete q se encarga de enviar al cliente las crest...pero en lugar de modificar el original prefieri crear este para evitar futuros problemas si es q los pudiera traer.

package com.l2jserver.gameserver.datatables;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.PledgeImage;
import com.l2jserver.util.DDSConverter;

/**
 * @author fissban
 */
public class ImageTable
{
	public static final Logger _log = Logger.getLogger(ImageTable.class.getName());
	
	public void generateImage(L2PcInstance player, int imgId)
	{
		try
		{
			File image = new File("data/images/" + imgId + ".png");
			PledgeImage packet = new PledgeImage(imgId, DDSConverter.convertToDDS(image).array());
			player.sendPacket(packet);
		}
		catch (Exception e)
		{
			_log.log(Level.SEVERE, "error al intentar leer la imagen " + imgId + " del disco");
		}
	}
	
	public static ImageTable getInstance()
	{
		return SingletonHolder._instance;
	}
	
	private static class SingletonHolder
	{
		protected static final ImageTable _instance = new ImageTable();
	}
}
Este clase contiene el metodo q usaremos para invocar las imagenes.


ahora algunos otros pasos a seguir.
+ Vamos a la carpeta data y crearemos una carpeta llamada images.
+ Dentro de ella pondremos nuestras imagenes PNG de 256 colores y sin transparencias.


bien ahora donde ustedes requieran invocar las imagenes las deberan hacer algo asi.

ImageTable.getInstance().generateImage(player, 5000);
Donde el 5000 sera el nombre del archivo...recuerden q solo pueden usar numeros.
ahora dentro de su html ponen algo asi

"<img src=\"Crest.crest_" + Config.SERVER_ID + "_" + 5000 + "\" width=256 height=256>"
Aqui ustedes remplazan el 5000 por el ID de la imagen a mostrar y q previamente invocaron y bueno logicamente tambien deberan especificar las dimensiones de la imagen

Aclaracion.

Esto no es algo q yo invente ni nada por el estilo...pero sinceramente no se quien fue el que descubrio q esto se podia realizar.

Gracias Fissban por la recopilación y descripción.