I completed a homework given by my instructor, but it is not doing what it's suppose to do completely.

I was able to add a message in the pixel, but if I run it, some wierd characters were added after the message

here's my work. I was only assigned to complete putSecretMessageInImage and extractSecretMessageFromImage, and instructions are given in the comments:

public class MacGuffinImageProcessor 
{
	/**
	 * This method should put the secret message
	 * into the pixels array. Note that the pixels
	 * array is an array of pixel values where the
	 * rgba values are all stored within singular
	 * integers. In other words, each int is really
	 * four bytes, one for each of the rgba components.
	 * 
	 * Note that the message data should be placed in
	 * the alpha channel along the left-edge of the image,
	 * top to bottom.
	 */
	public static void putSecretMessageInImage(int[] pixels,String message)
	{
		int alpha = pixels[0];
		alpha = alpha & 0xFFFFFF00;
		int length = message.length();
		alpha = alpha | length;
		pixels[0] = alpha;

		for (int i = 1; i < length; i++)
		{
			pixels[i] = message.charAt(i-1);
		}
	}

	/**
	 * This method works in cooperation with the
	 * putSecretMessageInImage method. It must know
	 * how that one works in order to extract the data
	 * properly.
	 * 
	 * This one will look at the pixel data and extract
	 * the text hidden inside, returning this as a String.
	 */
	public static String extractSecretMessageFromImage(int[] pixels)
	{
		String output = "";
		
		for (int i = 1; i < pixels.length; i++)
		{
			output += (char)pixels[i];
		}
		
		return output;
	}
}

and here is the MacGuffinImageMaker class, which was given by my instructor:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.ImageObserver;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.html.HTMLDocument;

public class MacGuffinImageMaker extends JFrame
{
	// NORTH
	private JPanel northPanel;
	private JLabel fileNameLabel;
	private DefaultComboBoxModel dcbm;
	private JComboBox fileNameComboBox;
	
	// CENTER
	private ImagePanel imagePanel;
	private BufferedImage renderImage;
	
	// SOUTH
	private JPanel southPanel;
	private JLabel messageLabel;
	private JTextField messageTF;
	private JButton setMessageButton;
	
	// WE'LL USE THIS FOR ALL TEXT
	Font font = new Font("Serif", Font.PLAIN, 20);	

	/**
	 * Default and only constructor, this lays out
	 * all of our GUI components.
	 */
	public MacGuffinImageMaker()
	{
		super("MacGuffin Image Maker");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setExtendedState(MAXIMIZED_BOTH);
		layoutGUI();
	}

	/**
	 * This sets up the entire GUI.
	 */
	public void layoutGUI()
	{
		// NORTH
		northPanel = new JPanel();
		fileNameLabel = new JLabel("File Name: ");
		fileNameLabel.setFont(font);
		fileNameComboBox = new JComboBox();
		ImageItemListener iil = new ImageItemListener();
		fileNameComboBox.addItemListener(iil);
		northPanel.add(fileNameLabel);
		northPanel.add(fileNameComboBox);
		
		// CENTER
		imagePanel = new ImagePanel();
		renderImage = null;
		
		// SOUTH
		southPanel = new JPanel();
		messageLabel = new JLabel("Message: ");
		messageLabel.setFont(font);
		messageTF = new JTextField(50);
		messageTF.setFont(font);
		setMessageButton = new JButton("Set Message");
		southPanel.add(messageLabel);
		southPanel.add(messageTF);
		southPanel.add(setMessageButton);
		SetMessageListener sml = new SetMessageListener();
		setMessageButton.addActionListener(sml);

		// ARRANGE EVERYTHING INSIDE THIS FRAME
		add(northPanel, BorderLayout.NORTH);
		add(imagePanel, BorderLayout.CENTER);
		add(southPanel, BorderLayout.SOUTH);
		
		// NOW THAT EVERYTHING'S IN PLACE, LET'S
		// LOAD THE COMBO BOX WITH ALL THE FILE NAMES
		loadComboBox();
	}

	/**
	 * This method finds all the PNG images in the
	 * working directory and places their file names
	 * in the combo box.
	 */
	public void loadComboBox()
	{
		// THIS STORES THE DATA FOR THE COMBO BOX
		dcbm = new DefaultComboBoxModel();
		
		// GET ALL THE FILES IN THIS DIRECTORY
		File f = new File(".");
		File[] files = f.listFiles();
		Vector<String> v = new Vector<String>();
		for (int i = 0; i < files.length; i++)
		{
			String fileName = files[i].getName();
			if (fileName.endsWith(".png"))
			{
				dcbm.addElement(files[i].getName());
			}
		}
		fileNameComboBox.setModel(dcbm);
		
		// LOAD THE FIRST IMAGE
		if (fileNameComboBox.getSelectedItem() != null)
		{
			String fileName = (String)fileNameComboBox.getSelectedItem();
			loadImage(fileName);
		}
	}

