/// <summary> /// Function to persist image data to a stream. /// </summary> /// <param name="imageData"><see cref="GorgonLibrary.Graphics.GorgonImageData">Gorgon image data</see> to persist.</param> /// <param name="stream">Stream that will contain the data.</param> protected internal override void SaveToStream(GorgonImageData imageData, Stream stream) { // Use a binary writer. using (var writer = new GorgonBinaryWriter(stream, true)) { // Write the header for the file. TGAConversionFlags conversionFlags; WriteHeader(imageData.Settings, writer, out conversionFlags); GorgonFormatPitch pitch; if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { pitch = new GorgonFormatPitch(imageData.Settings.Width * 3, imageData.Settings.Width * 3 * imageData.Settings.Height); } else { var formatInfo = GorgonBufferFormatInfo.GetInfo(imageData.Settings.Format); pitch = formatInfo.GetPitch(imageData.Settings.Width, imageData.Settings.Height, PitchFlags.None); } // Get the pointer to the first mip/array/depth level. var srcPointer = (byte *)imageData.Buffers[0].Data.UnsafePointer; var srcPitch = imageData.Buffers[0].PitchInformation; // If the two pitches are equal, then just write out the buffer. if ((pitch == srcPitch) && (conversionFlags == TGAConversionFlags.None)) { writer.Write(srcPointer, srcPitch.SlicePitch); return; } // If we have to do a conversion, create a worker buffer. using (var convertBuffer = new GorgonDataStream(pitch.SlicePitch)) { var destPtr = (byte *)convertBuffer.UnsafePointer; // Write out each scan line. for (int y = 0; y < imageData.Settings.Height; y++) { if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { Compress24BPPScanLine(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch); } else if ((conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle) { SwizzleScanline(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch, imageData.Settings.Format, ImageBitFlags.None); } else { CopyScanline(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch, imageData.Settings.Format, ImageBitFlags.None); } destPtr += pitch.RowPitch; srcPointer += srcPitch.RowPitch; } // Persist to the stream. writer.Write(convertBuffer.UnsafePointer, pitch.SlicePitch); } } }
/// <summary> /// Function to perform the copying of image data into the buffer. /// </summary> /// <param name="stream">Stream containing the image data.</param> /// <param name="image">Image data.</param> /// <param name="conversionFlags">Flags used to convert the image.</param> private void CopyImageData(GorgonDataStream stream, GorgonImageData image, TGAConversionFlags conversionFlags) { var buffer = image.Buffers[0]; // Get the first buffer only. var formatInfo = GorgonBufferFormatInfo.GetInfo(image.Settings.Format); GorgonFormatPitch srcPitch = (conversionFlags & TGAConversionFlags.Expand) == TGAConversionFlags.Expand ? new GorgonFormatPitch(image.Settings.Width * 3, image.Settings.Width * 3 * image.Settings.Height) : formatInfo.GetPitch(image.Settings.Width, image.Settings.Height, PitchFlags.None); // Otherwise, allocate a buffer for conversion. var srcPtr = (byte *)stream.PositionPointerUnsafe; var destPtr = (byte *)buffer.Data.UnsafePointer; // Adjust destination for inverted axes. if ((conversionFlags & TGAConversionFlags.InvertX) == TGAConversionFlags.InvertX) { destPtr += buffer.PitchInformation.RowPitch - formatInfo.SizeInBytes; } if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY) { destPtr += (image.Settings.Height - 1) * buffer.PitchInformation.RowPitch; } // Get bounds of image memory. var scanSize = (int)(stream.Length - stream.Position); byte *endScan = (byte *)stream.PositionPointerUnsafe + scanSize; int opaqueLineCount = 0; for (int y = 0; y < image.Settings.Height; y++) { bool setOpaque; if ((conversionFlags & TGAConversionFlags.RLE) == TGAConversionFlags.RLE) { setOpaque = ReadCompressed(ref srcPtr, image.Settings.Width, destPtr, endScan, image.Settings.Format, conversionFlags); } else { setOpaque = ReadUncompressed(srcPtr, srcPitch.RowPitch, destPtr, image.Settings.Format, conversionFlags); srcPtr += srcPitch.RowPitch; } if ((setOpaque) && (SetOpaqueIfZeroAlpha)) { opaqueLineCount++; } if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY) { destPtr -= buffer.PitchInformation.RowPitch; } else { destPtr += buffer.PitchInformation.RowPitch; } } if (opaqueLineCount != image.Settings.Height) { return; } // Set the alpha to opaque if we don't have any alpha values (i.e. alpha = 0 for all pixels). destPtr = (byte *)buffer.Data.UnsafePointer; for (int y = 0; y < image.Settings.Height; y++) { CopyScanline(destPtr, buffer.PitchInformation.RowPitch, destPtr, buffer.PitchInformation.RowPitch, image.Settings.Format, ImageBitFlags.OpaqueAlpha); destPtr += buffer.PitchInformation.RowPitch; } }