예제 #1
0
 private void DumpImage(SourceImage srcImg)
 {
     return;
     /*
     Bitmap bmp = new Bitmap(srcImg.CropW, srcImg.CropH, PixelFormat.Format32bppArgb);
     for(int x=0; x<srcImg.CropW; ++x)
         for (int y = 0; y < srcImg.CropH; ++y)
         {
             int sx = x + srcImg.CropX;
             int sy = y + srcImg.CropY;
             int color = srcImg.Data[sx + sy*srcImg.Stride];
             bmp.SetPixel(x, y, Color.FromArgb(color));
         }
     bmp.Save(@"c:\tmp\cram\" + srcImg.ShortName);
     */
 }
예제 #2
0
        private void ProcessImage(string filename)
        {
            // Try to load the image from file
            Image origImg;

            SourceImage srcImg = new SourceImage();
            srcImg.Filename = filename;

            // Some of these path conversions can fail, if they do, fallback to the
            // default path
            try
            {
                switch (Settings.PathMode)
                {
                    case PathMode.Short:
                        srcImg.ShortName = Path.GetFileName(filename);
                        break;
                    case PathMode.Full:
                        srcImg.ShortName = Path.GetFullPath(filename);
                        break;
                    case PathMode.Relative:
                        srcImg.ShortName = PathUtil.RelativePathTo(Path.GetFullPath(Settings.RelativePathBase),
                                                                        Path.GetFullPath(filename));
                        break;
                }
            }
            catch (Exception)
            {
                Log("Error adjusting path, using original: {0}", filename);
                srcImg.ShortName = filename;
            }

            try
            {
                origImg = Image.FromFile(srcImg.Filename);
            }
            catch (Exception)
            {
                Log("Error opening file, skipping: {0}", srcImg.ShortName);
                ++numFailed;
                return;
            }

            // Probably can't happen, but just in case
            if (origImg.Width == 0 || origImg.Height == 0)
            {
                Log("Image has a zero dimension, skipping: {0}", srcImg.Filename);
                ++numFailed;
                return;
            }

            // TODO: Only do this if necessary?

            // If that succeeded, then copy it to a bitmap with a known pixel format (ARGB)
            // to do some work on it
            Bitmap bmp = new Bitmap(origImg.Width, origImg.Height, PixelFormat.Format32bppArgb);
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                graphics.Clear(Color.Transparent);
                graphics.DrawImage(origImg, 0, 0, origImg.Width, origImg.Height);
            }

            // Clean up original image
            origImg.Dispose();

            // Copy pixels out toa raw int array -- this is about 400x faster than GetPixel()
            int pixels = bmp.Width * bmp.Height;
            int[] pixelData = new int[pixels];
            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                                            ImageLockMode.ReadOnly,
                                            PixelFormat.Format32bppArgb);
            System.Runtime.InteropServices.Marshal.Copy(data.Scan0, pixelData, 0, pixels);
            bmp.UnlockBits(data);

            // Fill in some fields on the source image info block
            srcImg.Data = pixelData;
            srcImg.Stride = data.Stride / 4;
            srcImg.Width = bmp.Width;
            srcImg.Height = bmp.Height;
            srcImg.CropW = srcImg.Width;
            srcImg.CropH = srcImg.Height;

            bmp.Dispose();

            // Perform colorkeying
            ColorKey(srcImg);

            // Perform cropping of transparent areas
            if (!Crop(srcImg))
            {
                Log("Image has zero dimension after cropping, skipping: {0}", srcImg.ShortName);
                ++numFailed;
                return;
            }

            DumpImage(srcImg);

            if (Settings.Unique)
            {
                string md5 = ComputeHash(srcImg);
                if (uniqueTable.ContainsKey(md5))
                {
                    // TODO: Check if the images are really exactly the same!
                    SourceImage dupOf = uniqueTable[md5];
                    srcImg.DuplicateOf = dupOf;
                    Log("Duplicate found! {0} has same hash as {1}: {2}", srcImg.ShortName, dupOf.ShortName, md5);
                    ++numDups;
                }
                else
                {
                    uniqueTable[md5] = srcImg;
                    ++numUnique;
                }
            }
            else
                ++numUnique;

            // Save this to the source image list
            SourceImages.Add(srcImg);
        }
