DotNetCampania
Il primo portale campano dedicato allo sviluppo software con tecnologie Microsoft

Resizing e generazione di thumbnail avanzata

100% of people found this useful
Resizing e generazione di thumbnail avanzata

Introduzione

Uno dei metodi più comodi e più utilizzati per ottenere thumbnail di immagini dinamicamente è il metodo GetThumbnailImage() dell'oggetto Image presente nel namespace System.Drawing, come ripreso anche da Turibbio in uno dei suoi post.

L'utilizzo di questo metodo pone però alcune limitazioni, come lo stesso msdn afferma:

Il metodo GetThumbnailImage garantisce migliori risultati quando le dimensioni dell'immagine di anteprima richiesta sono pari a circa 120x120 pixel. Se si richiede un'immagine di anteprima di grandi dimensioni, ad esempio 300x300, da un oggetto Image che dispone di un'anteprima incorporata, è possibile che si verifichi una notevole perdita di qualità dell'immagine dell'anteprima. Potrebbe essere consigliabile ridimensionare l'immagine principale, anziché ridimensionare l'anteprima incorporata, chiamando il metodo DrawImage. (1)

Qualche considerazione nata dalla mia esperienza di web developer: il più delle volte mi è capitato di dover gestire thumbnail che fossero in qualche modo uniformi nelle dimensioni e nel colore di sfondo. Ho deciso di affrontare entrambi gli aspetti in modo da approcciare il problema nella sua variegata casistica.

Ho affrontato il problema suddividendolo in due blocchi.
Dapprima viene curato l'aspetto legato al contenitore dell'immagine di output: è' possibile immaginarlo come il livello di sfondo dell'immagine risultante.
Successivamente viene curato il ridimensionamento dell'immagine e come questa viene disegnata rispetto al contenitore.
Utilizzare la procedura proposta in questo post per effettuare il ridimensionamento dell'immagine in input prevederà dunque la definizione delle dimensioni di larghezza ed altezza desiderate del container, e delle dimensioni di larghezza ed altezza desiderate dell'immagine ridimensionata, le quali non sempre e non necessariamente devono combaciare. Per questo motivo, ho definito quattro membri e relative proprietà:

private int _imageWidth;
private int _imageHeight;
private int _containerWidth;
private int _containerHeight;

public int Width
{
    get { return _imageWidth; }
    set { _imageWidth = value; }
}
public int Height
{
    get { return _imageHeight; }
    set { _imageHeight = value; }
}
public int ContainerWidth
{
    get { return _containerWidth; }
    set { _containerWidth = value; }
}
public int ContainerHeight
{
    get { return _containerHeight; }
    set { _containerHeight = value; }
}
         

Il modo in cui queste quattro dimensioni interagiscono tra di loro è definito dall'enumerazione

 
public enum ScaleMode
{
    FitContainer,
    FitImage,
    FitAuto,
    NoFit
}

dove:

  • FitContainer: forza il ridimensionamento in modo da adeguarsi alle dimensioni specificate per il contenitore. L'immagine di output avrà le dimensioni specificate per il contenitore.
  • FitImage: effettua il ridimensionamento in base alle dimensioni specificate per imageWidth e imageHeight, forzando il contenitore ad adeguarsi a queste dimensioni
  • FitAuto: esegue un ridimensionamento calcolando in automatico le dimensioni dell'immagine risultante all'interno del contenitore. L'immagine di output avrà le dimensioni specificate per il contenitore.
  • NoFit: non adegua il ridimensionamento al contenitore. Esegue il ridimensionamento sull'immagine in base alle dimensioni specificate, effettuando un taglio dell'immagine qualora questa superi le dimensioni del contenitore. L'immagine di output avrà le dimensioni specificate per il contenitore. E' possibile gestire la modalità di taglio con le strutture seguenti:
 
public enum Position
{
    TopLeft,
    TopCenter,
    TopRight,
    MiddleLeft,
    MiddleCenter,
    MiddleRight,
    BottomLeft,
    BottomCenter,
    BottomRight,
    Undefined
}

private int _offsetX;
private int _offsetY;
private Position _drawingPosition;