	/**
	 * This method checks the fileName image file. If
	 * it is an ARGB-type PNG it will simply load it and
	 * load the secret message, if any. 
	 * 
	 * If it is an RGB-type, it will convert it to an ARGB
	 * type and specify no message.
	 */
	public void loadImage(String fileName)
	{
		try
		{
			// READ IN THE IMAGE
			File imageFile = new File(fileName);
			renderImage = ImageIO.read(imageFile);
			MediaTracker mt = new MediaTracker(this);
			mt.addImage(renderImage, 0);
			try { mt.waitForID(0); }
			catch(InterruptedException ioe) {}
			
			// IS IT AN RGB TYPE?
			if (!renderImage.getColorModel().hasAlpha())
			{
				// LET'S CONVERT THE FILE
				int w = renderImage.getWidth();
				int h = renderImage.getHeight();
				
				// MAKE A NEW BufferedImage
				BufferedImage newImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
				Raster oldRaster = renderImage.getRaster();
				WritableRaster newRaster = newImage.getRaster();
				
				// AND COPY OVER ALL TEH DATA TO THE NEW ONE
				for (int i = 0; i < w; i++)
				{
					for (int j = 0; j < h; j++)
					{
						int[] oldPixel = new int[3];
						oldRaster.getPixel(i, j, oldPixel);
						int[] newPixel = new int[4];
						for(int k = 0; k < 3; k++)
							newPixel[k] = oldPixel[k];
						if (j == 0)
							newPixel[3] = 0;
						else
							newPixel[3] = 255;
						newRaster.setPixel(i, j, newPixel);
					}
				}
				// NOW LET'S SAVE IT
				renderImage = newImage;
				saveImageToFile(fileName);
			}
			// IT'S AN ARGB TYPE
			else
			{
				// SO LET'S READ IT IN AND EXTRACT
				// AND THEN DISPLAY THE MESSAGE
				int[] pixels = buildPixelArray();
				String message = MacGuffinImageProcessor.extractSecretMessageFromImage(pixels);
				messageTF.setText(message);
				imagePanel.repaint();
			}
		}
		catch(IOException ioe)
		{
			JOptionPane.showMessageDialog(this, "Error Reading " + fileName);
		}
	}	

	/**
	 * This method builds and returns an array
	 * of integers representing all the pixels
	 * in the image. Note that each int stores
	 * the four bytes for rgba (in that order),
	 * high bits to low bits.
	 */
	public int[] buildPixelArray()
	{
		Raster raster = renderImage.getRaster();
		int w = raster.getWidth();
		int h = raster.getHeight();
		int[] pixels = new int[w * h];
		int[] pixel = new int[4];
		int counter = 0;
		for (int i = 0; i < w; i++)
		{
			for (int j = 0; j < h; j++)
			{
				raster.getPixel(i, j, pixel);
				pixels[counter] = packPixelIntoInt(pixel);
				counter++;
			}
		}
		return pixels;
	}

	/**
	 * This method packs the four integers (all 0 - 255)
	 * in the pixel array into a single int. This int
	 * is then returned.
	 */
	public int packPixelIntoInt(int[] pixel)
	{
		int pixelInt = 0;
		pixelInt = pixel[0] << 24;
		pixelInt = pixelInt | (pixel[1] << 16);
		pixelInt = pixelInt | (pixel[2] << 8);
		pixelInt = pixelInt | pixel[3];
		return pixelInt;
	}
	
	/**
	 * This method provides the architecture for adding
	 * the secret message. Note that the implementation
	 * is done inside the MacGuffinImageProcessor class.
	 */
	public void addSecretMessageToImage(String newFileName,
										String message)
	{
		// IF IT'S A NEW FILE NAME, WE NEED TO
		// ADD IT TO THE COMBO BOX
		if (dcbm.getIndexOf(newFileName) == -1)
		{
			// IS IT THE FIRST ELEMENT?
			if (dcbm.getSize() == 0)
				dcbm.addElement(newFileName);
			// DOES IT GO AT THE END?
			else
			{
				String lastFileName = (String)dcbm.getElementAt(dcbm.getSize()-1);
				if (newFileName.compareTo(lastFileName) > 0)
					dcbm.addElement(newFileName);
				// IT MUST GO SOMEWHERE ELSE
				else
				{
					boolean locationFound = false;
					for (int i = 0; (i < dcbm.getSize()) && !locationFound; i++)
					{
						String testFileName = (String)dcbm.getElementAt(i);
						if (newFileName.compareTo(testFileName) < 0)
						{
							dcbm.insertElementAt(newFileName, i);
							locationFound = true;
						}
					}
				}
			}
		}
		int[] pixels = buildPixelArray();
		MacGuffinImageProcessor.putSecretMessageInImage(pixels, message);
		updatePixels(pixels);
		saveImageToFile(newFileName);		
		fileNameComboBox.setSelectedItem(newFileName);
	}