예제 #3
0
        private int DetectColorKey(SourceImage srcImg)
        {
            int w = srcImg.Width, h = srcImg.Height;
            // Lol repetitive code :(
            int a = srcImg.Data[0],
                    b = srcImg.Data[w - 1],
                    c = srcImg.Data[(h - 1)*(srcImg.Stride)],
                    d = srcImg.Data[(h - 1)*(srcImg.Stride) + w - 1];

            // If any of them are partially transparent, assume image had an alpha channel and thus
            // colorkey is 100% transparent -- i.e., don't do anything
            if (((a & 0xFF000000)>>24) != 255 ||
                ((b & 0xFF000000)>>24) != 255 ||
                ((c & 0xFF000000)>>24) != 255 ||
                ((d & 0xFF000000)>>24) != 255)
                return 0;

            int aScore = 0, bScore = 0, cScore = 0, dScore = 0;
            aScore = ((b == a) ? 1 : 0) +
                    ((c == a) ? 1 : 0) +
                    ((d == a) ? 1 : 0);

            bScore = ((a == b) ? 1 : 0) +
                    ((c == b) ? 1 : 0) +
                    ((d == b) ? 1 : 0);

            cScore = ((a == c) ? 1 : 0) +
                    ((b == c) ? 1 : 0) +
                    ((d == c) ? 1 : 0);

            dScore = ((a == d) ? 1 : 0) +
                    ((b == d) ? 1 : 0) +
                    ((c == d) ? 1 : 0);

            if (aScore >= bScore &&
                aScore >= cScore &&
                aScore >= dScore)
                return a;
            else if (bScore >= aScore &&
                 bScore >= cScore &&
                 bScore >= dScore)
                return b;
            else if (cScore >= aScore &&
                cScore >= bScore &&
                cScore >= dScore)
                return c;
            else
                return d;
        }
예제 #4
0
        private Bitmap DataToBitmap(SourceImage image)
        {
            Bitmap bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);

            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                                        ImageLockMode.WriteOnly,
                                        PixelFormat.Format32bppArgb);

            System.Runtime.InteropServices.Marshal.Copy(image.Data, 0, data.Scan0, image.Width*image.Height);
            bmp.UnlockBits(data);

            return bmp;
        }
예제 #5
0
        // Return true if image was nonzero sized
        private bool Crop(SourceImage srcImg)
        {
            // Bail if we're not cropping
            if (!Settings.Crop)
                return true;

            bool atTop = true;
            int topBlankRows = 0;
            int bottomBlankRows = 0;
            int leftBlankCols = srcImg.Width;
            int rightBlankCols = srcImg.Height;
            bool anySolidRows = false;

            // Basically, for each row we scan left to right to find the first solid pixel.
            // If there's none, then we treat this as an empty row, and thus affect the min/max
            // Y values. Otherwise, we scan from the right to get a right bound, then
            // use this to affect the min/max X values.
            for (int y = 0; y < srcImg.Height; ++y)
            {
                // First, start counting blanks from the left.
                // If we hit a solid pixel, save its position
                int x;
                for (x = 0; x < srcImg.Width; ++x)
                {
                    int c = srcImg.Data[x + y * srcImg.Stride];
                    if ((c & 0xFF000000) != 0)
                        break;
                }

                // If the row was totally blank
                if (x == srcImg.Width)
                {
                    if (atTop)
                        topBlankRows++;
                    else
                        bottomBlankRows++;
                }
                // Otherwise, we got a solid row.
                // First solid pixel is in column 'x'
                else
                {
                    atTop = false;
                    bottomBlankRows = 0;

                    int rowLeftBlankCols = x;
                    // If we hit any solid pixels, start counting from the right to
                    // see what the max X is
                    for (x = srcImg.Width - 1; x > rowLeftBlankCols; --x)
                    {
                        int c = srcImg.Data[x + y * srcImg.Stride];
                        if ((c & 0xFF000000) != 0)
                            break;
                    }

                    int rowRightBlankCols = srcImg.Width - x - 1;

                    leftBlankCols = Math.Min(leftBlankCols, rowLeftBlankCols);
                    rightBlankCols = Math.Min(rightBlankCols, rowRightBlankCols);
                    anySolidRows = true;
                }
            }

            // Now, perform the cropping crap (or at least set up the proper vars)
            if (anySolidRows)
            {
                srcImg.CropX = leftBlankCols;
                srcImg.CropY = topBlankRows;
                srcImg.CropW = srcImg.Width - leftBlankCols - rightBlankCols;
                srcImg.CropH = srcImg.Height - topBlankRows - bottomBlankRows;
                return true;
            }
            else
            {
                srcImg.CropX = 0;
                srcImg.CropY = 0;
                srcImg.CropW = 0;
                srcImg.CropH = 0;
                return false;
            }
        }