public int OffsetX
{
    get { return _offsetX; }
    set { _offsetX = value; }
}
public int OffsetY
{
    get { return _offsetY; }
    set { _offsetY = value; }
}
public Position DrawingPosition {
    get { return _drawingPosition; }
    set { _drawingPosition = value; }
}
        

L'enumerazione Position definisce in che modo l'immagine viene allineata rispetto al container. E se proprio questo non bastasse è possibile valorizzare le variabili OffsetX e OffsetY che consentono di avere uno scostamento rispetto all'origine (l'angolo superiore sinistro del contenitore).

A queste modalità operative si aggiunge il controllo sul ridimensionamento proporzionale, come si vedrà in seguito.

Per quanto riguarda invece la definizione del colore di sfondo, ho definito due variabili, la prima è il colore vero e proprio, la seconda l'intensità del canale alfa/trasparenza. Questi due valori vengono combinati nel metodo GetBackground() che inoltre effettua un controllo sulla coerenza del valore di Alpha, che ammette valori compresi tra 0 e 255.

 
private Color _backgroundColor;
private int _alpha;

public Color BackgroundColor
{
    get { return _backgroundColor; }
    set { _backgroundColor = value; }
}
public int Alpha
{
    get { return _alpha; }
    set { _alpha = value; }
}

private SolidBrush GetBackground()
{
    int alphaCheck = 255;
    if (_alpha < 0)
        alphaCheck = 0;
    else if (_alpha > 255)
        alphaCheck = 255;
    else
        alphaCheck = _alpha;
    return new SolidBrush(Color.FromArgb(alphaCheck, _backgroundColor));
}
            

Vediamo ora come utilizzare il metodo DrawImage, come suggerito in msdn.

 
public Image Resize(Image image)
{
    // Temp variables
    int newContainerWidth = _containerWidth;
    int newContainerHeight = _containerHeight;
    int newImageWidth = _imageWidth;
    int newImageHeight = _imageHeight;
    int newPositionX = 0;
    int newPositionY = 0;

    GetNewSizes(image, ref newContainerWidth, ref newContainerHeight, ref newImageWidth, ref newImageHeight);
    GetPositions(ref newPositionX, ref newPositionY, newContainerWidth, newContainerHeight, newImageWidth, newImageHeight);

    Bitmap retObj = new Bitmap(newContainerWidth, newContainerHeight);
    retObj.SetResolution(_horizontalResolution, _verticalResolution);

    Graphics g = Graphics.FromImage(retObj);
    g.CompositingMode = _compositingMode;
    g.CompositingQuality = _compositingQuality;
    g.SmoothingMode = _smoothing;
    g.InterpolationMode = _interpolation;
    
    g.FillRectangle(GetBackground(), 0f, 0f, newContainerWidth, newContainerHeight);
    g.DrawImage(image, newPositionX + _offsetX, newPositionY + _offsetY, newImageWidth, newImageHeight);
    g.Dispose();
    return (Image)retObj;
}
        

Come si osserva dal codice, vengono definite una serie di variabili temporanee che saranno popolate successivamente da due metodi di supporto che vengono richiamati per effettuare i dovuti controlli sulle dimensioni del container e dell'immagine e definire quindi i valori finali che saranno utilizzati durante il processo di resizing dell'immagine.

private void GetProportionalSizes(Image image, ref int imageWidth, ref int imageHeight)
{
    bool exceedWidth = image.Width > imageWidth ? true : false;
    bool exceedHeight = image.Height > imageHeight ? true : false;

    if (exceedWidth & exceedHeight)
    {
        int horizontalSurplus = image.Width - _imageWidth;
        int verticalSurplus = image.Height - _imageHeight;

        if (horizontalSurplus > verticalSurplus)
        {
            imageWidth = _imageWidth;
            imageHeight = imageWidth * image.Height / image.Width;
        }
        else
        {
            imageHeight = _imageHeight;
            imageWidth = imageHeight * image.Width / image.Height;
        }
    }
    else
    {
        if (exceedWidth)
        {
            imageWidth = _imageWidth;
            imageHeight = imageWidth * image.Height / image.Width;
        }
        else
        {
            imageHeight = _imageHeight;
            imageWidth = imageHeight * image.Width / image.Height;
        }
    }
}
        

