Mini Relógio para Desktop

2 respostas
Fox_McCloud

8)

Um mini-relógio para desktop completamente funcional, que usa a imagem do desktop do usuário e mantém um ícone no system tray (ao lado do relógio do Windows) com uma opção para sair da aplicação.

Contém inúmeras técnicas de animação, aceleração gráfica, processamento digital de imagens (pixel a pixel), entre outras coisas. Todas as imagens são geradas em tempo de execução (incluindo o ícone do system tray).

Eu demorei uns bons meses para reunir os diferentes conhecimentos necessários para isso, então pode servir de uma boa fonte de estudos para quem está pesquisando animação, processamento de imagens pixel a pixel (algo que dizem que não tem em Java, mas quem diz isso apenas não se aprofundou para saber) e programação de jogos em Java (embora aqui não haja controles via teclado ou mouse).

Segue o código e o jar auto-executável em anexo. Experimentem pra ver e postem quaisquer dúvidas aqui!

:wink:

obs: tomara que esse editor de texto não distorça alguns caracteres como às vezes faz. Por via das dúvidas segue o fonte em anexo também!

import java.awt.AWTException;
import java.awt.AlphaComposite;
import java.awt.BufferCapabilities;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.ImageCapabilities;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.SystemTray;
import java.awt.Transparency;
import java.awt.TrayIcon;
import java.awt.BufferCapabilities.FlipContents;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import javax.swing.JDialog;
import javax.swing.JFrame;


public class MiniClock extends JDialog implements Runnable {

	private static final GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
	private static final GraphicsDevice dev = env.getDefaultScreenDevice();
	private static final GraphicsConfiguration conf = dev.getDefaultConfiguration();
	private static final DisplayMode mode = dev.getDisplayMode();
	
	private static final long serialVersionUID = 1L;
	private static final Font font = new Font("Courier New", Font.BOLD, (int)(0.015 * mode.getHeight()));
	private static final RenderingHints rh = RendHints.getInstance();
	private static final int size = (int)(0.17 * (double)mode.getHeight());
	private static final int deskPad = 10;
	private static final double fps = 60;
	
	public static void main(String... args) {
		new MiniClock();
	}

	private volatile boolean running;
	
	private Integer[] raiosMenores;
	private Integer[] raiosMaiores;
	
	private BufferedImage trayIcon;
	private BufferedImage background;
	private BufferedImage[] numbers;
	
	private Calendar clock;

	public MiniClock() {
		config();
		initRaios();
		createImages();
		init();
		start();
	}
	
