Package com.ardor3d.image.util.dds

Source Code of com.ardor3d.image.util.dds.DdsLoader$DdsImageInfo

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.image.util.dds;

import static com.ardor3d.image.util.dds.DdsUtils.flipDXT;
import static com.ardor3d.image.util.dds.DdsUtils.getInt;
import static com.ardor3d.image.util.dds.DdsUtils.isSet;
import static com.ardor3d.image.util.dds.DdsUtils.shiftCount;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.logging.Logger;

import com.ardor3d.image.Image;
import com.ardor3d.image.ImageDataFormat;
import com.ardor3d.image.PixelDataType;
import com.ardor3d.image.util.ImageLoader;
import com.ardor3d.image.util.ImageUtils;
import com.ardor3d.util.LittleEndianDataInput;
import com.ardor3d.util.geom.BufferUtils;
import com.google.common.collect.Lists;

/**
* <p>
* <code>DdsLoader</code> is an image loader that reads in a DirectX DDS file.
* </p>
* Supports 2D images, volume images and cubemaps in the following formats:<br>
* Compressed:<br>
* <ul>
* <li>DXT1A</li>
* <li>DXT3</li>
* <li>DXT5</li>
* <li>LATC</li>
* </ul>
* Uncompressed:<br>
* <ul>
* <li>RGB</li>
* <li>RGBA</li>
* <li>Luminance</li>
* <li>LuminanceAlpha</li>
* <li>Alpha</li>
* </ul>
* Note that Cubemaps must have all 6 faces defined to load properly. FIXME: Needs a software inflater for compressed
* formats in cases where support is not present? Maybe JSquish?
*/
public class DdsLoader implements ImageLoader {
    private static final Logger logger = Logger.getLogger(DdsLoader.class.getName());

    public Image load(final InputStream is, final boolean flipVertically) throws IOException {
        final LittleEndianDataInput in = new LittleEndianDataInput(is);

        // Read and check magic word...
        final int dwMagic = in.readInt();
        if (dwMagic != getInt("DDS ")) {
            throw new Error("Not a dds file.");
        }
        logger.finest("Reading DDS file.");

        // Create our data store;
        final DdsImageInfo info = new DdsImageInfo();

        info.flipVertically = flipVertically;

        // Read standard dds header
        info.header = DdsHeader.read(in);

        // if applicable, read DX10 header
        info.headerDX10 = info.header.ddpf.dwFourCC == getInt("DX10") ? DdsHeaderDX10.read(in) : null;

        // Create our new image
        final Image image = new Image();
        image.setWidth(info.header.dwWidth);
        image.setHeight(info.header.dwHeight);

        // update depth based on flags / header
        updateDepth(image, info);

        // add our format and image data.
        populateImage(image, info, in);

        // return the loaded image
        return image;
    }