Il metodo GetProportionalSizes() calcola le dimensioni finali dell'immagine in modo da effettuare un ridimensionamento proporzionale ed evitare così il fastidioso effetto di stretching delle immagini. Nella serie di if-else innestate viene eseguito un controllo sulla coerenza della proporzioni in base alle dimensioni dell'immagine originale e delle dimensioni desiderate.

Infatti il primo controllo si occupa di verificare quale delle due dimensioni (orizzontale o verticale) richieste eccede rispetto alla dimensione dell'immagine originale. Se entrambe eccedono, viene verificata quale delle due dimensioni eccede maggiormente di modo che il calcolo proporzionale venga fatto su questa. Il discorso è analogo qualora solo una delle due dimensioni ecceda rispetto alle dimensioni dell'immagine originale. Il terzo caso, non contemplato nel corpo del metodo, è il caso in cui nessuna delle due dimensioni richieste per il ridimensionamento ecceda le dimensioni originali: in questo caso le dimensioni richieste sono superiori a quella dell'immagine originale, assumendo l'assenza di ridimensionamento, ed evitando quindi il calcolo delle dimensioni proporzionali.

 

 private void CheckContainerSizes(ref int imageWidth, ref int imageHeight)
{
    bool exceedWidth = imageWidth > _containerWidth ? true : false;
    bool exceedHeight = imageHeight > _containerHeight ? true : false;

    if (exceedWidth & exceedHeight)
    {
        int horizontalSurplus = imageWidth - _containerWidth;
        int verticalSurplus = imageHeight - _containerHeight;

        if (horizontalSurplus > verticalSurplus)
        {
            imageWidth = _containerWidth;
            imageHeight = imageWidth * _containerHeight / _containerWidth;
        }
        else
        {
            imageHeight = _containerHeight;
            imageWidth = imageHeight * _containerWidth / _containerHeight;
        }
    }
    else
    {
        if (exceedWidth)
        {
            imageWidth = _containerWidth;
            imageHeight = imageWidth * _containerHeight / _containerWidth;
        }
        else
        {
            imageHeight = _containerHeight;
            imageWidth = imageHeight * _containerWidth / _containerHeight;
        }
    }

    // Change required size for auto fit
    _imageWidth = imageWidth;
    _imageHeight = imageHeight;
}        
        

Il metodo CheckContainerSizes() viene richiamato nel solo caso in cui la modalità operativa scelta sia FitAuto, ovvero quando viene richiesto che l'immagine venga ridimensionata nel rispetto delle dimensioni del container, senza andare oltre queste. Leggendo il corpo di questo metodo, si nota che è praticamente identico al corpo del metodo GetProportionalSizes(), ma ha una differenza importante: oltre ad effettuare le verifiche del caso, è in grado di reimpostare le dimensioni di ridimensionamento richieste affinchè soddisfino il criterio operativo scelto.

public void Save(Image image, ImageFormat format, string outputFile)
{
    if (format == ImageFormat.Jpeg)
    {
        ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, _jpegCompression);
        image.Save(outputFile, info[1], encoderParams);
    }
    else
    {
        image.Save(outputFile, format);
    }
}

Il metodo Save() consente di salvare su filesystem l'immagine precedentemente ridimensionata (o qualunque altra immagine precedentemente istanziata, visto che tra i parametri di input c'è "image").
Si nota però che nel caso venga richiesto di salvare in formato Jpeg, è possibile controllare il grado di compressione dell'immagine jpeg risultante, semplicemente instanziando e valorizzando l'oggetto EncoderParameters e passandolo in input ad uno specifico overload del metodo Save() dell'oggetto Image.


Tra i vari membri presenti, opportunamente mappati su proprietà, ne spiccano cinque che consentono di avere un controllo dell'output piuttosto avanzato:

private InterpolationMode _interpolation;
private CompositingMode _compositingMode;
private CompositingQuality _compositingQuality;
private SmoothingMode _smoothing;
private PixelOffsetMode _pixelOffsetMode;

