org.apache.flex.compiler.internal.embedding.transcoders.JPEGTranscoder.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.flex.compiler.internal.embedding.transcoders.JPEGTranscoder.java

Source

/*
 *
 *  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.flex.compiler.internal.embedding.transcoders;

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.MemoryCacheImageOutputStream;

import org.apache.commons.io.IOUtils;

import org.apache.flex.compiler.common.ISourceLocation;
import org.apache.flex.compiler.internal.embedding.EmbedAttribute;
import org.apache.flex.compiler.internal.embedding.EmbedData;
import org.apache.flex.compiler.internal.workspaces.Workspace;
import org.apache.flex.compiler.problems.EmbedExceptionWhileTranscodingProblem;
import org.apache.flex.compiler.problems.EmbedQualityRequiresCompressionProblem;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.swf.tags.DefineBitsJPEG3Tag;
import org.apache.flex.swf.tags.DefineBitsTag;
import org.apache.flex.swf.tags.ICharacterTag;
import org.apache.flex.swf.tags.ITag;
import org.apache.flex.utils.DAByteArrayOutputStream;

/**
 * Handle the embedding of images which need to be transcoded
 */
public class JPEGTranscoder extends ImageTranscoder {
    public JPEGTranscoder(EmbedData data, Workspace workspace) {
        super(data, workspace);
        this.compression = false;
    }

    private boolean compression;
    private Float quality;

    @Override
    public boolean analyze(ISourceLocation location, Collection<ICompilerProblem> problems) {
        boolean result = super.analyze(location, problems);
        baseClassQName = CORE_PACKAGE + ".BitmapAsset";
        return result;
    }

    @Override
    protected boolean checkAttributeValues(ISourceLocation location, Collection<ICompilerProblem> problems) {
        boolean result = super.checkAttributeValues(location, problems);
        if (!result)
            return false;

        // quality doesn't make sense without compression
        if (!compression && (quality != null)) {
            problems.add(new EmbedQualityRequiresCompressionProblem(location));
            result = false;
        }

        return result;
    }

    @Override
    protected boolean setAttribute(EmbedAttribute attribute) {
        boolean isSupported = true;
        switch (attribute) {
        case COMPRESSION:
            compression = (Boolean) data.getAttribute(EmbedAttribute.COMPRESSION);
            break;
        case QUALITY:
            quality = (Float) data.getAttribute(EmbedAttribute.QUALITY);
            break;
        default:
            isSupported = super.setAttribute(attribute);
        }

        return isSupported;
    }

    @Override
    protected Map<String, ICharacterTag> doTranscode(Collection<ITag> tags, Collection<ICompilerProblem> problems) {
        byte[] bytes = getDataBytes(problems);
        if (bytes == null)
            return null;

        byte[] jpegBytes;
        byte[] compressedAlphaData;
        try {
            ImageInfo imageInfo = getImageInfo(bytes, problems);

            int imageSize = imageInfo.width * imageInfo.height;
            byte[] alphaData = new byte[imageSize];
            int[] pixels = (int[]) imageInfo.pixelGrabber.getPixels();
            for (int i = 0; i < imageSize; ++i) {
                alphaData[i] = (byte) ((pixels[i] >> 24) & 0xff);
            }
            // need to compress the alpha data
            compressedAlphaData = deflate(alphaData);

            jpegBytes = bufferedImageToJPEG(imageInfo, pixels);
        } catch (Throwable t) {
            problems.add(new EmbedExceptionWhileTranscodingProblem(t));
            return null;
        }

        DefineBitsTag image = buildImage(jpegBytes, compressedAlphaData);
        if (image == null)
            return null;

        Map<String, ICharacterTag> symbolTags = Collections.singletonMap(data.getQName(), (ICharacterTag) image);
        return symbolTags;
    }

    private byte[] bufferedImageToJPEG(ImageInfo imageInfo, int[] pixels) throws Exception {
        BufferedImage bufferedImage = new BufferedImage(imageInfo.width, imageInfo.height,
                BufferedImage.TYPE_INT_ARGB);
        bufferedImage.setRGB(0, 0, imageInfo.width, imageInfo.height, pixels, 0, imageInfo.width);

        ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
        ImageWriteParam writeParam = writer.getDefaultWriteParam();
        ColorModel colorModel = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
        ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(colorModel,
                colorModel.createCompatibleSampleModel(1, 1));
        writeParam.setDestinationType(imageTypeSpecifier);
        writeParam.setSourceBands(new int[] { 0, 1, 2 });
        writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

        float q = 1.0f;
        if (quality != null)
            q = quality.floatValue();
        writeParam.setCompressionQuality(q);

        DAByteArrayOutputStream buffer = new DAByteArrayOutputStream();
        writer.setOutput(new MemoryCacheImageOutputStream(buffer));

        IIOImage ioImage = new IIOImage(bufferedImage, null, null);

        writer.write(null, ioImage, writeParam);
        writer.dispose();

        return buffer.getDirectByteArray();
    }

    private DefineBitsTag buildImage(byte[] imageBytes, byte[] alphaBytes) {
        if (imageBytes == null || alphaBytes == null)
            return null;

        DefineBitsJPEG3Tag tag = new DefineBitsJPEG3Tag();
        tag.setImageData(imageBytes);
        tag.setBitmapAlphaData(alphaBytes);
        tag.setAlphaDataOffset(imageBytes.length);
        return tag;
    }

    public static byte[] deflate(byte[] buf) throws IOException {
        Deflater deflater = new Deflater(Deflater.BEST_SPEED);
        DAByteArrayOutputStream out = new DAByteArrayOutputStream();
        DeflaterOutputStream deflaterStream = new DeflaterOutputStream(out, deflater);
        try {
            deflaterStream.write(buf, 0, buf.length);
            deflaterStream.finish();
            deflater.end();
        } finally {
            IOUtils.closeQuietly(deflaterStream);
        }
        return out.getDirectByteArray();
    }

    @Override
    public boolean equals(Object o) {
        if (!super.equals(o))
            return false;

        if (!(o instanceof JPEGTranscoder))
            return false;

        JPEGTranscoder t = (JPEGTranscoder) o;
        if ((compression != t.compression) || !quality.equals(t.quality)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int hashCode = super.hashCode();

        hashCode += (compression ? 1 : 0);

        if (quality != null)
            hashCode ^= quality.hashCode();

        return hashCode;
    }
}