Implementing the MIDlet ImageCanvas class

To implement the ImageCanvas class:

  1. Create the ImageCanvas.java class file.

  2. Import the required packages and classes.

    package com.nokia.example.imagescaler;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Hashtable;
    
    import javax.microedition.amms.GlobalManager;
    import javax.microedition.amms.MediaProcessor;
    import javax.microedition.amms.MediaProcessorListener;
    import javax.microedition.amms.control.imageeffect.ImageEffectControl;
    import javax.microedition.io.Connector;
    import javax.microedition.io.file.FileConnection;
    import javax.microedition.lcdui.AlertType;
    import javax.microedition.lcdui.Canvas;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Font;
    import javax.microedition.lcdui.Graphics;
    import javax.microedition.lcdui.Image;
    import javax.microedition.media.MediaException;
    
    import com.nokia.mid.imagescale.ImageScaler;
    import com.nokia.mid.imagescale.ImageScalerException;
    import com.nokia.mid.imagescale.ImageScalerListener;
    import com.nokia.mid.ui.VirtualKeyboard;
    import com.nokia.mid.ui.gestures.GestureEvent;
    import com.nokia.mid.ui.gestures.GestureInteractiveZone;
    import com.nokia.mid.ui.gestures.GestureListener;
    import com.nokia.mid.ui.gestures.GestureRegistrationManager;
    import com.nokia.mid.ui.orientation.Orientation;
    import com.nokia.mid.ui.orientation.OrientationListener;
  3. Create the ImageCanvas class to extend Canvas and implement CommandListener, GestureListener, ImageScalerListener, MediaProcessorListener and OrientationListener.

    public class ImageCanvas
                extends Canvas
                implements CommandListener,
                GestureListener,
                ImageScalerListener,
                MediaProcessorListener,
                OrientationListener
    {
  4. Create required constants and variables.

    private static final String JPEG_TYPE = "image/jpeg";
    private static final String DESTINATION_FILE_NAME_PREFIX = "temp_";
    private static final double SEVENTYFIVE_PERCENTS = .75;
    private static final double FIFTY_PERCENTS = .5;
    private static final double TWENTYFIVE_PERCENTS = .25;
    
    private final Command backCommand = new Command("Back", Command.BACK, 1);
    private final Command downscaleTo75PercentsCommand = new Command("Scale to 75 %", Command.ITEM, 1);
    private final Command downscaleTo50PercentsCommand = new Command("Scale to 50 %", Command.ITEM, 2);
    private final Command downscaleTo25PercentsCommand = new Command("Scale to 25 %", Command.ITEM, 3);
    private final Command resetCommand = new Command("Reset to original size", Command.ITEM, 6);
    private final Command monochromeCommand = new Command("To monochrome", Command.ITEM, 7);
    private MainView mainView = null;
    private ImageScaler imageScaler = null;
    private Image originalImage = null;
    private Image currentImage = null;
    private Hashtable imageTable = new Hashtable();
    private String currentImageFilePath = null;
    private ByteArrayOutputStream byteArrayOutputStream = null;
    private int originalImageWidth = 0;
    private int originalImageHeight = 0;
    private int currentImageWidth = 0;
    private int currentImageHeight = 0;
    private int currentPointerX = 0;
    private int currentPointerY = 0;
    private int deltaPointerX = 0;
    private int deltaPointerY = 0;
    private int imagePosX;
    private int imagePosY;
    private int rgbImageData[];
  5. Create the ImageCanvas class constructor. In the constructor, add Commands to the Screen, hide the virtual keyboard, and start listening for pinch gestures.

    public ImageCanvas(MainView mainView) {
        if (System.getProperty("com.nokia.keyboard.type").equals("None")
                || System.getProperty("com.nokia.keyboard.type").equals("OnekeyBack"))
        {
            VirtualKeyboard.hideOpenKeypadCommand(true);
        }
    
        this.mainView = mainView;
    
        setTitle("Image Scaler");
    
        addCommand(backCommand);
        addCommand(downscaleTo75PercentsCommand);
        addCommand(downscaleTo50PercentsCommand);
        addCommand(downscaleTo25PercentsCommand);
        addCommand(resetCommand);
        setCommandListener(this);
    
        Orientation.addOrientationListener(this);
    
        GestureRegistrationManager.setListener(this, this);
        GestureInteractiveZone myGestureZone = new GestureInteractiveZone(
            GestureInteractiveZone.GESTURE_PINCH);
        myGestureZone.setRectangle(0, 0, getWidth(), getHeight());
        GestureRegistrationManager.register(this, myGestureZone);
    }
  6. Implement commandAction method from the CommandListener interface to capture UI events and handle them to call the respective functions calls.

    public void commandAction(Command command, Displayable displayable) {
        if (command == backCommand) {
            currentImage = null;
            rgbImageData = null;
            mainView.displayThis();
        }
        else if (command == downscaleTo75PercentsCommand
                 || command == downscaleTo50PercentsCommand
                 || command == downscaleTo25PercentsCommand)
        {
            int width = originalImageWidth;
            int height = originalImageHeight;
    
            if (command == downscaleTo75PercentsCommand) {
                width *= SEVENTYFIVE_PERCENTS;
                height *= SEVENTYFIVE_PERCENTS;
            }
            else if (command == downscaleTo50PercentsCommand) {
                width *= FIFTY_PERCENTS;
                height *= FIFTY_PERCENTS;
            }
            else {
                width *= TWENTYFIVE_PERCENTS;
                height *= TWENTYFIVE_PERCENTS;
            }
    
            final int newWidth = width;
            final int newHeight = height;
    
            new Thread(new Runnable() {
                public void run() {
                    scaleImage(newWidth, newHeight, currentImageFilePath);
                }
            }).start();
        }
        else if (command == resetCommand) {
            new Thread(new Runnable() {
                public void run() {
                    loadImage(currentImageFilePath);
                }
            }).start();
        }
        else if (command == monochromeCommand) {
            new Thread(new Runnable() {
                public void run() {
                    createMonochromeImage(currentImageFilePath);
                }
            }).start();
        }
    }
  7. Implement paint method from the Canvas to paint the Screen with updates.

    protected void paint(Graphics graphics) {
        final int width = getWidth();
        final int height = getHeight();
    
        graphics.setColor(0x000000);
        graphics.fillRect(0, 0, width, height);
        calculateImagePlacement();
    
        if (currentImage != null) {
            graphics.drawImage(currentImage, imagePosX, imagePosY,
                               Graphics.HCENTER | Graphics.VCENTER);
        }
        else {
            graphics.setColor(0x00f4f4f4);
            graphics.drawString("No image", 20,
                                height / 2 - Font.getDefaultFont().getHeight() / 2,
                                Graphics.HCENTER | Graphics.BASELINE);
        }
    }
  8. Implement keyReleased method from the Canvas to display mainView.

    protected void keyReleased(int keyCode) {
        mainView.displayThis();
    }
  9. Implement pointerPressed method from the Canvas to set x and y coordinates.

    protected void pointerPressed(int x, int y) {
        currentPointerX = x;
        currentPointerY = y;
    }
  10. Implement pointerReleased method from the Canvas to set x and y coordinates.

    protected void pointerReleased(int x, int y) {
        deltaPointerX = 0;
        deltaPointerY = 0;
    }
  11. Implement pointerDragged method from the Canvas to set x and y coordinates.

    protected void pointerDragged(int x, int y) {
        System.out.println("ImageCanvas::pointerDragged(): [" + x + ", " + y + "]");
        deltaPointerX = x - currentPointerX;
        deltaPointerY = y - currentPointerY;
        currentPointerX = x;
        currentPointerY = y;
        repaint();
    }
  12. Implement scaleFinished method from the ImageScalerListener. This method is called to deliver an event to a registered listener when a ImageScaling finish event is observed.

    public void scaleFinished(int requestId, int result) {
        final int reqId = requestId;
    
        new Thread(new Runnable() {
            public void run() {
                String imageFilePath = (String) imageTable.get(new Integer(reqId));
                String destinationFilePath =
                    Utils.PHOTOS_DIR + "/" + DESTINATION_FILE_NAME_PREFIX
                    + imageFilePath.substring(imageFilePath.lastIndexOf('/') + 1);
    
                FileConnection fileConnection = null;
                InputStream inputStream = null;
    
                try {
                    fileConnection = (FileConnection) Connector.open(destinationFilePath);
                }
                catch (IOException e) {
                    return;
                }
    
                Image image = null;
    
                if (fileConnection.exists()) {
                    try {
                        inputStream = fileConnection.openInputStream();
                        image = Image.createImage(inputStream);
                    }
                    catch (IOException e) {
                        return;
                    }
                }
    
                currentImage = image;
                currentImageWidth = currentImage.getWidth();
                currentImageHeight = currentImage.getHeight();
                repaint();
    
                try {
                    inputStream.close();
                    fileConnection.close();
                }
                catch (IOException e) {
                }
            }
        }).start();
    }
  13. Implement gestureAction method from the GestureListener. This method Indicates that a gesture event has occurred on a Canvas.

    public void gestureAction(Object arg0,
                              GestureInteractiveZone arg1,
                              GestureEvent gestureEvent)
    {
        switch (gestureEvent.getType()) {
        case GestureInteractiveZone.GESTURE_PINCH:
            Image scaledImage = null;
            int newWidth = currentImageWidth;
            int newHeight = currentImageHeight;
    
            if (gestureEvent.getPinchDistanceChange() == 0) {
                break;
            }
            else if (gestureEvent.getPinchDistanceChange() < 0) {
                newWidth *= 0.9;
                newHeight *= 0.9;
            }
            else if (gestureEvent.getPinchDistanceChange() > 0) {
                newWidth *= 1.1;
                newHeight *= 1.1;
            }
    
            scaledImage = Utils.scaleImage(originalImage, newWidth, newHeight);
    
            if (scaledImage != null) {
                // Image was scaled successfully
                currentImage = scaledImage;
                currentImageWidth = newWidth;
                currentImageHeight = newHeight;
                repaint();
            }
    
            break;
        default:
            break;
        }
    }
  14. Implement mediaProcessorUpdate method from the MediaProcessorListener. This method is called to deliver an event to a registered listener when a MediaProcessor event is observed.

    public void mediaProcessorUpdate(MediaProcessor processor,
                                     String event,
                                     Object eventData)
    {
        if (event.equals(MediaProcessorListener.PROCESSING_ABORTED)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSING_ABORTED: "
                               + eventData.toString());
        }
        else if (event.equals(MediaProcessorListener.PROCESSING_ERROR)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSING_ERROR: "
                               + eventData.toString());
        }
        else if (event.equals(MediaProcessorListener.PROCESSING_STARTED)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSING_STARTED: "
                               + eventData.toString());
        }
        else if (event.equals(MediaProcessorListener.PROCESSING_STOPPED)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSING_STOPPED: "
                               + eventData.toString());
        }
        else if (event.equals(MediaProcessorListener.PROCESSOR_REALIZED)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSOR_REALIZED: "
                               + eventData.toString());
        }
        else if (event.equals(MediaProcessorListener.PROCESSING_COMPLETED)) {
            System.out.println("ImageCanvas::mediaProcessorUpdate(): PROCESSING_COMPLETED: "
                               + eventData.toString());
    
            Image resultImage =
                Image.createImage(byteArrayOutputStream.toByteArray(), 0,
                                  byteArrayOutputStream.size());
    
            if (resultImage != null) {
                currentImage = resultImage;
                repaint();
            }
        }
    }
  15. Implement displayOrientationChanged method from the OrientationListener. This method is called when the display orientation has changed.

    public void displayOrientationChanged(int newDisplayOrientation) {
        Orientation.setAppOrientation(newDisplayOrientation);
    }
  16. Create the loadImage method. This method loads an image from the given file path and displays it on this canvas.

    public boolean loadImage(String filePath) {
        FileConnection fileConnection = null;
    
        try {
            fileConnection = (FileConnection) Connector.open(filePath, Connector.READ);
        }
        catch (IOException e) {
            return false;
        }
    
        currentImageFilePath = filePath;
    
        if (rgbImageData != null) {
            rgbImageData = null;
        }
    
        int fileSize = 0;
    
        try {
            fileSize = (int) fileConnection.fileSize();
        }
        catch (IOException e) {
            return false;
        }
    
        byte[] imageData = new byte[fileSize];
    
        try {
            InputStream inputStream = fileConnection.openInputStream();
            inputStream.read(imageData, 0, fileSize);
            inputStream.close();
            fileConnection.close();
        }
        catch (IOException e) {
            return false;
        }
    
        currentImage = Image.createImage(imageData, 0, fileSize);
    
        originalImageWidth = currentImageWidth = currentImage.getWidth();
        originalImageHeight = currentImageHeight = currentImage.getHeight();
    
        repaint();
        return true;
    }
  17. Create the scaleImage method. This method scales the image in the given path using the image scaler API.

    private void scaleImage(final int newWidth,
                            final int newHeight,
                            final String sourceImageFilePath)
    {
        final String destinationFilePath =
            Utils.PHOTOS_DIR + "/" + DESTINATION_FILE_NAME_PREFIX
            + sourceImageFilePath.substring(sourceImageFilePath.lastIndexOf('/') + 1);
    
        imageScaler = new ImageScaler(sourceImageFilePath, destinationFilePath);
        imageScaler.addListener(this);
        Integer requestId = null;
    
        try {
            requestId = new Integer(imageScaler.scaleImage(newWidth, newHeight, true));
            imageTable.put(requestId, sourceImageFilePath);
        }
        catch (ImageScalerException e) {
            mainView.showMessage(AlertType.ERROR, e.toString());
        }
    }
  18. Create the createMonochromeImage method. This method reads image data from the given path and creates a monochrome image of the original image.

    private void createMonochromeImage(String imageFilePath) {
        MediaProcessor mediaProcessor = null;
    
        try {
            mediaProcessor = GlobalManager.createMediaProcessor(JPEG_TYPE);
        }
        catch (MediaException e) {
            mainView.showMessage(AlertType.ERROR, e.toString());
            return;
        }
    
        mediaProcessor.addMediaProcessorListener(this);
        FileConnection fileConnection = null;
    
        try {
            fileConnection = (FileConnection) Connector.open(imageFilePath, Connector.READ);
        }
        catch (IOException e) {
            return;
        }
    
        if (fileConnection != null && fileConnection.exists()) {
            InputStream inputStream = null;
    
            try {
                inputStream = fileConnection.openInputStream();
            }
            catch (IOException e) {
                return;
            }
    
            try {
                mediaProcessor.setInput(inputStream, MediaProcessor.UNKNOWN);
            }
            catch (MediaException e) {
                mainView.showMessage(AlertType.ERROR, e.toString());
                return;
            }
    
            try {
                inputStream.close();
                fileConnection.close();
            }
            catch (IOException e) {
            }
        }
    
        byteArrayOutputStream = new ByteArrayOutputStream();
        mediaProcessor.setOutput(byteArrayOutputStream);
        ImageEffectControl imageEffect =
            (ImageEffectControl) mediaProcessor.getControl(
                "javax.microedition.amms.control.imageeffect.ImageEffectControl");
        imageEffect.setPreset("monochrome");
        imageEffect.setEnabled(true);
    
        try {
            mediaProcessor.start();
        }
        catch (MediaException e) {
            mainView.showMessage(AlertType.ERROR, e.toString());
        }
    
    }
  19. Create the calculateImagePlacement method. This method calculates the image placement on the screen.

    private void calculateImagePlacement() {
        final int canvasWidth = getWidth();
        final int canvasHeight = getHeight();
    
        if (currentImageWidth > canvasWidth && deltaPointerX != 0) {
            imagePosX += deltaPointerX;
    
            if (imagePosX < (canvasWidth - currentImageWidth / 2)) {
                imagePosX = canvasWidth - currentImageWidth / 2;
            }
            else if (imagePosX > (currentImageWidth / 2)) {
                imagePosX = (currentImageWidth / 2);
            }
        }
        else {
            imagePosX = canvasWidth / 2;
        }
    
        if (currentImageHeight > canvasHeight && deltaPointerY != 0) {
            imagePosY += deltaPointerY;
    
            if (imagePosY < (canvasHeight - currentImageHeight / 2)) {
                imagePosY = canvasHeight - currentImageHeight / 2;
            }
            else if (imagePosY > (currentImageHeight / 2)) {
                imagePosY = (currentImageHeight / 2);
            }
        }
        else {
            imagePosY = canvasHeight / 2;
        }
    }
    }