    private static final void updateDepth(final Image image, final DdsImageInfo info) {
        if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP)) {
            int depth = 0;
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_POSITIVEX)) {
                depth++;
            }
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_NEGATIVEX)) {
                depth++;
            }
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_POSITIVEY)) {
                depth++;
            }
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_NEGATIVEY)) {
                depth++;
            }
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_POSITIVEZ)) {
                depth++;
            }
            if (isSet(info.header.dwCaps2, DdsHeader.DDSCAPS2_CUBEMAP_NEGATIVEZ)) {
                depth++;
            }

            if (depth != 6) {
                throw new Error("Cubemaps without all faces defined are not currently supported.");
            }

            image.setDepth(depth);
        } else {
            // make sure we have at least depth of 1.
            image.setDepth(info.header.dwDepth > 0 ? info.header.dwDepth : 1);
        }
    }

    private static final void populateImage(final Image image, final DdsImageInfo info, final LittleEndianDataInput in)
            throws IOException {
        final int flags = info.header.ddpf.dwFlags;

        final boolean compressedFormat = isSet(flags, DdsPixelFormat.DDPF_FOURCC);
        final boolean rgb = isSet(flags, DdsPixelFormat.DDPF_RGB);
        final boolean alphaPixels = isSet(flags, DdsPixelFormat.DDPF_ALPHAPIXELS);
        final boolean lum = isSet(flags, DdsPixelFormat.DDPF_LUMINANCE);
        final boolean alpha = isSet(flags, DdsPixelFormat.DDPF_ALPHA);

        if (compressedFormat) {
            final int fourCC = info.header.ddpf.dwFourCC;
            // DXT1 format
            if (fourCC == getInt("DXT1")) {
                info.bpp = 4;
                // if (isSet(flags, DdsPixelFormat.DDPF_ALPHAPIXELS)) {
                // XXX: many authoring tools do not set alphapixels, so we'll error on the side of alpha
                logger.finest("DDS format: DXT1A");
                image.setDataFormat(ImageDataFormat.PrecompressedDXT1A);
                // } else {
                // logger.finest("DDS format: DXT1");
                // image.setDataFormat(ImageDataFormat.PrecompressedDXT1);
                // }
            }

            // DXT3 format
            else if (fourCC == getInt("DXT3")) {
                logger.finest("DDS format: DXT3");
                info.bpp = 8;
                image.setDataFormat(ImageDataFormat.PrecompressedDXT3);
            }

            // DXT5 format
            else if (fourCC == getInt("DXT5")) {
                logger.finest("DDS format: DXT5");
                info.bpp = 8;
                image.setDataFormat(ImageDataFormat.PrecompressedDXT5);
            }

            // DXT10 info present...
            else if (fourCC == getInt("DX10")) {
                switch (info.headerDX10.dxgiFormat) {
                    case DXGI_FORMAT_BC4_UNORM:
                        logger.finest("DXGI format: BC4_UNORM");
                        info.bpp = 4;
                        image.setDataFormat(ImageDataFormat.PrecompressedLATC_L);
                        break;
                    case DXGI_FORMAT_BC5_UNORM:
                        logger.finest("DXGI format: BC5_UNORM");
                        info.bpp = 8;
                        image.setDataFormat(ImageDataFormat.PrecompressedLATC_LA);
                        break;
                    default:
                        throw new Error("dxgiFormat not supported: " + info.headerDX10.dxgiFormat);
                }
            }

            // DXT2 format - unsupported
            else if (fourCC == getInt("DXT2")) {
                logger.finest("DDS format: DXT2");
                throw new Error("DXT2 is not supported.");
            }

            // DXT4 format - unsupported
            else if (fourCC == getInt("DXT4")) {
                logger.finest("DDS format: DXT4");
                throw new Error("DXT4 is not supported.");
            }

            // Unsupported compressed type.
            else {
                throw new Error("unsupported compressed dds format found (" + fourCC + ")");
            }
        }

        // not a compressed format
        else {
            // TODO: more use of bit masks?
            // TODO: Use bit size instead of hardcoded 8 bytes? (need to also implement in readUncompressed)
            image.setDataType(PixelDataType.UnsignedByte);

            info.bpp = info.header.ddpf.dwRGBBitCount;

            // One of the RGB formats?
            if (rgb) {
                if (alphaPixels) {
                    logger.finest("DDS format: uncompressed rgba");
                    image.setDataFormat(ImageDataFormat.RGBA);
                } else {
                    logger.finest("DDS format: uncompressed rgb ");
                    image.setDataFormat(ImageDataFormat.RGB);
                }
            }

            // A luminance or alpha format
            else if (lum || alphaPixels) {
                if (lum && alphaPixels) {
                    logger.finest("DDS format: uncompressed LumAlpha");
                    image.setDataFormat(ImageDataFormat.LuminanceAlpha);
                }

                else if (lum) {
                    logger.finest("DDS format: uncompressed Lum");
                    image.setDataFormat(ImageDataFormat.Luminance);
                }

                else if (alpha) {
                    logger.finest("DDS format: uncompressed Alpha");
                    image.setDataFormat(ImageDataFormat.Alpha);
                }
            } // end luminance/alpha type

            // Unsupported type.
            else {
                throw new Error("unsupported uncompressed dds format found.");
            }
        }

        info.calcMipmapSizes(compressedFormat);
        image.setMipMapByteSizes(info.mipmapByteSizes);

        // Add up total byte size of single depth layer
        int totalSize = 0;
        for (final int size : info.mipmapByteSizes) {
            totalSize += size;
        }

        // Go through and load in image data
        final List<ByteBuffer> imageData = Lists.newArrayList();
        for (int i = 0; i < image.getDepth(); i++) {
            // read in compressed data
            if (compressedFormat) {
                imageData.add(readDXT(in, totalSize, info, image));
            }

            // read in uncompressed data
            else if (rgb || lum || alpha) {
                imageData.add(readUncompressed(in, totalSize, rgb, lum, alpha, alphaPixels, info, image));
            }
        }

        // set on image
        image.setData(imageData);
    }

    static final ByteBuffer readDXT(final LittleEndianDataInput in, final int totalSize, final DdsImageInfo info,
            final Image image) throws IOException {
        int mipWidth = info.header.dwWidth;
        int mipHeight = info.header.dwHeight;

        final ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
        for (int mip = 0; mip < info.header.dwMipMapCount; mip++) {
            final byte[] data = new byte[info.mipmapByteSizes[mip]];
            in.readFully(data);
            if (!info.flipVertically) {
                buffer.put(data);
            } else {
                final byte[] flipped = flipDXT(data, mipWidth, mipHeight, image.getDataFormat());
                buffer.put(flipped);

                mipWidth = Math.max(mipWidth / 2, 1);
                mipHeight = Math.max(mipHeight / 2, 1);
            }
        }
        buffer.rewind();
        return buffer;
    }

    private static ByteBuffer readUncompressed(final LittleEndianDataInput in, final int totalSize,
            final boolean useRgb, final boolean useLum, final boolean useAlpha, final boolean useAlphaPixels,
            final DdsImageInfo info, final Image image) throws IOException {
        final int redLumShift = shiftCount(info.header.ddpf.dwRBitMask);
        final int greenShift = shiftCount(info.header.ddpf.dwGBitMask);
        final int blueShift = shiftCount(info.header.ddpf.dwBBitMask);
        final int alphaShift = shiftCount(info.header.ddpf.dwABitMask);

        final int sourcebytesPP = info.header.ddpf.dwRGBBitCount / 8;
        final int targetBytesPP = ImageUtils.getPixelByteSize(image.getDataFormat(), image.getDataType());

        final ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);

        int mipWidth = info.header.dwWidth;
        int mipHeight = info.header.dwHeight;
        int offset = 0;

        for (int mip = 0; mip < info.header.dwMipMapCount; mip++) {
            for (int y = 0; y < mipHeight; y++) {
                for (int x = 0; x < mipWidth; x++) {
                    final byte[] b = new byte[sourcebytesPP];
                    in.readFully(b);

                    final int i = getInt(b);

                    final byte redLum = (byte) (((i & info.header.ddpf.dwRBitMask) >> redLumShift));
                    final byte green = (byte) (((i & info.header.ddpf.dwGBitMask) >> greenShift));
                    final byte blue = (byte) (((i & info.header.ddpf.dwBBitMask) >> blueShift));
                    final byte alpha = (byte) (((i & info.header.ddpf.dwABitMask) >> alphaShift));

                    if (info.flipVertically) {
                        dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
                    }

                    if (useAlpha) {
                        dataBuffer.put(alpha);
                    } else if (useLum) {
                        if (useAlphaPixels) {
                            dataBuffer.put(redLum).put(alpha);
                        } else {
                            dataBuffer.put(redLum);
                        }
                    } else if (useRgb) {
                        if (useAlphaPixels) {
                            dataBuffer.put(redLum).put(green).put(blue).put(alpha);
                        } else {
                            dataBuffer.put(redLum).put(green).put(blue);
                        }
                    }
                }
            }

            offset += mipWidth * mipHeight * targetBytesPP;

            mipWidth = Math.max(mipWidth / 2, 1);
            mipHeight = Math.max(mipHeight / 2, 1);
        }

        return dataBuffer;
    }

    private final static class DdsImageInfo {
        boolean flipVertically;
        int bpp = 0;
        DdsHeader header;
        DdsHeaderDX10 headerDX10;
        int mipmapByteSizes[];

        void calcMipmapSizes(final boolean compressed) {
            int width = header.dwWidth;
            int height = header.dwHeight;
            int size = 0;

            mipmapByteSizes = new int[header.dwMipMapCount];

            for (int i = 0; i < header.dwMipMapCount; i++) {
                if (compressed) {
                    size = ((width + 3) / 4) * ((height + 3) / 4) * bpp * 2;
                } else {
                    size = width * height * bpp / 8;
                }

                mipmapByteSizes[i] = ((size + 3) / 4) * 4;

                width = Math.max(width / 2, 1);
                height = Math.max(height / 2, 1);
            }
        }
    }
}
TOP

Related Classes of com.ardor3d.image.util.dds.DdsLoader$DdsImageInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.