/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.batik.ext.awt.image.codec.png;

import org.apache.batik.test.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;

/**
 * This test validates the PNGEncoder operation. It creates a
 * BufferedImage, then encodes it with the PNGEncoder, then
 * decodes it and compares the decoded image with the original one.
 *
 * @author <a href="mailto:vhardy@eng.sun.com">Vincent Hardy</a>
 * @version $Id$
 */
public class PNGEncoderTest extends AbstractTest {
    /**
     * Error when image cannot be encoded
     * {0} = trace for the exception which was reported
     */
    public static final String ERROR_CANNOT_ENCODE_IMAGE
        = "PNGEncoderTest.error.cannot.encode.image";

    /**
     * Error when image cannot be decoded
     * {0} = trace for the exception which was reported
     */
    public static final String ERROR_CANNOT_DECODE_IMAGE
        = "PNGEncoderTest.error.cannot.decode.image";

    /**
     * Decoded image differs from encoded image
     */
    public static final String ERROR_DECODED_DOES_NOT_MATCH_ENCODED
        = "PNGEncoderTest.error.decoded.does.not.match.encoded";

    public TestReport runImpl() throws Exception {
        // Create a BufferedImage to be encoded
        BufferedImage image = new BufferedImage(100, 75, BufferedImage.TYPE_INT_ARGB);
        Graphics2D ig = image.createGraphics();
        ig.scale(.5, .5);
        ig.setPaint(new Color(128,0,0));
        ig.fillRect(0, 0, 100, 50);
        ig.setPaint(Color.orange);
        ig.fillRect(100, 0, 100, 50);
        ig.setPaint(Color.yellow);
        ig.fillRect(0, 50, 100, 50);
        ig.setPaint(Color.red);
        ig.fillRect(100, 50, 100, 50);
        ig.setPaint(new Color(255, 127, 127));
        ig.fillRect(0, 100, 100, 50);
        ig.setPaint(Color.black);
        ig.draw(new Rectangle2D.Double(0.5, 0.5, 199, 149));
        ig.dispose();

        image = image.getSubimage(50, 0, 50, 25);

        // Create an output stream where the PNG data
        // will be stored.
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        OutputStream os = buildOutputStream(bos);

        // Now, try to encode image
        PNGEncodeParam params =
            PNGEncodeParam.getDefaultEncodeParam(image);
        PNGImageEncoder pngImageEncoder = new PNGImageEncoder(os, params);

        try{
            pngImageEncoder.encode(image);
            os.close();
        }catch(Exception e){
            return reportException(ERROR_CANNOT_ENCODE_IMAGE, e);
        }

        // Now, try to decode image
        InputStream is
            = buildInputStream(bos);

        PNGImageDecoder pngImageDecoder
            = new PNGImageDecoder(is, new PNGDecodeParam());

        RenderedImage decodedRenderedImage = null;
        try{
            decodedRenderedImage = pngImageDecoder.decodeAsRenderedImage(0);
        }catch(Exception e){
            return reportException(ERROR_CANNOT_DECODE_IMAGE,
                            e);
        }

        BufferedImage decodedImage = null;
        if(decodedRenderedImage instanceof BufferedImage){
            decodedImage = (BufferedImage)decodedRenderedImage;
        }
        else{
            decodedImage = new BufferedImage(decodedRenderedImage.getWidth(),
                                             decodedRenderedImage.getHeight(),
                                             BufferedImage.TYPE_INT_ARGB);
            ig = decodedImage.createGraphics();
            ig.drawRenderedImage(decodedRenderedImage,
                                 new AffineTransform());
            ig.dispose();
        }

        // Compare images
        if( ! checkIdentical(image, decodedImage) ){
            return reportError(ERROR_DECODED_DOES_NOT_MATCH_ENCODED);
        }

        return reportSuccess();
    }

    /**
     * Template method for building the PNG output stream. This gives a
     * chance to sub-classes (e.g., Base64PNGEncoderTest) to add an
     * additional encoding.
     */
    public OutputStream buildOutputStream(ByteArrayOutputStream bos){
        return bos;
    }

    /**
     * Template method for building the PNG input stream. This gives a
     * chance to sub-classes (e.g., Base64PNGEncoderTest) to add an
     * additional decoding.
     */
    public InputStream buildInputStream(ByteArrayOutputStream bos){
        return new ByteArrayInputStream(bos.toByteArray());
    }

    /**
     * Compares the data for the two images
     */
    public static boolean checkIdentical(BufferedImage imgA,
                                         BufferedImage imgB){
        boolean identical = true;
        if(imgA.getWidth() == imgB.getWidth()
           &&
           imgA.getHeight() == imgB.getHeight()){
            int w = imgA.getWidth();
            int h = imgA.getHeight();
            for(int i=0; i<h; i++){
                for(int j=0; j<w; j++){
                    if(imgA.getRGB(j,i) != imgB.getRGB(j,i)){
                        identical = false;
                        break;
                    }
                }
                if( !identical ){
                    break;
                }
            }
        }

        return identical;
    }

}
