更改图像颜色

发布于 2024-11-16 16:01:06 字数 331 浏览 6 评论 0原文

我正在开发一个项目,用户可以在购物车中为商品选择某种颜色。

问题是有大量的颜色可供选择,但制造商只提供了一张产品图片。现在我需要想出一种根据用户选择改变该产品颜色的方法。这与汽车制造商网站上的内容非常相似……当您单击某种颜色时,图像就会更新为该颜色。

有什么想法如何实现这一目标吗?

注意:我最初的想法是使用 Photoshop 生成一个透明图像,该图像仅具有产品的阴影细节(并且周围区域是实心/不透明的),然后以编程方式将此透明图像与具有所选颜色的另一个图像合并。理论上它是可以完成的...我只是想知道是否有人之前已经这样做过和/或是否有更好的方法...比如也许使用 Photoshop 滤镜等:)

I am working on a project where in the shopping cart a user can select a certain color for an item.

The problem is there are tons of color choices available but the manufacturer has only provided with one product image. Now I need to come up with a way to change the color of this product based on user selection. This is pretty similar to going on car manufacturers website ... when you click on a color the image is updated with that color.

Any ideas how to go about accomplishing this?

Note: My initial thought was to use photoshop to comeup with a tranparent image which only has shadow details of the produc (and the surrounding areas are solid/opaque) and then programatically merge this transparent image with another image with the selected color. Theoratically it can be done ... I am just wanting to know if someone has already done this before and/or if there is a better way ... like perhaps using photoshop filters etc :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

×纯※雪 2024-11-23 16:01:06

您正在考虑的可能是最好的选择。并且不要在服务器端合并图像(假设它是 Asp.net 代码),而是使用 javascript 将图像放置在主图像上。最好的方法是加载所有彩色图像和主图像,并将彩色图像放置在主图像上。使用 PNG 格式可获得更好的透明图像。

当您知道有固定数量的颜色选项时,您不必每次每个用户更改颜色时都使用服务器资源。

The one you are thinking is probably best option to do. And instead of merging the image on server side (assuming it is Asp.net code) use javascript to place the image over main image. best way is to load all colored image and main image and place colored image over main impage. Use PNG format for better transparent images.

You dont have to use server resource everytime each user change the color when you know you have fixed number of color options.

2024-11-23 16:01:06

如果需要着色的部分在色调方面在原件中是唯一的(并且您可以通过 Photoshop 或其他工具在源图像中更改它来使其唯一),那么这应该可以解决问题。它的工作原理是锁定输入位图(以便可以操纵每个像素),然后将每个像素转换为 HSB,以便可以调整像素的色调(“颜色”)如果落在某个范围内色调范围。这将产生很好的效果,因为诸如阴影和轻微差异之类的渐变也将被正确着色。

着色代码:

Bitmap bmp = ...
byte minHue = 0;
byte maxHue = 10;
byte newHue = 128;