public InterpolationMode Interpolation
{
    get { return _interpolation; }
    set { _interpolation = value; }
}
public CompositingMode CompositingMode
{
    get { return _compositingMode; }
    set { _compositingMode = value; }
}
public CompositingQuality CompositingQuality
{
    get { return _compositingQuality; }
    set { _compositingQuality = value; }
}
public SmoothingMode Smoothing
{
    get { return _smoothing; }
    set { _smoothing = value; }
}
public PixelOffsetMode PixelOffset
{
    get { return _pixelOffsetMode; }
    set { _pixelOffsetMode = value; }
}
        

InterpolationMode

L'InterpolationMode è, in questo contesto, di importanza vitale. Specifica infatti 'algoritmo utilizzato quando le immagini vengono scalate o ruotate. Questi le possibili scelte:

  • Default: Specifica la modalità predefinita.
  • Low: Specifica un'interpolazione di bassa qualità.
  • High: Specifica un'interpolazione di elevata qualità.
  • Bilinear: Specifica l'interpolazione bilineare. Non viene effettuata alcuna operazione di prefiltraggio. Questa modalità non è adatta per la compattazione di un'immagine al di sotto del 50% delle dimensioni originali.
  • Bicubic: Specifica l'interpolazione bicubica. Non viene effettuata alcuna operazione di prefiltraggio. Questa modalità non è adatta per la compattazione di un'immagine al di sotto del 25% delle dimensioni originali.
  • NearestNeighbor: Specifica l'interpolazione più simile.
  • HighQualityBilinear: Specifica l'interpolazione bilineare di elevata qualità. Viene effettuata un'operazione di prefiltraggio per assicurare una compattazione di elevata qualità.
  • HighQualityBicubic: Specifica l'interpolazione bicubica di elevata qualità. Viene effettuata un'operazione di prefiltraggio per assicurare una compattazione di elevata qualità. Questa modalità produce le immagini trasformate di qualità più elevata.

CompositingMode

Il CompositingMode specifica il modo in cui i colori di origine vengono combinati con i colori dello sfondo. E' possibile assegnare uno dei due valori:

  • SourceOver: quando viene eseguito il rendering di un colore, tale colore viene miscelato con il colore dello sfondo. La miscela di colori viene determinata dal componente alpha del colore di cui viene eseguito il rendering.
  • SourceCopy: quando viene eseguito il rendering di un colore, tale colore sovrascrive il colore dello sfondo.

CompositingQuality

La CompositingQuality specifica il livello di qualità da utilizzare durante la composizione. Questi sono i valori ammissibili:

  • Default: qualità predefinita.
  • HighSpeed: alta velocità, bassa qualità.
  • HighQuality: composizione di elevata qualità e ridotta velocità.
  • GammaCorrected: viene utilizzata la correzione gamma.
  • AssumeLinear: vengono assunti i valori lineari.

SmoothingMode

Lo SmoothingMode specifica se e come viene applicato l'anti-aliasing. Anche i questo caso abbiamo una serie di valori ammissibili:

  • Default: Specifica l'assenza di antialias.
  • HighSpeed: Specifica l'assenza di antialias.
  • HighQuality: Specifica il rendering con antialias.
  • None: Specifica l'assenza di antialias.
  • AntiAlias: Specifica il rendering con antialias.

PixelOffsetMode

Il PixelOffsetMode specifica la modalità di offset dei pixel durante l'esecuzione del rendering, in modo da controllare la qualità del rendering finale. Questi i valori ammissibili:

  • Default: Specifica la modalità predefinita.
  • HighSpeed: Specifica un rendering di elevata velocità e ridotta qualità.
  • HighQuality: Specifica un rendering di elevata qualità e ridotta velocità.
  • None: Specifica l'assenza di offset di pixel.
  • Half: Specifica l'offset dei pixel in base a unità - .5, sia in senso orizzontale che verticale, per ottenere un anti-aliasing di elevata velocità.

g.FillRectangle(GetBackground(), 0f, 0f, newContainerWidth, newContainerHeight);
g.DrawImage(image, newPositionX + _offsetX, newPositionY + _offsetY, newImageWidth, newImageHeight);

