/// <summary> /// Returns the brightness of each pixel, storing their quantity of incidence into a 256-item array. /// Each index holds the number of pixels whose brightness was the index. /// Useful for quickly calculating median, mode, etc /// </summary> /// <param name="bitmap">The input bitmap</param> /// <returns>The array of quantities of indicence per brightness level, in pixels</returns> private int[] GetBrightnessSamples(Bitmap bitmap) { // Begin with an array of 0 for each brightness value int[] outputArray = new int[256]; Array.Clear(outputArray, 0, 256); // Scan through all the pixels of the bitmap BitmapData imageData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { // For each pixel of 'brightness', increment outputArray[brightness] for (uint *pixel = (uint *)imageData.Scan0.ToPointer(), lastPixel = &pixel[imageData.Height * imageData.Stride / 4]; pixel < lastPixel; ++pixel) { int brightness = ImageMath.Max((byte)(*pixel >> 16), (byte)(*pixel >> 8), (byte)*pixel); ++outputArray[brightness]; } } bitmap.UnlockBits(imageData); return(outputArray); }
public override void Apply(ref Bitmap bitmap, out Highlighter[] highlighters) { Bitmap outputBitmap = new Bitmap(bitmap); BitmapData imageData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); BitmapData outputImageData = outputBitmap.LockBits(new Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); unsafe { // Welcome back to unsafe-land, home of C++ and myself uint *pixels = (uint *)imageData.Scan0.ToPointer(); uint *outputPixels = (uint *)outputImageData.Scan0.ToPointer(); for (int baseY = 0; baseY < bitmap.Height - PixelRadius; ++baseY) { uint *pixelRow = &pixels[baseY * imageData.Stride / 4]; byte *pixelBs = (byte *)pixelRow, pixelGs = (byte *)&pixelRow[1], pixelRs = (byte *)&pixelRow[2]; for (int baseX = 0; baseX < bitmap.Width - PixelRadius; ++baseX) { byte curR = pixelRs[baseX << 2], curG = pixelGs[baseX << 2], curB = pixelBs[baseX << 2]; int rMin = 255, gMin = 255, bMin = 255; int rMax = 0, gMax = 0, bMax = 0; // Search the radius around this pixel and track the difference for (int y = 0; y <= PixelRadius; ++y) { byte *subBs = &pixelBs[(y * imageData.Stride) + (baseX << 2)], subGs = &subBs[1], subRs = &subBs[2]; for (int x = 0; x <= PixelRadius; ++x) { // Compare red // TODO is there a cleaner way to do this? if (subRs[x << 2] < rMin) { rMin = subRs[x << 2]; } if (subGs[x << 2] < gMin) { gMin = subGs[x << 2]; } if (subBs[x << 2] < bMin) { bMin = subBs[x << 2]; } if (subRs[x << 2] > rMax) { rMax = subRs[x << 2]; } if (subGs[x << 2] > gMax) { gMax = subGs[x << 2]; } if (subBs[x << 2] > bMax) { bMax = subBs[x << 2]; } } } // Write the difference to the output outputPixels[baseY * outputImageData.Stride / 4 + baseX] = 0xFF000000 | (uint)((rMax - rMin) << 16 | (gMax - gMin) << 8 | (bMax - bMin)); // or the maximum difference of all colour channels.. byte maxDiff = ImageMath.Max((byte)(rMax - rMin), (byte)(gMax - gMin), (byte)(bMax - bMin)); outputPixels[baseY * outputImageData.Stride / 4 + baseX] = 0xFF000000 | (uint)(maxDiff << 16 | maxDiff << 8 | maxDiff); } } } // Release resources bitmap.UnlockBits(imageData); outputBitmap.UnlockBits(outputImageData); // Copy output data to input data bitmap = new Bitmap(outputBitmap); // TODO there is a better way to do this...right? // Done!No highlighters to return highlighters = null; }