- Home /
How to get Sprite color palette
Hey there,
I have a psb image which contains of multiple layers, each layer represents a gameObject and I would love to get the color of each of those elements... usually each gameObject contains only 1-3 colors and i would love to know which ones these are.
Is there any other / better approach to get that information ?
My scripts is as follows:
using UnityEngine;
using UnityEngine.Rendering;
[System.Serializable]
public class PageData : MonoBehaviour
{
private void Start()
{
foreach (Transform child in this.transform)
{
Sprite sprite = child.GetComponent<SpriteRenderer>().sprite;
Color[] pix = sprite.texture.GetPixels();
Debug.Log( pix );
// ?
}
}
}
If you look into Sprite documentation page, you will see that there is a texture
property already.
Sprite sprite = child.GetComponent<SpriteRenderer>().sprite;
Texture2D sourceTex = sprite.texture;
Good to know, thx.
However even with this simplified script i still get the out of bounds error...
private void Start()
{
foreach (Transform child in this.transform)
{
Sprite sprite = child.GetComponent<SpriteRenderer>().sprite;
Texture2D sourceTex = sprite.texture;
Rect sourceRect = child.gameObject.GetComponent<RectTransform>().rect;
int x = $$anonymous$$athf.FloorToInt(sourceRect.x);
int y = $$anonymous$$athf.FloorToInt(sourceRect.y);
int width = $$anonymous$$athf.FloorToInt(sourceRect.width);
int height = $$anonymous$$athf.FloorToInt(sourceRect.height);
Color[] pix = sourceTex.GetPixels(x, y, width, height);
}
}
You're seeing this error because RectTransform
values are not texture coordinates but ui hierarchy coordinates. It's close equivalent to using transform.position
as texture x and y - not an ideal fit.
That being said - you can probably construct a RectTransform hierarchy to mirror texture coordinates OR figure out a way of calculating one from the other
Color[] pix = sourceTex.GetPixels();
That's it.
Alternatively:
Color[] pix = sourceTex.GetPixels( 0 , 0 , sourceTex.width , sourceTex.height );
But the result is the same.
ok this seems to work without any errors, thx a lot...
However, it seems that unity is struggling quite a bit now... showing the color arry in the editor shows that each image as 2-10 million pixels and unity is really struggling.
$$anonymous$$aybe my approach is also the wrong one here... What I would love to achieve is the following:
I have a psb image which contains of multiple layers, each layer represents a gameObject and I would love to get the color of each of those elements... usually each gameObject contains only 1-3 colors and i would love to know which ones these are.
Is there any other / better approach to get that information ?
Answer by andrew-lukasik · Sep 29, 2020 at 10:31 AM
That's right. Going through Color[]
to compile color palette data in an inefficient process ideally should be done as pre-processing step (in editor as asset/scene preparation) and saved for easy and fast access while application is running
Click RMB over component in Inspector window and choose "Update Color Data"
to update.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class PageData : MonoBehaviour
{
[SerializeField] ColorTrio _sharedColors;
[SerializeField] SpriteRenderer[] _spriteRenderers;
[SerializeField] ColorTrio[] _colors;
void Start ()
{
for( int i=0 ; i<_spriteRenderers.Length ; i++ )
{
var spriteRenderer = _spriteRenderers[i];
var color = _colors[i];
Debug.Log( $"{spriteRenderer.name}'s colors: {color}" , spriteRenderer.gameObject );
}
}
[ContextMenu("Update Color Data")]
void UpdateColorData ()
{
_spriteRenderers = GetComponentsInChildren<SpriteRenderer>( includeInactive:true );
_colors = new ColorTrio[ _spriteRenderers.Length ];
Dictionary<Color,int> shared_palette_unsorted = new Dictionary<Color,int>();
for( int i=0 ; i<_spriteRenderers.Length ; i++ )
{
var spriteRenderer = _spriteRenderers[i];
// compile sorted array of colors (from most to least common one):
Color[] palette;
{
Dictionary<Color,int> palette_unsorted = new Dictionary<Color,int>();//to represent Color and number of pixels of that Color
Sprite sprite = spriteRenderer.sprite;
Rect spriteRect = sprite.rect;
Color[] spritePixels = sprite.texture.GetPixels( (int)spriteRect.x , (int)spriteRect.y , (int)spriteRect.width , (int)spriteRect.height );
foreach( var color in spritePixels )
{
if( color.a!=1 ) continue;// lets skip all transparent and semi-transparent pixels
if( palette_unsorted.ContainsKey(color) )
palette_unsorted[color]++;// another pixel of this color
else
palette_unsorted.Add( color , 1 );// first
}
palette = palette_unsorted
.OrderByDescending( (kv)=>kv.Value )
.Select( (kv)=> kv.Key )
.ToArray();
foreach( var kv in palette_unsorted )
{
Color color = kv.Key;
int count = kv.Value;
if( shared_palette_unsorted.ContainsKey(color) )
shared_palette_unsorted[color] += count;
else
shared_palette_unsorted.Add( color , count );
}
}
// pick colors:
{
ColorTrio trio = new ColorTrio();
trio.primary = palette[0];
trio.secondary = palette.FirstOrDefault( (col)=> col!=trio.primary );// will result in Color(0,0,0,0) if none found
trio.tertiary = palette.FirstOrDefault( (col)=> col!=trio.primary && col!=trio.secondary );// will result in Color(0,0,0,0) if none found
_colors[i] = trio;
}
#if DEBUG
// print debug info
{
var sb = new System.Text.StringBuilder();
foreach( var color in palette ) sb.AppendLine( $"\tcolor: {color}" );
Debug.Log( $"'{spriteRenderer.name}' colors: {_colors[i].primary}, {_colors[i].secondary}, {_colors[i].tertiary}\nall colors:\n{sb}" , spriteRenderer.gameObject );
}
#endif
}
// pick shared colors:
{
Color[] shared_sorted = shared_palette_unsorted
.OrderByDescending( (kv)=>kv.Value )
.Select( (kv)=> kv.Key )
.ToArray();
ColorTrio trio = new ColorTrio();
trio.primary = shared_sorted[0];
trio.secondary = shared_sorted.FirstOrDefault( (col)=> col!=trio.primary );// will result in Color(0,0,0,0) if none found
trio.tertiary = shared_sorted.FirstOrDefault( (col)=> col!=trio.primary && col!=trio.secondary );// will result in Color(0,0,0,0) if none found
_sharedColors = trio;
#if DEBUG
Debug.Log( $"'{this.name}' SHARED COLORS: {_sharedColors.primary}, {_sharedColors.secondary}, {_sharedColors.tertiary}" , gameObject );
#endif
}
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty( this );
#endif
}
public ColorTrio GetColorsFor ( SpriteRenderer spriteRenderer )
{
for( int i=0 ; i<_spriteRenderers.Length ; i++ )
{
if( _spriteRenderers[i]==spriteRenderer )
return _colors[i];
}
// else not found:
Debug.LogWarning( $"'{spriteRenderer.name}' not found among '{this.name}' SpriteRenderers" , spriteRenderer );
return default(ColorTrio);
}
public ColorTrio GetColorsFor ( GameObject go )
{
for( int i=0 ; i<_spriteRenderers.Length ; i++ )
{
if( _spriteRenderers[i].gameObject==go )
return _colors[i];
}
// else not found:
Debug.LogWarning( $"'{go.name}' not found among '{this.name}' SpriteRenderers" , go );
return default(ColorTrio);
}
[System.Serializable]
public struct ColorTrio
{
public Color primary, secondary, tertiary;
public override string ToString () => $"( primary:{primary} , secondary:{secondary} , tertiary:{tertiary} )";
}
}
Understood, the issue is that i want to make a color by number game, meaning each picture consits of up to 300 parts (GameObjects) setting the proper colour for each of them is literally a pain in the ***, therefore i made a shader to recolour the gameobjects white upon game start. In order to deter$$anonymous$$e now which colors are being needed (show the buttons etc.) and setting up ids for each gameobject to deter$$anonymous$$e wether the right color got selected upon taping these i would need to know the acctual color of each sprite...
$$anonymous$$aybe there is also a different option like using a polygon collider and getting the centerpoint of that or something?!
Consider creating a lookup table of colors for each SpriteRenderer
(sample code added above)
Sorry for the confusion - I just updated the answer with code that actually works now :)
[Context$$anonymous$$enu("Update Color Data")]
adds an Inspector context menu with that name - click over this component with R$$anonymous$$B and this new option should be right there now (it will update both arrays automagically)