예제 #6
0
        private string ComputeHash(SourceImage srcImg)
        {
            MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
            // NOTE: This assumes that stride is always equal to width and thus
            // rows don't have junk in them

            // TODO: Note that this is a bit of a waste of time :(
            // I miss pointer casts, maybe I can learn to do it with 'unsafe'...
            byte[] buf = new byte[srcImg.CropW * srcImg.CropH * 4];

            // Only convert the area inside the crop rect
            int i = 0;
            for (int x=0; x < srcImg.CropW; ++x)
                for (int y = 0; y < srcImg.CropH; ++y)
                {
                    int col = srcImg.Data[srcImg.CropX + x + (srcImg.CropY + y)*srcImg.Stride];
                    // Treat blank pixels as equal
                    if (((col & 0xFF000000) >> 24) == 0)
                    {
                        buf[i * 4] = buf[i * 4 + 1] = buf[i * 4 + 2] = buf[i * 4 + 3] = 0;
                    }
                    else
                    {
                        buf[i * 4] = (byte)(col & 0x000000FF);
                        buf[i * 4 + 1] = (byte)((col & 0x0000FF00) >> 8);
                        buf[i * 4 + 2] = (byte)((col & 0x00FF0000) >> 16);
                        buf[i * 4 + 3] = (byte)((col & 0xFF000000) >> 24);
                    }
                    ++i;
                }

            byte[] hash = provider.ComputeHash(buf);

            StringBuilder builder = new StringBuilder();
            foreach (byte b in hash)
                builder.AppendFormat("{0:x2}", b);
            return builder.ToString();
            /*
            Log("   MD5: {0}", builder.ToString());
            Log("   Pixels: {0}", i);
            */
        }
예제 #7
0
        private void ColorKey(SourceImage srcImg)
        {
            // Bail if we're not colorkeying at all
            if (Settings.ColorKeyMode == ColorKeyMode.Disabled)
                return;

            int colorKey;

            // Figure out color key
            if (Settings.ColorKeyMode == ColorKeyMode.Automatic)
            {
                colorKey = DetectColorKey(srcImg);
            }
            else // specific
            {
                colorKey = Settings.ColorKeyColor.ToArgb();
            }

            if ((colorKey & 0xFF000000) == 0)
                return;

            // Replace pixels matching colorkey w/ transparent
            // (NOTE: This is probably not fast)
            for (int y = 0; y < srcImg.Height; ++y)
                for (int x = 0; x < srcImg.Width; ++x)
                {
                    int c = srcImg.Data[x + srcImg.Stride*y];
                    if (c == colorKey)
                        srcImg.Data[x + srcImg.Stride * y] = 0;
                }
        }