Iterate over image pixels and rotate Colors

前端 未结 2 1744
醉梦人生
醉梦人生 2020-12-12 07:14

I am wanting to be able to iterate over a bitmap image and increase the value by one, but currently the image comes back from the memory stream a poorer quality than before

相关标签:
2条回答
  • 2020-12-12 07:35

    This creates a palette that rotates the colours smoothly. It happens that 3 * 85 = 255 leaving 1 palette entry for the Mset pixels. I haven't coded it to suit your platform (and it would be better coded as a struct array), but you can see that I fade 2 primaries at a time. This avoids muddy colours.

    #include <stdio.h>
    #include <memory.h>
    
    #define MAXITER 1000   // iteration depth
    
    unsigned char pal [256][3];
    
    int Mandelplot (int x, int y) {
        // return (palette) colour for pixel
        int c, i=iterate (x, y);      // your iteration function
        if (i >= MAXITER)
            c = 255;
         else
            c = i % 255;
        return c;
    }
    
    void rotate_palette (void) {
        // leaves index 255 unchanged for Mset pixels
        unsigned char r=pal[0][0], g=pal[0][1], b=pal[0][2];
        memcpy (&pal[0][0], &pal[1][0], 254 * 3 * sizeof(unsigned char));
        pal[254][0] = r;
        pal[254][1] = g;
        pal[254][2] = b;
    }
    
    int main (void) {
        unsigned char r=0, g=0, b=255;
        int i;
    
        for (i=0; i<85; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            b -= 3;
            g += 3;
        }
    
        for (i=85; i<170; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            g -= 3;
            r += 3;
        }
    
        for (i=170; i<255; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            r -= 3;
            b += 3;
        }
    
        pal [255][0] = 0;  // black on the Mset
        pal [255][1] = 0;
        pal [255][2] = 0;
    
        for (i=0; i<256; i++)
           printf ("%02X %02X %02X, ", pal[i][0], pal[i][1], pal[i][2]);
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-12 07:52

    First a word about GIF files and your original solution.

    It is pretty simple to make it run real fast, but when, for fun, I did just that I had to laugh - now I know what you meant by the quality degraded..!!

    Let me explain: There are a few options in GIF files, but here the important one is whether it is dithered or not.

    I have already mentioned that they have only 256 colors; to look nice, which they can, at least from a distance, the normal GIF files use a trick: They dither blocks of pixels to show a mix of colors! This works pretty well, but it completely prohibits to use the palette for color rotation..

    Of course one can switch off dithering but the result not only looks pretty coarse and pixelated; with a suitable palette it is possible to do the rotation but the result is mediocre at best. I have appended code to do that in the function palRotate.

    Which leaves us with my original suggestion: Forget GIF and go for for ARGB-color rotation! This also allows you to work with the full range of Ks you have calculated.. But you need tight code to make it run fast.

    Here is a complete testbed using LockBits to load data and do full color cycling with them. You need to add a PictureBox, a Button and a Timer to your form. Note that you need to keep the size of the data equal to the size of the PictureBox. I have included a 500x500 testdata file.

    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    using System.IO;
    //..
    
    // the data array
    int[,] Ks;
    // the offset for cycling
    int koffset = 0;
    // a list of colors
    List<Color> colors = new List<Color>();
    
    public void paintKs()
    {
        if (Ks == null) return;
    
        Size s1 = pb_image.ClientSize;
        pb_image.Image = new Bitmap(s1.Width, s1.Height);
        Bitmap bmp = new Bitmap(pb_image.Image);
    
        PixelFormat fmt1 = bmp.PixelFormat;
        byte bpp1 = 4;
    
        Rectangle rect = new Rectangle(Point.Empty, s1);
        BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
    
        int size1 = bmpData.Stride * bmpData.Height;
        byte[] data = new byte[size1];
        System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);
    
        for (int y = 0; y < s1.Height; y++)
        {
            for (int x = 0; x < s1.Width; x++)
            {
                int index = y * bmpData.Stride + x * bpp1;
                Color c = colors[(Ks[x, y] + koffset) % (colors.Count)];
                if (Ks[x, y] == 0) c = Color.Black;
                data[index + 0] = c.B;
                data[index + 1] = c.G;
                data[index + 2] = c.R;
                data[index + 3] = 255;
            }
        }
    
        System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
        bmp.UnlockBits(bmpData);
    
        pb_image.Image = bmp;
    }
    
    void saveKs(string dataFile)
    {
       using (BinaryWriter writer = new BinaryWriter(File.Open(dataFile, FileMode.Create)))
       {
            for (int y = 0; y < Ks.GetLength(0); y++)
                for (int x = 0; x < Ks.GetLength(1); x++)
                    writer.Write((Int16)Ks[x, y]);
       }
    }
    
    void loadKs(string dataFile)
    {
       int w = pb_image.ClientSize.Width;
       if (Ks == null) Ks = new int[w, w];
    
       using (BinaryReader reader = new BinaryReader(File.Open(dataFile, FileMode.Open)))
       {
            for (int y = 0; y < Ks.GetLength(0); y++)
                for (int x = 0; x < Ks.GetLength(1); x++)
                     Ks[x, y] = reader.ReadInt16();
       }
    
    }
    
    private void Test_Click(object sender, EventArgs e)
    {
        loadKs("fractalData021.dat");
        for (int i = 0; i < 256; i++)
        {
            // a very simple and rather awful palette!
            for (int i = 0; i < 256; i++)  
                 colors.Add(Color.FromArgb(255, i, i, 255 - i));
            for (int i = 0; i < 100; i++) 
                 colors.Add(Color.FromArgb(255, i + 100, 255 -i, 155 - i));
            for (int i = 0; i < 100; i++) 
                 colors.Add(Color.FromArgb(255, i + i+ 50, 255 - i - i, 155 - i/2));
        }
        paintKs();
        timer1.Intervall = 33;  // 30 fps
        timer1.Start();
    }
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        koffset++;
        if (koffset >= colors.Count) koffset = 0;;
        paintKs();
    }
    

    Here are a few files with test data; the test file has a size of 500x500 pixels:

    http://www.file-upload.net/download-9796723/fractalData021.dat.html

    http://www.file-upload.net/download-9796722/fractalData021.jpg.html

    http://www.file-upload.net/download-9796721/fractalData021.txt.html

    Update:

    Here is code to do a plaette rotation on a non-dithered GIF file.

    void palRotate()
    {
        Bitmap bmp = (Bitmap)pb_image.Image;
        var pal = bmp.Palette;
        for (int i = 0; i < 256; i++)  pal.Entries[(i + koffset) % 256] = colors[i];
        bmp.Palette = pal;
        pb_image.Image = bmp;
    }
    

    For preparation these calls would extract the original palette colors into the colors list:

    pb_image.Image = new Bitmap("d:\\fff.gif");
    Bitmap bmp = (Bitmap)pb_image.Image;
    var pal = bmp.Palette;
    for (int i = 0; i < 256; i++) colors.Add(pal.Entries[i]);
    

    For this to look anything but totally crappy the pallete would have to have some sort of order; but even then the pixelated image will look pathetic..

    It would be called in a similar way as the other rotation code from a Timer which advances the koffset variable.

    0 讨论(0)
提交回复
热议问题