2012-03-10 14 views
12

Ho GradientStopCollection:ottenere il colore in una posizione specifica sul gradiente

GradientStopCollection grsc = new GradientStopCollection(3); 
grsc.Add(new GradientStop(Colors.Red, 0)); 
grsc.Add(new GradientStop(Colors.Yellow, .5)); 
grsc.Add(new GradientStop(Colors.Green, 1)); 

Posso ottenere il colore nella posizione specifica? Ad esempio: Ubicazione 0: Rosso Posizione .5: Giallo Posizione .75: ??

C'è una classe di terze parti che può farlo?

+0

Non penso che questo sia definito da nessuna parte in WPF. Mi aspetto che dipenda dall'implementazione del driver della scheda video, dal livello di zoom, dalla profondità del colore degli utenti, ecc. È possibile utilizzare il metodo Visual.PointToScreen e poi Graphics.CopyFromScreen per catturare quel pixel. Quindi utilizzare Bitmap.GetPixel per recuperare i dettagli del colore. – akhisp

risposta

17

Per ottenere un colore in un punto specifico è necessario comprendere la sfumatura in questione e questo non è il ruolo della classe GradientStopCollection. Il concetto di questa classe non è capire un gradiente, ma dovrebbe essere una semplice raccolta di supporto a un gradiente.

È importante comprendere il concetto di ogni classe.

Per ottenere un colore, è necessario creare un'istanza di una classe che rappresenta un gradiente utilizzando il gradiente per dipingere e ottenere infine il colore dal dipinto.

ma ti darò una soluzione più rapida. È possibile utilizzare un algoritmo di sfumatura per generare un singolo punto. Si tratta di un'implementazione di come fare questo utilizzando un algoritmo gradiente lineare:

public static class GradientStopCollectionExtensions 
{ 
    public static Color GetRelativeColor(this GradientStopCollection gsc, double offset) 
    { 
     GradientStop before = gsc.Where(w => w.Offset == gsc.Min(m => m.Offset)).First(); 
     GradientStop after = gsc.Where(w => w.Offset == gsc.Max(m => m.Offset)).First(); 

     foreach (var gs in gsc) 
     { 
      if (gs.Offset < offset && gs.Offset > before.Offset) 
      { 
       before = gs; 
      } 
      if (gs.Offset > offset && gs.Offset < after.Offset) 
      { 
       after = gs; 
      } 
     } 

     var color = new Color(); 

     color.ScA = (float)((offset - before.Offset) * (after.Color.ScA - before.Color.ScA)/(after.Offset - before.Offset) + before.Color.ScA); 
     color.ScR = (float)((offset - before.Offset) * (after.Color.ScR - before.Color.ScR)/(after.Offset - before.Offset) + before.Color.ScR); 
     color.ScG = (float)((offset - before.Offset) * (after.Color.ScG - before.Color.ScG)/(after.Offset - before.Offset) + before.Color.ScG); 
     color.ScB = (float)((offset - before.Offset) * (after.Color.ScB - before.Color.ScB)/(after.Offset - before.Offset) + before.Color.ScB); 

     return color; 
    } 
} 

Aggiungi questa classe nel contesto attuale (contesto namespace)

Per ottenere il colore in qualsiasi luogo si inserisce qualcosa di simile a questo:

var color = grsc.GetRelativeColor(.75); 
+1

Johnny, pensi che potresti venire su http://stackoverflow.com/questions/16161931/how-to-read-the-color-from-an-offset-of-a-xaml-lineargradientbrush e pubblicare questa risposta ? Mi piacerebbe che tu prendessi i punti. –

+0

Questo è esattamente quello che stavo cercando, con un difetto che se l'offset è esattamente uguale a un gradiente, ignorerà completamente quella sfumatura. Da qui la mia modifica. – Underdetermined

+0

@Underdeterminato: uh, oh, ... e dov'è quella modifica? – quetzalcoatl

0
foreach (var gs in gsc) 
      { 
       if (gs.Offset == offset) return gs.Color; //new line added 
       if (gs.Offset < offset && gs.Offset > before.Offset) 
       { 
        before = gs; 
       } 

       if (gs.Offset > offset && gs.Offset < after.Offset) 
       { 
        after = gs; 
       } 
      } 
2

ho provato il metodo scritto da Jonny Piazzi. Ma non ha funzionato correttamente.
Così ho scrivere la mia uno sotto:

private static Color GetColorByOffset(GradientStopCollection collection, double offset) 
{ 
    GradientStop[] stops = collection.OrderBy(x => x.Offset).ToArray(); 
    if (offset <= 0) return stops[0].Color; 
    if (offset >= 1) return stops[stops.Length - 1].Color; 
    GradientStop left = stops[0], right = null; 
    foreach (GradientStop stop in stops) 
    { 
     if (stop.Offset >= offset) 
     { 
      right = stop; 
      break; 
     } 
     left = stop; 
    } 
    Debug.Assert(right != null); 
    offset = Math.Round((offset - left.Offset)/(right.Offset - left.Offset), 2); 
    byte a = (byte) ((right.Color.A - left.Color.A)*offset + left.Color.A); 
    byte r = (byte) ((right.Color.R - left.Color.R)*offset + left.Color.R); 
    byte g = (byte) ((right.Color.G - left.Color.G)*offset + left.Color.G); 
    byte b = (byte) ((right.Color.B - left.Color.B)*offset + left.Color.B); 
    return Color.FromArgb(a, r, g, b); 
} 

Spero che lavora per voi!

Ho usato questo metodo nel mio codice xaml qui sotto per mostrare un numero specificato come posizione della mappa di calore.

<LinearGradientBrush x:Key="CountBrush" StartPoint="0 0" EndPoint="1 0"> 
    <GradientStop Offset="0.00" Color="ForestGreen"/> 
    <GradientStop Offset="0.50" Color="Yellow"/> 
    <GradientStop Offset="1.00" Color="OrangeRed"/> 
</LinearGradientBrush> 
<local:Int32ToColorConverter x:Key="CountToColorConverter" Min="0" Max="200" LinearBrush="{StaticResource CountBrush}"/> 
+0

Mi piace usare la pausa per evitare inutili iterazioni – Wobbles

+0

Puoi semplificare il codice rimuovendo le iterazioni visibili e usando qualcosa come 'GradientStop left = stops.Where (s => s.Offset <= offset) .Last(); GradientStop right = stops.Where (s => s.Offset> offset) .First(); ' – Wobbles

+0

Grazie! Ha davvero semplificato il mio codice. In questo caso, tutti i miei "' {'" e "'} '" sono spariti. Ma suppongo che dovresti usare 'FirstOrDefault' e' LastOrDefault' con '??' invece di 'First' e' Last'. – walterlv

Problemi correlati