private void _unpackDXTColor(PixelFormat pf, DXTColorBlock block, ColorEx[] pCol) { // Note - we assume all values have already been endian swapped // Colour lookup table var derivedColours = new ColorEx[4]; using (var src = BufferBase.Wrap(block.colour_0, 1)) { derivedColours[0] = PixelConverter.UnpackColor(PixelFormat.R5G6B5, src); } using (var src = BufferBase.Wrap(block.colour_1, 1)) { derivedColours[1] = PixelConverter.UnpackColor(PixelFormat.R5G6B5, src); } if (pf == PixelFormat.DXT1 && block.colour_0 <= block.colour_1) { // 1-bit alpha // one intermediate colour, half way between the other two derivedColours[2] = (derivedColours[0] + derivedColours[1]) / 2; // transparent colour derivedColours[3] = new ColorEx(0, 0, 0, 0); } else { // first interpolated colour, 1/3 of the way along derivedColours[2] = (derivedColours[0] * 2 + derivedColours[1]) / 3; // second interpolated colour, 2/3 of the way along derivedColours[3] = (derivedColours[0] + derivedColours[1] * 2) / 3; } // Process 4x4 block of texels for (var row = 0; row < 4; ++row) { for (var x = 0; x < 4; ++x) { // LSB come first var colIdx = (byte)block.indexRow[row] >> (x * 2) & 0x3; if (pf == PixelFormat.DXT1) { // Overwrite entire colour pCol[(row * 4) + x] = derivedColours[colIdx]; } else { // alpha has already been read (alpha precedes colour) var col = pCol[(row * 4) + x]; col.r = derivedColours[colIdx].r; col.g = derivedColours[colIdx].g; col.b = derivedColours[colIdx].b; } } } }
public byte[] indexRow; // length = 4 internal static DXTColorBlock Read(BinaryReader br) { var cBlock = new DXTColorBlock(); cBlock.colour_0 = br.ReadUInt16(); cBlock.colour_1 = br.ReadUInt16(); cBlock.indexRow = br.ReadBytes(4); return(cBlock); }
private void _unpackDXTColor( PixelFormat pf, DXTColorBlock block, ColorEx[] pCol ) { // Note - we assume all values have already been endian swapped // Colour lookup table var derivedColours = new ColorEx[4]; using ( var src = BufferBase.Wrap( block.colour_0, 1 ) ) { derivedColours[ 0 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, src ); } using ( var src = BufferBase.Wrap( block.colour_1, 1 ) ) { derivedColours[ 1 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, src ); } if ( pf == PixelFormat.DXT1 && block.colour_0 <= block.colour_1 ) { // 1-bit alpha // one intermediate colour, half way between the other two derivedColours[ 2 ] = ( derivedColours[ 0 ] + derivedColours[ 1 ] )/2; // transparent colour derivedColours[ 3 ] = new ColorEx( 0, 0, 0, 0 ); } else { // first interpolated colour, 1/3 of the way along derivedColours[ 2 ] = ( derivedColours[ 0 ]*2 + derivedColours[ 1 ] )/3; // second interpolated colour, 2/3 of the way along derivedColours[ 3 ] = ( derivedColours[ 0 ] + derivedColours[ 1 ]*2 )/3; } // Process 4x4 block of texels for ( var row = 0; row < 4; ++row ) { for ( var x = 0; x < 4; ++x ) { // LSB come first var colIdx = (byte)block.indexRow[ row ] >> ( x*2 ) & 0x3; if ( pf == PixelFormat.DXT1 ) { // Overwrite entire colour pCol[ ( row*4 ) + x ] = derivedColours[ colIdx ]; } else { // alpha has already been read (alpha precedes colour) var col = pCol[ ( row*4 ) + x ]; col.r = derivedColours[ colIdx ].r; col.g = derivedColours[ colIdx ].g; col.b = derivedColours[ colIdx ].b; } } } }
public byte[] indexRow; // length = 4 internal static DXTColorBlock Read( BinaryReader br ) { var cBlock = new DXTColorBlock(); cBlock.colour_0 = br.ReadUInt16(); cBlock.colour_1 = br.ReadUInt16(); cBlock.indexRow = br.ReadBytes( 4 ); return cBlock; }
public override Codec.DecodeResult Decode(Stream input) { using (var br = new BinaryReader(input)) { // Read 4 character code var fileType = br.ReadInt32(); using (var wrap = BufferBase.Wrap(fileType, 2)) { _flipEndian(wrap, sizeof(uint), 1); } if (FOURCC('D', 'D', 'S', ' ') != fileType) { throw new AxiomException("This is not a DDS file!"); } // Read header in full var header = DDSHeader.Read(br); // Endian flip if required, all 32-bit values using (var wrap = BufferBase.Wrap(header, Memory.SizeOf(typeof(DDSHeader)))) { _flipEndian(wrap, 4, Memory.SizeOf(typeof(DDSHeader)) / 4); } // Check some sizes if (header.size != DDS_HEADER_SIZE) { throw new AxiomException("DDS header size mismatch!"); } if (header.pixelFormat.size != DDS_PIXELFORMAT_SIZE) { throw new AxiomException("DDS header size mismatch!"); } var imgData = new ImageData(); imgData.depth = 1; // (deal with volume later) imgData.width = header.width; imgData.height = header.height; var numFaces = 1; // assume one face until we know otherwise if ((header.caps.caps1 & DDSCAPS_MIPMAP) != 0) { imgData.numMipMaps = header.mipMapCount - 1; } else { imgData.numMipMaps = 0; } imgData.flags = 0; var decompressDXT = false; // Figure out basic image type if ((header.caps.caps2 & DDSCAPS2_CUBEMAP) != 0) { imgData.flags |= ImageFlags.CubeMap; numFaces = 6; } else if ((header.caps.caps2 & DDSCAPS2_VOLUME) != 0) { imgData.flags |= ImageFlags.Volume; imgData.depth = header.depth; } // Pixel format var sourceFormat = PixelFormat.Unknown; if ((header.pixelFormat.flags & DDPF_FOURCC) != 0) { sourceFormat = _convertFourCCFormat(header.pixelFormat.fourCC); } else { sourceFormat = _convertPixelFormat(header.pixelFormat.rgbBits, header.pixelFormat.redMask, header.pixelFormat.greenMask, header.pixelFormat.blueMask, (header.pixelFormat.flags & DDPF_ALPHAPIXELS) != 0 ? header.pixelFormat.alphaMask : 0); } if (PixelUtil.IsCompressed(sourceFormat)) { if (!Root.Instance.RenderSystem.Capabilities.HasCapability(Capabilities.TextureCompressionDXT)) { // We'll need to decompress decompressDXT = true; // Convert format switch (sourceFormat) { case PixelFormat.DXT1: // source can be either 565 or 5551 depending on whether alpha present // unfortunately you have to read a block to figure out which // Note that we upgrade to 32-bit pixel formats here, even // though the source is 16-bit; this is because the interpolated // values will benefit from the 32-bit results, and the source // from which the 16-bit samples are calculated may have been // 32-bit so can benefit from this. var block = DXTColorBlock.Read(br); using (var wrap = BufferBase.Wrap(block.colour_0, sizeof(ushort))) { _flipEndian(wrap, sizeof(ushort), 1); } using (var wrap = BufferBase.Wrap(block.colour_1, sizeof(ushort))) { _flipEndian(wrap, sizeof(ushort), 1); } // skip back since we'll need to read this again br.BaseStream.Seek(0 - (long)Memory.SizeOf(typeof(DXTColorBlock)), SeekOrigin.Current); // colour_0 <= colour_1 means transparency in DXT1 if (block.colour_0 <= block.colour_1) { imgData.format = PixelFormat.BYTE_RGBA; } else { imgData.format = PixelFormat.BYTE_RGB; } break; case PixelFormat.DXT2: case PixelFormat.DXT3: case PixelFormat.DXT4: case PixelFormat.DXT5: // full alpha present, formats vary only in encoding imgData.format = PixelFormat.BYTE_RGBA; break; default: // all other cases need no special format handling break; } } else { // Use original format imgData.format = sourceFormat; // Keep DXT data compressed imgData.flags |= ImageFlags.Compressed; } } else // not compressed { // Don't test against DDPF_RGB since greyscale DDS doesn't set this // just derive any other kind of format imgData.format = sourceFormat; } // Calculate total size from number of mipmaps, faces and size imgData.size = Image.CalculateSize(imgData.numMipMaps, numFaces, imgData.width, imgData.height, imgData.depth, imgData.format); // Now deal with the data var dest = new byte[imgData.size]; var destBuffer = BufferBase.Wrap(dest); // all mips for a face, then each face for (var i = 0; i < numFaces; ++i) { var width = imgData.width; var height = imgData.height; var depth = imgData.depth; for (var mip = 0; mip <= imgData.numMipMaps; ++mip) { var dstPitch = width * PixelUtil.GetNumElemBytes(imgData.format); if (PixelUtil.IsCompressed(sourceFormat)) { // Compressed data if (decompressDXT) { DXTColorBlock col; DXTInterpolatedAlphaBlock iAlpha; DXTExplicitAlphaBlock eAlpha; // 4x4 block of decompressed colour var tempColours = new ColorEx[16]; var destBpp = PixelUtil.GetNumElemBytes(imgData.format); var sx = Utility.Min(width, 4); var sy = Utility.Min(height, 4); var destPitchMinus4 = dstPitch - destBpp * sx; // slices are done individually for (var z = 0; z < depth; ++z) { // 4x4 blocks in x/y for (var y = 0; y < height; y += 4) { for (var x = 0; x < width; x += 4) { if (sourceFormat == PixelFormat.DXT2 || sourceFormat == PixelFormat.DXT3) { // explicit alpha eAlpha = DXTExplicitAlphaBlock.Read(br); using (var wrap = BufferBase.Wrap(eAlpha.alphaRow, eAlpha.alphaRow.Length * sizeof(ushort))) { _flipEndian(wrap, sizeof(ushort), 4); } _unpackDXTAlpha(eAlpha, tempColours); } else if (sourceFormat == PixelFormat.DXT4 || sourceFormat == PixelFormat.DXT5) { // interpolated alpha iAlpha = DXTInterpolatedAlphaBlock.Read(br); using (var wrap = BufferBase.Wrap(iAlpha.alpha_0, 1)) { _flipEndian(wrap, sizeof(ushort), 1); } using (var wrap = BufferBase.Wrap(iAlpha.alpha_1, 1)) { _flipEndian(wrap, sizeof(ushort), 1); } _unpackDXTAlpha(iAlpha, tempColours); } // always read colour col = DXTColorBlock.Read(br); using (var wrap = BufferBase.Wrap(col.colour_0, sizeof(ushort))) { _flipEndian(wrap, sizeof(ushort), 1); } using (var wrap = BufferBase.Wrap(col.colour_1, sizeof(ushort))) { _flipEndian(wrap, sizeof(ushort), 1); } _unpackDXTColor(sourceFormat, col, tempColours); // write 4x4 block to uncompressed version for (var by = 0; by < sy; ++by) { for (var bx = 0; bx < sx; ++bx) { PixelConverter.PackColor(tempColours[by * 4 + bx], imgData.format, destBuffer); destBuffer += destBpp; } // advance to next row destBuffer += destPitchMinus4; } // next block. Our dest pointer is 4 lines down // from where it started if (x + 4 >= width) { // Jump back to the start of the line destBuffer += -destPitchMinus4; } else { // Jump back up 4 rows and 4 pixels to the // right to be at the next block to the right destBuffer += -(dstPitch * sy + destBpp * sx); } } } } } else { // load directly // DDS format lies! sizeOrPitch is not always set for DXT!! var dxtSize = PixelUtil.GetMemorySize(width, height, depth, imgData.format); using (var src = BufferBase.Wrap(br.ReadBytes(dxtSize))) { Memory.Copy(src, destBuffer, dxtSize); } destBuffer += dxtSize; } } else { // Final data - trim incoming pitch int srcPitch; if ((header.flags & DDSD_PITCH) != 0) { srcPitch = header.sizeOrPitch / Utility.Max(1, mip * 2); } else { // assume same as final pitch srcPitch = dstPitch; } Contract.Requires(dstPitch <= srcPitch); var srcAdvance = (long)(srcPitch - dstPitch); for (var z = 0; z < imgData.depth; ++z) { for (var y = 0; y < imgData.height; ++y) { using (var src = BufferBase.Wrap(br.ReadBytes(dstPitch))) { Memory.Copy(src, destBuffer, dstPitch); } if (srcAdvance > 0) { br.BaseStream.Seek(srcAdvance, SeekOrigin.Current); } destBuffer += dstPitch; } } } // Next mip if (width != 1) { width /= 2; } if (height != 1) { height /= 2; } if (depth != 1) { depth /= 2; } } } destBuffer.Dispose(); return(new DecodeResult(new MemoryStream(dest), imgData)); } }
/// Unpack DXT colours into array of 16 colour values private void UnpackDXTColour(PixelFormat pf, DXTColorBlock block, ColorEx[] pCol) { // Note - we assume all values have already been endian swapped // Colour lookup table ColorEx[] derivedColours = new ColorEx[4]; IntPtr color0 = Memory.PinObject( block.Color0 ); IntPtr color1 = Memory.PinObject( block.Color1 ); if ( pf == PixelFormat.DXT1 && block.Color0 <= block.Color1 ) { // 1-bit alpha derivedColours[ 0 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, color0 ); derivedColours[ 1 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, color1 ); // one intermediate colour, half way between the other two derivedColours[ 2 ] = ( derivedColours[ 0 ] + derivedColours[ 1 ] ) / 2; // transparent colour derivedColours[ 3 ] = ColorEx.Black; } else { derivedColours[ 0 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, color0 ); derivedColours[ 1 ] = PixelConverter.UnpackColor( PixelFormat.R5G6B5, color1 ); // first interpolated colour, 1/3 of the way along derivedColours[ 2 ] = ( derivedColours[ 0 ] * 2f + derivedColours[ 1 ] ) / 3; // second interpolated colour, 2/3 of the way along derivedColours[ 3 ] = ( derivedColours[ 0 ] + derivedColours[ 1 ] * 2f ) / 3; } Memory.UnpinObject( block.Color0 ); Memory.UnpinObject( block.Color1 ); // Process 4x4 block of texels for ( int row = 0; row < 4; ++row ) { for ( int x = 0; x < 4; ++x ) { // LSB come first int colIdx = ( block.IndexRow[ row ] >> ( x * 2 ) & 0x3 ); if ( pf == PixelFormat.DXT1 ) { // Overwrite entire colour pCol[ ( row * 4 ) + x ] = derivedColours[ colIdx ]; } else { // alpha has already been read (alpha precedes color) ColorEx col = pCol[ ( row * 4 ) + x ]; col.r = derivedColours[ colIdx ].r; col.g = derivedColours[ colIdx ].g; col.b = derivedColours[ colIdx ].b; } } } }