Skip to content

Instantly share code, notes, and snippets.

@sushovande
Created November 5, 2012 04:44
Show Gist options
  • Save sushovande/4015383 to your computer and use it in GitHub Desktop.
Save sushovande/4015383 to your computer and use it in GitHub Desktop.
Create a checkered background image
// example output: http://imgur.com/dSix8
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace CreateColorfulBackgrounds
{
class Program
{
/// <summary>
/// The dimensions of the checkers
/// </summary>
const int squareSide = 70;
/// <summary>
/// The color of the square on the top middle
/// </summary>
static Color squareStartColor = Color.FromArgb(145, 211, 20);
/// <summary>
/// The color of the square on the bottom edges
/// </summary>
static Color squareEndColor = Color.FromArgb(22, 55, 10);
/// <summary>
/// The color of the edge of the square on the top-middle
/// </summary>
static Color borderStartColor = Color.FromArgb(121, 187, 0);
/// <summary>
/// The color of the edge on the bottom fringes
/// </summary>
static Color borderEndColor = Color.FromArgb(14, 45, 3);
/// <summary>
/// Smoothing factor for the gradient. Higher values give a sharper transition.
/// </summary>
const double smoothingFactor = 3;
/// <summary>
/// How much the colors vary in the checkers.
/// </summary>
const double randomness = 0.15;
/// <summary>
/// Although the randomness was supposed to be 0-biased, I found that giving it a small offset helped.
/// </summary>
const double randomnessDcOffset = 0.2;
static Random rnd = new Random();
static void Main(string[] args)
{
CreateBackground(1680, 1050);
}
/// <summary>
/// Creates a background of the specified width and height
/// </summary>
static void CreateBackground(int width, int height)
{
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var bmp_data = bmp.LockBits(new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
bmp.PixelFormat);
int bytes = Math.Abs(bmp_data.Stride) * height;
byte[] rgbValues = new byte[bytes];
FillValues(rgbValues, width, height, bmp_data.Stride);
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, bmp_data.Scan0, bytes);
bmp.Save("foo.png", System.Drawing.Imaging.ImageFormat.Png);
}
/// <summary>
/// First creates a smooth radial gradient to serve as the dark borders, then fills the image with checkers
/// </summary>
/// <param name="rgbValues">The array of bytes that form the image</param>
/// <param name="width">width of the image</param>
/// <param name="height">height of the image</param>
/// <param name="stride">The stride of the bitmap bits</param>
private static void FillValues(byte[] rgbValues, int width, int height, int stride)
{
GradientFillRect(new Rectangle(0, 0, width, height), new Point(width / 2, 0), new Point(0, height), borderStartColor, borderEndColor, rgbValues, stride);
CheckerFillSquares(rgbValues, width, height, stride);
}
/// <summary>
/// Fills an area of the bitmap with a series of squares, randomly colored
/// </summary>
/// <param name="rgbValues">The array of bytes that form the image</param>
/// <param name="width">width of the image</param>
/// <param name="height">height of the image</param>
/// <param name="stride">The stride of the bitmap bits</param>
private static void CheckerFillSquares(byte[] rgbValues, int width, int height, int stride)
{
int midx = width / squareSide / 2; // the starting point of the radial gradient
int midy = -4;
double maxt = GetDist(midx, midy, 0, height / squareSide); // the maximum possible param value we will have
for (int y = 0; y < height / squareSide; y++)
{
for (int x = 0; x < width / squareSide; x++)
{
double t = Shape(GetDist(midx, midy, x, y) / maxt); // find the interpolation parameter
t += (rnd.NextDouble() - 0.5) * randomness - randomnessDcOffset;
Color tcol = Color.FromArgb(
(byte)(squareStartColor.R * (1 - t) + squareEndColor.R * t),
(byte)(squareStartColor.G * (1 - t) + squareEndColor.G * t),
(byte)(squareStartColor.B * (1 - t) + squareEndColor.B * t)); // find the interpolated color
FillRect(new Rectangle(x * squareSide + 1, y * squareSide + 1, squareSide - 2, squareSide - 2), rgbValues, stride, tcol);
}
}
}
/// <summary>
/// Fills a rectangle with a radial gradient
/// </summary>
/// <param name="rectangle">The rectangle to fill. Not bounds checked.</param>
/// <param name="point1">The starting point of the radial gradient</param>
/// <param name="point2">The ending point of the radial gradient</param>
/// <param name="color1">The color of the first point of the radial gradient</param>
/// <param name="color2">The color of the second point of the radial gradient</param>
/// <param name="rgbValues">The array of bytes that form image</param>
/// <param name="stride">The stride of the bitmap bits</param>
private static void GradientFillRect(Rectangle rectangle, Point point1, Point point2, Color color1, Color color2, byte[] rgbValues, int stride)
{
double maxt = GetDist(point1.X, point1.Y, point2.X, point2.Y);
for (int y = rectangle.Top; y < rectangle.Bottom; y++)
{
for (int x = rectangle.Left; x < rectangle.Right; x++)
{
double t = Shape(GetDist(x, y, point1.X, point1.Y) / maxt);
Color tcol = Color.FromArgb(
(byte)(color1.R * (1 - t) + color2.R * t),
(byte)(color1.G * (1 - t) + color2.G * t),
(byte)(color1.B * (1 - t) + color2.B * t)); // find the interpolated color
rgbValues[y * stride + 4 * x + 0] = tcol.B;
rgbValues[y * stride + 4 * x + 1] = tcol.G;
rgbValues[y * stride + 4 * x + 2] = tcol.R;
rgbValues[y * stride + 4 * x + 3] = 255;
}
}
}
/// <summary>
/// Shaping function for the parameter, smooths it using a sigmoid function
/// </summary>
/// <param name="p">The parameter to shape, between 0 and 1</param>
/// <returns>a new smoothed param, between 0 and 1</returns>
private static double Shape(double p)
{
return 1.0 / (1 + Math.Exp(-smoothingFactor * (p - 0.5) * 2));
}
/// <summary>
/// FInds the Euclidean distance between two points
/// </summary>
private static double GetDist(int x1, int y1, int x2, int y2)
{
int d2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
return Math.Sqrt(d2);
}
/// <summary>
/// Fills in a rectangle with a single color
/// </summary>
/// <param name="rectangle">The area of the bits to fill. NOT bounds checked.</param>
/// <param name="rgbValues">an array of bytes locked from the bitmap</param>
/// <param name="stride">stride of the bitmap</param>
/// <param name="color">the color to fill it with</param>
private static void FillRect(Rectangle rectangle, byte[] rgbValues, int stride, Color color)
{
for (int y = rectangle.Top; y < rectangle.Bottom; y++)
{
for (int x = rectangle.Left; x < rectangle.Right; x++)
{
rgbValues[y * stride + 4 * x + 0] = color.B;
rgbValues[y * stride + 4 * x + 1] = color.G;
rgbValues[y * stride + 4 * x + 2] = color.R;
rgbValues[y * stride + 4 * x + 3] = color.A;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment