Skip to content

Technical Documentation

Daniel Senff edited this page Nov 19, 2021 · 3 revisions

Third party stuff

DDSUtil utilizes these main functions

  • DDSImage-Class from Java OpenGL-project (JOGL)
  • JSquish, an Implementation of the DXT-compression algorithm based on the Library Squish by Simon Brown

They include the main functions and what I do basically is combine them into one workflow.
This is very low-level, pushing bytes and buffers.

To use the read data in Java, I added ways, to get some higher level objects, mostly BufferedImages, which then can be easily handled by any Java-Application.

Scenarios

So you can break down for basic scenarios.

  1. You have an BufferedImage and want to save to DDS
  2. You have a Pixelstream and want to save to DDS
  3. You have a DDS and want to display it as BufferedImage
  4. you have a pixelstream and want to display as BufferedImage

The last one is basically a by-product. Actually a necessity for the rest, but still one of the harder parts actually, because BufferedImages can come in all kinds of flavors and to either reduce them to one flavor (for example RGBA) or to support all of them is not an easy thing.

Apart from the simple reading and writing scenarios. There is one more thing, which I required specifically for my programs. This is one of the bits I refactored about 3-4 times and I am still not 100% satisfied. DDSFile and DDSImageFile (and other derivates of this classes) are objects to store all important information about the DDS-file in a reduced, yet high-level fashion. You have access to pixelformat, meta-data, Image-data and you don’t have to think about compression or anything. I use this classes in my filelists.

  1. Flavors, conversion, redundancies

One of the tricky parts is to find common ways to convert data.
For example the pixelformat. BufferedImage has an int symbolising the order of colors in the Raster. JSquish has an Enum-class for the CompressionTypes, DDSImage, has int constants defining the pixelformat of the DDS-file. This can be a lot of conversion back and forth to ensure compatibility.

All in all, I tried to use the DDSImage pixelformat constants because they are the most accurate and unique identifiers. The closer you get to the DXTCompressors, you get the choice to input data as pixelformat or as CompressionType.

Basic class descriptions

DDSUtil

Basically, this is supposed to be the public interface for the most basic methods.
decompress(DDS-files) to BufferedImage, compress(BufferedImage) to compressed Buffer and write(File), which not only creates the Buffer, but writes it to the disc.

It has a few left over methods, for which I didn’t know yet, where to put them instead.

DXTCompressor

This are 2 classes for Compressing and Decompressing. Beforehand, I used static methods for this, but I refactored actually I prefered the new way. Not sure if this is the choice with the better performance, though.

You give them a ByteBuffer or a Pixel-Array in the constructor and internally they convert it either to (uncompressed) buffer, which you can get() and continue to work with.

Rescaler

Most of the time I used the regular Image-downscaling methods of Java. I added this interface to add different rescaling algorithms.

DDSFile

This could be described as many things. When I designed my programms, I needed a representational model for the DDSImage. Somethings that stores meta-data, compressiontype and the BufferedImage.
My first try was an extended BufferedImage class, which was slow and memory consuming.
Second try was extending the DDSImage. this didn’t work, because in order to build proper constructors, I had to change the DDSImage-class, which was thirdparty and therefore I didn’t want to change much (I found bugs, so I did changes later, but anyway.)
Last try was building an own model, in different steps. DDSFile is just the meta information, no actual data.
DDSImageFile extends DDSFile and adds management for one 2D surface ie one BufferedImage. Later implementations could be done for CubeMaps and Volumemaps, but before that’d like to get to a stable point with the current features.

TextureMap

AbstractTextureMap is just the abstract class for several implementation. Implementations are the TextureMap (simple 2D-Texture), MipMaps (one top-image and automatically genereated MipMaps, each 1/4 the size of the one above). Future implementations can be Cubemap and Volumemap

This class is actually doing the conversion from giving a ByteBuffer and returning a BufferedImage.
The MipMaps-class also has the ability to generate new MipMaps, the method these are rescaled can be set by defining the Rescaler.
MipMaps can also return the ByteBuffer of all MipMaps generated and compressed.

One could argue, that this class could require performance tests. Whether it’s better to store the uncompressed buffer and only decompress when instructed, or to decompresse the data imidiately, or to store both, which of course would be most memory consuming.

DDSImage (third party)

This is a class from the JOGL-project. I encapsulates everything required for reading and writing the actual file format. So I don’t have to bicker with inputstreams and reading the file header (which, despite open specifications by Microsoft still can be a mess).

DDSImage has no constructor, you get an instance of this object by calling an init-function, which reads the object from a specified file or which constructs a DDSImage-object from ByteBuffer, image dimensions and pixelformat.
On the other hand, this object has a write()-method to write itself back to disc.

So writing to disc means, you initialize a new DDSImage from your data, and this can be written to disc. Afterwards it can be discarded.

I tried using this class, or an extended form of this class instead of the DDSFile and DDSImageFile-clases. but it wasn’t handy, because of it’s unique structure, it didn’t allow me to do everything I wanted. It have been nice to get rid of this extra layer of abstraction, but it was better to keep it seperate as it is now, than to work around it in a contrieved fashion.

ImageOperations

I guess this is my oldest class, which combined all static methods for handling pixel-arrays. It used to have methods for certain BufferedImages-functions, like convertBufferedImagetoArray, but I excluded those into BIUtil and MipMapsUtil.

JSquish (third party)

This one is quite simple. Thanks to the folks at JavaGaming.
It’s a static library. Squish.compress(data) receives the uncompressed databuffer and you get the compressed. Same way around for decompression.
JSquish only works with byte-arrays, so I did some capsulation to also utilize ByteBuffers.

Unsupported features

I guess there is a lot to do with handling more kinds of BufferedImages, just on a sidenote.

The biggest improvement could be with DDS-compatibilty. At the moment the supported functions are rather small.
Only 2D-Textures(one surface with MipMaps) and DXTn-compressed work properly.

Not supported are Cubemaps (6 surfaces with MipMaps) and Volumemap (n surfaces with MipMaps).
Also other CompressionTypes are not supported. Uncompressed is no problem, but Ati-compression for example can not be read.

Planned features

Some things I planed for later versions are of course support for Cube- and Volumemaps.

Other than that there are some native Java-things I’d like to add. Using the native ImageReader-Interfaces for example.
Also I’d like to add a hook in the process to calculate progress for example for JProgressbars.

Clone this wiki locally