Definiti tutti i parametri di disegno ed effettuati i calcoli sul ridimensionamento, sono le due chiamate finali a generare l'immagine di output. Nella prima, il metodo FillRectangle, in uno dei suoi overload (2), disegna il livello di sfondo. Su di questo, la chiamata successiva, DrawImage, anch'essa in uno dei suoi overload (3), disegna l'immagine ridimensionata.

Il codice sorgente

 
#region Using directives
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
#endregion

namespace DotNetCampania.Imager
{
    public class ImageProcessor
    {
        #region Enums
        public enum ScaleMode
        {
            FitContainer,
            FitImage,
            FitAuto,
            NoFit
        }

        public enum Position
        {
            TopLeft,
            TopCenter,
            TopRight,
            MiddleLeft,
            MiddleCenter,
            MiddleRight,
            BottomLeft,
            BottomCenter,
            BottomRight,
            Undefined
        }
        #endregion

        #region Members
        private int _imageWidth;
        private int _imageHeight;
        private int _containerWidth;
        private int _containerHeight;
        private ScaleMode _scale;
        private Position _drawingPosition;
        private bool _proportional;
        private InterpolationMode _interpolation;
        private CompositingMode _compositingMode;
        private CompositingQuality _compositingQuality;
        private SmoothingMode _smoothing;
        private PixelOffsetMode _pixelOffsetMode;
        private Color _backgroundColor;
        private int _alpha;

        private int _offsetX;
        private int _offsetY;

        private ImageFormat _outputImageFormat;
        private long _jpegCompression;
        private float _horizontalResolution;
        private float _verticalResolution;
        #endregion

        #region Properties
        public int Width
        {
            get { return _imageWidth; }
            set { _imageWidth = value; }
        }
        public int Height
        {
            get { return _imageHeight; }
            set { _imageHeight = value; }
        }
        public int ContainerWidth
        {
            get { return _containerWidth; }
            set { _containerWidth = value; }
        }
        public int ContainerHeight
        {
            get { return _containerHeight; }
            set { _containerHeight = value; }
        }
        public Position DrawingPosition
        {
            get { return _drawingPosition; }
            set { _drawingPosition = value; }
        }
        public bool Proportional
        {
            get { return _proportional; }
            set { _proportional = value; }
        }
        public InterpolationMode Interpolation
        {
            get { return _interpolation; }
            set { _interpolation = value; }
        }
        public CompositingMode CompositingMode
        {
            get { return _compositingMode; }
            set { _compositingMode = value; }
        }
        public CompositingQuality CompositingQuality
        {
            get { return _compositingQuality; }
            set { _compositingQuality = value; }
        }
        public SmoothingMode Smoothing
        {
            get { return _smoothing; }
            set { _smoothing = value; }
        }
        public PixelOffsetMode PixelOffset
        {
            get { return _pixelOffsetMode; }
            set { _pixelOffsetMode = value; }
        }
        public Color BackgroundColor
        {
            get { return _backgroundColor; }
            set { _backgroundColor = value; }
        }
        public int Alpha
        {
            get { return _alpha; }
            set { _alpha = value; }
        }
        public int OffsetX
        {
            get { return _offsetX; }
            set { _offsetX = value; }
        }
        public int OffsetY
        {
            get { return _offsetY; }
            set { _offsetY = value; }
        }
        public ImageFormat OutputImageFormat
        {
            get { return this._outputImageFormat; }
            set { _outputImageFormat = value; }
        }
        public long JPEGCompression
        {
            get { return _jpegCompression; }
            set { _jpegCompression = value; }
        }
        public float HorizontalResolution
        {
            get { return _horizontalResolution; }
            set { _horizontalResolution = value; }
        }
        public float VerticalResolution
        {
            get { return _verticalResolution; }
            set { _verticalResolution = value; }
        }
        public ScaleMode Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }
        #endregion

        #region Constructor(s)
        public ImageProcessor()
        {
            _imageWidth = default(int);
            _imageHeight = default(int);
            _containerWidth = default(int);
            _containerHeight = default(int);
            _drawingPosition = Position.Undefined;
            _interpolation = InterpolationMode.HighQualityBicubic;
            _compositingMode = CompositingMode.SourceOver;
            _compositingQuality = CompositingQuality.HighQuality;
            _smoothing = SmoothingMode.HighQuality;
            _pixelOffsetMode = PixelOffsetMode.HighQuality;

            _proportional = true;
            _alpha = 255;
            _backgroundColor = Color.White;

            _offsetX = 0;
            _offsetY = 0;
            _scale = ScaleMode.FitAuto;

            _outputImageFormat = ImageFormat.Jpeg;
            _jpegCompression = 80L;
            _horizontalResolution = 72f;
            _verticalResolution = 72f;
        }
        #endregion

