Android 12 取色引擎相关问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 取色引擎相关问题相关的知识,希望对你有一定的参考价值。

参考技术A

其实WallpaperManager从很早之前就提供了getWallpaperColors接口

==Tip: LocalWallpaperColorConsumer是hide接口,非源码编译的Apk无法引用==

取出的数据都封装在WallpaperColors中,可通过以下接口获取到原始数据

框架中只是负责提取出原始颜色 ,但有些时候直接使用原始颜色,并不能达到最好的效果。

例如:PixelLauncher的插件实现颜色动态变化,除了从Wallpaper中拿到颜色,还进行了二次加工,使其更符合Material Desgin的风格,这也是符合“Monet”主题系统 中的一部分,目前是没有开源的。所以一方或三方若想使用该特性,可以按需进行选择和二次加工。

很可惜,Google并没有为动态壁纸提供默认的取色逻辑。

壁纸的取色,最终会调用到WallpaperService的onComputeColors中,但该方法是空实现,框架只是提供了最基本的获取接口而已。

所以在android 12之前,无论是静态壁纸或是动态 壁纸,若想要其他模块能获取到壁纸颜色的话,需要壁纸提供方,在实现WallpaperService的Engine时,也一并实现onCompleteColors方法,然后在颜色变化时调用notifyColorsChanged。

如图:

壁纸其实是通过Palette接口进行颜色提取的,基本上都能取到至少一种颜色。当真的取出失败或取出的颜色如果不符合期望,需要采用另外一套默认颜色,这是由各个各个业务去处理的,比如Launcher中的LauncherAppWidgetHostView,重写了setColorResources方法

具体流程见下图:

PixelLauncher是通过LocalWallpaperColorsExtractor的实现类进行颜色提取,该类继承于LocalColorExtractor,该类存在于Launcher3中,是空实现。可以看出,Google专门将Monet算法部分折分隐藏,只公开框架部分。

而LocalWallpaperColorsExtractor是通过调用WallpaperManager.addOnColorsChangedListener接口,获取onColorsChanged(RectF rectF, WallpaperColors wallpaperColors)中的返回信息,然后再对WallpaperColors中的颜色信息进行二次加工处理。

可以,Android 12已经在secure表中预定义了theme_customization_overlay_packages字段,在其中配置了是否要根据主屏幕变色还是预置颜色,然后进行资源Overlay的。

我们可以直接使用或借鉴这套逻辑,预置几套色值,并设置为使用基本颜色。

WPF Adorner 简易图片取色器

原文:WPF Adorner 简易图片取色器

回答MSDN问题所写。

使用Adorner+附加属性

技术图片

 

图片类(来自这位博主的博客

技术图片
  /// <summary>
    /// 用于获取位图像素的类
    /// </summary>
    public class Imghelper
    {
        /// <summary>
        /// 位图宽度
        /// </summary>
        public int Width { get; protected set; }
        /// <summary>
        /// 位图高度
        /// </summary>
        public int Height { get; protected set; }
        /// <summary>
        /// 像素
        /// </summary>
        public Color[][] Pixels { get; protected set; }

        /// <summary>
        /// 根据指定的位图生成BitmapPixelHelper类的新实例。
        /// </summary>
        /// <param name="bitmap">指定的位图</param>
        public Imghelper(BitmapSource bitmap)
        {
            FormatConvertedBitmap newBitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Bgra32, BitmapPalettes.WebPaletteTransparent, 0);
            const int bytesPerPixel = 4;
            Height = newBitmap.PixelHeight;
            Width = newBitmap.PixelWidth;
            byte[] data = new byte[Height * Width * bytesPerPixel];
            newBitmap.CopyPixels(data, Width * bytesPerPixel, 0);

            Pixels = new Color[Height][];
            for (int i = 0; i < Height; ++i)
            {
                Pixels[i] = new Color[Width];
                for (int j = 0; j < Width; ++j)
                {
                    Pixels[i][j] = Color.FromArgb(
                        data[(i * Width + j) * bytesPerPixel + 3],
                        data[(i * Width + j) * bytesPerPixel + 2],
                        data[(i * Width + j) * bytesPerPixel + 1],
                        data[(i * Width + j) * bytesPerPixel + 0]);
                }
            }
        }

        /// <summary>
        /// 获取图片的平均色
        /// </summary>
        public Color GetAverageColor()
        {
            int a = 0, r = 0, g = 0, b = 0;
            for (int i = 0; i < Height; ++i)
            {
                for (int j = 0; j < Width; ++j)
                {
                    a += Pixels[i][j].A;
                    r += Pixels[i][j].R;
                    g += Pixels[i][j].G;
                    b += Pixels[i][j].B;
                }
            }
            a = a / Height / Width;
            r = r / Height / Width;
            g = g / Height / Width;
            b = b / Height / Width;
            return Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b);
        }
    }
技术图片

adorner代码

