android L Palette 实现原理

有时候,看到一些界面上的色彩,心情可能会很舒畅,有时候,看到一些其他色彩,就觉得很讨厌,不爽,看到android L Palette 从图片中提取筛选出来的颜色,觉得都挺好看的,就去了解了下Palette调色板。

看了代码,根据我的理解,大概说说主要的步骤:

第一步,将图片缩小,再整个过程中,可以降低计算量和减少内存的使用,跟不缩小也能达到一样的效果

/**
     * Scale the bitmap down so that it's smallest dimension is
     * {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it
     * is returned.
     */
    private static Bitmap scaleBitmapDown(Bitmap bitmap) {
        final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());

        if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
            // If the bitmap is small enough already, just return it
            return bitmap;
        }

        final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
        return Bitmap.createScaledBitmap(bitmap,
                Math.round(bitmap.getWidth() * scaleRatio),
                Math.round(bitmap.getHeight() * scaleRatio),
                false);
    }

第二步,将缩小后的图片数据,放在一个int 数组里

*
    * Factory-method to generate a {@link ColorCutQuantizer} from a {@link Bitmap} object.
    *
    * @param bitmap Bitmap to extract the pixel data from
    * @param maxColors The maximum number of colors that should be in the result palette.
    */
   static ColorCutQuantizer fromBitmap(Bitmap bitmap, int maxColors) {
       final int width = bitmap.getWidth();
       final int height = bitmap.getHeight();


       final int[] pixels = new int[width * height];
       bitmap.getPixels(pixels, 0, width, 0, 0, width, height);


       return new ColorCutQuantizer(new ColorHistogram(pixels), maxColors);
   }

第三步,将这个int 数组由小到大排序,就相当于,将一张图片一样的颜色堆在一起,然后计算共有多少种颜色,每种颜色它是多大,这些是在一个叫ColorHistogram(颜色直方图)类里面计算的,用颜色直方图来说,就是共有多少柱颜色,每柱颜色有多高

/**
 * Class which provides a histogram for RGB values.
 */
final class ColorHistogram {

    private final int[] mColors;
    private final int[] mColorCounts;
    private final int mNumberColors;

    /**
     * A new {@link ColorHistogram} instance.
     *
     * @param pixels array of image contents
     */
    ColorHistogram(final int[] pixels) {
        // Sort the pixels to enable counting below
        Arrays.sort(pixels);

        // Count number of distinct colors
        mNumberColors = countDistinctColors(pixels);

        // Create arrays
        mColors = new int[mNumberColors];
        mColorCounts = new int[mNumberColors];

        // Finally count the frequency of each color
        countFrequencies(pixels);
    }

    /**
     * @return 获取共用多少柱不同颜色 number of distinct colors in the image.
     */
    int getNumberOfColors() {
        return mNumberColors;
    }

    /**
     * @return 获取排好序后的不同颜色的数组  an array containing all of the distinct colors in the image.
     */
    int[] getColors() {
        return mColors;
    }

    /**
     * @return 获取保存每一柱有多高的数组 an array containing the frequency of a distinct colors within the image.
     */
    int[] getColorCounts() {
        return mColorCounts;
    }

    //计算共用多少柱不同颜色
    private static int countDistinctColors(final 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(final int[] pixels) {
        if (pixels.length == 0) {
            return;
        }

        int currentColorIndex = 0;
        int currentColor = pixels[0];

        mColors[currentColorIndex] = currentColor;
        mColorCounts[currentColorIndex] = 1;

        Log.i(pixels.length,+ pixels.length);

        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
                mColorCounts[currentColorIndex]++;
            } else {
                // We've hit a new color, increase index
                currentColor = pixels[i];

                currentColorIndex++;
                mColors[currentColorIndex] = currentColor;
                mColorCounts[currentColorIndex] = 1;
            }
        }
    }

}

第四步,将各种颜色,根据RGB转HSL算法,得出对应的HSL(H: Hue 色相,S:Saturation 饱和度L Lightness 明度),根据特定的条件,比如是明度L是否接近白色,黑色,还有一个判断叫isNearRedILine,解释是@return true if the color lies close to the red side of the I line(接近红色私密区域附近?).,然后根据这三个条件,过滤掉这些颜色,什么是HSL和RGB转HSL算法可以查看下百科,比较有详细说明

/**
     * Private constructor.
     *
     * @param colorHistogram histogram representing an image's pixel data
     * @param maxColors The maximum number of colors that should be in the result palette.
     */
    private ColorCutQuantizer(ColorHistogram colorHistogram, int maxColors) {
        final int rawColorCount = colorHistogram.getNumberOfColors();
        final int[] rawColors = colorHistogram.getColors();//颜色数组
        final int[] rawColorCounts = colorHistogram.getColorCounts();//对应rawColors每一个颜色数组的大小

        // First, lets pack the populations into a SparseIntArray so that they can be easily
        // retrieved without knowing a color's index
        mColorPopulations = new SparseIntArray(rawColorCount);
        for (int i = 0; i < rawColors.length; i++) {
            mColorPopulations.append(rawColors[i], rawColorCounts[i]);
        }

        // Now go through all of the colors and keep those which we do not want to ignore
        mColors = new int[rawColorCount];
        int validColorCount = 0;
        for (int color : rawColors) {
            if (!shouldIgnoreColor(color)) {
                mColors[validColorCount++] = color;
            }
        }
        Log.d(mColors length, +mColors.length);
        if (validColorCount <= maxColors) {
            // The image has fewer colors than the maximum requested, so just return the colors
            mQuantizedColors = new ArrayList<swatch>();

            for (final int color : mColors) {
                mQuantizedColors.add(new Swatch(color, mColorPopulations.get(color)));
            }
        } else {
            // We need use quantization to reduce the number of colors
            mQuantizedColors = quantizePixels(validColorCount - 1, maxColors);
        }
    }
<span style="font-weight:">
</span>


<span style="font-weight:">这里截了张图看看</span>

这里写图片描述

第五步,根据是各种亮度,饱和度的取值范围,比如有活力的暗色,有活力的亮色,柔和的颜色,柔和的暗色,柔和的亮色,找到对应的颜色

private Swatch findColor(float targetLuma, float minLuma, float maxLuma,
                            float targetSaturation, float minSaturation, float maxSaturation) {
       Swatch max = null;
       float maxValue = 0f;

       for (Swatch swatch : mSwatches) {
           final float sat = swatch.getHsl()[1];
           final 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(), mHighestPopulation);
               if (max == null || thisValue > maxValue) {
                   max = swatch;
                   maxValue = thisValue;
               }
           }
       }

       return max;
   }

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值