Vamos a programar #107 - Agregando imagenes a archivos FLAC usando C# (Parte 1).
Hola de nuevo a todos. El día de hoy vamos a continuar con mas de los archivos FLAC. En post anteriores vimos cómo insertar la imagen usando solo un editor hexadecimal, pero hoy, vamos a usar C# para crear un programa que lo haga por nosotros.
Antes de continuar te recomiendo que leas los post relacionados:
- Vamos a programar #102
- Vamos a programar #103
- Vamos a programar #104
- Vamos a programar #105
- Vamos a programar #106
Una vez leidos los post anteriores, vamos a ver el código que hace funcionar el programa. Algo importante que debo aclarar, es que por primera vez en la historia del blog, voy a usar visual studio 2022 y .Net 8.0, la mayor parte del código es retro-compatible, pero en c# 7.0 se agregaron algunas caracteristicas que hacen las cosas mas sencillas, por lo que si quieres probar el código deberías de instalar al menos esa version de visual studio.Además el código es mas que nada una prueba de concepto por lo que se puede optimizar, pero además, todo se hace para que sea lo mas similar al proceso que se hizo a mano (Vamos a programar #102) por lo que si quieres optimizarlo, puedes hacerlo sin ningun problema.
Una vez dicho eso, el código es el siguiente:
using System.Diagnostics.Eventing.Reader;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing.Imaging;
using System.Net.Http.Headers;
using System.ComponentModel;
namespace FlacPictureWriter
{
public partial class Form1 : Form
{
///Picture type:
/// $00 Other
/// $01 32x32 pixels 'file icon' (PNG only)
/// $02 Other file icon
/// $03 Cover (front)
/// $04 Cover (back)
/// $05 Leaflet page
/// $06 Media (e.g. lable side of CD)
/// $07 Lead artist/lead performer/soloist
/// $08 Artist/performer
/// $09 Conductor
/// $0A Band/Orchestra
/// $0B Composer
/// $0C Lyricist/text writer
/// $0D Recording Location
/// $0E During recording
/// $0F During performance
/// $10 Movie/video screen capture
/// $11 A bright coloured fish
/// $12 Illustration
/// $13 Band/artist logotype
/// $14 Publisher/Studio logotype
private enum APICType
{
/// <summary>
/// Imagen génerica
/// </summary>
APIC_TYPE_Other = 0,
/// <summary>
/// ícono (solo PNG)
/// </summary>
APIC_TYPE_FileIcon,
/// <summary>
/// Otro ícono
/// </summary>
APIC_TYPE_OtherFile_Icon,
/// <summary>
/// Cubierta frontal del disco
/// </summary>
APIC_TYPE_CoverFront,
/// <summary>
/// Cubierta trasera del disco
/// </summary>
APIC_TYPE_CoverBack,
/// <summary>
/// Folleto
/// </summary>
APIC_TYPE_LeafletPage,
/// <summary>
/// Otra ímagen
/// </summary>
APIC_TYPE_Media,
/// <summary>
/// Artista principal
/// </summary>
APIC_TYPE_LeadArtist,
/// <summary>
/// Artista
/// </summary>
APIC_TYPE_Artist,
/// <summary>
/// Conductor
/// </summary>
APIC_TYPE_Conductor,
/// <summary>
/// Banda
/// </summary>
APIC_TYPE_Band,
/// <summary>
/// Compositor
/// </summary>
APIC_TYPE_Composer,
/// <summary>
/// Letrista
/// </summary>
APIC_TYPE_Lyricist,
/// <summary>
/// Lugar de la grabacion
/// </summary>
APIC_TYPE_RecordingLocation,
/// <summary>
/// Captura durante la grabacion
/// </summary>
APIC_TYPE_DuringRecording,
/// <summary>
/// Captura durante concierto
/// </summary>
APIC_TYPE_DuringPerformance,
/// <summary>
/// Captura de videclip
/// </summary>
APIC_TYPE_VideoScreenCapture,
/// <summary>
/// ???
/// </summary>
APIC_TYPE_ABrightColouredFish,
/// <summary>
/// Ilustracion
/// </summary>
APIC_TYPE_Illustration,
/// <summary>
/// Logotipo de la banda/artista
/// </summary>
APIC_TYPE_ArtistLogotype,
/// <summary>
/// Logotipo de la discografica
/// </summary>
APIC_TYPE_PublisherLogotype,
APIC_TYPE_NoValid
}
// 0 : STREAMINFO 0000000
// 1 : PADDING 0000001
// 2 : APPLICATION 0000010
// 3 : SEEKTABLE 0000011
// 4 : VORBIS_COMMENT 0000100
// 5 : CUESHEET 0000101
// 6 : PICTURE 0000110
// 7-126 : reserved 1111000 - Invalid
// 127 : invalid, to avoid confusion with a frame sync code
/// <summary>
/// Enumeración con los posibles bloques contenidos en un archivo FLAC
/// </summary>
private enum BlocksTypes
{
/// <summary>
/// Bloque mandatorio con la información del Stream
/// </summary>
Block_Type_StreamInfo = 0,
/// <summary>
/// Bloque de Padding
/// </summary>
Block_Type_Padding = 1,
/// <summary>
/// Bloque con la informacion de la aplicación
/// </summary>
Block_Type_Application = 2,
/// <summary>
/// Bloque Seektable
/// </summary>
Block_Type_SeekTable = 3,
/// <summary>
/// Bloque con metadatos
/// </summary>
Block_Type_VorbisComment = 4,
/// <summary>
/// Bloque con cuesheet
/// </summary>
Block_Type_CueSheet = 5,
/// <summary>
/// Bloque con imagen
/// </summary>
Block_Type_Picture = 6,
/// <summary>
/// Bloque no válido
/// </summary>
Block_Type_NoValid = 7
}
public Form1()
{
InitializeComponent();
}
List<UInt32> Direcciones;
bool TieneImagen = false;
UInt32 ImageSize = 0;
UInt32 LastBLockPosition = 0;
UInt32 VorbisCommentPosition = 0;
UInt32 ImagePosition = 0;
private byte[] NormalizeToFourBytes(UInt32 Value, bool Reverse = false)
{
byte[] buff = BitConverter.GetBytes(Value);
if (Reverse)
Array.Reverse(buff);
return buff;
}
/// <summary>
/// Obtiene el "Mime Type" basado en la extension de un archivo
/// </summary>
/// <param name="FilePath">Ruta de la cual se va a extraer la información</param>
/// <returns></returns>
private string GetMIMEType(string FilePath)
{
FileInfo FI = new(FilePath);
if (string.Compare(FI.Extension, ".jpg", StringComparison.OrdinalIgnoreCase) == 0)
{
return @"image/jpeg";
}
else if (string.Compare(FI.Extension, ".png", StringComparison.OrdinalIgnoreCase) == 0)
{
return @"image/PNG";
}
else
{
return "No valido";
}
}
/// <summary>
/// Crea una imagen a partir de una secuencia de bytes
/// </summary>
/// <param name="bytesArr">Matriz de bytes que contiene los datos de la imagen</param>
/// <returns>Regresa una imagen</returns>
public Image ByteArrayToImage(byte[] bytesArr)
{
using (MemoryStream memstr = new(bytesArr))
{
Image img = Image.FromStream(memstr);
return img;
}
}
// 4 4 n*4 4 n*4 4 4 4 4 !4 !n*4
//<32>,<32>,<n*8>,<32>,<n*8>,<32>,<32>,<32>,<32>,<32>,<n*8>
// 1 2 3 4 5 6 7 8 9 10 11
/// <summary>
/// Lee todo los datos del bloque PICTURE y extrae la imagen contenida
/// </summary>
/// <param name="TheData">Arreglos bytes que contiene todo el bloque PICTURE</param>
/// <returns>Regresa una imagen</returns>
private Image ReadPictureData(byte[] TheData)
{
uint CurrentPos = 4;//1
uint CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
CurrentPos += 4;//2
string MimeType = Encoding.ASCII.GetString(TheData, (int)CurrentPos, (int)CurrentSize);
CurrentPos += CurrentSize;//3
CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
CurrentPos += (CurrentSize + (4 * 5));//4-9
CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
CurrentPos += 4;
byte[] TempImage = new byte[CurrentSize];
Array.Copy(TheData, CurrentPos, TempImage, 0, CurrentSize);
return ByteArrayToImage(TempImage);
}
/// <summary>
/// Actualiza la imagen de un archivo FLAC.
/// </summary>
/// <param name="ImagePath">Ruta de la imagen JPG</param>
private byte[] BuildAPICFrame(string ImagePath, int PictureType, string MimeType, UInt32 ImageSize)
{
List<byte> Data = new List<byte>();
byte[] Buff = new byte[ImageSize];
using (FileStream FS = new FileStream(ImagePath, FileMode.Open))
{
using (BinaryReader BR = new(FS))
{
Buff = BR.ReadBytes((int)ImageSize);
}
}
//Tipo de Imagen APIC
Data.AddRange(NormalizeToFourBytes((UInt32)PictureType, true));
//Tamaño del tipo de contenido
Data.AddRange(NormalizeToFourBytes((UInt32)MimeType.Length, true));
//Tipo de contenido
Data.AddRange(Encoding.ASCII.GetBytes(MimeType));
//Descripcion del tipo de contenido
Data.AddRange(NormalizeToFourBytes((UInt32)MimeType.Length, true));
//Tamaño de la descripcion del contenido
Data.AddRange(Encoding.ASCII.GetBytes(MimeType));
//Ancho de la imagen
Data.AddRange(NormalizeToFourBytes(0, true));
//Alto de la imagen
Data.AddRange(NormalizeToFourBytes(0, true));
//Profundidad del color
Data.AddRange(NormalizeToFourBytes(0, true));
//Indice de la paleta de colores
Data.AddRange(NormalizeToFourBytes(0, true));
//Tamaño de la imagen en bytes
Data.AddRange(NormalizeToFourBytes(ImageSize, true));
//Datos de la Imagen
Data.AddRange(Buff);
return Data.ToArray();
}
/// <summary>
/// Construye el bloque de imagen
/// </summary>
/// <param name="APICData"></param>
/// <returns></returns>
private byte[] BuildPictureBlock(byte[] APICData)
{
byte[] Buff = new byte[APICData.Length + 4];
byte[] PICSize = new byte[4];
PICSize = NormalizeToFourBytes((UInt32)APICData.Length, true);
PICSize.CopyTo(Buff, 0);
//siempre pondremos la imagen al final de los metadatos
//Establecemos el marcador de ultimo bloque y el tipo de bloque en 6 (Block_Type_Picture)
//Quedando 10000110b o 134
Buff[0] = 134;
APICData.CopyTo(Buff, 4);
return Buff;
}
private List<UInt32> GetOffsets(string FileName)
{
byte[] DataBuff;
List<UInt32> offsets = new List<UInt32>();
bool LastFrame = false;
using (FileStream FS = new(FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
offsets.Add((UInt32)FS.Position);
using (BinaryReader BR = new(FS, Encoding.ASCII))
{
DataBuff = BR.ReadBytes(4);
if (string.Equals(Encoding.ASCII.GetString(DataBuff), "fLaC", StringComparison.Ordinal))
{
UInt32 CurrentBlockSize;
UInt32 CurrentPosition = (UInt32)FS.Position;
BlocksTypes CurrentBlockType = NumberToBlockType(GetBlockType(DataBuff[0]));
while (!LastFrame)
{
//primer bloque siempre ser� Block_Type_StreamInfo
offsets.Add((UInt32)FS.Position);
DataBuff = BR.ReadBytes(4);
LastFrame = IsLastFrame(DataBuff[0]);
CurrentBlockType = NumberToBlockType(GetBlockType(DataBuff[0]));
CurrentBlockSize = GetBlockSize(DataBuff);
FS.Seek(FS.Position + CurrentBlockSize, SeekOrigin.Begin);
if (CurrentBlockType == BlocksTypes.Block_Type_VorbisComment)
{
VorbisCommentPosition = offsets[offsets.Count - 1];
}
if (CurrentBlockType == BlocksTypes.Block_Type_Picture)
{
TieneImagen = true;
ImagePosition = offsets[offsets.Count - 1];
}
if (LastFrame)
LastBLockPosition = offsets[offsets.Count - 1];
}
}
else
{
MessageBox.Show("Archivo Flac no v�lido");
}
}
}
return offsets;
}
/// <summary>
/// Construye el encabezado de un bloque de informacion
/// </summary>
/// <param name="Number">Valor que indica la longitud del bloque en bytes</param>
/// <param name="BlockType">Valor que indica el tipo de bloque de acuerdo a <c>BlockTypes</c></param>
/// <param name="IsLastFrame">Indica si es el ultimo bloque de informacion</param>
/// <returns>Regresa un arreglo de cuatro bytes que representan el encabezado del bloque</returns>
private byte[] BuildHeader(uint Number, BlocksTypes BlockType, bool IsLastFrame)
{
byte[] HeaderBuff = new byte[4];
HeaderBuff = BitConverter.GetBytes(Number);
HeaderBuff[3] = (byte)BlockType;
if (IsLastFrame)
{
HeaderBuff[3] |= 128;
}
Array.Reverse(HeaderBuff);
return HeaderBuff;
}
/// <summary>
/// Convierte un número en su valor equivalente al tipo de bloque
/// </summary>
/// <param name="Data">
/// Entero sin signo con la representacion del bloque</param>
/// <returns>Regresa un valor de la enumeración <typeparamref name="BlockTypes"/>BlockTypes</returns>
private BlocksTypes NumberToBlockType(uint Data)
{
switch (Data)
{
case 0:
return BlocksTypes.Block_Type_StreamInfo;
case 1:
return BlocksTypes.Block_Type_Padding;
case 2:
return BlocksTypes.Block_Type_Application;
case 3:
return BlocksTypes.Block_Type_SeekTable;
case 4:
return BlocksTypes.Block_Type_VorbisComment;
case 5:
return BlocksTypes.Block_Type_CueSheet;
case 6:
return BlocksTypes.Block_Type_Picture;
default:
return BlocksTypes.Block_Type_NoValid;
}
}
/// <summary>
/// Lee el bloque con la informacion estructurada
/// </summary>
/// <param name="TheData">Arreglo de bytes del cual se extraera la información</param>
/// <returns>Regresa un arreglo del tipo string con todos los campos que se encontraron</returns>
private string[] ReadVorbisData(byte[] TheData)
{
uint NumberOfFields = 0;
uint CurrentField = 1;
uint CurrentPosition = 0;
//Vendor
uint CurrentSize = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
CurrentPosition += 4;
byte[] CurrentChunk = new byte[CurrentSize];
Array.Copy(TheData, CurrentPosition, CurrentChunk, 0, CurrentSize);
string Currenttext = Encoding.UTF8.GetString(CurrentChunk);
CurrentPosition += CurrentSize;
NumberOfFields = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
CurrentPosition += 4;
string[] Fields = new string[NumberOfFields];
//CommentField
while (CurrentField <= NumberOfFields)
{
CurrentSize = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
CurrentPosition += 4;
byte[] CurrenUserCommentList = new byte[CurrentSize];
Array.Copy(TheData, CurrentPosition, CurrenUserCommentList, 0, CurrentSize);
Currenttext = Encoding.UTF8.GetString(CurrenUserCommentList);
CurrentPosition += CurrentSize;
Fields[CurrentField - 1] = Currenttext;
CurrentField += 1;
}
return Fields;
}
/// <summary>
/// Obtiene el valor del bloque desde un byte
/// </summary>
/// <param name="Data">byte del cual se va a obtener la información</param>
/// <returns>Regresa un valor que es equivalente al tipo de bloque</returns>
private uint GetBlockType(byte Data)
{
Data <<= 3;
Data >>= 3;
return Data;
}
/// <summary>
/// Obtiene si el bloque es el último de la serie
/// </summary>
/// <param name="Data">byte del cual se obtendra la información</param>
/// <returns>true si el bloque es el último, false en caso contrario</returns>
private bool IsLastFrame(byte Data)
{
Data >>= 7;
if (Data == 1)
return true;
else
return false;
}
/// <summary>
/// Obtiene el tamaño de un bloque
/// </summary>
/// <param name="Data">Arreglo de bytes que contiene el tamaño del bloque</param>
/// <returns>Regresa el tamaño del bloque actual</returns>
private uint GetBlockSize(byte[] Data)
{
byte[] CurrentData = Data;
//Ponemos el primer byte en 0 porque se usa para identificar el bloque
//a la hora de convertir a UInt32 se esperan 4 bytes, pero este siempre será 0 u otro valor no relevante
//para el tamaño del bloque
CurrentData[0] = 0;
Array.Reverse(CurrentData);
return BitConverter.ToUInt32(CurrentData, 0);
}
/// <summary>
/// Obtiene el tamaño de un bloque
/// </summary>
/// <param name="Data">Arreglo de bytes que contiene el tamaño del bloque</param>
/// <param name="StartIndex">Indica la posicion en donde se empezará a leer</param>
/// <param name="Size">Indica el tamaño en bytes que se van a leer</param>
/// <returns>Regresa el tamaño del bloque actual</returns>
private uint GetBlockSize(byte[] Data, int StartIndex, int Size)
{
byte[] CurrentData = new byte[4];
Array.Copy(Data, StartIndex, CurrentData, 0, Size);
Array.Reverse(CurrentData);
return BitConverter.ToUInt32(CurrentData, 0);
}
/// <summary>
/// Obtiene una imagen desde un archivo
/// </summary>
private void GetImageFromFile()
{
using OpenFileDialog OpDiag = new()
{
Filter = "Imagenes|*.jpg;*.png|Archivos JPG|*.jpg|Imagenes PNG|*.png",
Multiselect = false
};
if (OpDiag.ShowDialog() == DialogResult.OK)
{
TxtInputPic.Text = OpDiag.FileName;
PicInput.ImageLocation = TxtInputPic.Text;
PicInput.SizeMode = PictureBoxSizeMode.StretchImage;
FileInfo FileSize = new FileInfo(TxtInputPic.Text);
ImageSize = (UInt32)FileSize.Length;
LblPicInfo.Text = "Tamaño de la imagen en bytes " + ImageSize.ToString() + " bytes";
}
}
/// <summary>
/// Obtiene un bloque de datos de un archivo a partir de una posicion definida
/// </summary>
/// <param name="FileName">Archivo del cual se va a extraer el bloque</param>
/// <param name="StartPosition">Posicion des la cual se va a obtener el bloque relativo al inicio del archivo</param>
/// <returns>Regresa un arreglo de bytes con el bloque completo</returns>
private byte[] GetBlockData(string FileName, int StartPosition)
{
byte[] DataBuff;
using (FileStream FS = new(FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (BinaryReader BR = new(FS, Encoding.ASCII))
{
UInt32 CurrentBlockSize;
UInt32 CurrentPosition = (UInt32)FS.Position;
FS.Seek(StartPosition, SeekOrigin.Begin);
DataBuff = BR.ReadBytes(4);
CurrentBlockSize = GetBlockSize(DataBuff);
FS.Seek(StartPosition, SeekOrigin.Begin);
return (BR.ReadBytes((int)(CurrentBlockSize + 4)));
}
}
}
/// <summary>
/// Obtiene los datos de un bloque
/// </summary>
/// <param name="FileName">Nombre del archivo del cual se obtendra</param>
/// <param name="TypeOfBlock">Tipo de bloque que se va a buscar</param>
private void GetBlock(string FileName, BlocksTypes TypeOfBlock, int StartPosition)
{
byte[] DataBuff;
using (FileStream FS = new(FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (BinaryReader BR = new(FS, Encoding.ASCII))
{
UInt32 CurrentBlockSize;
UInt32 CurrentPosition = (UInt32)FS.Position;
BlocksTypes CurrentBlockType = BlocksTypes.Block_Type_NoValid;
FS.Seek(StartPosition, SeekOrigin.Begin);
DataBuff = BR.ReadBytes(4);
CurrentBlockType = NumberToBlockType(GetBlockType(DataBuff[0]));
if (CurrentBlockType == TypeOfBlock)
{
if (TypeOfBlock == BlocksTypes.Block_Type_VorbisComment)
{
string[] Fields;
CurrentBlockSize = GetBlockSize(DataBuff);
Fields = ReadVorbisData(BR.ReadBytes((int)CurrentBlockSize));
TxtInfo.Clear();
for (int i = 0; i < Fields.Length; i++)
{
TxtInfo.Text += Fields[i] + " | ";
}
}
else if (TypeOfBlock == BlocksTypes.Block_Type_Picture)
{
CurrentBlockSize = GetBlockSize(DataBuff);
PicFlac.Image = ReadPictureData(BR.ReadBytes((int)CurrentBlockSize));
}
}
else
{
return;
}
}
}
}
private void BtnOpen_Click(object sender, EventArgs e)
{
TieneImagen = false;
VorbisCommentPosition = 0;
ImagePosition = 0;
OpenFileDialog OpDiag = new()
{
Filter = "Archivos Flac|*.flac|Todos los arvhivos|*.*",
Multiselect = false
};
if (OpDiag.ShowDialog() == DialogResult.OK)
{
txtInputFlac.Text = OpDiag.FileName;
PicFlac.SizeMode = PictureBoxSizeMode.StretchImage;
Direcciones = GetOffsets(txtInputFlac.Text);
GetBlock(txtInputFlac.Text, BlocksTypes.Block_Type_VorbisComment, (int)VorbisCommentPosition);
if (TieneImagen)
GetBlock(txtInputFlac.Text, BlocksTypes.Block_Type_Picture, (int)ImagePosition);
}
}
private void BtnOpenPic_Click(object sender, EventArgs e)
{
ImageSize = 0;
GetImageFromFile();
}
private void BtnUpdateImage_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtInputFlac.Text))
{
MessageBox.Show("No hay arhivo de entrada", "Flac Image Writer", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}else if (string.IsNullOrEmpty(TxtInputPic.Text))
{
MessageBox.Show("No hay imagen de entrada", "Flac Image Writer", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string SaveFile;
bool HasImage = false;
List<UInt32> Direcciones = GetOffsets(txtInputFlac.Text);
int CurrentSize = (int)Direcciones[Direcciones.Count - 1];
int bufferSize = 1024 * 1024;
using (SaveFileDialog SavDiag = new SaveFileDialog())
{
SavDiag.Filter = "Archivos FLAC|*.flac";
SavDiag.AddExtension = true;
SavDiag.OverwritePrompt = true;
if (SavDiag.ShowDialog() == DialogResult.OK)
SaveFile = SavDiag.FileName;
else
return;
}
using (FileStream FSWrite = new FileStream(SaveFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
using (BinaryWriter BW = new(FSWrite))
{
BW.Write(Encoding.ASCII.GetBytes("fLaC"));
for (int x = 1; x < Direcciones.Count; x++)
{
byte[] FileBuff = GetBlockData(txtInputFlac.Text, (int)Direcciones[x]);
BlocksTypes CurrentBlockType = BlocksTypes.Block_Type_NoValid;
CurrentBlockType = NumberToBlockType(GetBlockType(FileBuff[0]));
if (CurrentBlockType == BlocksTypes.Block_Type_Picture)
HasImage = true;
if (x == Direcciones.Count - 1)
{
if (!HasImage)
{
CurrentSize += FileBuff.Length;
BW.Write(BuildHeader((uint)(FileBuff.Length - 4), CurrentBlockType, false));
}
else
{
CurrentSize += FileBuff.Length;
BW.Write(BuildHeader((uint)(FileBuff.Length - 4), CurrentBlockType, true));
}
}
else
{
BW.Write(BuildHeader((uint)(FileBuff.Length - 4), CurrentBlockType, false));
}
BW.Write(FileBuff, 4, FileBuff.Length - 4);
}
if (HasImage == false)
BW.Write(BuildPictureBlock(BuildAPICFrame(TxtInputPic.Text, CboPictureType.SelectedIndex, GetMIMEType(TxtInputPic.Text), ImageSize)));
FileStream fs = new FileStream(txtInputFlac.Text, FileMode.Open, FileAccess.ReadWrite);
fs.Position = CurrentSize;
int bytesRead = -1;
byte[] bytes = new byte[bufferSize];
while ((bytesRead = fs.Read(bytes, 0, bufferSize)) > 0)
{
BW.Write(bytes, 0, bytesRead);
}
fs.Close();
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
CboPictureType.SelectedIndex = 3;
}
private void PicFlac_DoubleClick(object sender, EventArgs e)
{
try
{
if (TieneImagen)
{
using (SaveFileDialog SavDiag = new SaveFileDialog())
{
SavDiag.Filter = "Arhivo Jpeg|*.jpg";
if (SavDiag.ShowDialog() == DialogResult.OK)
PicFlac.Image.Save(SavDiag.FileName);
}
//PicFlac.Image.Save
}
}
catch
{
MessageBox.Show("Error al guardar la imagen","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
Ahora veamos las funciones.
Primero empezamos con la enumeración "APICType", en ella se definen los posibles valores para el tipo de imagen que se va incrustarde acuerdo a lo establecido en la documentacion. Esto solo se hace para poder identificar las cosas de la forma mas humana posible, no es del todo necesario, pero ayuda bastante.
La primera funcion es "NormalizeToFourBytes()". Esta función recibe dos parámetros, el primero, es un valor del tipo UInt32 llamado "Value" y el otro es un valor del tipo bool llamado "Reverse". Esta función se encarga de convertir un numero en su equivalente en una secuencia de bytes, pero además tiene la opción de invertir el orden de la secuencia de bytes, dependiendo de la maquina en la que se trabaje o del idioma, los números se pueden representar de forma distinta, por ejemplo, si tenemos el número hexadecimal 0x3259 , puede representarse cómo 0x32 0x59 o 0x59 0x32. La función devuelve un arreglo de bytes con la representación del número.
La siguiente función es "GetMIMEType()". Esta función recibe un valor del tipo string con una cadena de texto. Se espera solo la extensión de las imágenes compatibles, es decir solo .jpg o .png. Regresa el Mime Type basado en la cadena de texto.
La siguiente funcion es "ByteArrayToImage()" Esta funcion recibe un arreglo de bytes, se espera que la secuencia de bytes en si conformen una imagen. Regresa un valor del tipo Image que contiene la imagen de la secuencia de bytes.
Y bien, por ahora es todo, las siguientes funciones merecen una explicacion mas detallada para poder entender mejor cada una. el código del formulario principal lo puedes copiar y pegar para probarlo, pero al final cuando todo este listo lo publicaré en mi dropbox para que lo puedas descargar y probar.
Los leo luego



No hay comentarios. :
Publicar un comentario