private unsafe void buttonConvertNew_Click( object sender, EventArgs _e ) { if ( openFileDialog.ShowDialog( this ) != DialogResult.OK ) return; if ( saveFileDialog.ShowDialog( this ) != DialogResult.OK ) return; FileInfo SourceFileName = new FileInfo( openFileDialog.FileName ); FileInfo TargetFileName = new FileInfo( saveFileDialog.FileName ); int W, H; float3[,] Vectors; float x, y, z; using ( TargaImage TGA = new TargaImage( SourceFileName.FullName, false ) ) { // Convert byte[] ImageContent = Bitmap.LoadBitmap( TGA.Image, out W, out H ); Vectors = new float3[W,H]; int rha = 0; for ( int Y=0; Y < H; Y++ ) for ( int X=0; X < W; X++ ) { x = 2.0f * ImageContent[rha++] / 255 - 1.0f; y = 2.0f * ImageContent[rha++] / 255 - 1.0f; z = 2.0f * ImageContent[rha++] / 255 - 1.0f; rha++; // Skip alpha z = Math.Max( 0.0f, z ); float Norm = 1.0f / (float) Math.Sqrt( x*x + y*y + z*z ); Vectors[X,Y].x = x * Norm; Vectors[X,Y].y = y * Norm; Vectors[X,Y].z = z * Norm; } } // Convert to RG improved normal double Nx, Ny; double CosPhi, SinPhi, CosTheta, SinTheta, Normalizer; double a = 1.0, b, c, d = 0.0, e, t; ushort[,] PackedNormal = new ushort[W,H]; for ( int Y=0; Y < H; Y++ ) for ( int X=0; X < W; X++ ) { x = Vectors[X,Y].x; y = Vectors[X,Y].y; z = Vectors[X,Y].z; CosTheta = z; SinTheta = Math.Sqrt( 1 - z*z ); Normalizer = 1.0 / Math.Max( 1e-10, SinTheta ); CosPhi = x * Normalizer; SinPhi = y * Normalizer; e = SinTheta*SinTheta*SinTheta*SinTheta * CosPhi*CosPhi * SinPhi*SinPhi; c = -SinTheta*SinTheta; b = -CosTheta; double[] roots = Polynomial.solvePolynomial( a, b, c, d, e ); t = Math.Sqrt( 2.0 ); for ( int i=0; i < roots.Length; i++ ) if ( !double.IsNaN( roots[i] ) && roots[i] >= 0.0 ) t = Math.Min( t, roots[i] ); // Nx = t * CosPhi * SinTheta; // Ny = t * SinPhi * SinTheta; Nx = t * x; Ny = t * y; Vectors[X,Y].x = (float) (0.5 * (1.0 + Nx)); Vectors[X,Y].y = (float) (0.5 * (1.0 + Ny)); Vectors[X,Y].z = 0.0f; } // Save as target PNG using ( System.Drawing.Bitmap B = new System.Drawing.Bitmap( W, H, System.Drawing.Imaging.PixelFormat.Format32bppRgb ) ) { System.Drawing.Imaging.BitmapData LockedBitmap = B.LockBits( new System.Drawing.Rectangle( 0, 0, W, H ), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb ); for ( int Y=0; Y < H; Y++ ) { byte* pScanline = (byte*) LockedBitmap.Scan0 + LockedBitmap.Stride * Y; for ( int X=0; X < W; X++ ) { *pScanline++ = 0; *pScanline++ = (byte) (255 * Vectors[X,Y].y); *pScanline++ = (byte) (255 * Vectors[X,Y].x); *pScanline++ = 0xFF; } } B.UnlockBits( LockedBitmap ); B.Save( TargetFileName.FullName ); } }
/// <summary> /// Loads a Targa image file into a Bitmap object. /// </summary> /// <param name="sFileName">The Targa image filename</param> /// <returns>A Bitmap object with the Targa image loaded into it.</returns> public static System.Drawing.Bitmap LoadTargaImage(string sFileName) { System.Drawing.Bitmap b = null; using (TargaImage ti = new TargaImage(sFileName, false)) { b = new System.Drawing.Bitmap(ti.Image); } return b; }
private unsafe void buttonConvertOld_Click( object sender, EventArgs args ) { if ( openFileDialog.ShowDialog( this ) != DialogResult.OK ) return; if ( saveFileDialog.ShowDialog( this ) != DialogResult.OK ) return; FileInfo SourceFileName = new FileInfo( openFileDialog.FileName ); FileInfo TargetFileName = new FileInfo( saveFileDialog.FileName ); int W, H; float3[,] Vectors; float x, y, z; using ( TargaImage TGA = new TargaImage( SourceFileName.FullName, false ) ) { // Convert byte[] ImageContent = Bitmap.LoadBitmap( TGA.Image, out W, out H ); Vectors = new float3[W,H]; int rha = 0; for ( int Y=0; Y < H; Y++ ) for ( int X=0; X < W; X++ ) { x = 2.0f * ImageContent[rha++] / 255 - 1.0f; y = 2.0f * ImageContent[rha++] / 255 - 1.0f; z = 2.0f * ImageContent[rha++] / 255 - 1.0f; rha++; // Skip alpha z = Math.Max( 0.0f, z ); float Norm = 1.0f / (float) Math.Sqrt( x*x + y*y + z*z ); Vectors[X,Y].x = x * Norm; Vectors[X,Y].y = y * Norm; Vectors[X,Y].z = z * Norm; } } // Convert to RG slightly less improved normal double Nx, Ny; double a, b, c = -1.0, d, t; ushort[,] PackedNormal = new ushort[W,H]; for ( int Y=0; Y < H; Y++ ) for ( int X=0; X < W; X++ ) { x = Vectors[X,Y].x; y = Vectors[X,Y].y; z = Vectors[X,Y].z; // Here I'm using the exact algorithm described by http://rgba32.blogspot.fr/2011/02/improved-normal-map-distributions.html a = (x * x) + (y * y); b = z; d = b*b - 4.0*a*c; t = (-b + Math.Sqrt( d )) / (2.0 * a); Nx = x * t; Ny = y * t; Vectors[X,Y].x = (float) (0.5 * (1.0 + Nx)); Vectors[X,Y].y = (float) (0.5 * (1.0 + Ny)); Vectors[X,Y].z = 0.0f; } // Save as target PNG using ( System.Drawing.Bitmap B = new System.Drawing.Bitmap( W, H, System.Drawing.Imaging.PixelFormat.Format32bppRgb ) ) { System.Drawing.Imaging.BitmapData LockedBitmap = B.LockBits( new System.Drawing.Rectangle( 0, 0, W, H ), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb ); for ( int Y=0; Y < H; Y++ ) { byte* pScanline = (byte*) LockedBitmap.Scan0 + LockedBitmap.Stride * Y; for ( int X=0; X < W; X++ ) { *pScanline++ = 0; *pScanline++ = (byte) (255 * Vectors[X,Y].y); *pScanline++ = (byte) (255 * Vectors[X,Y].x); *pScanline++ = 0xFF; } } B.UnlockBits( LockedBitmap ); B.Save( TargetFileName.FullName ); } }
public void Load( byte[] _ImageFileContent, FILE_TYPE _FileType, ColorProfile _ProfileOverride ) { m_Type = _FileType; try { switch ( _FileType ) { case FILE_TYPE.JPEG: case FILE_TYPE.PNG: case FILE_TYPE.TIFF: case FILE_TYPE.GIF: case FILE_TYPE.BMP: using ( System.IO.MemoryStream Stream = new System.IO.MemoryStream( _ImageFileContent ) ) { // ===== 1] Load the bitmap source ===== BitmapDecoder Decoder = BitmapDecoder.Create( Stream, BitmapCreateOptions.IgnoreColorProfile | BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnDemand ); if ( Decoder.Frames.Count == 0 ) throw new Exception( "BitmapDecoder failed to read at least one bitmap frame!" ); BitmapFrame Frame = Decoder.Frames[0]; if ( Frame == null ) throw new Exception( "Invalid decoded bitmap!" ); // DEBUG // int StrideX = (Frame.Format.BitsPerPixel>>3)*Frame.PixelWidth; // byte[] DebugImageSource = new byte[StrideX*Frame.PixelHeight]; // Frame.CopyPixels( DebugImageSource, StrideX, 0 ); // DEBUG // pas de gamma sur les JPEG si non spécifié ! // Il y a bien une magouille faite lors de la conversion par le FormatConvertedBitmap! // ===== 2] Build the color profile ===== m_ColorProfile = _ProfileOverride != null ? _ProfileOverride : new ColorProfile( Frame.Metadata as BitmapMetadata, _FileType ); // ===== 3] Convert the frame to generic RGBA32F ===== ConvertFrame( Frame ); // ===== 4] Convert to CIE XYZ (our device-independent profile connection space) ===== if ( ms_ReadContent && ms_ConvertContent2XYZ ) m_ColorProfile.RGB2XYZ( m_Bitmap, m_Bitmap ); } break; case FILE_TYPE.TGA: { // Load as a System.Drawing.Bitmap and convert to float4 using ( System.IO.MemoryStream Stream = new System.IO.MemoryStream( _ImageFileContent ) ) using ( TargaImage TGA = new TargaImage( Stream, !ms_ReadContent ) ) { // Create a default sRGB linear color profile m_ColorProfile = _ProfileOverride != null ? _ProfileOverride : new ColorProfile( ColorProfile.Chromaticities.sRGB, // Use default sRGB color profile ColorProfile.GAMMA_CURVE.STANDARD, // But with a standard gamma curve... TGA.ExtensionArea.GammaRatio // ...whose gamma is retrieved from extension data ); if ( ms_ReadContent ) { // Convert byte[] ImageContent = LoadBitmap( TGA.Image, out m_Width, out m_Height ); m_Bitmap = new float4[m_Width,m_Height]; byte A; int i = 0; for ( int Y=0; Y < m_Height; Y++ ) for ( int X=0; X < m_Width; X++ ) { m_Bitmap[X,Y].x = BYTE_TO_FLOAT * ImageContent[i++]; m_Bitmap[X,Y].y = BYTE_TO_FLOAT * ImageContent[i++]; m_Bitmap[X,Y].z = BYTE_TO_FLOAT * ImageContent[i++]; A = ImageContent[i++]; m_bHasAlpha |= A != 0xFF; m_Bitmap[X,Y].w = BYTE_TO_FLOAT * A; } if ( ms_ConvertContent2XYZ ) { // Convert to CIEXYZ m_ColorProfile.RGB2XYZ( m_Bitmap, m_Bitmap ); } } else { // Only read dimensions m_Width = TGA.Header.Width; m_Height = TGA.Header.Height; } } return; } case FILE_TYPE.HDR: { // Load as XYZ m_Bitmap = LoadAndDecodeHDRFormat( _ImageFileContent, true, _ProfileOverride, out m_ColorProfile ); m_Width = m_Bitmap.GetLength( 0 ); m_Height = m_Bitmap.GetLength( 1 ); return; } #if USE_LIB_RAW case FILE_TYPE.CRW: case FILE_TYPE.CR2: case FILE_TYPE.DNG: { using ( System.IO.MemoryStream Stream = new System.IO.MemoryStream( _ImageFileContent ) ) using ( LibRawManaged.RawFile Raw = new LibRawManaged.RawFile() ) { Raw.UnpackRAW( Stream ); ColorProfile.Chromaticities Chroma = Raw.ColorProfile == LibRawManaged.RawFile.COLOR_PROFILE.ADOBE_RGB ? ColorProfile.Chromaticities.AdobeRGB_D65 // Use Adobe RGB : ColorProfile.Chromaticities.sRGB; // Use default sRGB color profile // Create a default sRGB linear color profile m_ColorProfile = _ProfileOverride != null ? _ProfileOverride : new ColorProfile( Chroma, ColorProfile.GAMMA_CURVE.STANDARD, // But with a standard gamma curve... 1.0f // Linear ); // Also get back valid camera shot info m_bHasValidShotInfo = true; m_ISOSpeed = Raw.ISOSpeed; m_ShutterSpeed = Raw.ShutterSpeed; m_Aperture = Raw.Aperture; m_FocalLength = Raw.FocalLength; // Convert m_Width = Raw.Width; m_Height = Raw.Height; // float ColorNormalizer = 1.0f / Raw.Maximum; float ColorNormalizer = 1.0f / 65535.0f; if ( ms_ReadContent ) { m_Bitmap = new float4[m_Width,m_Height]; UInt16[,][] ImageContent = Raw.Image; for ( int Y=0; Y < m_Height; Y++ ) for ( int X=0; X < m_Width; X++ ) { m_Bitmap[X,Y].x = ImageContent[X,Y][0] * ColorNormalizer; m_Bitmap[X,Y].y = ImageContent[X,Y][1] * ColorNormalizer; m_Bitmap[X,Y].z = ImageContent[X,Y][2] * ColorNormalizer; m_Bitmap[X,Y].w = ImageContent[X,Y][3] * ColorNormalizer; } if ( ms_ConvertContent2XYZ ) { // Convert to CIEXYZ m_ColorProfile.RGB2XYZ( m_Bitmap, m_Bitmap ); } } } #region My poor attempt at reading CRW files // using ( System.IO.MemoryStream Stream = new System.IO.MemoryStream( _ImageFileContent ) ) // using ( CanonRawLoader CRWLoader = new CanonRawLoader( Stream ) ) // { // ColorProfile.Chromaticities Chroma = CRWLoader.m_ColorProfile == CanonRawLoader.DataColorProfile.COLOR_PROFILE.ADOBE_RGB // ? ColorProfile.Chromaticities.AdobeRGB_D65 // Use Adobe RGB // : ColorProfile.Chromaticities.sRGB; // Use default sRGB color profile // // // Create a default sRGB linear color profile // m_ColorProfile = new ColorProfile( // Chroma, // ColorProfile.GAMMA_CURVE.STANDARD, // But with a standard gamma curve... // 1.0f // Linear // ); // // // Convert // m_Width = CRWLoader.m_RAWImage.m_Width; // m_Height = CRWLoader.m_RAWImage.m_Height; // // m_Bitmap = new float4[m_Width,m_Height]; // UInt16[] ImageContent = CRWLoader.m_RAWImage.m_DecodedImage; // int i = 0; // // for ( int Y=0; Y < m_Height; Y++ ) // // for ( int X=0; X < m_Width; X++ ) // // { // // m_Bitmap[X,Y].x = ImageContent[i++] / 4096.0f; // // m_Bitmap[X,Y].y = ImageContent[i++] / 4096.0f; // // m_Bitmap[X,Y].z = ImageContent[i++] / 4096.0f; // // i++; // // } // // i=0; // for ( int Y=0; Y < m_Height; Y++ ) // for ( int X=0; X < m_Width; X++ ) // m_Bitmap[X,Y].x = ImageContent[i++] / 4096.0f; // i=0; // for ( int Y=0; Y < m_Height; Y++ ) // for ( int X=0; X < m_Width; X++ ) // m_Bitmap[X,Y].y = ImageContent[i++] / 4096.0f; // i=0; // for ( int Y=0; Y < m_Height; Y++ ) // for ( int X=0; X < m_Width; X++ ) // m_Bitmap[X,Y].z = ImageContent[i++] / 4096.0f; // // // Convert to CIEXYZ // m_ColorProfile.RGB2XYZ( m_Bitmap ); // } #endregion return; } #endif default: throw new NotSupportedException( "The image file type \"" + _FileType + "\" is not supported by the Bitmap class!" ); } } catch ( Exception ) { throw; // Go on ! } }