        #region Public methods
        public Image Resize(Image image)
        {
            // Temp variables
            int newContainerWidth = _containerWidth;
            int newContainerHeight = _containerHeight;
            int newImageWidth = _imageWidth;
            int newImageHeight = _imageHeight;
            int newPositionX = 0;
            int newPositionY = 0;

            GetNewSizes(image, ref newContainerWidth, ref newContainerHeight, ref newImageWidth, ref newImageHeight);
            GetPositions(ref newPositionX, ref newPositionY, newContainerWidth, newContainerHeight, newImageWidth, newImageHeight);

            Bitmap retObj = new Bitmap(newContainerWidth, newContainerHeight);
            retObj.SetResolution(_horizontalResolution, _verticalResolution);

            Graphics g = Graphics.FromImage(retObj);
            g.CompositingMode = _compositingMode;
            g.CompositingQuality = _compositingQuality;
            g.SmoothingMode = _smoothing;
            g.InterpolationMode = _interpolation;
            g.PixelOffsetMode = _pixelOffsetMode;
            
            g.FillRectangle(GetBackground(), 0f, 0f, newContainerWidth, newContainerHeight);
            g.DrawImage(image, newPositionX + _offsetX, newPositionY + _offsetY, newImageWidth, newImageHeight);
            g.Dispose();
            return (Image)retObj;
        }