	/**
	 * This method would be called after a secret
	 * message has been put into a pixels array, which
	 * is the method argument. This method will then
	 * load this data into the image.
	 */
	public void updatePixels(int[] pixels)
	{
		WritableRaster wr = renderImage.getRaster();
		int length = pixels[0] & 0x000000ff;
		for (int i = 0; i <= length; i++)
		{
			int[] pixel = new int[4];
			pixel[0] = (pixels[i] >> 25) & 0x000000ff;
			pixel[1] = (pixels[i] & 0x00ff0000)>>16;
			pixel[2] = (pixels[i] & 0x0000ff00)>>8;
			pixel[3] = (pixels[i] & 0x000000ff);
			wr.setPixel(0, i, pixel);
		}
	}

	/**
	 * This method saves the image, with the secret
	 * message, to its file.
	 */
	public void saveImageToFile(String fileName)
	{
		try
		{
			File f = new File(fileName);
			ImageIO.write(renderImage, "png", f);
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}	

	/**
	 * This helper class responds to the user selecting
	 * an image from the combo box. It does so by loading
	 * the selected image, rendering it, and loading and
	 * displaying any secret message found inside.
	 */
	private class ImageItemListener implements ItemListener
	{
		public void itemStateChanged(ItemEvent ie)
		{
			if (ie.getStateChange() == ItemEvent.SELECTED)
			{
				String fileName = (String)ie.getItem();
				loadImage(fileName);
			}
		}
	}

	/**
	 * This helper class provides the response for when
	 * the user clicks on the Set Message button. At that
	 * time, we want to take the text in the text field and
	 * embed it inside the image.
	 */
	private class SetMessageListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ae)
		{
			// THIS IS THE IMAGE WE'RE USING
			BufferedImage img = MacGuffinImageMaker.this.renderImage;
			if (img != null)
			{
				// FIRST ASK THE USER WHAT TO NAME THE IMAGE
				String fileName = JOptionPane.showInputDialog(
									MacGuffinImageMaker.this,
									"What would you like to name your "
									+ " image with the secret message?",
									MacGuffinImageMaker.this.fileNameComboBox.getSelectedItem());
				if (fileName != null)
				{
					int w = img.getWidth(null);
					int h = img.getHeight(null);

					String secretMessage = MacGuffinImageMaker.this.messageTF.getText();
					if (secretMessage.length() > (h-1))
						JOptionPane.showMessageDialog(MacGuffinImageMaker.this, "For this image, your message is too long. It many not have more than " + w + " characters", "Message too long", JOptionPane.ERROR_MESSAGE);
					else
						MacGuffinImageMaker.this.addSecretMessageToImage(fileName, secretMessage);
				}
			}
		}
	}

	/**
	 * This helper class performs all the image 
	 * rendering inside our application.
	 */
	private class ImagePanel extends JPanel
	{
		public void paintComponent(Graphics g)
		{
			super.paintComponent(g);
			Image img = MacGuffinImageMaker.this.renderImage;
			if (img != null)
			{
				int w = getWidth();
				int h = getHeight();
				if (w > 0)
				{
					int imgW = img.getWidth(null);
					int imgH = img.getHeight(null);
					int x = (w/2) - (imgW/2);
					int y = (h/2) - (imgH/2);
					g.drawImage(img, x, y, null);
				}	
			}
		}
	}

	/**
	 * This is where our program starts. We make a new
	 * window (our MacGuffinImageMaker object), and start
	 * it up. Once started, the program is in event-
	 * handling mode, waiting for the user to do something.
	 */
	public static void main(String[] args)
	{
		MacGuffinImageMaker frame = new MacGuffinImageMaker();
		frame.setVisible(true);
	}
}

Could anyone point out what I did wrong here? I'm assuming that my putSecretMessageInImage() works but the problem is in the extractSecretMessageInImage()...I could be wrong both ways

This article has been dead for over six months. Start a new discussion instead.