...

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
    ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * bmpData.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int c = 0; c < rgbValues.Length; c += 4)
{
    HSBColor hsb = new HSBColor(Color.FromArgb(
        rgbValues[c + 3], rgbValues[c + 2],
        rgbValues[c + 1], rgbValues[c]));
if(hsb.H > minHue && hsb.H < maxHue)
    {
        hsb.H = Convert.ToByte(newHue);
    }
    Color color = hsb.ToRGB();
    rgbValues[c] = color.B;
    rgbValues[c + 1] = color.G;
    rgbValues[c + 2] = color.R;
    rgbValues[c + 3] = color.A;
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bmp.UnlockBits(bmpData);

HSBColor.cs(来自 ZedGraph):

/// <summary>
/// Hue-Saturation-Brightness Color class to store a color value, and to manage conversions
/// to and from RGB colors in the <see cref="Color" /> struct.
/// </summary>
/// <remarks>
/// This class is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
/// This struct stores the hue, saturation, brightness, and alpha values internally as
/// <see cref="byte" /> values from 0 to 255.  The hue represents a fraction of the 360 degrees
/// of color space available. The saturation is the color intensity, where 0 represents gray scale
/// and 255 is the most colored.  For the brightness, 0 represents black and 255
/// represents white.
/// </remarks>
[Serializable]
public struct HSBColor
{
    /// <summary>
    /// The color hue value, ranging from 0 to 255.
    /// </summary>
    /// <remarks>
    /// This property is actually a rescaling of the 360 degrees on the color wheel to 255
    /// possible values.  Therefore, every 42.5 units is a new sector, with the following
    /// convention:  red=0, yellow=42.5, green=85, cyan=127.5, blue=170, magenta=212.5
    /// </remarks>
    public byte H;
    /// <summary>
    /// The color saturation (intensity) value, ranging from 0 (gray scale) to 255 (most colored).
    /// </summary>
    public byte S;
    /// <summary>
    /// The brightness value, ranging from 0 (black) to 255 (white).
    /// </summary>
    public byte B;
    /// <summary>
    /// The alpha value (opacity), ranging from 0 (transparent) to 255 (opaque).
    /// </summary>
    public byte A;

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from hue, saturation and
    /// brightness values
    /// </summary>
    /// <param name="h">The color hue value, ranging from 0 to 255</param>
    /// <param name="s">The color saturation (intensity) value, ranging from 0 (gray scale)
    /// to 255 (most colored)</param>
    /// <param name="b">The brightness value, ranging from 0 (black) to 255 (white)</param>
    public HSBColor(int h, int s, int b)
    {
        this.H = (byte)h;
        this.S = (byte)s;
        this.B = (byte)b;
        this.A = 255;
    }

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from hue, saturation,
    /// brightness, and alpha values
    /// </summary>
    /// <param name="h">The color hue value, ranging from 0 to 255</param>
    /// <param name="s">The color saturation (intensity) value, ranging from 0 (gray scale)
    /// to 255 (most colored)</param>
    /// <param name="b">The brightness value, ranging from 0 (black) to 255 (white)</param>
    /// <param name="a">The alpha value (opacity), ranging from 0 (transparent) to
    /// 255 (opaque)</param>
    public HSBColor(int a, int h, int s, int b)
        : this(h, s, b)
    {
        this.A = (byte)a;
    }

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from a system
    /// <see cref="Color" /> struct.
    /// </summary>
    /// <param name="color">An rgb <see cref="Color" /> struct containing the equivalent
    /// color you want to generate</param>
    public HSBColor(Color color)
    {
        this = FromRGB(color);
    }


    /// <summary>
    /// Implicit conversion operator to convert directly from an <see cref="HSBColor" /> to
    /// a <see cref="Color" /> struct.
    /// </summary>
    /// <param name="hsbColor">The <see cref="HSBColor" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="Color" /> struct that can be used in the GDI+
    /// graphics library</returns>
    public static implicit operator Color(HSBColor hsbColor)
    {
        return ToRGB(hsbColor);
    }

    /// <summary>
    /// Convert an <see cref="HSBColor" /> value to an equivalent <see cref="Color" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <param name="hsbColor">The <see cref="HSBColor" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="Color" /> struct, compatible with the GDI+ library</returns>
    public static Color ToRGB(HSBColor hsbColor)
    {
        Color rgbColor = Color.Black;

        // Determine which sector of the color wheel contains this hue
        // hsbColor.H ranges from 0 to 255, and there are 6 sectors, so 42.5 per sector
        int sector = (int)Math.Floor((double)hsbColor.H / 42.5);
        // Calculate where the hue lies within the sector for interpolation purpose
        double fraction = (double)hsbColor.H / 42.5 - (double)sector;

        double sFrac = (double)hsbColor.S / 255.0;
        byte p = (byte)(((double)hsbColor.B * (1.0 - sFrac)) + 0.5);
        byte q = (byte)(((double)hsbColor.B * (1.0 - sFrac * fraction)) + 0.5);
        byte t = (byte)(((double)hsbColor.B * (1.0 - sFrac * (1.0 - fraction))) + 0.5);


        switch (sector)
        {
            case 0:         // red - yellow
                rgbColor = Color.FromArgb(hsbColor.A, hsbColor.B, t, p);
                break;
            case 1:         // yellow - green
                rgbColor = Color.FromArgb(hsbColor.A, q, hsbColor.B, p);
                break;
            case 2:         // green - cyan
                rgbColor = Color.FromArgb(hsbColor.A, p, hsbColor.B, t);
                break;
            case 3:         // cyan - blue
                rgbColor = Color.FromArgb(hsbColor.A, p, q, hsbColor.B);
                break;
            case 4:         // blue - magenta
                rgbColor = Color.FromArgb(hsbColor.A, t, p, hsbColor.B);
                break;
            case 5:
            default:        // magenta - red
                rgbColor = Color.FromArgb(hsbColor.A, hsbColor.B, p, q);
                break;
        }

        return rgbColor;
    }

    /// <summary>
    /// Convert this <see cref="HSBColor" /> value to an equivalent <see cref="Color" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <returns>An equivalent <see cref="Color" /> struct, compatible with the GDI+ library</returns>
    public Color ToRGB()
    {
        return ToRGB(this);
    }

    /// <summary>
    /// Convert a <see cref="Color" /> value to an equivalent <see cref="HSBColor" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <returns>An equivalent <see cref="HSBColor" /> struct</returns>
    public HSBColor FromRGB()
    {
        return FromRGB(this);
    }

    /// <summary>
    /// Convert a <see cref="Color" /> value to an equivalent <see cref="HSBColor" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <param name="rgbColor">The <see cref="Color" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="HSBColor" /> struct</returns>
    public static HSBColor FromRGB(Color rgbColor)
    {
        double r = (double)rgbColor.R / 255.0;
        double g = (double)rgbColor.G / 255.0;
        double b = (double)rgbColor.B / 255.0;

        double min = Math.Min(Math.Min(r, g), b);
        double max = Math.Max(Math.Max(r, g), b);

        HSBColor hsbColor = new HSBColor(rgbColor.A, 0, 0, 0);

        hsbColor.B = (byte)(max * 255.0 + 0.5);

        double delta = max - min;

        if (max != 0.0)
        {
            hsbColor.S = (byte)(delta / max * 255.0 + 0.5);
        }
        else
        {
            hsbColor.S = 0;
            hsbColor.H = 0;
            return hsbColor;
        }

        double h;
        if (r == max)
            h = (g - b) / delta;        // between yellow & magenta
        else if (g == max)
            h = 2 + (b - r) / delta;    // between cyan & yellow
        else
            h = 4 + (r - g) / delta;    // between magenta & cyan

        hsbColor.H = (byte)(h * 42.5);
        if (hsbColor.H < 0)
            hsbColor.H += 255;

        return hsbColor;
    }

}

If the part that needs to be colored is unique in the original in terms of hue (and you could make it unique by changing it in the source image through Photoshop or something), this should do the trick. It works by locking an input bitmap (so each pixel can be manipulated) and then converting each pixel to HSB so that hue (the "color") of the pixel can be adjusted if it falls within a certain range of hues. This will have a nice effect because gradients such as shadow and slight variances will also be colorized correctly.

Colorizing code:

Bitmap bmp = ...
byte minHue = 0;
byte maxHue = 10;
byte newHue = 128;

...

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
    ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * bmpData.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int c = 0; c < rgbValues.Length; c += 4)
{
    HSBColor hsb = new HSBColor(Color.FromArgb(
        rgbValues[c + 3], rgbValues[c + 2],
        rgbValues[c + 1], rgbValues[c]));
if(hsb.H > minHue && hsb.H < maxHue)
    {
        hsb.H = Convert.ToByte(newHue);
    }
    Color color = hsb.ToRGB();
    rgbValues[c] = color.B;
    rgbValues[c + 1] = color.G;
    rgbValues[c + 2] = color.R;
    rgbValues[c + 3] = color.A;
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bmp.UnlockBits(bmpData);

HSBColor.cs (from ZedGraph):

/// <summary>
/// Hue-Saturation-Brightness Color class to store a color value, and to manage conversions
/// to and from RGB colors in the <see cref="Color" /> struct.
/// </summary>
/// <remarks>
/// This class is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
/// This struct stores the hue, saturation, brightness, and alpha values internally as
/// <see cref="byte" /> values from 0 to 255.  The hue represents a fraction of the 360 degrees
/// of color space available. The saturation is the color intensity, where 0 represents gray scale
/// and 255 is the most colored.  For the brightness, 0 represents black and 255
/// represents white.
/// </remarks>
[Serializable]
public struct HSBColor
{
    /// <summary>
    /// The color hue value, ranging from 0 to 255.
    /// </summary>
    /// <remarks>
    /// This property is actually a rescaling of the 360 degrees on the color wheel to 255
    /// possible values.  Therefore, every 42.5 units is a new sector, with the following
    /// convention:  red=0, yellow=42.5, green=85, cyan=127.5, blue=170, magenta=212.5
    /// </remarks>
    public byte H;
    /// <summary>
    /// The color saturation (intensity) value, ranging from 0 (gray scale) to 255 (most colored).
    /// </summary>
    public byte S;
    /// <summary>
    /// The brightness value, ranging from 0 (black) to 255 (white).
    /// </summary>
    public byte B;
    /// <summary>
    /// The alpha value (opacity), ranging from 0 (transparent) to 255 (opaque).
    /// </summary>
    public byte A;

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from hue, saturation and
    /// brightness values
    /// </summary>
    /// <param name="h">The color hue value, ranging from 0 to 255</param>
    /// <param name="s">The color saturation (intensity) value, ranging from 0 (gray scale)
    /// to 255 (most colored)</param>
    /// <param name="b">The brightness value, ranging from 0 (black) to 255 (white)</param>
    public HSBColor(int h, int s, int b)
    {
        this.H = (byte)h;
        this.S = (byte)s;
        this.B = (byte)b;
        this.A = 255;
    }

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from hue, saturation,
    /// brightness, and alpha values
    /// </summary>
    /// <param name="h">The color hue value, ranging from 0 to 255</param>
    /// <param name="s">The color saturation (intensity) value, ranging from 0 (gray scale)
    /// to 255 (most colored)</param>
    /// <param name="b">The brightness value, ranging from 0 (black) to 255 (white)</param>
    /// <param name="a">The alpha value (opacity), ranging from 0 (transparent) to
    /// 255 (opaque)</param>
    public HSBColor(int a, int h, int s, int b)
        : this(h, s, b)
    {
        this.A = (byte)a;
    }

    /// <summary>
    /// Constructor to load an <see cref="HSBColor" /> struct from a system
    /// <see cref="Color" /> struct.
    /// </summary>
    /// <param name="color">An rgb <see cref="Color" /> struct containing the equivalent
    /// color you want to generate</param>
    public HSBColor(Color color)
    {
        this = FromRGB(color);
    }


    /// <summary>
    /// Implicit conversion operator to convert directly from an <see cref="HSBColor" /> to
    /// a <see cref="Color" /> struct.
    /// </summary>
    /// <param name="hsbColor">The <see cref="HSBColor" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="Color" /> struct that can be used in the GDI+
    /// graphics library</returns>
    public static implicit operator Color(HSBColor hsbColor)
    {
        return ToRGB(hsbColor);
    }

    /// <summary>
    /// Convert an <see cref="HSBColor" /> value to an equivalent <see cref="Color" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <param name="hsbColor">The <see cref="HSBColor" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="Color" /> struct, compatible with the GDI+ library</returns>
    public static Color ToRGB(HSBColor hsbColor)
    {
        Color rgbColor = Color.Black;

        // Determine which sector of the color wheel contains this hue
        // hsbColor.H ranges from 0 to 255, and there are 6 sectors, so 42.5 per sector
        int sector = (int)Math.Floor((double)hsbColor.H / 42.5);
        // Calculate where the hue lies within the sector for interpolation purpose
        double fraction = (double)hsbColor.H / 42.5 - (double)sector;

        double sFrac = (double)hsbColor.S / 255.0;
        byte p = (byte)(((double)hsbColor.B * (1.0 - sFrac)) + 0.5);
        byte q = (byte)(((double)hsbColor.B * (1.0 - sFrac * fraction)) + 0.5);
        byte t = (byte)(((double)hsbColor.B * (1.0 - sFrac * (1.0 - fraction))) + 0.5);


        switch (sector)
        {
            case 0:         // red - yellow
                rgbColor = Color.FromArgb(hsbColor.A, hsbColor.B, t, p);
                break;
            case 1:         // yellow - green
                rgbColor = Color.FromArgb(hsbColor.A, q, hsbColor.B, p);
                break;
            case 2:         // green - cyan
                rgbColor = Color.FromArgb(hsbColor.A, p, hsbColor.B, t);
                break;
            case 3:         // cyan - blue
                rgbColor = Color.FromArgb(hsbColor.A, p, q, hsbColor.B);
                break;
            case 4:         // blue - magenta
                rgbColor = Color.FromArgb(hsbColor.A, t, p, hsbColor.B);
                break;
            case 5:
            default:        // magenta - red
                rgbColor = Color.FromArgb(hsbColor.A, hsbColor.B, p, q);
                break;
        }

        return rgbColor;
    }

    /// <summary>
    /// Convert this <see cref="HSBColor" /> value to an equivalent <see cref="Color" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <returns>An equivalent <see cref="Color" /> struct, compatible with the GDI+ library</returns>
    public Color ToRGB()
    {
        return ToRGB(this);
    }

    /// <summary>
    /// Convert a <see cref="Color" /> value to an equivalent <see cref="HSBColor" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <returns>An equivalent <see cref="HSBColor" /> struct</returns>
    public HSBColor FromRGB()
    {
        return FromRGB(this);
    }

    /// <summary>
    /// Convert a <see cref="Color" /> value to an equivalent <see cref="HSBColor" /> value.
    /// </summary>
    /// <remarks>
    /// This method is based on code from http://www.cs.rit.edu/~ncs/color/ by Eugene Vishnevsky.
    /// </remarks>
    /// <param name="rgbColor">The <see cref="Color" /> struct to be converted</param>
    /// <returns>An equivalent <see cref="HSBColor" /> struct</returns>
    public static HSBColor FromRGB(Color rgbColor)
    {
        double r = (double)rgbColor.R / 255.0;
        double g = (double)rgbColor.G / 255.0;
        double b = (double)rgbColor.B / 255.0;

        double min = Math.Min(Math.Min(r, g), b);
        double max = Math.Max(Math.Max(r, g), b);

        HSBColor hsbColor = new HSBColor(rgbColor.A, 0, 0, 0);

        hsbColor.B = (byte)(max * 255.0 + 0.5);

        double delta = max - min;

        if (max != 0.0)
        {
            hsbColor.S = (byte)(delta / max * 255.0 + 0.5);
        }
        else
        {
            hsbColor.S = 0;
            hsbColor.H = 0;
            return hsbColor;
        }

        double h;
        if (r == max)
            h = (g - b) / delta;        // between yellow & magenta
        else if (g == max)
            h = 2 + (b - r) / delta;    // between cyan & yellow
        else
            h = 4 + (r - g) / delta;    // between magenta & cyan

        hsbColor.H = (byte)(h * 42.5);
        if (hsbColor.H < 0)
            hsbColor.H += 255;

        return hsbColor;
    }

}
月牙弯弯 2024-11-23 16:01:06

Scene7 可以为您完成此操作,但成本相当高。

Scene7 does this for you but it's pretty costly.

不顾 2024-11-23 16:01:06

您可以使用 ColourMap[] 数组相当轻松地将一个图像中的颜色映射到一组新的颜色 - 问题在于源颜色和目标颜色。

我使用这样的代码和具有各种 alpha 透明度级别的单色(黑色)贴图(尽管您可以以相同的方式使用灰度图像),然后将比例中的每种颜色映射到新比例:

// Get the source file and create a result bitmap of the same size
using (var source = Image.FromFile("old colour image.png", false))
using (var result = new Bitmap(source.Width, source.Height))
{
    // Build a colour map of old to new colour
    ColorMap[] colorMap = BuildArrayOfOldAndNewColours();

    // Build image attributes with the map
    var attr = new ImageAttributes();
    attr.SetRemapTable(colorMap);

    // Draw a rectangle the same size as the image
    var rect = new Rectangle(0, 0, source.Width, source.Height);

    // Draw the old image into the new one with one colour mapped to the other
    var g = Graphics.FromImage(result);
    g.DrawImage(source, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr);

    result.Save("new colour image.png");
}

这有效非常适合简单的图标,但您可能需要相当复杂的地图来处理任何照片质量的东西。

You can map the colours in one image to a new set of colours fairly easily with an array of ColourMap[] - the problem is the source colours and the destination colours.

I use a code like this with a map of a single colour (black) with various alpha levels of transparency (though you could use a greyscale image in the same way), then map every colour in the scale to the new scale:

// Get the source file and create a result bitmap of the same size
using (var source = Image.FromFile("old colour image.png", false))
using (var result = new Bitmap(source.Width, source.Height))
{
    // Build a colour map of old to new colour
    ColorMap[] colorMap = BuildArrayOfOldAndNewColours();

    // Build image attributes with the map
    var attr = new ImageAttributes();
    attr.SetRemapTable(colorMap);

    // Draw a rectangle the same size as the image
    var rect = new Rectangle(0, 0, source.Width, source.Height);

    // Draw the old image into the new one with one colour mapped to the other
    var g = Graphics.FromImage(result);
    g.DrawImage(source, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr);

    result.Save("new colour image.png");
}

This works great with simple icons, but you may need quite a complicated map to deal with anything photo-quality.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文