	private void config() {
		this.setAlwaysOnTop(true);
		this.setIgnoreRepaint(true);
		this.setUndecorated(true);
		this.setResizable(false);
		this.setFocusable(true);
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent e) {
				MiniClock.this.stop();
			}
		});
		this.addFocusListener(new FocusAdapter(){
			public void focusLost(FocusEvent e) {
				MiniClock.this.requestFocus();
			}
		});
		this.pack();
		this.setSize(size, size);
		this.setLocation(deskPad, deskPad);
		this.createBufferStrategy();
	}

	private void createBufferStrategy() {
		try {
			this.createBufferStrategy(2, new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), FlipContents.UNDEFINED));
			System.out.println("Flip Contents ON");
		} catch (AWTException e) {
			this.createBufferStrategy(2);
			System.out.println("Flip Contents OFF");
		}
	}
	
	private void initRaios() {
		double raioMedio = size / 2.0;
		double raioInc = 0.075 * raioMedio;
		double maiorRaio = Math.pow(2 * Math.pow(raioMedio, 2), 0.5);
		double menorRaio = 3 * raioInc;
		double raioMostrador = raioMedio - raioInc;
		
		List<Integer> raiosMaiores = new ArrayList<Integer>();
		for(double raio = maiorRaio; raio >= raioMedio; raio -= raioInc) {
			raiosMaiores.add((int)raio);
		}
		this.raiosMaiores = raiosMaiores.toArray(new Integer[]{});
		
		List<Integer> raiosMenores = new ArrayList<Integer>();
		for(double raio = raioMostrador; raio >= menorRaio; raio -= raioInc) {
			raiosMenores.add((int)raio);
		}
		this.raiosMenores = raiosMenores.toArray(new Integer[]{});
	}
	
	private void createImages() {
		createIconImage();
		createNumberImages();
		createBackgroundImage();
	}
	
	private void createIconImage() {
		Dimension trayIconSize = SystemTray.getSystemTray().getTrayIconSize();
		double posXCentro = trayIconSize.getWidth()/2.0;
		double posYCentro = trayIconSize.getHeight()/2.0;
		BufferedImage image = new BufferedImage(trayIconSize.width, trayIconSize.height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2 = image.createGraphics();
		g2.setRenderingHints(rh);
		g2.setColor(Color.CYAN);
		g2.drawRect(0, 0, image.getWidth()-1, image.getHeight()-1);
		g2.drawLine((int)posXCentro, (int)posYCentro, (int)posXCentro+3, (int)posYCentro);
		g2.drawLine((int)posXCentro, (int)posYCentro, (int)posXCentro, (int)posYCentro-3);
		g2.dispose();
		Raster raster = (Raster)image.getRaster();
		SampleModel model = raster.getSampleModel();
		double raio = (trayIconSize.getWidth() - 4.0) / 2.0;
		int[] pixel = new int[]{0, 0xFF, 0xFF, 0xFF};
		for(double angle = 270, ctrlAngle=0; ctrlAngle < 360; ctrlAngle += (360.0/12.0), angle = (angle + 360.0/12.0) % 360.0) {
			double posX = posXCentro + raio * Math.cos(Math.toRadians(angle));
			double posY = posYCentro + raio * Math.sin(Math.toRadians(angle));
			model.setPixel((int)posX, (int)posY, pixel, raster.getDataBuffer());
		}
		
		// Acelerando
		BufferedImage finalImage = conf.createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
		g2 = finalImage.createGraphics();
		g2.setRenderingHints(rh);
		g2.drawImage(image, 0, 0, null);
		g2.dispose();
		this.trayIcon = finalImage;
	}
	
	private void createNumberImages() {
		BufferedImage tmp = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
		Graphics2D gTmp = tmp.createGraphics();
		gTmp.setRenderingHints(rh);
		gTmp.setFont(font);
		FontMetrics fm = gTmp.getFontMetrics();
		
		List<BufferedImage> list = new ArrayList<BufferedImage>();
		for(int number = 12, ctrl=0; ctrl<12; ctrl++, number = (number + 1) % 13) {
			if(number == 0) number = 1;
			String sNumber = leftPadding(number, '0', 2);
			
			BufferedImage image = new BufferedImage(fm.stringWidth(sNumber), fm.getHeight(), BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2 = image.createGraphics();
			g2.setRenderingHints(rh);
			g2.setColor(ColorType.FONTE.getColor());
			g2.drawString(sNumber, 0, fm.getAscent());
			g2.dispose();
			list.add(trimTransp(image));
		}
		this.numbers = list.toArray(new BufferedImage[]{});
		
		gTmp.dispose();
	}
	
	public void createBackgroundImage() {
		BufferedImage image = conf.createCompatibleImage(this.getWidth(), this.getHeight(), Transparency.TRANSLUCENT);
		Graphics2D g2 = image.createGraphics();
		g2.setRenderingHints(rh);
		Composite comp = g2.getComposite();
		try {
			g2.drawImage(new Robot().createScreenCapture(new Rectangle(deskPad, deskPad, this.getWidth(), this.getHeight())), 0, 0, null);
			g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.85F));
			g2.setColor(ColorType.FUNDO.getColor());
			g2.fillRect(-2, -2, image.getWidth()+2, image.getHeight()+2);
			g2.setComposite(comp);
		} catch (AWTException e) {
			g2.setColor(ColorType.FUNDO.getColor());
			g2.fillRect(-2, -2, image.getWidth()+2, image.getHeight()+2);
		}
		
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3F));
		g2.setColor(ColorType.MOLDURA.getColor());
		double posXCentro = this.getWidth() / 2.0;
		double posYCentro = this.getHeight() / 2.0;
		double raioExterno = raiosMaiores[0];
		double raioInterno = raiosMenores[raiosMenores.length-1];
		for(double raio = raioInterno; raio <= raioExterno; raio += 0.1 * (this.getWidth() / 2.0)) {
			g2.drawOval((int)(posXCentro - raio), (int)(posYCentro - raio), (int)(raio*2), (int)(raio*2));
		}
		raioExterno = raiosMenores[0];
		for(double angle=270, angleCtrl=0; angleCtrl<=360.0; angleCtrl += (360.0/48.0), angle = (angle + (360.0/48.0)) % 360.0) {
			double posXMenor = posXCentro + raioInterno * Math.cos(Math.toRadians(angle));
			double posYMenor = posYCentro + raioInterno * Math.sin(Math.toRadians(angle));
			double posXMaior = posXCentro + raioExterno * Math.cos(Math.toRadians(angle));
			double posYMaior = posYCentro + raioExterno * Math.sin(Math.toRadians(angle));
			g2.drawLine((int)(posXMenor), (int)(posYMenor), (int)(posXMaior), (int)(posYMaior));
		}
		raioInterno = raioExterno;
		raioExterno = raiosMaiores[0];
		for(double angle=270, angleCtrl=0; angleCtrl<=360.0; angleCtrl += (360.0/96.0), angle = (angle + (360.0/96.0)) % 360.0) {
			double posXMenor = posXCentro + raioInterno * Math.cos(Math.toRadians(angle));
			double posYMenor = posYCentro + raioInterno * Math.sin(Math.toRadians(angle));
			double posXMaior = posXCentro + raioExterno * Math.cos(Math.toRadians(angle));
			double posYMaior = posYCentro + raioExterno * Math.sin(Math.toRadians(angle));
			g2.drawLine((int)(posXMenor), (int)(posYMenor), (int)(posXMaior), (int)(posYMaior));
		}
		g2.setComposite(comp);
		
		g2.drawRect(0, 0, this.getWidth()-1, this.getHeight()-1);
		
		double raioMaior = raiosMenores[0];
		double raioCentral = raiosMenores[1];
		double raioMenor = raiosMenores[2];
		for(double angle = 270.0, ctrlAngle = 0; ctrlAngle < 360.0; angle = (angle + (360.0 / 60.0)) % 360.0, ctrlAngle += (360.0 / 60.0)) {
			double posXMaior = posXCentro + raioMaior * Math.cos(Math.toRadians(angle));
			double posYMaior = posYCentro + raioMaior * Math.sin(Math.toRadians(angle));
			double posXMenor;
			double posYMenor;
			if(angle % 30.0 == 0) {
				g2.setColor(ColorType.MOSTRADOR.getColor());
				posXMenor = posXCentro + raioMenor * Math.cos(Math.toRadians(angle));
				posYMenor = posYCentro + raioMenor * Math.sin(Math.toRadians(angle));
			} else {
				g2.setColor(ColorType.MOSTRADOR.getColor().darker());
				posXMenor = posXCentro + raioCentral * Math.cos(Math.toRadians(angle));
				posYMenor = posYCentro + raioCentral * Math.sin(Math.toRadians(angle));
			}
			g2.drawLine((int)posXMaior, (int)posYMaior, (int)posXMenor, (int)posYMenor);
		}
	
		double raio = raiosMenores[4];
		for(double angle = 270.0, ctrlAngle = 0, i=0; ctrlAngle < 360.0; angle = (angle + (360.0 / 12.0)) % 360.0, ctrlAngle += (360.0 / 12.0), i++) {
			double posX = posXCentro + raio * Math.cos(Math.toRadians(angle));
			double posY = posYCentro + raio * Math.sin(Math.toRadians(angle));
			BufferedImage number = numbers[(int)i];
			posX = posX - number.getWidth() / 2.0;
			posY = posY - number.getHeight() / 2.0;
			g2.drawImage(number, (int)posX, (int)posY, null);
		}
		
		g2.dispose();
		this.background = image;
	}
	
	private String leftPadding(int number, char c, int size) {
		String sNumber = String.valueOf(number);
		while(sNumber.length() < size) {
			sNumber = c + sNumber;
		}
		return sNumber;
	}
	
	private BufferedImage trimTransp(BufferedImage image) {
		int minX = Integer.MAX_VALUE;
		int minY = Integer.MAX_VALUE;
		int maxX = 0;
		int maxY = 0;
		Raster rasterI = image.getRaster();
		SampleModel modelI = rasterI.getSampleModel();
		for(int y=0; y<image.getHeight(); y++) {
			for(int x=0; x><image.getWidth(); x++) {
				int[] pixel = new int[4];
				modelI.getPixel(x, y, pixel, rasterI.getDataBuffer());
				if(pixel[3]==0)continue;
				minX = Math.min(minX, x);
				minY = Math.min(minY, y);
				maxX = Math.max(maxX, x);
				maxY = Math.max(maxY, y);
			}
		}
		BufferedImage trimmed = new BufferedImage(maxX-minX+1, maxY-minY+1, BufferedImage.TYPE_INT_ARGB);
		Raster rasterT = trimmed.getRaster();
		SampleModel modelT = rasterT.getSampleModel();
		for(int yI = minY, yT = 0; yT><trimmed.getHeight(); yI++, yT++) {
			for(int xI = minX, xT = 0; xT><trimmed.getWidth(); xI++, xT++) {
				int[] pixel = new int[4];
				modelI.getPixel(xI, yI, pixel, rasterI.getDataBuffer());
				modelT.setPixel(xT, yT, pixel, rasterT.getDataBuffer());
			}
		}
		// Acelerando
		BufferedImage finalImage = conf.createCompatibleImage(trimmed.getWidth(), trimmed.getHeight(), Transparency.TRANSLUCENT);
		Graphics2D g2 = finalImage.createGraphics();
		g2.setRenderingHints(rh);
		g2.drawImage(trimmed, 0, 0, null);
		g2.dispose();
		return finalImage;
	}

	private void init() {
		PopupMenu popup = new PopupMenu();
        MenuItem defaultItem = new MenuItem("Sair");
        defaultItem.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				MiniClock.this.stop();
			}
        });
        popup.add(defaultItem);
		try {
			SystemTray.getSystemTray().add(new TrayIcon(trayIcon, "Mini Clock", popup));
		} catch (AWTException e) {}
		clock = new GregorianCalendar();
		Graphics2D g2 = (Graphics2D)this.getBufferStrategy().getDrawGraphics();
		clearScreen(g2);
		g2.dispose();
	}

	private void start(){
		new Thread(this).start();
	}
	
	private void stop(){
		running = false;
	}
	
	public void run() {
		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		this.setVisible(true);
		running = true;
		while(running) {
			long initialTime = System.nanoTime();
			this.getBufferStrategy().show();
			paint();
			update();
			sleep(initialTime);
		}
		this.setVisible(false);
		this.dispose();
		System.exit(0);
	}

	private void paint() {
		Graphics2D g2 = (Graphics2D)this.getBufferStrategy().getDrawGraphics();
		g2.setRenderingHints(rh);
		clearScreen(g2);
		paint(g2);
		g2.dispose();
	}
	
	private void clearScreen(Graphics2D g2) {
		g2.drawImage(background, 0, 0, null);
	}
	
	private void paint(Graphics2D g2) {
		g2.setColor(ColorType.MOLDURA.getColor());
		g2.drawRect(0, 0, this.getWidth()-1, this.getHeight()-1);
		double percHoras = (clock.get(Calendar.HOUR) + clock.get(Calendar.MINUTE) / 60.0 + clock.get(Calendar.SECOND) / (60.0 * 60.0) + clock.get(Calendar.MILLISECOND) / (1000.0 * 60.0 * 60.0)) / 12.0;
		double percMinutos = (clock.get(Calendar.MINUTE) + clock.get(Calendar.SECOND) / 60.0 + clock.get(Calendar.MILLISECOND) / (1000.0 * 60.0)) / 60.0;
		double percSegundos = (clock.get(Calendar.SECOND) /*+ clock.get(Calendar.MILLISECOND) / 1000.0*/) / 60.0;
		double posXCentro = this.getWidth() / 2.0;
		double posYCentro = this.getHeight() / 2.0;
		double raioSegundos = raiosMenores[3];
		double raioMinutos = raiosMenores[5];
		double raioHoras = raiosMenores[7];
		
		double posX = posXCentro + raioSegundos * Math.cos(Math.toRadians((270.0 + (360.0 * percSegundos)) % 360.0));
		double posY = posYCentro + raioSegundos * Math.sin(Math.toRadians((270.0 + (360.0 * percSegundos)) % 360.0));
		g2.setColor(ColorType.SEGUNDOS.getColor());
		g2.drawLine((int)posXCentro,(int)posYCentro,(int)posX,(int)posY);
		double posXT1 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percSegundos) + 180.0 + 10.0) % 360.0));
		double posYT1 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percSegundos) + 180.0 + 10.0) % 360.0));
		double posXT2 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percSegundos) + 180.0 - 10.0) % 360.0));
		double posYT2 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percSegundos) + 180.0 - 10.0) % 360.0));
		g2.fillPolygon(new int[]{(int)posX,(int)posXT1,(int)posXT2}, new int[]{(int)posY,(int)posYT1,(int)posYT2}, 3);
		
		posX = posXCentro + raioMinutos * Math.cos(Math.toRadians((270.0 + (360.0 * percMinutos)) % 360.0));
		posY = posYCentro + raioMinutos * Math.sin(Math.toRadians((270.0 + (360.0 * percMinutos)) % 360.0));
		g2.setColor(ColorType.MINUTOS.getColor());
		g2.drawLine((int)posXCentro,(int)posYCentro,(int)posX,(int)posY);
		posXT1 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percMinutos) + 180.0 + 10.0) % 360.0));
		posYT1 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percMinutos) + 180.0 + 10.0) % 360.0));
		posXT2 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percMinutos) + 180.0 - 10.0) % 360.0));
		posYT2 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percMinutos) + 180.0 - 10.0) % 360.0));
		g2.fillPolygon(new int[]{(int)posX,(int)posXT1,(int)posXT2}, new int[]{(int)posY,(int)posYT1,(int)posYT2}, 3);
		
		posX = posXCentro + raioHoras * Math.cos(Math.toRadians((270.0 + (360.0 * percHoras)) % 360.0));
		posY = posYCentro + raioHoras * Math.sin(Math.toRadians((270.0 + (360.0 * percHoras)) % 360.0));
		g2.setColor(ColorType.HORAS.getColor());
		g2.drawLine((int)posXCentro,(int)posYCentro,(int)posX,(int)posY);
		posXT1 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percHoras) + 180.0 + 10.0) % 360.0));
		posYT1 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percHoras) + 180.0 + 10.0) % 360.0));
		posXT2 = posX + 7.5 * Math.cos(Math.toRadians((270.0 + (360.0 * percHoras) + 180.0 - 10.0) % 360.0));
		posYT2 = posY + 7.5 * Math.sin(Math.toRadians((270.0 + (360.0 * percHoras) + 180.0 - 10.0) % 360.0));
		g2.fillPolygon(new int[]{(int)posX,(int)posXT1,(int)posXT2}, new int[]{(int)posY,(int)posYT1,(int)posYT2}, 3);
	}
	
	private void update() {
		clock = new GregorianCalendar();
	}

	private void sleep(long initialTime) {
		long sleepTime = (long)(1000/fps - (System.nanoTime() - initialTime)/1000000.0);
		try {
			Thread.sleep(sleepTime > 5 ? sleepTime : 5);
		} catch (InterruptedException e) {}
	}

	private static class RendHints extends RenderingHints {

		private static final RendHints instance = new RendHints();
		
		public RendHints() {
			super(null);
			this.put(KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY);
			this.put(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
			this.put(KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY);
			this.put(KEY_DITHERING, VALUE_DITHER_DISABLE);
			this.put(KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON);
			this.put(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC);
			this.put(KEY_RENDERING, VALUE_RENDER_QUALITY);
			this.put(KEY_STROKE_CONTROL, VALUE_STROKE_NORMALIZE);
			this.put(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON);
		}

		public static RendHints getInstance() {
			return instance;
		}
		
	}

	private enum ColorType {
		FONTE(new Color(0x40FF80)),
		FUNDO(new Color(0x200040)),
		MOLDURA(new Color(0x0060FF)),
		MOSTRADOR(new Color(0x00FFFF)),
		HORAS(new Color(0xFF0080)),
		MINUTOS(new Color(0xFF8040)),
		SEGUNDOS(new Color(0xFFFF00));
		
		private Color color;
		
		private ColorType(Color color) {
			this.color = color;
		}

		public Color getColor() {
			return color;
		}
		
	}
	
}