技术图片
public class ShowImagePixelsPopup : Adorner
    {

        private TextBlock GetTextBlock;
        private VisualCollection collection;
        private UIElement _UIElement;
        private Border GetBorder;


        public ShowImagePixelsPopup(UIElement adornedElement) : base(adornedElement)
        {
            collection = new VisualCollection(this);

            GetTextBlock = new TextBlock();

            GetTextBlock.Height = 20;
            GetTextBlock.Width = 120;
            GetTextBlock.Background = new SolidColorBrush(Colors.Wheat); 
            GetTextBlock.HorizontalAlignment = HorizontalAlignment.Left;
            GetTextBlock.VerticalAlignment = VerticalAlignment.Top;
            GetBorder = new Border();
            GetBorder.Height = 15;
            GetBorder.Width = 15;
            GetBorder.HorizontalAlignment = HorizontalAlignment.Left;
            GetBorder.VerticalAlignment = VerticalAlignment.Top;
            collection.Add(GetTextBlock);
            collection.Add(GetBorder);
            _UIElement = adornedElement;
        }
        protected override int VisualChildrenCount => collection.Count;


        protected override Visual GetVisualChild(int index) => collection[index];


        protected override Size MeasureOverride(Size constraint) => base.MeasureOverride(constraint);

        public void SetData(Point MousePoint, String Pixels,Color color)
        {
            GetTextBlock.Margin = new Thickness(MousePoint.X+7.5, MousePoint.Y-15, 0,0);
            GetBorder.Margin = new Thickness(MousePoint.X-7.5 , MousePoint.Y-7.5 , 0, 0);
            GetBorder.Background = new SolidColorBrush(color);
            GetTextBlock.Text = Pixels;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            GetTextBlock.Arrange(new Rect(finalSize));
            GetBorder.Arrange(new Rect(finalSize));
            return base.ArrangeOverride(finalSize);

        }
    }
技术图片

附加属性类

技术图片
public class IsShowImagePixels
    {
        public static readonly DependencyProperty IsShowImagePixelsProperty = DependencyProperty.RegisterAttached("IsShowImagePixels", typeof(bool), typeof(IsShowImagePixels), new PropertyMetadata(false, new PropertyChangedCallback(OnIsShowImagePixelsChanged)));

        public static void SetIsShowImagePixels(DependencyObject d, bool value) => d.SetValue(IsShowImagePixelsProperty, value);

        public static bool GetIsShowImagePixels(DependencyObject d) => (bool)d.GetValue(IsShowImagePixelsProperty);

        public static readonly DependencyProperty ShowImagePixelsPointProperty = DependencyProperty.RegisterAttached("ShowImagePixelsPoint", typeof(Point), typeof(IsShowImagePixels), new PropertyMetadata(new Point(0, 0),new PropertyChangedCallback(OnShowImagePixelsPointChanged)));

        public static void SetIsShowImagePixelsPoint(DependencyObject d, Point value) => d.SetValue(ShowImagePixelsPointProperty, value);

        public static Point GetShowImagePixelsPoint(DependencyObject d) => (Point)d.GetValue(ShowImagePixelsPointProperty);

        private static void OnShowImagePixelsPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var c =(Point)e.NewValue;

            popup.SetData(c, $"X={(((int)c.X ) < 0 ? 0 : (int)c.X )},Y={(((int)c.Y ) < 0 ? 0 : (int)c.Y )}", imghelper.Pixels[((int)c.Y - 1) < 0 ? 0 : (int)c.Y - 1][((int)c.X - 1) < 0 ? 0 : (int)c.X - 1]);

        }
        private static AdornerLayer layer;
        private static Imghelper imghelper;
        private static ShowImagePixelsPopup popup;

        private static void OnIsShowImagePixelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

            var NewValue = (bool)e.NewValue;
            UIElement element = d as UIElement;
            if (!NewValue)
            {

                AdornerLayer l = AdornerLayer.GetAdornerLayer(element);
                var ado = l.GetAdorners(element);
                for (var o = 0; o < ado.Length; o++)
                    l.Remove(ado[o]);
                element.MouseMove -= Element_MouseMove;
               
                imghelper = null;
                popup = null;
                layer = null;
                element = null;
            }
            if (element == null)
                return;
            layer = AdornerLayer.GetAdornerLayer(element);
            popup = new ShowImagePixelsPopup(element);
            layer.Add(popup);
            imghelper = new Imghelper((element as Image).Source as BitmapSource);
            //显示鼠标位置
            element.MouseMove += Element_MouseMove;       
        }

        

        private static void Element_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {

            var c = e.GetPosition(sender as FrameworkElement);
            //此处只是用了鼠标位置,也可以用ShowImagePixelsPoint直接指定位置
            popup.SetData(c, $"X={(((int)c.X - 1) < 0 ? 0 : (int)c.X - 1)},Y={(((int)c.Y - 1) < 0 ? 0 : (int)c.Y - 1)}", imghelper.Pixels[((int)c.Y - 1) < 0 ? 0 : (int)c.Y - 1][((int)c.X - 1) < 0 ? 0 : (int)c.X - 1]);

        }
    }
技术图片

 

xaml代码

技术图片
<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <AdornerDecorator Grid.Row="0">
            <Image x:Name="img"  />
        </AdornerDecorator>
        <Button Click="Button_Click" Grid.Row="1" Height="40" Content="ShowOrNot"/>
    </Grid>
技术图片

 

cs页面代码

技术图片
  public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var b = new BitmapImage(new Uri("timg.jpg", UriKind.RelativeOrAbsolute));
            Imghelper imghelper = new Imghelper(b);
            img.Source = b;
            img.SetValue(IsShowImagePixels.IsShowImagePixelsProperty, true);
            Set = true;
        }
        private bool Set = false;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
           
            if (Set)
                Set = false;
            else
                Set = true;
            img.SetValue(IsShowImagePixels.IsShowImagePixelsProperty, Set);
            return;

        }
    }
技术图片

 

以上是关于Android 12 取色引擎相关问题的主要内容,如果未能解决你的问题,请参考以下文章

Android jetpack利用palette进行图片取色

Unity移动平台相关Android打包设置

androi 多线程

Mac OS下的取色小工具-Menubar-Colors

第十二周总结

Vue 之 获取并修改元素样式(比如案例取色器的实现)