/// <summary> /// Constructs a targa image from a targa image and raw data /// </summary> private Targa(TargaHeader header, PfimConfig config, byte[] data, int dataLen) { _config = config; Header = header; Data = data; DataLen = dataLen; }
/// <summary>Fills data starting from the bottom left</summary> public byte[] BottomLeft(Stream str, TargaHeader header, PfimConfig config) { var stride = Util.Stride(header.Width, header.PixelDepthBits); var len = header.Height * stride; var data = config.Allocator.Rent(len); var trueStride = header.PixelDepthBytes * header.Width; InnerBottomLeft(str, config, data, len, stride, trueStride); return(data); }
private static Targa DecodeTarga(Stream str, PfimConfig config, TargaHeader header) { var targa = (header.IsCompressed) ? (IDecodeTarga)(new CompressedTarga()) : new UncompressedTarga(); byte[] data; switch (header.Orientation) { case TargaHeader.TargaOrientation.BottomLeft: data = targa.BottomLeft(str, header, config); break; case TargaHeader.TargaOrientation.BottomRight: data = targa.BottomRight(str, header, config); break; case TargaHeader.TargaOrientation.TopRight: data = targa.TopRight(str, header, config); break; case TargaHeader.TargaOrientation.TopLeft: data = targa.TopLeft(str, header, config); break; default: throw new Exception("Targa orientation not recognized"); } var stride = Util.Stride(header.Width, header.PixelDepthBits); var len = header.Height * stride; var result = new Targa(header, config, data, len); if (config.ApplyColorMap) { result.ApplyColorMap(); } return(result); }
internal static IImage CreateWithPartialHeader(Stream str, PfimConfig config, byte[] magic) { var header = new TargaHeader(str, magic, 4, config); return(DecodeTarga(str, config, header)); }
/// <summary> /// Creates a targa image from a given stream. The type of targa is determined from the /// targa header, which is assumed to be a part of the stream /// </summary> /// <param name="str">Stream to read the targa image from</param> /// <returns>A targa image</returns> public static Targa Create(Stream str, PfimConfig config) { var header = new TargaHeader(str, config); return(DecodeTarga(str, config, header)); }
/// <summary>Fills data starting from the bottom left</summary> public byte[] BottomLeft(Stream str, TargaHeader header, PfimConfig config) { var stride = Util.Stride(header.Width, header.PixelDepthBits); var dataLen = header.Height * stride; var data = config.Allocator.Rent(dataLen); if (str is MemoryStream s && s.TryGetBuffer(out var arr)) { return(FastPass(data, arr, header, stride, s.Position)); } int dataIndex = dataLen - stride; int bytesPerPixel = header.PixelDepthBytes; int fileBufferIndex = 0; // Calculate the maximum number of bytes potentially needed from the buffer. // If our buffer doesn't have enough to decode the maximum number of bytes, // fetch another batch of bytes from the stream. int maxRead = bytesPerPixel * 128 + 1; byte[] filebuffer = config.Allocator.Rent(config.BufferSize); try { int workingSize = str.Read(filebuffer, 0, config.BufferSize); while (dataIndex >= 0) { int colIndex = 0; do { if (config.BufferSize - fileBufferIndex < maxRead && workingSize == config.BufferSize) { workingSize = Util.Translate(str, filebuffer, config.BufferSize, fileBufferIndex); fileBufferIndex = 0; } bool isRunLength = (filebuffer[fileBufferIndex] & 128) != 0; int count = isRunLength ? bytesPerPixel + 1 : filebuffer[fileBufferIndex] + 1; // If the first bit is on, it means that the next packet is run length encoded if (isRunLength) { RunLength(data, filebuffer, dataIndex, fileBufferIndex, bytesPerPixel); dataIndex += (filebuffer[fileBufferIndex] - 127) * bytesPerPixel; colIndex += filebuffer[fileBufferIndex] - 127; fileBufferIndex += count; } else { int bytcount = count * bytesPerPixel; fileBufferIndex++; Buffer.BlockCopy(filebuffer, fileBufferIndex, data, dataIndex, bytcount); fileBufferIndex += bytcount; colIndex += count; dataIndex += bytcount; } } while (colIndex < header.Width); dataIndex -= bytesPerPixel * header.Width + stride; } return(data); } finally { config.Allocator.Return(filebuffer); } }
public byte[] TopRight(Stream str, TargaHeader header, PfimConfig config) { return(BottomLeft(str, header, config)); }
unsafe byte[] FastPass(byte[] data, ArraySegment <byte> arr, TargaHeader header, int stride, long arrPosition) { var dataLen = header.Height * stride; int bytesPerPixel = header.PixelDepthBytes; fixed(byte *startDataPtr = data) fixed(byte *fixedInputPtr = &arr.Array[arrPosition]) { byte *endSentinal = startDataPtr + dataLen; byte *dataPtr = endSentinal - stride; byte *inputPtr = fixedInputPtr; while (dataPtr >= startDataPtr) { int pixelIndex = 0; do { bool isRunLength = (*inputPtr & 128) != 0; if (isRunLength) { var run = *inputPtr++ - 127; if (bytesPerPixel == 3) { RunLength3(dataPtr, run, *inputPtr++, *inputPtr++, *inputPtr++); } else if (bytesPerPixel == 4) { byte color0 = *inputPtr++; byte color1 = *inputPtr++; byte color2 = *inputPtr++; byte color3 = *inputPtr++; var comb = (color0 | color1 << 8 | color2 << 16 | color3 << 24); Util.memset((int *)dataPtr, comb, run * 4); } else if (bytesPerPixel == 1) { Util.memset(dataPtr, *inputPtr++, run); } else if (bytesPerPixel == 2) { byte color0 = *inputPtr++; byte color1 = *inputPtr++; var comb = color0 | color1 << 8 | color0 << 16 | color1 << 24; Util.memset((int *)dataPtr, comb, run * 2); } dataPtr += run * bytesPerPixel; pixelIndex += run; } else { int pixels = *inputPtr++ + 1; int bytes = pixels * bytesPerPixel; Buffer.MemoryCopy(inputPtr, dataPtr, endSentinal - dataPtr, bytes); dataPtr += bytes; pixelIndex += pixels; inputPtr += bytes; } } while (pixelIndex < header.Width); dataPtr -= bytesPerPixel * header.Width + stride; } } return(data); }