So, I decided to try to slice it up into smaller, manageable chunks and just butt them against one another to simulate one large file. The problem was that I couldn't find a tool to do this that didn't also trip up over the size of the file. So, I wrote my own. There are likely better ways to do this, this was just a quick and dirty attempt to make something that didn't totally crawl to a halt due to page faulting (or outright throw OutOfMemoryException).
1: using System;
2: using System.Collections.Generic;
3: using System.Drawing;
4: using System.Drawing.Imaging;
5: using System.IO;
6: using System.Linq;
7: using System.Runtime.InteropServices;
8:
9: namespace ImageSlicer { 10: static class Extensions { 11: public static IEnumerable<int> Times(this int number) { 12: for (var i = 0; i < number; i++) yield return i;
13: }
14: }
15:
16: class Program { 17: [DllImport("msvcrt.dll", SetLastError = false)] 18: static unsafe extern byte* memcpy(byte* dest, byte* src, int count);
19:
20: static void Main(string[] args) { 21: var sourcePath = args.FirstOrDefault();
22: if (String.IsNullOrEmpty(sourcePath)) { 23: Console.WriteLine("No source image path provided"); 24: ShowUsage();
25: return;
26: }
27: if (!File.Exists(sourcePath)) { 28: Console.WriteLine("Source image path doesn't exist"); 29: return;
30: }
31: var gridSizeStr = args.Skip(1).FirstOrDefault();
32: int gridSize = 4;
33: if (gridSizeStr != null && !int.TryParse(gridSizeStr, out gridSize)) { 34: Console.WriteLine("Could not convert {0} to a valid grid size", 35: gridSizeStr);
36: return;
37: }
38: if (gridSize < 2) { 39: Console.WriteLine("The grid size must be greater than 1."); 40: return;
41: }
42: try { 43: Console.WriteLine("Slicing {0} into a {1}x{1} grid.", 44: Path.GetFileName(sourcePath), gridSize);
45: Console.WriteLine("Loading..."); 46: using (var sourceBitmap = new Bitmap(sourcePath)) { 47: Console.WriteLine("Source Image: {0}x{1}", 48: sourceBitmap.Width, sourceBitmap.Height);
49: var sliceWidth = sourceBitmap.Width / gridSize;
50: var sliceHeight = sourceBitmap.Height / gridSize;
51: Console.WriteLine("Each slice: {0}x{1}", sliceWidth, sliceHeight); 52: int tile = 0;
53: foreach (var row in gridSize.Times()) { 54: foreach (var column in gridSize.Times()) { 55: Console.WriteLine("Creating {0} of {1} ({2},{3})", 56: ++tile, gridSize * gridSize, column, row);
57: using (var destBitmap = new Bitmap(sliceWidth, sliceHeight)) { 58: var sourceData = sourceBitmap.LockBits(
59: new Rectangle(column * sliceWidth,
60: row * sliceHeight, sliceWidth,
61: sliceHeight),
62: ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
63: var destData = destBitmap.LockBits(
64: new Rectangle(0, 0, sliceWidth, sliceHeight),
65: ImageLockMode.WriteOnly, sourceData.PixelFormat);
66: unsafe { 67: byte* pSrc = (byte*)sourceData.Scan0.ToPointer();
68: byte* pDest = (byte*)destData.Scan0.ToPointer();
69: foreach (var line in sliceHeight.Times()) { 70: memcpy(pDest, pSrc, sliceWidth * 3);
71: pSrc += sourceData.Stride;
72: pDest += destData.Stride;
73: }
74: }
75: sourceBitmap.UnlockBits(sourceData);
76: destBitmap.UnlockBits(destData);
77: destBitmap.Save(String.Format("{0}_{1}_{2}.png", 78: Path.GetFileNameWithoutExtension(sourcePath),
79: column, row), ImageFormat.Png);
80: }
81: }
82: }
83: }
84: }
85: catch (Exception ex) { 86: Console.WriteLine("Error processing image."); 87: Console.WriteLine(ex.ToString());
88: }
89: }
90:
91: private static void ShowUsage() { 92: Console.WriteLine("Usage:"); 93: Console.WriteLine("ImageSlicer.exe sourceImage [gridSize]"); 94: }
95: }
96: }