[NUI] Introduce NUI Palette APIs
authorWoochanlee <wc0917.lee@samsung.com>
Tue, 16 Feb 2021 11:04:00 +0000 (20:04 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Thu, 25 Feb 2021 08:24:58 +0000 (17:24 +0900)
src/Tizen.NUI/Tizen.NUI.csproj
src/Tizen.NUI/src/internal/Utility/ColorCutQuantizer.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/Utility/ColorHistogram.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/Utility/ColorUtils.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/Utility/Palette.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/PaletteSample.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Tizen.NUI.Samples.csproj
test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/10by10tree.png [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/red2.jpg [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/rock.jpg [new file with mode: 0755]

index bcffc90..0106f8d 100644 (file)
@@ -14,6 +14,7 @@
     <PackageReference Include="System.Reflection" Version="4.3.0" />
     <PackageReference Include="System.Reflection.TypeExtensions" Version="4.3.0" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
+    <PackageReference Include="System.Drawing.Common" Version="5.0.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Tizen.NUI/src/internal/Utility/ColorCutQuantizer.cs b/src/Tizen.NUI/src/internal/Utility/ColorCutQuantizer.cs
new file mode 100755 (executable)
index 0000000..e432f53
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Modified by Woochan Lee(wc0917.lee@samsung.com)
+ */
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tizen.NUI
+{
+    internal sealed class ColorCutQuantizer
+    {
+        private const float blackMaxLightness = 0.05f;
+        private const float whiteMinLightness = 0.95f;
+        private const int componentRed = -3;
+        private const int componentGreen = -2;
+        private const int componentBlue = -1;
+
+        private static Dictionary<int, int> colorPopulations;
+        private static int[] colors;
+        private List<Palette.Swatch> quantizedColors;
+        private float[] tempHsl = new float[3];
+
+        private ColorCutQuantizer(ColorHistogram colorHistogram, int maxColors)
+        {
+            if (colorHistogram == null)
+            {
+                throw new ArgumentNullException(nameof(colorHistogram), "colorHistogram should not be null.");
+            }
+
+            if (maxColors < 1)
+            {
+                throw new ArgumentNullException(nameof(maxColors), "maxColors should not be null.");
+            }
+
+            int rawColorCount = colorHistogram.GetNumberOfColors();
+            int[] rawColors = colorHistogram.GetColors();
+            int[] rawColorCounts = colorHistogram.GetColorCounts();
+
+            // First, lets pack the populations into a SparseIntArray so that they can be easily
+            // retrieved without knowing a color's index
+            colorPopulations = new Dictionary<int, int>();
+
+            for (int i = 0; i < rawColors.Length; i++)
+            {
+                System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(rawColors[i]);
+                colorPopulations.Add(rawColors[i], rawColorCounts[i]);
+            }
+
+            // Now go through all of the colors and keep those which we do not want to ignore
+            colors = new int[rawColorCount];
+            int validColorCount = 0;
+
+            foreach (int color in rawColors)
+            {
+                System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color);
+                if (!ShouldIgnoreColor(color))
+                {
+                    colors[validColorCount++] = color;
+                }
+            }
+
+            Tizen.Log.Info("Palette", "ValidColorCount = " + validColorCount + "\n");
+            if (validColorCount <= maxColors)
+            {
+                // The image has fewer colors than the maximum requested, so just return the colors
+                quantizedColors = new List<Palette.Swatch>();
+
+                for (int i = 0; i < validColorCount; i++)
+                {
+                    quantizedColors.Add(new Palette.Swatch(colors[i], colorPopulations[colors[i]]));
+                }
+            }
+            else
+            {
+                // We need use quantization to reduce the number of colors
+                quantizedColors = QuantizePixels(validColorCount - 1, maxColors);
+            }
+        }
+
+        /// <summary>
+        /// Factory-method to generate a ColorCutQuantizer from a  Bitmap object.
+        /// </summary>
+        public static ColorCutQuantizer FromBitmap(Bitmap bitmap, Tizen.NUI.Rectangle region, int maxColors)
+        {
+            int width;
+            int height;
+            int[] pixels;
+            int i, j, index = 0;
+
+            if (region == null)
+            {
+                width = bitmap.Width; height = bitmap.Height; i = 0; j = 0;
+            }
+
+            else
+            {
+                width = region.Width; height = region.Height; i = region.X; j = region.Y;
+            }
+            pixels = new int[width * height];
+
+            Tizen.Log.Info("Palette", "Get pixels rawdata from (" + i + " " + j + " " + width + " " + height + ")" + "\n");
+            for (i = 0; i < bitmap.Width; i++)
+                for (j = 0; j < bitmap.Height; j++)
+                {
+                    //FIXME: GetPixel is slow... Convert int also not good to use for all the pixels
+                    //       Need to improve performance Using LockBit or FastGetPixel implement
+                    System.Drawing.Color Color = bitmap.GetPixel(i, j);
+                    pixels[index++] = Convert.ToInt32(Color.ToArgb());
+                }
+
+            return new ColorCutQuantizer(new ColorHistogram(pixels), maxColors);
+        }
+
+        /// <summary>
+        /// return the list of quantized colors
+        /// </summary>
+        public List<Palette.Swatch> GetQuantizedColors()
+        {
+            return quantizedColors;
+        }
+
+        private List<Palette.Swatch> QuantizePixels(int maxColorIndex, int maxColors)
+        {
+            // Create the priority queue which is sorted by volume descending. This means we always
+            // split the largest box in the queue
+            CustomHeap<Vbox> customHeap = new CustomHeap<Vbox>(new VboxComparatorVolume());
+            // To start, offer a box which contains all of the colors
+            customHeap.Offer(new Vbox(0, maxColorIndex));
+            // Now go through the boxes, splitting them until we have reached maxColors or there are no
+            // more boxes to split
+            SplitBoxes(customHeap, maxColors);
+            // Finally, return the average colors of the color boxes
+            return GenerateAverageColors(customHeap);
+        }
+
+        /// <summary>
+        /// Iterate through the queue, popping
+        /// ColorCutQuantizer.Vbox objects from the queue
+        /// and splitting them. Once split, the new box and the remaining box are offered back to the
+        /// queue.
+        ///
+        /// param queue PriorityQueue to poll for boxes
+        /// param maxSize Maximum amount of boxes to split
+        /// </summary>
+        private void SplitBoxes(CustomHeap<Vbox> queue, int maxSize)
+        {
+            while (queue.count < maxSize)
+            {
+                Vbox vbox = queue.Poll();
+                if (vbox != null && vbox.CanSplit())
+                {
+                    // First split the box, and offer the result
+                    queue.Offer(vbox.SplitBox());
+                    // Then offer the box back
+                    queue.Offer(vbox);
+                }
+                else
+                {
+                    // If we get here then there are no more boxes to split, so return
+                    return;
+                }
+            }
+        }
+
+        private List<Palette.Swatch> GenerateAverageColors(CustomHeap<Vbox> vboxes)
+        {
+            List<Palette.Swatch> colors = new List<Palette.Swatch>(vboxes.count);
+            foreach (Vbox vbox in vboxes)
+            {
+                Palette.Swatch color = vbox.GetAverageColor();
+                if (!ShouldIgnoreColor(color))
+                {
+                    // As we're averaging a color box, we can still get colors which we do not want, so
+                    // we check again here
+                    colors.Add(color);
+                }
+            }
+            Tizen.Log.Info("Palette", "Final generated color count = " + colors.Count + "\n");
+            return colors;
+        }
+
+        private Boolean ShouldIgnoreColor(int color)
+        {
+            System.Drawing.Color convertColor = System.Drawing.Color.FromArgb(color);
+            ColorUtils.RgbToHsl(convertColor.R, convertColor.G, convertColor.B, tempHsl);
+            return ShouldIgnoreColor(tempHsl);
+        }
+
+        private static Boolean ShouldIgnoreColor(Palette.Swatch color)
+        {
+            return ShouldIgnoreColor(color.GetHsl());
+        }
+
+        private static Boolean ShouldIgnoreColor(float[] hslColor)
+        {
+            return IsWhite(hslColor) || IsBlack(hslColor) || IsNearRedILine(hslColor);
+        }
+
+        /// <summary>
+        ///return true if the color represents a color which is close to black.
+        /// </summary>
+        private static Boolean IsBlack(float[] hslColor)
+        {
+            return hslColor[2] <= blackMaxLightness;
+        }
+
+        /// <summary>
+        /// return true if the color represents a color which is close to white.
+        /// </summary>
+        private static Boolean IsWhite(float[] hslColor)
+        {
+            return hslColor[2] >= whiteMinLightness;
+        }
+
+        /// <summary>
+        /// return true if the color lies close to the red side of the I line.
+        /// </summary>
+        private static Boolean IsNearRedILine(float[] hslColor)
+        {
+            return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
+        }
+
+        private sealed class CustomHeap<T> : IEnumerable<T>
+        {
+            private const int initialcapacity = 0;
+            private const int growFactor = 2;
+            private const int minGrow = 1;
+
+            private int capacity = initialcapacity;
+            private int tail = 0;
+            private T[] heap = Array.Empty<T>();
+
+            public CustomHeap(Comparer<T> comparer)
+            {
+                if (comparer == null) throw new ArgumentNullException(nameof(comparer), "comparer is null");
+                Comparer = comparer;
+            }
+
+            private Comparer<T> Comparer { get; set; }
+
+            public IEnumerator<T> GetEnumerator()
+            {
+                return heap.Take(count).GetEnumerator();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+
+            public int count { get { return tail; } }
+
+            public void Offer(T item)
+            {
+                if (count == capacity)
+                    Grow();
+
+                heap[tail++] = item;
+                BubbleUp(tail - 1);
+            }
+
+            public T Peek()
+            {
+                if (count == 0) throw new InvalidOperationException("CustomHeap is empty");
+                return heap[0];
+            }
+
+            public T Poll()
+            {
+                if (count == 0) throw new InvalidOperationException("CustomHeap is empty");
+                T ret = heap[0];
+
+                tail--;
+                Swap(tail, 0);
+                BubbleDown(0);
+
+                return ret;
+            }
+
+            private void BubbleDown(int i)
+            {
+                int dominatingNode = Dominating(i);
+
+                if (dominatingNode == i) return;
+                Swap(i, dominatingNode);
+                BubbleDown(dominatingNode);
+            }
+
+            private void BubbleUp(int i)
+            {
+                if (i == 0 || Dominates(heap[Parent(i)], heap[i]))
+                    return;
+
+                Swap(i, Parent(i));
+                BubbleUp(Parent(i));
+            }
+
+            private int Dominating(int i)
+            {
+                int dominatingNode = i;
+
+                dominatingNode = GetDominating(YoungChild(i), dominatingNode);
+                dominatingNode = GetDominating(OldChild(i), dominatingNode);
+                return dominatingNode;
+            }
+
+            private int GetDominating(int newNode, int dominatingNode)
+            {
+                if (newNode < tail && !Dominates(heap[dominatingNode], heap[newNode]))
+                    return newNode;
+                else
+                    return dominatingNode;
+            }
+
+            private void Swap(int i, int j)
+            {
+                T tmp = heap[i];
+
+                heap[i] = heap[j];
+                heap[j] = tmp;
+            }
+
+            private static int Parent(int i)
+            {
+                return (i + 1) / 2 - 1;
+            }
+
+            private static int YoungChild(int i)
+            {
+                return (i + 1) * 2 - 1;
+            }
+
+            private static int OldChild(int i)
+            {
+                return YoungChild(i) + 1;
+            }
+
+            private void Grow()
+            {
+                int newcapacity = capacity * growFactor + minGrow;
+                var newHeap = new T[newcapacity];
+
+                Array.Copy(heap, newHeap, capacity);
+                heap = newHeap;
+                capacity = newcapacity;
+            }
+
+            private bool Dominates(T x, T y)
+            {
+                return Comparer.Compare(x, y) <= 0;
+            }
+
+        }
+
+        /// <summary>
+        /// Comparator which sorts Vbox instances based on their volume, in descending order
+        /// </summary>
+        private sealed class VboxComparatorVolume : Comparer<Vbox>
+        {
+            public override int Compare(Vbox lhs, Vbox rhs)
+            {
+                return rhs.GetVolume() - lhs.GetVolume();
+            }
+        }
+
+        /// <summary>
+        /// Represents a tightly fitting box around a color space.
+        /// </summary>
+        private sealed class Vbox
+        {
+            private int lowerIndex;
+            private int upperIndex;
+            private int minRed, maxRed, minGreen, maxGreen, minBlue, maxBlue;
+
+            public Vbox(int lowerIndex, int upperIndex)
+            {
+                this.lowerIndex = lowerIndex;
+                this.upperIndex = upperIndex;
+                FitBox();
+            }
+
+            public int GetVolume()
+            {
+                return (maxRed - minRed + 1) * (maxGreen - minGreen + 1) * (maxBlue - minBlue + 1);
+            }
+
+            public Boolean CanSplit()
+            {
+                return GetColorCount() > 1;
+            }
+
+            public int GetColorCount()
+            {
+                return upperIndex - lowerIndex;
+            }
+
+            /// <summary>
+            /// Recomputes the boundaries of this box to tightly fit the colors within the box.
+            /// </summary>
+            public void FitBox()
+            {
+                // Reset the min and max to opposite values
+                minRed = minGreen = minBlue = 0xff;
+                maxRed = maxGreen = maxBlue = 0x0;
+                for (int i = lowerIndex; i <= upperIndex; i++)
+                {
+                    int colorInt = colors[i];
+                    System.Drawing.Color color = System.Drawing.Color.FromArgb(colorInt);
+                    int red = color.R;
+                    int green = color.G;
+                    int blue = color.B;
+                    if (red > maxRed)
+                    {
+                        maxRed = red;
+                    }
+                    if (red < minRed)
+                    {
+                        minRed = red;
+                    }
+                    if (green > maxGreen)
+                    {
+                        maxGreen = green;
+                    }
+                    if (green < minGreen)
+                    {
+                        minGreen = green;
+                    }
+                    if (blue > maxBlue)
+                    {
+                        maxBlue = blue;
+                    }
+                    if (blue < minBlue)
+                    {
+                        minBlue = blue;
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Split this color box at the mid-point along it's longest dimension
+            ///
+            /// return the new ColorBox
+            /// </summary>
+            public Vbox SplitBox()
+            {
+                if (!CanSplit())
+                {
+                    throw new InvalidOperationException("Can not split a box with only 1 color");
+                }
+
+                // find median along the longest dimension
+                int splitPoint = FindSplitPoint();
+                Vbox newBox = new Vbox(splitPoint + 1, upperIndex);
+                // Now change this box's upperIndex and recompute the color boundaries
+                upperIndex = splitPoint;
+                FitBox();
+
+                return newBox;
+            }
+
+            /// <summary>
+            /// return the dimension which this box is largest in
+            /// </summary>
+            public int GetLongestColorDimension()
+            {
+                int redLength = maxRed - minRed;
+                int greenLength = maxGreen - minGreen;
+                int blueLength = maxBlue - minBlue;
+
+                if (redLength >= greenLength && redLength >= blueLength)
+                {
+                    return componentRed;
+                }
+                else if (greenLength >= redLength && greenLength >= blueLength)
+                {
+                    return componentGreen;
+                }
+                else
+                {
+                    return componentBlue;
+                }
+            }
+
+            /// <summary>
+            /// Finds the point within this box's lowerIndex and upperIndex index of where to split.
+            ///
+            /// This is calculated by finding the longest color dimension, and then sorting the
+            /// sub-array based on that dimension value in each color. The colors are then iterated over
+            /// until a color is found with at least the midpoint of the whole box's dimension midpoint.
+            ///
+            /// return the index of the colors array to split from
+            /// </summary>
+            public int FindSplitPoint()
+            {
+                int longestDimension = GetLongestColorDimension();
+                // We need to sort the colors in this box based on the longest color dimension.
+                // As we can't use a Comparator to define the sort logic, we modify each color so that
+                // it's most significant is the desired dimension
+
+                ModifySignificantOctet(longestDimension, lowerIndex, upperIndex);
+
+                Array.Sort(colors, lowerIndex, upperIndex + 1 - lowerIndex);
+
+                // Now revert all of the colors so that they are packed as RGB again
+                ModifySignificantOctet(longestDimension, lowerIndex, upperIndex);
+
+                int dimensionMidPoint = MidPoint(longestDimension);
+                for (int i = lowerIndex; i < upperIndex; i++)
+                {
+                    int colorInt = colors[i];
+                    System.Drawing.Color color = System.Drawing.Color.FromArgb(colorInt);
+                    switch (longestDimension)
+                    {
+                        case componentRed:
+                            if (color.R >= dimensionMidPoint)
+                            {
+                                return i;
+                            }
+                            break;
+                        case componentGreen:
+                            if (color.G >= dimensionMidPoint)
+                            {
+                                return i;
+                            }
+                            break;
+                        case componentBlue:
+                            if (color.B > dimensionMidPoint)
+                            {
+                                return i;
+                            }
+                            break;
+                    }
+                }
+
+                return lowerIndex;
+            }
+
+            /// <summary>
+            /// return the average color of this box.
+            /// </summary>
+            public Palette.Swatch GetAverageColor()
+            {
+                int redSum = 0;
+                int greenSum = 0;
+                int blueSum = 0;
+                int totalPopulation = 0;
+
+                for (int i = lowerIndex; i <= upperIndex; i++)
+                {
+                    int colorInt = colors[i];
+                    System.Drawing.Color color = System.Drawing.Color.FromArgb(colorInt);
+                    int colorPopulation = colorPopulations[colorInt];
+                    totalPopulation += colorPopulation;
+                    redSum += colorPopulation * color.R;
+                    greenSum += colorPopulation * color.G;
+                    blueSum += colorPopulation * color.B;
+                }
+
+                int redAverage = (int)Math.Round(redSum / (float)totalPopulation);
+                int greenAverage = (int)Math.Round(greenSum / (float)totalPopulation);
+                int blueAverage = (int)Math.Round(blueSum / (float)totalPopulation);
+
+                return new Palette.Swatch(redAverage, greenAverage, blueAverage, totalPopulation);
+            }
+
+            /// <summary>
+            /// return the midpoint of this box in the given dimension
+            /// </summary>
+            private int MidPoint(int dimension)
+            {
+                switch (dimension)
+                {
+                    case componentRed:
+                    default:
+                        return (minRed + maxRed) / 2;
+                    case componentGreen:
+                        return (minGreen + maxGreen) / 2;
+                    case componentBlue:
+                        return (minBlue + maxBlue) / 2;
+                }
+            }
+
+            /// <summary>
+            /// Modify the significant octet in a packed color int. Allows sorting based on the value of a
+            /// single color component.
+            ///
+            /// see Vbox#findSplitPoint()
+            /// </summary>
+            private void ModifySignificantOctet(int dimension, int lowIndex, int highIndex)
+            {
+                switch (dimension)
+                {
+                    case componentRed:
+                        // Already in RGB, no need to do anything
+                        break;
+                    case componentGreen:
+                        // We need to do a RGB to GRB swap, or vice-versa
+                        for (int i = lowIndex; i <= highIndex; i++)
+                        {
+                            int color = colors[i];
+                            colors[i] = 255 << 24 | (color >> 8 & 0xff) << 16 | (color >> 16 & 0xff) << 8 | (color & 0xff);
+                        }
+
+                        break;
+                    case componentBlue:
+                        // We need to do a RGB to BGR swap, or vice-versa
+                        for (int i = lowIndex; i <= highIndex; i++)
+                        {
+                            int color = colors[i];
+                            colors[i] = 255 << 24 | (color & 0xff) << 16 | (color >> 8 & 0xff) << 8 | (color >> 16 & 0xff);
+                        }
+                        break;
+                }
+            }
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/internal/Utility/ColorHistogram.cs b/src/Tizen.NUI/src/internal/Utility/ColorHistogram.cs
new file mode 100755 (executable)
index 0000000..e6e4de0
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Modified by Woochan Lee(wc0917.lee@samsung.com)
+ */
+
+using System;
+
+namespace Tizen.NUI
+{
+    internal sealed class ColorHistogram
+    {
+        private int numberColors;
+        private int[] colors;
+        private int[] colorCounts;
+
+        /// <summary>
+        /// A new ColorHistogram instance.
+        /// </summary>
+        public ColorHistogram(int[] pixels)
+        {
+            // Sort the pixels to enable counting below
+            Array.Sort(pixels);
+
+            // Count number of distinct colors
+            numberColors = CountDistinctColors(pixels);
+            Tizen.Log.Info("Palette", "DistinctColor Num= " + numberColors + "\n");
+
+            // Create arrays
+            colors = new int[numberColors];
+            colorCounts = new int[numberColors];
+
+            // Finally count the frequency of each color
+            CountFrequencies(pixels);
+        }
+
+        /// <summary>
+        /// return number of distinct colors in the image.
+        /// </summary>
+        public int GetNumberOfColors()
+        {
+            return numberColors;
+        }
+
+        /// <summary>
+        /// return an array containing all of the distinct colors in the image.
+        /// </summary>
+        public int[] GetColors()
+        {
+            return colors;
+        }
+
+        /// <summary>
+        /// return an array containing the frequency of a distinct colors within the image.
+        /// </summary>
+        public int[] GetColorCounts()
+        {
+            return colorCounts;
+        }
+
+        private static int CountDistinctColors(int[] pixels)
+        {
+            if (pixels.Length < 2)
+            {
+                // If we have less than 2 pixels we can stop here
+                return pixels.Length;
+            }
+            // If we have at least 2 pixels, we have a minimum of 1 color...
+            int colorCount = 1;
+            int currentColor = pixels[0];
+
+            // Now iterate from the second pixel to the end, counting distinct colors
+            for (int i = 1; i < pixels.Length; i++)
+            {
+                // If we encounter a new color, increase the population
+                if (pixels[i] != currentColor)
+                {
+                    currentColor = pixels[i];
+                    colorCount++;
+                }
+            }
+
+            return colorCount;
+        }
+
+        private void CountFrequencies(int[] pixels)
+        {
+            if (pixels.Length == 0)
+            {
+                return;
+            }
+
+            int currentColorIndex = 0;
+            int currentColor = pixels[0];
+
+            colors[currentColorIndex] = currentColor;
+            colorCounts[currentColorIndex] = 1;
+
+            if (pixels.Length == 1)
+            {
+                // If we only have one pixel, we can stop here
+                return;
+            }
+
+            // Now iterate from the second pixel to the end, population distinct colors
+            for (int i = 1; i < pixels.Length; i++)
+            {
+                if (pixels[i] == currentColor)
+                {
+                    // We've hit the same color as before, increase population
+                    colorCounts[currentColorIndex]++;
+                }
+                else
+                {
+                    // We've hit a new color, increase index
+                    currentColor = pixels[i];
+                    currentColorIndex++;
+                    colors[currentColorIndex] = currentColor;
+                    colorCounts[currentColorIndex] = 1;
+                }
+            }
+            Tizen.Log.Info("Palette", "Color Frequence Num= " + currentColorIndex + "\n");
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/internal/Utility/ColorUtils.cs b/src/Tizen.NUI/src/internal/Utility/ColorUtils.cs
new file mode 100755 (executable)
index 0000000..52708e5
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Modified by Woochan Lee(wc0917.lee@samsung.com)
+ */
+
+using System;
+
+namespace Tizen.NUI
+{
+    internal sealed class ColorUtils
+    {
+        private const int minAlphaSearchMaxIterations = 10;
+        private const int minAlphaSearchPrecision = 1;
+
+        /// <summary>
+        /// Convert the ARGB color to its CIE XYZ representative components.
+        ///
+        /// The resulting XYZ representation will use the D65 illuminant and the CIE
+        /// 2° Standard Observer (1931).
+        ///
+        /// outXyz[0] is X [0 ...95.047)
+        /// outXyz[1] is Y [0...100)
+        /// outXyz[2] is Z [0...108.883)
+        ///
+        /// param color  the ARGB color to convert. The alpha component is ignored
+        /// param outXyz 3-element array which holds the resulting LAB components
+        /// </summary>
+        public static void ColorToXyz(int color, double[] outXyz)
+        {
+            System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color);
+            RgbToXyz(rgbColor.R, rgbColor.G, rgbColor.B, outXyz);
+        }
+
+        /// <summary>
+        /// Convert RGB components to its CIE XYZ representative components.
+        ///
+        /// The resulting XYZ representation will use the D65 illuminant and the CIE
+        /// 2° Standard Observer (1931).
+        ///
+        /// outXyz[0] is X [0 ...95.047)
+        /// outXyz[1] is Y [0...100)
+        /// outXyz[2] is Z [0...108.883)
+        ///
+        /// r      red component value [0..255]
+        /// g      green component value [0..255]
+        /// b      blue component value [0..255]
+        /// outXyz 3-element array which holds the resulting XYZ components
+        /// </summary>
+        public static void RgbToXyz(int red, int green, int blue, double[] outXyz)
+        {
+            if (outXyz.Length != 3)
+            {
+                throw new ArgumentException("Array legnth must be 3", nameof(outXyz));
+            }
+
+            double floatRed = red / 255.0;
+            floatRed = floatRed < 0.04045 ? floatRed / 12.92 : Math.Pow((floatRed + 0.055) / 1.055, 2.4);
+            double floatGreen = green / 255.0;
+            floatGreen = floatGreen < 0.04045 ? floatGreen / 12.92 : Math.Pow((floatGreen + 0.055) / 1.055, 2.4);
+            double floatBlue = blue / 255.0;
+            floatBlue = floatBlue < 0.04045 ? floatBlue / 12.92 : Math.Pow((floatBlue + 0.055) / 1.055, 2.4);
+
+            outXyz[0] = 100 * (floatRed * 0.4124 + floatGreen * 0.3576 + floatBlue * 0.1805);
+            outXyz[1] = 100 * (floatRed * 0.2126 + floatGreen * 0.7152 + floatBlue * 0.0722);
+            outXyz[2] = 100 * (floatRed * 0.0193 + floatGreen * 0.1192 + floatBlue * 0.9505);
+        }
+
+        /// <summary>
+        /// Returns the luminance of a color as a float between 0.0 and 1.0
+        /// Defined as the Y component in the XYZ representation of color.
+        /// </summary>
+        public static double CalculateLuminance(int color)
+        {
+            double[] result = new double[3];
+            ColorToXyz(color, result);
+            // Luminance is the Y component
+            return result[1] / 100;
+        }
+
+        /// <summary>
+        /// Composite two potentially translucent colors over each other and returns the result.
+        /// </summary>
+        public static int CompositeColors(int foreground, int background)
+        {
+            System.Drawing.Color foreColor = System.Drawing.Color.FromArgb(foreground);
+            System.Drawing.Color backColor = System.Drawing.Color.FromArgb(background);
+            int bgAlpha = backColor.A;
+            int fgAlpha = foreColor.A;
+
+            int alpha = CompositeAlpha(fgAlpha, bgAlpha);
+
+            int red = CompositeComponent(foreColor.R, fgAlpha,
+                    backColor.R, bgAlpha, alpha);
+            int green = CompositeComponent(foreColor.G, fgAlpha,
+                    backColor.G, bgAlpha, alpha);
+            int blue = CompositeComponent(foreColor.B, fgAlpha,
+                    backColor.B, bgAlpha, alpha);
+
+            return ((alpha & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
+        }
+
+        /// <summary>
+        /// Returns the contrast ratio between foreground and background.
+        /// background must be opaque.
+        ///
+        /// Formula defined
+        /// <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
+        /// </summary>
+        public static double CalculateContrast(int foreground, int background)
+        {
+            if (((background >> 24) & 0xff) != 255)
+            {
+                throw new ArgumentException("background can not be translucent.");
+            }
+            if (((foreground >> 24) & 0xff) < 255)
+            {
+                System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(foreground);
+
+                // If the foreground is translucent, composite the foreground over the background
+                foreground = CompositeColors(foreground, background);
+                rgbColor = System.Drawing.Color.FromArgb(foreground);
+            }
+
+            double luminance1 = CalculateLuminance(foreground) + 0.05;
+            double luminance2 = CalculateLuminance(background) + 0.05;
+
+            // Now return the lighter luminance divided by the darker luminance
+            return Math.Max(luminance1, luminance2) / Math.Min(luminance1, luminance2);
+        }
+
+        /// <summary>
+        /// Set the alpha component of color to be alpha.
+        /// </summary>
+        public static int SetAlphaComponent(int color, int alpha)
+        {
+            if (alpha < 0 || alpha > 255)
+            {
+                throw new ArgumentException("alpha must be between 0 and 255.");
+            }
+
+            return (color & 0x00ffffff) | (alpha << 24);
+        }
+
+        /// <summary>
+        /// Calculates the minimum alpha value which can be applied to foreground so that would
+        /// have a contrast value of at least minContrastRatio when compared to
+        /// background.
+        ///
+        /// param foreground       the foreground color
+        /// param background       the opaque background color
+        /// param minContrastRatio the minimum contrast ratio
+        /// return the alpha value in the range 0-255, or -1 if no value could be calculated
+        /// </summary>
+        public static int CalculateMinimumAlpha(int foreground, int background,
+                float minContrastRatio)
+        {
+            if (((background >> 24) & 0xff) != 255)
+            {
+                throw new ArgumentException("background can not be translucent");
+            }
+
+            // First lets check that a fully opaque foreground has sufficient contrast
+            int testForeground = SetAlphaComponent(foreground, 255);
+            System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(testForeground);
+            double testRatio = CalculateContrast(testForeground, background);
+
+            if (testRatio < minContrastRatio)
+            {
+                // Fully opaque foreground does not have sufficient contrast, return error
+                return -1;
+            }
+
+            // Binary search to find a value with the minimum value which provides sufficient contrast
+            int numIterations = 0;
+            int minAlpha = 0;
+            int maxAlpha = 255;
+
+            while (numIterations <= minAlphaSearchMaxIterations &&
+                    (maxAlpha - minAlpha) > minAlphaSearchPrecision)
+            {
+                int testAlpha = (minAlpha + maxAlpha) / 2;
+
+                testForeground = SetAlphaComponent(foreground, testAlpha);
+                rgbColor = System.Drawing.Color.FromArgb(testForeground);
+                testRatio = CalculateContrast(testForeground, background);
+                if (testRatio < minContrastRatio)
+                {
+                    minAlpha = testAlpha;
+                }
+                else
+                {
+                    maxAlpha = testAlpha;
+                }
+
+                numIterations++;
+            }
+
+            // Conservatively return the max of the range of possible alphas, which is known to pass.
+            return maxAlpha;
+        }
+
+        public static void RgbToHsl(int red, int green, int blue, float[] hsl)
+        {
+            float floatRed = red / 255f;
+            float floatGreen = green / 255f;
+            float floatBlue = blue / 255f;
+            float max = Math.Max(floatRed, Math.Max(floatGreen, floatBlue));
+            float min = Math.Min(floatRed, Math.Min(floatGreen, floatBlue));
+            float deltaMaxMin = max - min;
+            float hue, saturation;
+            float lightness = (max + min) / 2f;
+            if (max == min)
+            {
+                // Monochromatic
+                hue = saturation = 0f;
+            }
+            else
+            {
+                if (max == floatRed)
+                {
+                    hue = ((floatGreen - floatBlue) / deltaMaxMin) % 6f;
+                }
+                else if (max == floatGreen)
+                {
+                    hue = ((floatBlue - floatRed) / deltaMaxMin) + 2f;
+                }
+                else
+                {
+                    hue = ((floatRed - floatGreen) / deltaMaxMin) + 4f;
+                }
+                saturation = deltaMaxMin / (1f - Math.Abs(2f * lightness - 1f));
+            }
+            hsl[0] = ((hue * 60f) + 360f) % 360f;
+            hsl[1] = saturation;
+            hsl[2] = lightness;
+        }
+
+        public static int HslToRgb(float[] hsl)
+        {
+            float hue = hsl[0];
+            float saturation = hsl[1];
+            float lightness = hsl[2];
+            float constC = (1f - Math.Abs(2 * lightness - 1f)) * saturation;
+            float constM = lightness - 0.5f * constC;
+            float constX = constC * (1f - Math.Abs((hue / 60f % 2f) - 1f));
+            int hueSegment = (int)hue / 60;
+            int red = 0, green = 0, blue = 0;
+            switch (hueSegment)
+            {
+                case 0:
+                    red = (int)Math.Round(255 * (constC + constM));
+                    green = (int)Math.Round(255 * (constX + constM));
+                    blue = (int)Math.Round(255 * constM);
+                    break;
+                case 1:
+                    red = (int)Math.Round(255 * (constX + constM));
+                    green = (int)Math.Round(255 * (constC + constM));
+                    blue = (int)Math.Round(255 * constM);
+                    break;
+                case 2:
+                    red = (int)Math.Round(255 * constM);
+                    green = (int)Math.Round(255 * (constC + constM));
+                    blue = (int)Math.Round(255 * (constX + constM));
+                    break;
+                case 3:
+                    red = (int)Math.Round(255 * constM);
+                    green = (int)Math.Round(255 * (constX + constM));
+                    blue = (int)Math.Round(255 * (constC + constM));
+                    break;
+                case 4:
+                    red = (int)Math.Round(255 * (constX + constM));
+                    green = (int)Math.Round(255 * constM);
+                    blue = (int)Math.Round(255 * (constC + constM));
+                    break;
+                case 5:
+                case 6:
+                    red = (int)Math.Round(255 * (constC + constM));
+                    green = (int)Math.Round(255 * constM);
+                    blue = (int)Math.Round(255 * (constX + constM));
+                    break;
+            }
+            red = Math.Max(0, Math.Min(255, red));
+            green = Math.Max(0, Math.Min(255, green));
+            blue = Math.Max(0, Math.Min(255, blue));
+
+            //ARGB Encoding
+            return (255 << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff));
+        }
+
+        /// <summary>
+        /// return luma value according to to XYZ color space in the range 0.0 - 1.0
+        /// </summary>
+        private static int CompositeAlpha(int foregroundAlpha, int backgroundAlpha)
+        {
+            return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
+        }
+
+        private static int CompositeComponent(int fgC, int fgA, int bgC, int bgA, int alpha)
+        {
+            if (alpha == 0) return 0;
+            return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (alpha * 0xFF);
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/public/Utility/Palette.cs b/src/Tizen.NUI/src/public/Utility/Palette.cs
new file mode 100755 (executable)
index 0000000..5f445c0
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Modified by Woochan Lee(wc0917.lee@samsung.com)
+ */
+
+using System;
+using System.Drawing;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace Tizen.NUI
+{
+    /// <summary>
+    /// A helper class to extract prominent colors from an image.
+    ///
+    /// A number of colors with different profiles are extracted from the image:
+    /// Vibrant, Vibrant Dark, Vibrant Light, Muted, Muted Dark, Muted Light
+    ///
+    /// These can be retrieved from the appropriate getter method.
+    ///
+    /// Palette supports both synchronous and asynchronous generation:
+    ///
+    /// Synchronous
+    /// Palette P = Palette.Generate(bitmap);
+    ///
+    /// Asynchronous
+    /// Palette.GenerateAsync(bitmap, (Palette p) => {
+    ///     // Use generated instance
+    /// }};
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public sealed class Palette
+    {
+        private const int defaultCalculateNumberColors = 16;
+        private const int calculateBitmapMinDimension = 100;
+        private const float targetDarkLuma = 0.26f;
+        private const float maxDarkLuma = 0.45f;
+        private const float minLightLuma = 0.55f;
+        private const float targetLightLuma = 0.74f;
+        private const float minNormalLuma = 0.3f;
+        private const float targetNormalLuma = 0.5f;
+        private const float maxNormalLuma = 0.7f;
+        private const float targetMutedSaturation = 0.3f;
+        private const float maxMutedSaturation = 0.4f;
+        private const float targetVibrantSaturation = 1f;
+        private const float minVibrantSaturation = 0.35f;
+
+        private int highestPopulation;
+        private Swatch dominantSwatch;
+        private Swatch vibrantSwatch;
+        private Swatch mutedSwatch;
+        private Swatch darkVibrantSwatch;
+        private Swatch darkMutedSwatch;
+        private Swatch lightVibrantSwatch;
+        private Swatch lightMutedColor;
+        private List<Swatch> swatches;
+
+        private Palette() { }
+
+        private Palette(List<Swatch> swatcheList)
+        {
+            swatches = swatcheList;
+            highestPopulation = FindMaxPopulation();
+            vibrantSwatch = FindColor(targetNormalLuma, minNormalLuma, maxNormalLuma,
+                    targetVibrantSaturation, minVibrantSaturation, 1f);
+
+            lightVibrantSwatch = FindColor(targetLightLuma, minLightLuma, 1f,
+                    targetVibrantSaturation, minVibrantSaturation, 1f);
+
+            darkVibrantSwatch = FindColor(targetDarkLuma, 0f, maxDarkLuma,
+                    targetVibrantSaturation, minVibrantSaturation, 1f);
+
+            mutedSwatch = FindColor(targetNormalLuma, minNormalLuma, maxNormalLuma,
+                    targetMutedSaturation, 0f, maxMutedSaturation);
+
+            lightMutedColor = FindColor(targetLightLuma, minLightLuma, 1f,
+                    targetMutedSaturation, 0f, maxMutedSaturation);
+
+            darkMutedSwatch = FindColor(targetDarkLuma, 0f, maxDarkLuma,
+                    targetMutedSaturation, 0f, maxMutedSaturation);
+            // Now try and generate any missing colors
+            GenerateEmptyswatches();
+
+            // To get swatch infomation as string.
+            String[] swatchInfo = new String[6];
+
+            if (vibrantSwatch != null) swatchInfo[0] = vibrantSwatch.ToString();
+            if (lightVibrantSwatch != null) swatchInfo[1] = lightVibrantSwatch.ToString();
+            if (darkVibrantSwatch != null) swatchInfo[2] = darkVibrantSwatch.ToString();
+            if (mutedSwatch != null) swatchInfo[3] = mutedSwatch.ToString();
+            if (lightMutedColor != null) swatchInfo[4] = lightMutedColor.ToString();
+            if (darkMutedSwatch != null) swatchInfo[5] = darkMutedSwatch.ToString();
+
+            Tizen.Log.Info("Palette", "VibrantSwatch [" + swatchInfo[0] + "] " +
+                                      "lightVibrantSwatch [" + swatchInfo[1] + "] " +
+                                      "darkVibrantSwatch [" + swatchInfo[2] + "] " +
+                                      "MutedSwatch [" + swatchInfo[3] + "] " +
+                                      "lightMutedColor [" + swatchInfo[4] + "] " +
+                                      "darkMutedSwatch [" + swatchInfo[5] + "] \n");
+        }
+
+        public delegate void PaletteGeneratedEventHandler(Palette palette);
+
+        /// <summary>
+        /// Generate a Palette asynchronously using bitmap as source.
+        /// </summary>
+        /// <param name="bitmap">A Target image's bitmap.</param>
+        /// <param name="paletteGeneratedEventHandler">A method will be called with the palette when generated.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument bitmap, PaletteGeneratedEventHandler is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void GenerateAsync(Bitmap bitmap, PaletteGeneratedEventHandler paletteGeneratedEventHandler)
+        {
+            _ = AsyncTask(bitmap, null, paletteGeneratedEventHandler);
+        }
+
+        /// <summary>
+        /// Generate a Palette asynchronously using bitmap as source.
+        /// And set a region of the bitmap to be used exclusively when calculating the palette.
+        /// </summary>
+        /// <param name="bitmap">A Target image's bitmap.</param>
+        /// <param name="region">A rectangle used for region.</param>    
+        /// <param name="paletteGeneratedEventHandler">A method will be called with the palette when generated.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument bitmap, PaletteGeneratedEventHandler is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void GenerateAsync(Bitmap bitmap, Tizen.NUI.Rectangle region, PaletteGeneratedEventHandler paletteGeneratedEventHandler)
+        {
+            _ = AsyncTask(bitmap, region, paletteGeneratedEventHandler);
+        }
+
+        /// <summary>
+        /// Generate a Palette synchronously using bitmap as source.
+        /// </summary>
+        /// <param name="bitmap">A Target image's bitmap.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument bitmap is null.</exception>
+        /// <returns>the palette instance.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static Palette generate(Bitmap bitmap)
+        {
+            return Generate(bitmap, null);
+        }
+
+        /// <summary>
+        /// Generate a Palette synchronously using bitmap as source.
+        /// And set a region of the bitmap to be used exclusively when calculating the palette.
+        /// </summary>
+        /// <param name="bitmap">A Target image's bitmap.</param>
+        /// <param name="region">A rectangle used for region.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument bitmap is null.</exception>
+        /// <returns>the palette instance.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static Palette Generate(Bitmap bitmap, Tizen.NUI.Rectangle region)
+        {
+            Tizen.Log.Info("Palette", "bitmap generate start with region: " + region + "\n");
+            if (bitmap == null)
+            {
+                throw new ArgumentNullException(nameof(bitmap), "bitmap should not be null.");
+            }
+
+            // First we'll scale down the bitmap so it's shortest dimension is 100px
+            // NOTE: scaledBitmap can gets bitmap origin value and new bitmap instance as well
+            //       When ScaleBitmap created newly it will be dispose below.
+            //       otherwise it should not disposed because of this instance from user side.
+            Bitmap scaledBitmap = ScaleBitmapDown(bitmap);
+
+            // Region set
+            if (scaledBitmap != bitmap && region != null)
+            {
+                double scale = scaledBitmap.Width / (double)bitmap.Width;
+                region.X = (int)Math.Floor(region.X * scale);
+                region.Y = (int)Math.Floor(region.Y * scale);
+                region.Width = Math.Min((int)Math.Ceiling(region.Width * scale), bitmap.Width);
+                region.Height = Math.Min((int)Math.Ceiling(region.Height * scale), bitmap.Height);
+            }
+
+            // Now generate a Quantizer from the Bitmap
+            // FIXME: defaultCalculateNumberColors should be changeable?
+            ColorCutQuantizer quantizer = ColorCutQuantizer.FromBitmap(scaledBitmap, region, defaultCalculateNumberColors);
+
+            if (scaledBitmap != bitmap) scaledBitmap.Dispose();
+
+            // Now return a ColorExtractor instance
+            return new Palette(quantizer.GetQuantizedColors());
+        }
+
+        /// <summary>
+        /// Returns all of the swatches which make up the palette.
+        /// </summary>
+        /// <returns>The swatch list</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ReadOnlyCollection<Swatch> GetSwatches()
+        {
+            return new ReadOnlyCollection<Swatch>(swatches);
+        }
+
+        /// <summary>
+        /// Returns the dominant swatch from the palette.
+        /// The dominant swatch is defined as the swatch with the greatest population (frequency) within the palette.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetDominantSwatch()
+        {
+            String swatchInfo = null;
+            if (dominantSwatch == null)
+                dominantSwatch = FinddominantSwatch();
+
+            if (dominantSwatch != null) swatchInfo = dominantSwatch.ToString();
+            Tizen.Log.Info("Palette", "dominantSwatch [" + swatchInfo + "] \n");
+
+            return dominantSwatch;
+        }
+
+        /// <summary>
+        /// Returns the most vibrant swatch in the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetVibrantSwatch()
+        {
+            return vibrantSwatch;
+        }
+
+        /// <summary>
+        /// Returns a light and vibrant swatch from the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetLightVibrantSwatch()
+        {
+            return lightVibrantSwatch;
+        }
+
+        /// <summary>
+        /// Returns a dark and vibrant swatch from the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetDarkVibrantSwatch()
+        {
+            return darkVibrantSwatch;
+        }
+
+        /// <summary>
+        /// Returns a muted swatch from the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetMutedSwatch()
+        {
+            return mutedSwatch;
+        }
+
+        /// <summary>
+        /// Returns a muted and light swatch from the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetLightMutedSwatch()
+        {
+            return lightMutedColor;
+        }
+
+        /// <summary>
+        /// Returns a muted and dark swatch from the palette. Might be null.
+        /// </summary>
+        /// <returns>The swatch instance</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Swatch GetDarkMutedSwatch()
+        {
+            return darkMutedSwatch;
+        }
+
+               private static async Task<Palette> AsyncTask(Bitmap bitmap, Tizen.NUI.Rectangle region, PaletteGeneratedEventHandler paletteGeneratedEventHandler)
+        {
+            if (paletteGeneratedEventHandler == null)
+            {
+                throw new ArgumentNullException(nameof(paletteGeneratedEventHandler), "PaletteGeneratedEventHandlergate should not be null.");
+            }
+
+            var GenerateTask = Task.Run(() =>
+            {
+                return Generate(bitmap, region);
+            }).ConfigureAwait(false);
+
+            Palette ret = await GenerateTask;
+            paletteGeneratedEventHandler(ret);
+
+            return null; ;
+        }
+
+        /// <summary>
+        /// Try and generate any missing swatches from the swatches we did find.
+        /// </summary>
+        private void GenerateEmptyswatches()
+        {
+            if (vibrantSwatch == null)
+            {
+                // If we do not have a vibrant color...
+                if (darkVibrantSwatch != null)
+                {
+                    // ...but we do have a dark vibrant, generate the value by modifying the luma
+                    float[] newHsl = CopyhslValues(darkVibrantSwatch);
+                    newHsl[2] = targetNormalLuma;
+                    vibrantSwatch = new Swatch(ColorUtils.HslToRgb(newHsl), 0);
+                    Tizen.Log.Info("Palette", "Generate Vibrant Swatch \n");
+                }
+            }
+            if (darkVibrantSwatch == null)
+            {
+                // If we do not have a dark vibrant color...
+                if (vibrantSwatch != null)
+                {
+                    // ...but we do have a vibrant, generate the value by modifying the luma
+                    float[] newHsl = CopyhslValues(vibrantSwatch);
+                    newHsl[2] = targetDarkLuma;
+                    darkVibrantSwatch = new Swatch(ColorUtils.HslToRgb(newHsl), 0);
+                    Tizen.Log.Info("Palette", "Generate DarkVibrant Swatch \n");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Copy a Swatch's hsl values into a new float[].
+        /// </summary>
+        private static float[] CopyhslValues(Swatch color)
+        {
+            float[] newHsl = new float[3];
+            Array.Copy(color.GetHsl(), 0, newHsl, 0, 3);
+
+            return newHsl;
+        }
+
+        /// <summary>
+        /// return true if we have already selected swatch
+        /// </summary>
+        private bool IsAlreadySelected(Swatch swatch)
+        {
+            return vibrantSwatch == swatch || darkVibrantSwatch == swatch ||
+                   lightVibrantSwatch == swatch || mutedSwatch == swatch ||
+                   darkMutedSwatch == swatch || lightMutedColor == swatch;
+        }
+
+        private Swatch FindColor(float targetLuma, float minLuma, float maxLuma,
+                                 float targetSaturation, float minSaturation, float maxSaturation)
+        {
+            Swatch max = null;
+            float maxValue = 0f;
+
+            foreach (Swatch swatch in swatches)
+            {
+                float sat = swatch.GetHsl()[1];
+                float luma = swatch.GetHsl()[2];
+                if (sat >= minSaturation && sat <= maxSaturation &&
+                    luma >= minLuma && luma <= maxLuma &&
+                    !IsAlreadySelected(swatch))
+                {
+                    float thisValue = CreateComparisonValue(sat, targetSaturation, luma, targetLuma,
+                        swatch.GetPopulation(), highestPopulation);
+                    if (max == null || thisValue > maxValue)
+                    {
+                        max = swatch;
+                        maxValue = thisValue;
+                    }
+                }
+            }
+
+            return max;
+        }
+
+        /// <summary>
+        /// Find the Swatch with the highest population value and return the population.
+        /// </summary>
+        private int FindMaxPopulation()
+        {
+            int population = 0;
+
+            foreach (Swatch swatch in swatches)
+            {
+                population = Math.Max(population, swatch.GetPopulation());
+            }
+
+            return population;
+        }
+
+        private Swatch FinddominantSwatch()
+        {
+            int maxPop = -1;
+            Swatch maxSwatch = null;
+
+            foreach (Swatch swatch in swatches)
+            {
+                if (swatch.GetPopulation() > maxPop)
+                {
+                    maxSwatch = swatch;
+                    maxPop = swatch.GetPopulation();
+                }
+            }
+
+            return maxSwatch;
+        }
+
+        /// <summary>
+        /// Scale the bitmap down so that it's smallest dimension is
+        /// calculateBitmapMinDimensionpx. If bitmap is smaller than this, than it
+        /// is returned.
+        /// </summary>
+        private static Bitmap ScaleBitmapDown(Bitmap bitmap)
+        {
+            int minDimension = Math.Min(bitmap.Width, bitmap.Height);
+
+            if (minDimension <= calculateBitmapMinDimension)
+            {
+                // If the bitmap is small enough already, just return it
+                return bitmap;
+            }
+
+            float scaleRatio = calculateBitmapMinDimension / (float)minDimension;
+
+            int width = (int)Math.Round(bitmap.Width * scaleRatio);
+            int height = (int)Math.Round(bitmap.Height * scaleRatio);
+
+            System.Drawing.Size Resize = new System.Drawing.Size(width, height);
+            Tizen.Log.Info("Palette", "bitmap resize to  " + width + " " + height + "\n");
+
+            return new Bitmap(bitmap, Resize);
+        }
+
+        private static float CreateComparisonValue(float saturation, float targetSaturation,
+                float luma, float targetLuma,
+                int population, int highestPopulation)
+        {
+            return WeightedMean(InvertDiff(saturation, targetSaturation), 3f,
+                                InvertDiff(luma, targetLuma), 6.5f,
+                                population / (float)highestPopulation, 0.5f);
+        }
+
+        /// <summary>
+        /// Returns a value in the range 0-1. 1 is returned when value equals the
+        /// targetValue and then decreases as the absolute difference between value and
+        /// targetValue increases.
+        ///
+        /// param value the item's value
+        /// param targetValue the value which we desire
+        /// </summary>
+        private static float InvertDiff(float value, float targetValue)
+        {
+            return 1f - Math.Abs(value - targetValue);
+        }
+
+        private static float WeightedMean(params float[] values)
+        {
+            float sum = 0f;
+            float sumWeight = 0f;
+
+            for (int i = 0; i < values.Length; i += 2)
+            {
+                float value = values[i];
+                float weight = values[i + 1];
+                sum += (value * weight);
+                sumWeight += weight;
+            }
+
+            return sum / sumWeight;
+        }
+
+        // This is nested class for use by other internal classes(Color*), but is declared public.
+        // Futher confirmantion need of this architect.
+
+        /// <summary>
+        /// Represents a color swatch generated from an image's palette. The RGB color can be retrieved calling getRgb()
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public sealed class Swatch
+        {
+            private const float minContrastTitleText = 3.0f;
+            private const float minContrastBodyText = 4.5f;
+
+            private int red, green, blue;
+            private int colorInt, bodyTextColor, titleTextColor;
+            private int population;
+            private bool generatedTextColors;
+            private System.Drawing.Color rgbDrawingColor;
+            private float[] hsl;
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Swatch(int rgbcolorInt, int populationOfSwatch)
+            {
+                System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(rgbcolorInt);
+                red = rgbColor.R;
+                green = rgbColor.G;
+                blue = rgbColor.B;
+                rgbDrawingColor = rgbColor;
+                colorInt = rgbcolorInt;
+                population = populationOfSwatch;
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Swatch(int redValueOfSwatch, int greenValueOfSwatch, int blueValueOfSwatch, int populationOfSwatch)
+            {
+                red = redValueOfSwatch;
+                green = greenValueOfSwatch;
+                blue = blueValueOfSwatch;
+                rgbDrawingColor = System.Drawing.Color.FromArgb(red, green, blue);
+                colorInt = Convert.ToInt32(rgbDrawingColor.ToArgb());
+                population = populationOfSwatch;
+            }
+
+            /// <summary>
+            /// return this swatch's RGB color value
+            /// </summary>
+            /// <returns>A Tizen.NUI.Color value.</returns>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Tizen.NUI.Color GetRgb()
+            {
+                return new Tizen.NUI.Color((float)red / 255, (float)green / 255, (float)blue / 255, 1.0f);
+            }
+
+            /// <summary>
+            /// Return this swatch's hsl values.
+            ///    hsv[0] is Hue [0 .. 360)
+            ///    hsv[1] is Saturation [0...1]
+            ///    hsv[2] is Lightness [0...1]
+            /// </summary>
+            /// <returns>A float array value.</returns>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public float[] GetHsl()
+            {
+                if (hsl == null)
+                {
+                    // Lazily generate hsl values from RGB
+                    hsl = new float[3];
+                    ColorUtils.RgbToHsl(red, green, blue, hsl);
+                }
+
+                return hsl;
+            }
+
+            /// <summary>
+            /// Returns an appropriate color to use for any 'title' text which is displayed over this
+            /// Palette.Swatchs color. This color is guaranteed to have sufficient contrast.
+            /// </summary>
+            /// <returns>A Tizen.NUI.Color value.</returns>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Tizen.NUI.Color GetTitleTextColor()
+            {
+                EnsureTextColorsGenerated();
+
+                System.Drawing.Color titleRgbColor = System.Drawing.Color.FromArgb(titleTextColor);
+                Tizen.Log.Info("Palette", "Swatch Title Text Color = " + titleRgbColor + "\n");
+
+                return new Tizen.NUI.Color((float)titleRgbColor.R / 255, (float)titleRgbColor.G / 255, (float)titleRgbColor.B / 255, (float)titleRgbColor.A / 255);
+            }
+
+            /// <summary>
+            /// Returns an appropriate color to use for any 'body' text which is displayed over this
+            /// Palette.Swatchs color. This color is guaranteed to have sufficient contrast.
+            /// </summary>
+            /// <returns>A Tizen.NUI.Color value.</returns>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Tizen.NUI.Color GetBodyTextColor()
+            {
+                EnsureTextColorsGenerated();
+
+                System.Drawing.Color bodyRgbColor = System.Drawing.Color.FromArgb(bodyTextColor);
+                Tizen.Log.Info("Palette", "Swatch Body Text Color = " + bodyRgbColor + "\n");
+
+                return new Tizen.NUI.Color((float)bodyRgbColor.R / 255, (float)bodyRgbColor.G / 255, (float)bodyRgbColor.B / 255, (float)bodyRgbColor.A / 255);
+            }
+
+            /// <summary>
+            /// return the number of pixels represented by this swatch.
+            /// </summary>
+            /// <returns>A number of pixels value.</returns>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public int GetPopulation()
+            {
+                return population;
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public override String ToString()
+            {
+                return rgbDrawingColor + " " + population;
+            }
+
+            private void EnsureTextColorsGenerated()
+            {
+                if (!generatedTextColors)
+                {
+                    int colorWhite = (255 & 0xff) << 24 | (255 & 0xff) << 16 | (255 & 0xff) << 8 | (255 & 0xff);
+                    int colorBlack = (255 & 0xff) << 24 | (0 & 0xff) << 16 | (0 & 0xff) << 8 | (0 & 0xff);
+
+                    // First check white, as most colors will be dark
+                    System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(colorInt);
+                    int lightBodyAlpha = ColorUtils.CalculateMinimumAlpha(
+                            colorWhite, colorInt, minContrastBodyText);
+                    int lightTitleAlpha = ColorUtils.CalculateMinimumAlpha(
+                            colorWhite, colorInt, minContrastTitleText);
+
+                    if (lightBodyAlpha != -1 && lightTitleAlpha != -1)
+                    {
+                        // If we found valid light values, use them and return
+                        bodyTextColor = ColorUtils.SetAlphaComponent(colorWhite, lightBodyAlpha);
+                        titleTextColor = ColorUtils.SetAlphaComponent(colorWhite, lightTitleAlpha);
+                        generatedTextColors = true;
+
+                        return;
+                    }
+
+                    int darkBodyAlpha = ColorUtils.CalculateMinimumAlpha(
+                            colorBlack, colorInt, minContrastBodyText);
+                    int darkTitleAlpha = ColorUtils.CalculateMinimumAlpha(
+                            colorBlack, colorInt, minContrastTitleText);
+
+                    if (darkBodyAlpha != -1 && darkTitleAlpha != -1)
+                    {
+                        // If we found valid dark values, use them and return
+                        bodyTextColor = ColorUtils.SetAlphaComponent(colorBlack, darkBodyAlpha);
+                        titleTextColor = ColorUtils.SetAlphaComponent(colorBlack, darkTitleAlpha);
+                        generatedTextColors = true;
+
+                        return;
+                    }
+
+                    // If we reach here then we can not find title and body values which use the same
+                    // lightness, we need to use mismatched values
+                    bodyTextColor = lightBodyAlpha != -1
+                            ? ColorUtils.SetAlphaComponent(colorWhite, lightBodyAlpha)
+                            : ColorUtils.SetAlphaComponent(colorWhite, darkBodyAlpha);
+                    titleTextColor = lightTitleAlpha != -1
+                            ? ColorUtils.SetAlphaComponent(colorWhite, lightTitleAlpha)
+                            : ColorUtils.SetAlphaComponent(colorWhite, darkTitleAlpha);
+                    generatedTextColors = true;
+                }
+            }
+        }
+    }
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/PaletteSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/PaletteSample.cs
new file mode 100755 (executable)
index 0000000..8309b43
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+* Copyright (c) 2021 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+using System;
+using Tizen.NUI;
+using System.Drawing;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+namespace Tizen.NUI.Samples
+{
+    class PaletteSample : IExample
+    {
+        private static int bottomHeight = 60;
+        private static int buttonWeight = 100;
+        private static int buttonHeight = 40;
+        private static int maxView = 2;
+        private static string resourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
+        private static string[] imagePath = {
+            resourcePath + "/images/PaletteTest/rock.jpg",
+            resourcePath + "/images/PaletteTest/red2.jpg",
+            resourcePath + "/images/PaletteTest/10by10tree.png"
+        };
+
+        private int viewIndex = 0;
+        private int windowWidth, windowHeight;
+        private Window currentWindow;
+        private View view;
+        private View bottomView;
+        private ImageView imageView;
+        private Palette palette;
+        private Palette.Swatch dominantSwatch;
+        private Palette.Swatch vibrantSwatch;
+        private Palette.Swatch mutedSwatch;
+        private Palette.Swatch darkVibrantSwatch;
+        private Palette.Swatch darkMutedSwatch;
+        private Palette.Swatch lightVibrantSwatch;
+        private Palette.Swatch lightMutedSwatch;
+        private Stopwatch timer = new Stopwatch();
+
+        public void Activate()
+        {
+            Initialize();
+        }
+
+        public void Initialize()
+        {
+            currentWindow = NUIApplication.GetDefaultWindow();
+            currentWindow.BackgroundColor = Color.White;
+
+            windowWidth = Window.Instance.Size.Width;
+            windowHeight = Window.Instance.Size.Height;
+
+            CreatePage(viewIndex);
+            CreateBottomLayout();
+        }
+
+        public void CreateBottomLayout()
+        {
+            bottomView = new View()
+            {
+                Size = new Size(windowWidth, bottomHeight),
+                Position2D = new Position2D(0, windowHeight - bottomHeight),
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    LinearAlignment = LinearLayout.Alignment.Center,
+                }
+
+            };
+            currentWindow.Add(bottomView);
+
+            Button prevBtn = new Button()
+            {
+                Text = "Prev",
+                Size = new Size(buttonWeight, buttonHeight),
+                Margin = 10,
+            };
+            Button nextBtn = new Button()
+            {
+                Text = "next",
+                Size = new Size(buttonWeight, buttonHeight),
+                Margin = 10,
+            };
+            bottomView.Add(prevBtn);
+            bottomView.Add(nextBtn);
+
+            prevBtn.Clicked += PrevClicked;
+            nextBtn.Clicked += NextClicked;
+        }
+
+        private void PrevClicked(object sender, ClickedEventArgs e)
+        {
+            if (viewIndex == 0) return;
+
+            viewIndex--;
+            view.Unparent();
+            CreatePage(viewIndex);
+
+        }
+
+        private void NextClicked(object sender, ClickedEventArgs e)
+        {
+            if (viewIndex == maxView) return;
+
+            viewIndex++;
+            view.Unparent();
+            CreatePage(viewIndex);
+        }
+
+        public void CreatePage(int idx)
+        {
+            view = new View()
+            {
+                Size = new Size(windowWidth, windowHeight - bottomHeight),
+                Layout = new LinearLayout() { LinearOrientation = LinearLayout.Orientation.Vertical },
+            };
+            currentWindow.Add(view);
+
+            imageView = CreateImageView(viewIndex);
+            view.Add(imageView);
+
+            timer.Start();
+            palette = ImageGenerate(viewIndex);
+            timer.Stop();
+
+            TextLabel label = new TextLabel("Time = " + timer.ElapsedMilliseconds.ToString() + "ms")
+            {
+                Size2D  = new Size2D((int)(windowWidth), (int)((windowHeight - windowWidth) / 9)),
+                HorizontalAlignment = HorizontalAlignment.End,
+                VerticalAlignment = VerticalAlignment.Center,
+            };
+            view.Add(label);
+
+            dominantSwatch = palette.GetDominantSwatch();
+             if (dominantSwatch != null) {
+                CreateLabel(dominantSwatch);
+            }
+
+            vibrantSwatch = palette.GetVibrantSwatch();
+            if (vibrantSwatch != null) {
+                CreateLabel(vibrantSwatch);
+            }
+
+            mutedSwatch = palette.GetMutedSwatch();
+            if (mutedSwatch != null) {
+                CreateLabel(mutedSwatch);
+            }
+
+            darkVibrantSwatch = palette.GetDarkVibrantSwatch();
+            if (darkVibrantSwatch != null) {
+                CreateLabel(darkVibrantSwatch);
+            }
+
+            darkMutedSwatch = palette.GetDarkMutedSwatch();
+            if (darkMutedSwatch != null) {
+                CreateLabel(darkMutedSwatch);
+            }
+
+            lightVibrantSwatch = palette.GetLightVibrantSwatch();
+            if (lightVibrantSwatch != null) {
+                CreateLabel(lightVibrantSwatch);
+            }
+
+            lightMutedSwatch = palette.GetLightMutedSwatch();
+            if (lightMutedSwatch != null) {
+                CreateLabel(lightMutedSwatch);
+            }
+
+            timer.Reset();
+        }
+
+        public void CreateLabel(Palette.Swatch swatch)
+        {
+            Tizen.NUI.Color color = swatch.GetRgb();
+
+            string txt = " RGB(" + (int)(color.R * 255) + " " + (int)(color.G * 255) + " " + (int)(color.B * 255) + ")";
+            TextLabel label = new TextLabel(txt)
+            {
+                TextColor = swatch.GetBodyTextColor(),
+                BackgroundColor = color,
+                Size2D  = new Size2D((int)(windowWidth), (int)((windowHeight - windowWidth) / 9)),
+                HorizontalAlignment = HorizontalAlignment.Begin,
+                VerticalAlignment = VerticalAlignment.Center,
+            };
+
+            view.Add(label);
+        }
+
+        public Palette ImageGenerate(int idx)
+        {
+            Bitmap bitmap = new Bitmap(imagePath[idx]);
+            Palette palette = Palette.generate(bitmap);
+            
+            return palette;
+        }
+
+        public ImageView CreateImageView(int idx)
+        {
+            ImageView tempImage = new ImageView()
+            {
+                ResourceUrl = imagePath[idx],
+                Size = new Tizen.NUI.Size(Window.Instance.Size.Width, Window.Instance.Size.Width),
+                HeightResizePolicy = ResizePolicyType.Fixed,
+                WidthResizePolicy = ResizePolicyType.Fixed,
+            };
+
+            return tempImage;
+        }
+
+        public void Deactivate()
+        {
+            //Will Do FullGC in DailDemo Class
+            view.Unparent();
+            bottomView.Unparent();
+
+            view.Dispose();
+            bottomView.Dispose();
+
+            view = null;
+            bottomView = null;
+        }
+    }
+}
index 0589739..fa16e47 100755 (executable)
@@ -38,6 +38,7 @@
     <PackageReference Include="nunit" Version="3.12.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
     <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
+    <PackageReference Include="System.Drawing.Common" Version="5.0.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/10by10tree.png b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/10by10tree.png
new file mode 100755 (executable)
index 0000000..639b724
Binary files /dev/null and b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/10by10tree.png differ
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/red2.jpg b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/red2.jpg
new file mode 100755 (executable)
index 0000000..e52dc4c
Binary files /dev/null and b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/red2.jpg differ
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/rock.jpg b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/rock.jpg
new file mode 100755 (executable)
index 0000000..6014264
Binary files /dev/null and b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/PaletteTest/rock.jpg differ