40package com.jogamp.opengl.util.texture.spi;
42import java.io.BufferedInputStream;
44import java.io.FileInputStream;
45import java.io.FileOutputStream;
46import java.io.IOException;
47import java.io.InputStream;
48import java.io.PrintStream;
49import java.nio.ByteBuffer;
50import java.nio.ByteOrder;
51import java.nio.channels.FileChannel;
53import com.jogamp.opengl.GL;
54import com.jogamp.common.nio.Buffers;
55import com.jogamp.common.util.IOUtil;
56import com.jogamp.opengl.util.texture.ImageType;
70 private final ByteBuffer data;
71 private final int width;
72 private final int height;
73 private final boolean isCompressed;
74 private final int compressionFormat;
76 public ImageInfo(
final ByteBuffer data,
final int width,
final int height,
final boolean compressed,
final int compressionFormat) {
77 this.data = data; this.width = width; this.height = height;
78 this.isCompressed = compressed; this.compressionFormat = compressionFormat;
82 public ByteBuffer
getData() {
return data; }
86 throw new RuntimeException(
"Should not call unless compressed");
87 return compressionFormat;
91 private FileInputStream fis;
92 private FileChannel chan;
93 private ByteBuffer buf;
94 private Header header;
166 public static DDSImage read(
final String filename)
throws IOException {
167 return read(
new File(filename));
179 image.readFromFile(file);
192 image.readFromBuffer(buf);
210 }
catch (
final IOException e) {
234 final ByteBuffer[] mipmapData)
throws IllegalArgumentException {
236 image.initFromData(d3dFormat, width, height, mipmapData);
245 public void write(
final String filename)
throws IOException {
246 write(
new File(filename));
254 public void write(
final File file)
throws IOException {
255 final FileOutputStream stream = IOUtil.getFileOutputStream(file,
true);
256 final FileChannel chan = stream.getChannel();
259 final ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize());
260 hdr.order(ByteOrder.LITTLE_ENDIAN);
264 buf.position(Header.writtenSize());
276 return ((header.flags & flag) != 0);
281 return ((header.pfFlags & flag) != 0);
293 header.pfRBitMask == 0x00FF0000 &&
294 header.pfGBitMask == 0x0000FF00 &&
295 header.pfBBitMask == 0x000000FF &&
296 header.pfABitMask == 0xFF000000) {
301 header.pfRBitMask == 0x00FF0000 &&
302 header.pfGBitMask == 0x0000FF00 &&
303 header.pfBBitMask == 0x000000FF) {
306 header.pfRBitMask == 0x00FF0000 &&
307 header.pfGBitMask == 0x0000FF00 &&
308 header.pfBBitMask == 0x000000FF) {
331 return isCubemap() && (header.ddsCaps2 & side) != 0;
342 return header.pfFourCC;
354 return header.height;
360 return header.pfRGBBitCount;
368 return header.mipMapCountOrAux;
387 throw new RuntimeException(
"Illegal side for 2D texture: " + side );
390 throw new RuntimeException(
"Illegal side, side not present: " + side );
394 throw new RuntimeException(
"Illegal mipmap number " + map +
" (0.." + (
getNumMipMaps() - 1) +
")");
398 int seek = Header.writtenSize();
400 seek += sideShiftInBytes(side);
402 for (
int i = 0; i < map; i++) {
403 seek += mipMapSizeInBytes(i);
405 buf.limit(seek + mipMapSizeInBytes(map));
407 final ByteBuffer next = buf.slice();
409 buf.limit(buf.capacity());
429 if (numLevels == 0) {
433 for (
int i = 0; i < numLevels; i++) {
445 final StringBuilder buf =
new StringBuilder();
446 for (
int i = 0; i < 4; i++) {
447 final char c = (char) (compressionFormat & 0xFF);
449 compressionFormat = compressionFormat >> 8;
451 return buf.toString();
466 final int openGLInternalFormat) {
467 int size = width * height;
468 switch (openGLInternalFormat) {
479 throw new IllegalArgumentException(
"Illegal OpenGL texture internal format " +
480 openGLInternalFormat);
484 return Buffers.newDirectByteBuffer(size);
488 final PrintStream tty = System.err;
493 tty.println(
"Compression format: 0x" + Integer.toHexString(fmt) +
" (" + name +
")");
495 tty.println(
"Width: " + header.width +
" Height: " + header.height);
496 tty.println(
"header.pitchOrLinearSize: " + header.pitchOrLinearSize);
497 tty.println(
"header.pfRBitMask: 0x" + Integer.toHexString(header.pfRBitMask));
498 tty.println(
"header.pfGBitMask: 0x" + Integer.toHexString(header.pfGBitMask));
499 tty.println(
"header.pfBBitMask: 0x" + Integer.toHexString(header.pfBBitMask));
500 tty.println(
"SurfaceDesc flags:");
501 boolean recognizedAny =
false;
502 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_CAPS,
"DDSD_CAPS");
503 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_HEIGHT,
"DDSD_HEIGHT");
504 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_WIDTH,
"DDSD_WIDTH");
505 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_PITCH,
"DDSD_PITCH");
506 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_BACKBUFFERCOUNT,
"DDSD_BACKBUFFERCOUNT");
507 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_ZBUFFERBITDEPTH,
"DDSD_ZBUFFERBITDEPTH");
508 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_ALPHABITDEPTH,
"DDSD_ALPHABITDEPTH");
509 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_LPSURFACE,
"DDSD_LPSURFACE");
510 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_PIXELFORMAT,
"DDSD_PIXELFORMAT");
511 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_MIPMAPCOUNT,
"DDSD_MIPMAPCOUNT");
512 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_LINEARSIZE,
"DDSD_LINEARSIZE");
513 recognizedAny |= printIfRecognized(tty, header.flags,
DDSD_DEPTH,
"DDSD_DEPTH");
514 if (!recognizedAny) {
515 tty.println(
"(none)");
517 tty.println(
"Raw SurfaceDesc flags: 0x" + Integer.toHexString(header.flags));
518 tty.println(
"Pixel format flags:");
519 recognizedAny =
false;
520 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_ALPHAPIXELS,
"DDPF_ALPHAPIXELS");
521 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_ALPHA,
"DDPF_ALPHA");
522 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_FOURCC,
"DDPF_FOURCC");
523 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_PALETTEINDEXED4,
"DDPF_PALETTEINDEXED4");
524 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_PALETTEINDEXEDTO8,
"DDPF_PALETTEINDEXEDTO8");
525 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_PALETTEINDEXED8,
"DDPF_PALETTEINDEXED8");
526 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_RGB,
"DDPF_RGB");
527 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_COMPRESSED,
"DDPF_COMPRESSED");
528 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_RGBTOYUV,
"DDPF_RGBTOYUV");
529 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_YUV,
"DDPF_YUV");
530 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_ZBUFFER,
"DDPF_ZBUFFER");
531 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_PALETTEINDEXED1,
"DDPF_PALETTEINDEXED1");
532 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_PALETTEINDEXED2,
"DDPF_PALETTEINDEXED2");
533 recognizedAny |= printIfRecognized(tty, header.pfFlags,
DDPF_ZPIXELS,
"DDPF_ZPIXELS");
534 if (!recognizedAny) {
535 tty.println(
"(none)");
537 tty.println(
"Raw pixel format flags: 0x" + Integer.toHexString(header.pfFlags));
538 tty.println(
"Depth: " +
getDepth());
541 tty.print(
"Pixel format: ");
546 case D3DFMT_DXT1: tty.println(
"D3DFMT_DXT1");
break;
547 case D3DFMT_DXT2: tty.println(
"D3DFMT_DXT2");
break;
548 case D3DFMT_DXT3: tty.println(
"D3DFMT_DXT3");
break;
549 case D3DFMT_DXT4: tty.println(
"D3DFMT_DXT4");
break;
550 case D3DFMT_DXT5: tty.println(
"D3DFMT_DXT5");
break;
552 default: tty.println(
"(unknown pixel format " + fmt +
")");
break;
560 private static final int MAGIC = 0x20534444;
562 static class Header {
567 int pitchOrLinearSize;
568 int backBufferCountOrDepth;
569 int mipMapCountOrAux;
575 int colorSpaceLowValue;
576 int colorSpaceHighValue;
577 int destBltColorSpaceLowValue;
578 int destBltColorSpaceHighValue;
579 int srcOverlayColorSpaceLowValue;
580 int srcOverlayColorSpaceHighValue;
581 int srcBltColorSpaceLowValue;
582 int srcBltColorSpaceHighValue;
598 int ddsCapsReserved1;
599 int ddsCapsReserved2;
602 void read(
final ByteBuffer buf)
throws IOException {
603 final int magic = buf.getInt();
604 if (magic != MAGIC) {
605 throw new IOException(
"Incorrect magic number 0x" +
606 Integer.toHexString(magic) +
607 " (expected " + MAGIC +
")");
611 flags = buf.getInt();
612 height = buf.getInt();
613 width = buf.getInt();
614 pitchOrLinearSize = buf.getInt();
615 backBufferCountOrDepth = buf.getInt();
616 mipMapCountOrAux = buf.getInt();
617 alphaBitDepth = buf.getInt();
618 reserved1 = buf.getInt();
619 surface = buf.getInt();
620 colorSpaceLowValue = buf.getInt();
621 colorSpaceHighValue = buf.getInt();
622 destBltColorSpaceLowValue = buf.getInt();
623 destBltColorSpaceHighValue = buf.getInt();
624 srcOverlayColorSpaceLowValue = buf.getInt();
625 srcOverlayColorSpaceHighValue = buf.getInt();
626 srcBltColorSpaceLowValue = buf.getInt();
627 srcBltColorSpaceHighValue = buf.getInt();
628 pfSize = buf.getInt();
629 pfFlags = buf.getInt();
630 pfFourCC = buf.getInt();
631 pfRGBBitCount = buf.getInt();
632 pfRBitMask = buf.getInt();
633 pfGBitMask = buf.getInt();
634 pfBBitMask = buf.getInt();
635 pfABitMask = buf.getInt();
636 ddsCaps1 = buf.getInt();
637 ddsCaps2 = buf.getInt();
638 ddsCapsReserved1 = buf.getInt();
639 ddsCapsReserved2 = buf.getInt();
640 textureStage = buf.getInt();
644 void write(
final ByteBuffer buf) {
650 buf.putInt(pitchOrLinearSize);
651 buf.putInt(backBufferCountOrDepth);
652 buf.putInt(mipMapCountOrAux);
653 buf.putInt(alphaBitDepth);
654 buf.putInt(reserved1);
656 buf.putInt(colorSpaceLowValue);
657 buf.putInt(colorSpaceHighValue);
658 buf.putInt(destBltColorSpaceLowValue);
659 buf.putInt(destBltColorSpaceHighValue);
660 buf.putInt(srcOverlayColorSpaceLowValue);
661 buf.putInt(srcOverlayColorSpaceHighValue);
662 buf.putInt(srcBltColorSpaceLowValue);
663 buf.putInt(srcBltColorSpaceHighValue);
666 buf.putInt(pfFourCC);
667 buf.putInt(pfRGBBitCount);
668 buf.putInt(pfRBitMask);
669 buf.putInt(pfGBitMask);
670 buf.putInt(pfBBitMask);
671 buf.putInt(pfABitMask);
672 buf.putInt(ddsCaps1);
673 buf.putInt(ddsCaps2);
674 buf.putInt(ddsCapsReserved1);
675 buf.putInt(ddsCapsReserved2);
676 buf.putInt(textureStage);
679 private static int size() {
683 private static int pfSize() {
687 private static int writtenSize() {
695 private void readFromFile(
final File file)
throws IOException {
696 fis =
new FileInputStream(file);
697 chan = fis.getChannel();
698 final ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY,
699 0, (
int) file.length());
703 private void readFromBuffer(
final ByteBuffer buf)
throws IOException {
705 buf.order(ByteOrder.LITTLE_ENDIAN);
706 header =
new Header();
711 private void initFromData(
final int d3dFormat,
714 final ByteBuffer[] mipmapData)
throws IllegalArgumentException {
717 int topmostMipmapSize = width * height;
718 int pitchOrLinearSize = width;
721 case D3DFMT_R8G8B8: topmostMipmapSize *= 3; pitchOrLinearSize *= 3;
break;
722 case D3DFMT_A8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4;
break;
723 case D3DFMT_X8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4;
break;
729 topmostMipmapSize = computeCompressedBlockSize(width, height, 1, d3dFormat);
730 pitchOrLinearSize = topmostMipmapSize;
734 throw new IllegalArgumentException(
"d3dFormat must be one of the known formats");
738 int curSize = topmostMipmapSize;
739 int mipmapWidth = width;
740 int mipmapHeight = height;
742 for (
int i = 0; i < mipmapData.length; i++) {
743 if (mipmapData[i].remaining() != curSize) {
744 throw new IllegalArgumentException(
"Mipmap level " + i +
745 " didn't match expected data size (expected " + curSize +
", got " +
746 mipmapData[i].remaining() +
")");
749 if (mipmapWidth > 1) mipmapWidth /= 2;
750 if (mipmapHeight > 1) mipmapHeight /= 2;
751 curSize = computeBlockSize(mipmapWidth, mipmapHeight, 1, d3dFormat);
752 totalSize += mipmapData[i].remaining();
756 totalSize += Header.writtenSize();
757 final ByteBuffer buf = ByteBuffer.allocate(totalSize);
758 buf.position(Header.writtenSize());
759 for (
int i = 0; i < mipmapData.length; i++) {
760 buf.put(mipmapData[i]);
765 header =
new Header();
766 header.size = Header.size();
768 if (mipmapData.length > 1) {
770 header.mipMapCountOrAux = mipmapData.length;
772 header.width = width;
773 header.height = height;
777 header.pfFourCC = d3dFormat;
787 header.pfRBitMask = 0x00FF0000;
788 header.pfGBitMask = 0x0000FF00;
789 header.pfBBitMask = 0x000000FF;
791 header.pfABitMask = 0xFF000000;
794 header.pitchOrLinearSize = pitchOrLinearSize;
795 header.pfSize = Header.pfSize();
805 private void fixupHeader() {
808 int depth = header.backBufferCountOrDepth;
818 private static int computeCompressedBlockSize(
final int width,
821 final int compressionFormat) {
822 int blockSize = ((width + 3)/4) * ((height + 3)/4) * ((depth + 3)/4);
823 switch (compressionFormat) {
825 default: blockSize *= 16;
break;
830 private static int computeBlockSize(
final int width,
833 final int pixelFormat) {
835 switch (pixelFormat) {
837 blocksize = width*height*3;
841 blocksize = width*height*4;
848 blocksize = computeCompressedBlockSize(width, height, 1, pixelFormat);
851 throw new IllegalArgumentException(
"d3dFormat must be one of the known formats");
856 private int mipMapWidth(
final int map) {
858 for (
int i = 0; i < map; i++) {
861 return Math.max(width, 1);
864 private int mipMapHeight(
final int map) {
866 for (
int i = 0; i < map; i++) {
869 return Math.max(height, 1);
872 private int mipMapSizeInBytes(
final int map) {
873 final int width = mipMapWidth(map);
874 final int height = mipMapHeight(map);
877 return ((width+3)/4)*((height+3)/4)*blockSize;
879 return width * height * (
getDepth() / 8);
883 private int sideSizeInBytes() {
885 if (numLevels == 0) {
890 for (
int i = 0; i < numLevels; i++) {
891 size += mipMapSizeInBytes(i);
897 private int sideShiftInBytes(
final int side) {
898 final int[] sides = {
908 final int sideSize = sideSizeInBytes();
909 for (
int i = 0; i < sides.length; i++) {
910 final int temp = sides[i];
911 if ((temp & side) != 0) {
918 throw new RuntimeException(
"Illegal side: " + side);
921 private boolean printIfRecognized(
final PrintStream tty,
final int flags,
final int flag,
final String what) {
922 if ((flags & flag) != 0) {
Simple class describing images and data; does not encapsulate image format information.
ImageInfo(final ByteBuffer data, final int width, final int height, final boolean compressed, final int compressionFormat)
int getCompressionFormat()
A reader and writer for DirectDraw Surface (.dds) files, which are used to describe textures.
static final int DDSD_BACKBUFFERCOUNT
static final int DDSCAPS2_CUBEMAP
static final int DDPF_RGB
static final int DDPF_FOURCC
static final int DDPF_ALPHA
int getCompressionFormat()
If this surface is compressed, returns the kind of compression used (DXT1..DXT5).
static final int DDSD_LPSURFACE
static final int D3DFMT_DXT4
static final int DDSCAPS_MIPMAP
void write(final String filename)
Writes this DDSImage to the specified file name.
boolean isSurfaceDescFlagSet(final int flag)
Test for presence/absence of surface description flags (DDSD_*)
ImageInfo getMipMap(final int side, final int map)
Gets the ith mipmap data (0..getNumMipMaps() - 1)
static final int DDPF_ZBUFFER
static final int D3DFMT_UNKNOWN
static final int DDSCAPS2_CUBEMAP_NEGATIVEY
static final int DDSD_ALPHABITDEPTH
static final int DDSCAPS2_CUBEMAP_POSITIVEY
static final int DDSCAPS_COMPLEX
static final int DDPF_PALETTEINDEXED8
static final int D3DFMT_DXT1
static final int D3DFMT_A8R8G8B8
int getNumMipMaps()
Number of mip maps in the texture.
boolean isCompressed()
Indicates whether this texture is compressed.
static final int D3DFMT_DXT3
static final int D3DFMT_R8G8B8
static final int DDSD_HEIGHT
static final int DDPF_PALETTEINDEXED2
static final int DDSCAPS2_CUBEMAP_POSITIVEX
void close()
Closes open files and resources associated with the open DDSImage.
static final int DDSD_LINEARSIZE
static final int DDPF_PALETTEINDEXED1
static ByteBuffer allocateBlankBuffer(final int width, final int height, final int openGLInternalFormat)
Allocates a temporary, empty ByteBuffer suitable for use in a call to glCompressedTexImage2D.
boolean isCubemap()
Indicates whether this texture is cubemap.
static final int DDSD_PITCH
void write(final File file)
Writes this DDSImage to the specified file name.
static final int DDSD_MIPMAPCOUNT
static final int DDSCAPS2_CUBEMAP_POSITIVEZ
static final int DDSD_PIXELFORMAT
static final int DDSD_CAPS
static DDSImage read(final ByteBuffer buf)
Reads a DirectDraw surface from the specified ByteBuffer, returning the resulting DDSImage.
static final int DDPF_COMPRESSED
static DDSImage read(final String filename)
Reads a DirectDraw surface from the specified file name, returning the resulting DDSImage.
int getWidth()
Width of the texture (or the top-most mipmap if mipmaps are present)
int getPixelFormat()
Gets the pixel format of this texture (D3DFMT_*) based on some heuristics.
boolean isPixelFormatFlagSet(final int flag)
Test for presence/absence of pixel format flags (DDPF_*)
int getDepth()
Total number of bits per pixel.
static final int DDSCAPS_TEXTURE
static final int DDPF_ZPIXELS
static final int DDSCAPS2_CUBEMAP_NEGATIVEZ
static DDSImage createFromData(final int d3dFormat, final int width, final int height, final ByteBuffer[] mipmapData)
Creates a new DDSImage from data supplied by the user.
static final int D3DFMT_X8R8G8B8
ImageInfo[] getAllMipMaps(final int side)
Returns an array of ImageInfos corresponding to all mipmap levels of this DDS file.
static final int DDPF_PALETTEINDEXEDTO8
static final int DDSCAPS2_CUBEMAP_NEGATIVEX
boolean isCubemapSidePresent(final int side)
Indicates whethe this cubemap side present.
static final int DDSD_ZBUFFERBITDEPTH
static final int D3DFMT_DXT5
static final int DDPF_YUV
ImageInfo getMipMap(final int map)
Gets the ith mipmap data (0..getNumMipMaps() - 1)
static final int DDSD_WIDTH
static DDSImage read(final File file)
Reads a DirectDraw surface from the specified file, returning the resulting DDSImage.
static final int DDPF_ALPHAPIXELS
int getHeight()
Height of the texture (or the top-most mipmap if mipmaps are present)
ImageInfo[] getAllMipMaps()
Returns an array of ImageInfos corresponding to all mipmap levels of this DDS file.
static final int D3DFMT_DXT2
static final int DDPF_RGBTOYUV
static String getCompressionFormatName(int compressionFormat)
Converts e.g.
static final int DDSD_DEPTH
static final int DDPF_PALETTEINDEXED4
static final int GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
GL_EXT_texture_compression_s3tc Define "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT" with expression '0x83F3',...
static final int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_EXT_texture_compression_s3tc Define "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT" with expression '0x83F2',...
static final int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
GL_EXT_texture_compression_s3tc, GL_EXT_texture_compression_dxt1 Define "GL_COMPRESSED_RGBA_S3TC_DXT1...
static final int GL_COMPRESSED_RGB_S3TC_DXT1_EXT
GL_EXT_texture_compression_s3tc, GL_EXT_texture_compression_dxt1 Define "GL_COMPRESSED_RGB_S3TC_DXT1_...