2 Respostas

ViniGodoy

Muito legal.

Por que não abstraiu a matemática vetorial numa classe Vector2D? Creio que ia tornar essa calculeira toda muito mais clara…

Fox_McCloud

ViniGodoy:
Muito legal.

Por que não abstraiu a matemática vetorial numa classe Vector2D? Creio que ia tornar essa calculeira toda muito mais clara…


Na verdade é possível efetuar uma série de refatorações subdividindo a aplicação em classes e pacotes de acordo com um pattern MVC, por exemplo, usando interfaces e classes abstratas e diminuindo bastante o acoplamento…

Mas foi apenas uma aplicação rápida que fiz de ontem para hoje no intuito de praticar algumas coisas e também por ter algum tempo livre ocioso (rs).

Tem um “touché” aí no meio que é o método trimTransp(). Esse método, que eu mesmo desenvolvi, corta pixels transparentes externos de uma imagem gerando uma nova imagem sem espaços externos, e o grande truque está na linha if(pixel[3]==0)continue;

Se o valor da banda alpha (transparência) for zero, ela é ignorada e eu consigo assim obter as posições iniciais e finais da imagem sem a transparência e colocar os pixels dentro dessas posições em uma nova imagem. Dá até vontade de não contar o “pulo do gato” devido a quanta pesquisa precisei fazer, mas como o GUJ me ajudou em muitas coisas, uma mão lava a outra! Viva o código-fonte aberto!

Tenho uma outra aplicação de processamento de imagens que transforma os pixels em escalas de cinza (de acordo com um percentual de pesos para as bandas RGB), e depois recolore a imagem transformando a escala de cinza em outras escalas de cores.

O Photoshop faz isso (Gradient Map), mas ter uma biblioteca self-made de processamento gráfico elimina todos os problemas de uso de softwares não-licenciados, e ainda permite a modificação desses recursos do jeito exato que você quer utilizá-los!

Fica aqui um “start point” pra quem quiser brincar com o código, melhorá-lo, aproveitar alguns algoritmos, enfim… Divirtam-se!

Criado 1 de julho de 2009
Ultima resposta 1 de jul. de 2009
Respostas 2
Participantes 2