Generate a 1920 x 1080 graphic with 2,073,600 unique colours

Python - 660+ bytes

Incremental Version

Full Size: http://www.pictureshack.net/images/57626_all_colors.png (4.52MB)

This is an image based on a Julia Set fractal. Each color is added to the image incrementally, although there is a substantial amount of pre-calculation.


Luminosity Version

Full size: http://www.pictureshack.net/images/95389_all_colors4.png (5.24MB)

I've added an option to iterate each color by luminosity, rather than by index. This doesn't qualify as "counting up" from zero, but this seems to be a lax requirement. It's interesting that these two orderings expose completely different structures in the image. To use this, set the use_luminosity flag to True.


Source

Requires PIL.

Be warned: this will take several minutes to execute. Using PyPy with Pillow runs in about one fifth the time of CPython with PIL, so I would recommend that, if possible.

from PIL import Image, ImageDraw

use_luminosity = True

dim = (1920,1080)

img = Image.new('RGB', dim)
draw = ImageDraw.Draw(img)

xstart = -.776707
ystart = -.134663

a = []

xd = 1.6 / 8192 / dim[0]
yd = 0.9 / 8192 / dim[1]

for x in range(dim[0]):
  print x
  for y in range(dim[1]):
    z = d = complex(xstart + x*xd, ystart + y*yd)
    c = 0
    while abs(z) < 2 and c < 5000:
      z = z*z + d
      c += 1
    a += [(c, -abs(z), x, y)]

a = a[1:]
a.sort(reverse = True)

t = [(i>>16, 255&i>>8, 255&i) for i in range(1, dim[0]*dim[1])]
if use_luminosity:
  t.sort(key = lambda c: c[0]*3 + c[1]*10 + c[2], reverse = True)

r = 0
for c,d,x,y in a:
  draw.point((x,y), t[r])
  r += 1

img.show()

Edit: updated so that #000000 is at the upper-left, as specified.
Edit: added a flag to iterate colors by luminosity.
Edit: switched to native complex calculations, and integer luminosity weights, which are slightly faster.


Solutions I worked on before the primary criterion became popularity-contest

PHP - 161 bytes

<?header('Content-type: image/bmp');
ob_start();
echo'BM'.pack('I5S2',0,0,26,12,70780800,1,24);
for(;$i=($i-256)%2073601;)echo pack('CS',~$i,~$i>>8);
ob_end_flush();

This is going for fastest output possible. No library is used, just a pre-computed header, and direct byte output. Runs in less than 2s on my comp. By incrementing by 256 rather than 1, it produces a pseudo-gradient effect, with no real computation needed. The only downfall is that (0, 0) is not black, but the result looks a lot nicer.

ob_start(); and ob_end_flush(); aren't strictly necessary, but buffering the output makes it run a lot faster.

Other interesting increments include:

17: http://i.stack.imgur.com/ieyyZ.png
103: http://i.stack.imgur.com/WD2wa.png
326: http://i.stack.imgur.com/c4DSF.png
557: http://i.stack.imgur.com/eTTWE.png
943: http://i.stack.imgur.com/7rrmR.png
2125: http://i.stack.imgur.com/Ct1kM.png

And many others. Although, most patterns that look like anything resemble stripes of some sort


PHP - 105 bytes

<?=BM.pack(I5S2,header('Content-type:'),0,26,12,70780800,1,24);
for(;$i<2073600;)echo pack(CS,$i,$i++>>8);

Reckless disregard version.

  • Given a broken Content-type header, Chrome will do its best to figure out what it was sent. In this case, it correctly identifies it as image/bmp. The lastest versions of FireFox and IE are also able to fix the broken header.
  • The barewords BM, I5S2, and CS will generate a NOTICE error. To prevent corruption of the image, error reporting in php.ini will need to be set to prevent this (e.g. error_reporting = E_ALL & ~E_NOTICE).
  • No output buffering. The image is constructed 3 bytes at a time, which is noticeably slower.
  • The point (0, 0) is considered to be the lower left, rather than upper left.


PHP-CLI - 83 bytes

<?=BM.pack(I5S2,0,0,26,12,70780800,1,24);
for(;$i<2073600;)echo pack(CS,$i,$i++>>8);

Run directly from the command line and piped to a file (e.g. $ php all-colors.php > out.bmp), no Content-type header is necessary. The resulting file is identical to the 105 byte version, and can be viewed in Chrome.


C with the GD graphics library (err, about 2.5 KB?)

The rules didn't prohibit modifying an existing image. I made a program to replace all of an image's pixels with sequential RGB values from #000000 to #1fa3ff, and I'm quite pleased with the results. Here's what it produced from from a photo posted to Flickr by Michael Carian (cc-by-sa 2.0):

640x360 pixel thumbnail of test results

(The raw output image is rather large (5.6 MB))

Here's a close-up of the top left corner (scaled up 400%):

Enlarged view of top left corner

The processing time is about 3 seconds for an image of this size:

$ time ./a.out foodface.png outfile.png
File accepted; 1920x1080 pixels
Saving...
Finished

real    0m3.251s
user    0m2.392s
sys 0m0.169s

and yes, all the pixels are different colours:

$ identify -format %k outfile.png
2073600

