<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>
+++ /dev/null
-/*
- * 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;
- }
- }
- }
- }
-}
+++ /dev/null
-/*
- * 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");
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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;
- }
- }
- }
- }
-}
+++ /dev/null
-/*
-* 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;
- }
- }
-}
<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>