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.