(identify is an ImageMagick utility; the -format %k option counts the number of unique colours in an image)

Here's the source code:

#include <stdio.h>
#include <stdlib.h>
#include <gd.h>

#define FIRST_PIXEL_MUST_BE_BLACK 1

#define luminance(rgb) (((rgb>>16)&0xff)*77+((rgb>>8)&0xff)*150+(rgb&0xff)*29)

typedef struct { int lum; int rgb; } pal;      /* Colour palette */
typedef struct { int lum; int x; int y; } pix; /* Pixel list */

/* Callback function for qsort */
int pcomp(const void *a, const void *b) {
  return ((pal *)(a))->lum-((pal *)(b))->lum;
}

int main(int argv, char *argc[]) {
  FILE        *infile,*outfile;
  gdImagePtr  img;
  int         img_width;
  int         img_height;
  int         npixels;
  int         x,y,i;
  int         rgb,colour_ref,c;
  pal         *palette;
  pix         *pixels;
  
  if (argv!=3) return printf("Usage: %s <source> <destination>\n",argc[0]);
  
  if (!(infile=fopen(argc[1],"r"))) {
    return printf("Can't open source file <%s>\n",argc[1]);
  }
  if (!(img=gdImageCreateFromPng(infile))) {
    return printf("Bad PNG file <%s>\n",argc[1]);
  }
  fclose(infile);
  
  img_width=img->sx;
  img_height=img->sy;
  npixels = img_width * img_height;
  printf("File accepted; %dx%d pixels\n",img_width,img_height);
  
  /* Allocate storage for palette and pixel data */
  palette = malloc(npixels * sizeof(pal));
  if (!palette) return printf("Out of memory\n");
  pixels = malloc(npixels * sizeof(pix));
  if (!pixels) return printf("Out of memory\n");
  
  /* Create palette and sort by luminance */
  for (i=0; i<npixels; i++) {
    palette[i].rgb=i;
    palette[i].lum=luminance(i);
  }
  qsort(palette,npixels,sizeof(pal),pcomp);
  
  /* Sort image pixels by luminance */
#if FIRST_PIXEL_MUST_BE_BLACK == 1
  colour_ref = gdImageColorAllocate(img,0,0,0);
  gdImageSetPixel(img,0,0,colour_ref);
#endif
  
  for (x=y=i=0;i<npixels;i++) {
    rgb = gdImageGetTrueColorPixel(img,x,y);
    pixels[i].x=x;
    pixels[i].y=y;
    pixels[i].lum=luminance(rgb);
    if (!(x=++x%img_width)) y++;
  }
#if FIRST_PIXEL_MUST_BE_BLACK == 1
  qsort(pixels+1,npixels-1,sizeof(pix),pcomp);
#else
  qsort(pixels,npixels,sizeof(pix),pcomp);
#endif
  
  /* Now use the palette to redraw all the pixels */
  for (i=0;i<npixels;i++) {
    c = palette[i].rgb;
    colour_ref = gdImageColorAllocate(img,c>>16,(c>>8)&0xff,c&0xff);
    gdImageSetPixel(img,pixels[i].x,pixels[i].y,colour_ref);
  }
  
  printf("Saving...\n");
  if (!(outfile=fopen(argc[2],"w"))) {
    return printf("Can't open <%s> for writing\n",argc[2]);
  }
  gdImagePng(img,outfile);
  fclose(outfile);
  gdImageDestroy(img);
  printf("Finished\n");
  return 0;
}

C++, 750 bytes

Mandelbrot set A Full resolution PNG (5.1MB)

The code creates a collection of all integers from 0-1080*1920, then sorts them by overall brightness. It then creates a Mandelbrot set, and sorts the positions based on their escape iteration and value. Then it walks through both sets, assigning colors in order from dark to bright to the Mandelbrot values smallest to largest. Finally, it writes a 32 bit per pixel BMP image to the output filename specified as a command line parameter.

#include <windows.h>
#include <vector>
#include <algorithm>
#define X _complex
#define U int
#define S(j,g)std::sort(j.begin(),j.end(),g);
U w=1920,h=1080;
WORD q[27]={'MB',36918,126,0,0,54,0,40,0,w,0,h,0,1,32,0,0,36864,126};
#define V(a,b)((a>>b)&255)
#define L(z)V(z,16)*.3+V(z,8)*.6+V(z,0)*.1
#define F for(c=0;c<w*h;c++)
U C(U a,U b){return L(a)<L(b);}
U M(X a,X b){return a.x<b.x;}
U main(U c,char**v){
std::vector<U>l;
std::vector<X>D;
F l.push_back(c);
U*i=new U[c];
DWORD p;
F{float r=0,i=0,R;p=0;
for(;p<w&&r*r+i*i<4;p++){R=r*r-i*i;i=2*r*i+(c/w-h/2)/400.;r=R+(c%w-w/2)/400.;}
X d={-p-r*r-i*i,c};
D.push_back(d);}
S(l,C)
S(D,M)
F i[(U)D[c].y]=l[c];
void*f=CreateFileA(v[1],4<<28,0,0,2,0,0);
WriteFile(f,q,54,&p,0);
WriteFile(f,i,w*h*4,&p,0);}

The code isn't fully golfed, but it's not going to get too much smaller.