        public void Save(Image image, ImageFormat format, string outputFile)
        {
            if (format == ImageFormat.Jpeg)
            {
                ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
                EncoderParameters encoderParams = new EncoderParameters(1);
                encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, _jpegCompression);
                image.Save(outputFile, info[1], encoderParams);
            }
            else
            {
                image.Save(outputFile, format);
            }
        }
        #endregion

        #region Private support methods
        private void GetNewSizes(Image image, ref int containerWidth, ref int containerHeight, ref int imageWidth, ref int imageHeight)
        {
            switch (_scale)
            {
                case ScaleMode.NoFit:
                    if (_proportional)
                    {
                        GetProportionalSizes(image, ref  imageWidth, ref  imageHeight);
                    }
                    break;
                case ScaleMode.FitAuto:
                    CheckContainerSizes(ref imageWidth, ref imageHeight); 
                    if (_proportional)
                        GetProportionalSizes(image, ref  imageWidth, ref  imageHeight);
                    break;
                case ScaleMode.FitContainer:
                    imageWidth = containerWidth;
                    imageHeight = containerHeight;
                    break;
                case ScaleMode.FitImage:
                    if (_proportional)
                        GetProportionalSizes(image, ref  imageWidth, ref  imageHeight);
                    containerWidth = imageWidth;
                    containerHeight = imageHeight;
                    break;
            }
        }

        private void GetPositions(ref int positionX, ref int positionY, int containerWidth, int containerHeight, int imageWidth, int imageHeight) {
            switch (_drawingPosition) {
                case Position.BottomCenter:
                    positionX = (int)((containerWidth / 2) - (imageWidth / 2));                    
                    positionY = containerHeight - imageHeight;
                    break;
                case Position.BottomLeft: 
                    positionX = 0;
                    positionY = containerHeight - imageHeight;
                    break;
                case Position.BottomRight: 
                    positionX = containerWidth - imageWidth;
                    positionY = containerHeight - imageHeight;
                    break;
                case Position.MiddleCenter: 
                    positionX = (int)((containerWidth / 2) - (imageWidth / 2));
                    positionY = (int)((containerHeight / 2) - (imageHeight / 2));
                    break;
                case Position.MiddleLeft:
                    positionX = 0;
                    positionY = (int)((containerHeight / 2) - (imageHeight / 2));
                    break;
                case Position.MiddleRight: 
                    positionX = containerWidth - imageWidth;
                    positionY = (int)((containerHeight / 2) - (imageHeight / 2));
                    break;
                case Position.TopCenter: 
                    positionX = (int)((containerWidth / 2) - (imageWidth / 2));
                    positionY = 0;
                    break;
                case Position.TopLeft:
                    positionX = 0;
                    positionY = 0;
                    break;
                case Position.TopRight: 
                    positionX = containerWidth - imageWidth;
                    positionY = 0;
                    break;
            }
        }

        private void CheckContainerSizes(ref int imageWidth, ref int imageHeight)
        {
            bool exceedWidth = imageWidth > _containerWidth ? true : false;
            bool exceedHeight = imageHeight > _containerHeight ? true : false;

            if (exceedWidth & exceedHeight)
            {
                int horizontalSurplus = imageWidth - _containerWidth;
                int verticalSurplus = imageHeight - _containerHeight;

                if (horizontalSurplus > verticalSurplus)
                {
                    imageWidth = _containerWidth;
                    imageHeight = imageWidth * _containerHeight / _containerWidth;
                }
                else
                {
                    imageHeight = _containerHeight;
                    imageWidth = imageHeight * _containerWidth / _containerHeight;
                }
            }
            else
            {
                if (exceedWidth)
                {
                    imageWidth = _containerWidth;
                    imageHeight = imageWidth * _containerHeight / _containerWidth;
                }
                else
                {
                    imageHeight = _containerHeight;
                    imageWidth = imageHeight * _containerWidth / _containerHeight;
                }
            }

            // Change required size for auto fit
            _imageWidth = imageWidth;
            _imageHeight = imageHeight;
        }

        private void GetProportionalSizes(Image image, ref int imageWidth, ref int imageHeight)
        {
            bool exceedWidth = image.Width > imageWidth ? true : false;
            bool exceedHeight = image.Height > imageHeight ? true : false;

            if (exceedWidth & exceedHeight)
            {
                int horizontalSurplus = image.Width - _imageWidth;
                int verticalSurplus = image.Height - _imageHeight;

                if (horizontalSurplus > verticalSurplus)
                {
                    imageWidth = _imageWidth;
                    imageHeight = imageWidth * image.Height / image.Width;
                }
                else
                {
                    imageHeight = _imageHeight;
                    imageWidth = imageHeight * image.Width / image.Height;
                }
            }
            else
            {
                if (exceedWidth)
                {
                    imageWidth = _imageWidth;
                    imageHeight = imageWidth * image.Height / image.Width;
                }
                else
                {
                    imageHeight = _imageHeight;
                    imageWidth = imageHeight * image.Width / image.Height;
                }
            }
        }

        private SolidBrush GetBackground()
        {
            int alphaCheck = 255;
            if (_alpha < 0)
                alphaCheck = 0;
            else if (_alpha > 255)
                alphaCheck = 255;
            else
                alphaCheck = _alpha;
            return new SolidBrush(Color.FromArgb(alphaCheck, _backgroundColor));
        }
        #endregion
    }
}
        

Download

Riferimenti

  1. Riferimento a .NET Framework - Metodo Image.GetThumbnailImage [http://msdn.microsoft.com/it-it/library/system.drawing.image.getthumbnailimage.aspx]
  2. Graphics.FillRectangle Method (Brush, Rectangle) [http://msdn.microsoft.com/en-us/library/yysstebh.aspx
  3. Metodo Graphics.DrawImage (Image, Int32, Int32, Int32, Int32) [http://msdn.microsoft.com/it-it/library/dbsak4dc(v=VS.80).aspx]

Recent Comments

By: Liccardi Antonio Posted on 9 Apr 2010 1:58

Complimenti! Davvero un bel post, magari avessi avuto il codice di esempio in passato... avrei risolto un sacco di rogne :)

By: ilNero Posted on 9 Apr 2010 11:01

Grazie Antonio! ...Sperando che questo sia il primo post di una lunga serie :)

By: lucarob Posted on 28 May 2010 15:49

Ottimo post, complimenti

Associazione Culturale DotNetCampania - C.F.: 95127870632

Powered by Community Server (Commercial Edition), by Telligent Systems