/// <summary> /// Copy constructor. Performs a shallow copy, and creates a new, /// blank array of previous size in BitmapData /// </summary> /// <param name="old"></param> public BitmapBuilder(BitmapBuilder old) { PaletteSize = old.PaletteSize; PaletteLocation = old.PaletteLocation; DataLocation = old.DataLocation; m_Width = old.Width; m_Height = old.Height; BitsPerPixel = old.BitsPerPixel; m_BytesPerLine = old.BytesPerLine; m_PaddingPerLine = old.PaddingPerLine; m_BitmapData = new byte[old.BitmapData.Length]; }
/// <summary> /// /// </summary> /// <param name="keyColor"></param> /// <param name="rawKeyColor"></param> /// <param name="dLeft"></param> /// <param name="dTop"></param> /// <returns></returns> public BitmapBuilder Crop(System.Drawing.Color keyColor, ushort rawKeyColor, out uint dLeft, out uint dTop) { BitmapBuilder newBB; switch (BitsPerPixel) { case 16: // // bottom cropping // uint cropBottom = 0; bool done = false; for (long y = 0; y < Height; ++y) { long i = DataLocation + BytesPerLine * y; for (long x = 0; x < Width; ++x) { ushort color = (ushort)(BitmapData[i] + (BitmapData[i + 1] << 8)); if (color != rawKeyColor) { done = true; break; } i += 2; } if (done) { break; } ++cropBottom; } // is the image all transparent? if (cropBottom == m_Height) { // leave a 1x1 transparent image newBB = new BitmapBuilder(BitsPerPixel, 1, 1); newBB.BitmapData[BitmapHeaderSize] = (byte)(rawKeyColor & 0xFF); newBB.BitmapData[BitmapHeaderSize] = (byte)((rawKeyColor >> 8) & 0xFF); #if XDEBUG rowsCropped += m_Height - 1; columnsCropped += m_Width - 1; #endif dLeft = 0; dTop = 0; // done. exit break; } // // top cropping // uint cropTop = 0; done = false; for (long y = Height - 1; y >= 0; --y) { long i = DataLocation + BytesPerLine * y; for (long x = 0; x < Width; ++x) { ushort color = (ushort)(BitmapData[i] + (BitmapData[i + 1] << 8)); if (color != rawKeyColor) { done = true; break; } i += 2; } if (done) { break; } ++cropTop; } // // left cropping // uint cropLeft = 0; done = false; for (long x = 0; x < Width; ++x) { for (long y = cropBottom; y < m_Height - cropTop; ++y) { long i = DataLocation + BytesPerLine * y + 2 * x; ushort color = (ushort)(BitmapData[i] + (BitmapData[i + 1] << 8)); if (color != rawKeyColor) { done = true; break; } } if (done) { break; } ++cropLeft; } // // right cropping // uint cropRight = 0; done = false; for (long x = Width - 1; x >= 0; --x) { for (long y = cropBottom; y < m_Height - cropTop; ++y) { long i = DataLocation + BytesPerLine * y + 2 * x; ushort color = (ushort)(BitmapData[i] + (BitmapData[i + 1] << 8)); if (color != rawKeyColor) { done = true; break; } } if (done) { break; } ++cropRight; } // // do cropping // // do we have anything to crop? if (cropTop + cropBottom + cropLeft + cropRight > 0) { uint newWidth = (uint)(m_Width - cropLeft - cropRight); uint newHeight = (uint)(m_Height - cropTop - cropBottom); newBB = new BitmapBuilder(BitsPerPixel, newWidth, newHeight); // bytes per line (to copy). without padding. uint newBytesPerLine = newBB.BytesPerLine - newBB.m_PaddingPerLine; uint newBytesPerLineWithPadding = newBB.BytesPerLine; for (int y = 0; y < newHeight; ++y) { Array.Copy(m_BitmapData, (int)(BitmapHeaderSize + (y + cropBottom) * BytesPerLine + 2 * cropLeft), newBB.BitmapData, (int)(BitmapHeaderSize + y * newBytesPerLineWithPadding), (int)newBytesPerLine); } } else { newBB = this; } #if XDEBUG rowsCropped += cropTop + cropBottom; columnsCropped += cropLeft + cropRight; #endif dLeft = cropLeft; dTop = cropTop; break; // TODO: image formats // we can and need currently only crop 16 bpp images // This might change when using custom animations case 4: case 8: case 24: default: throw new FormatException("Cannot remap unexpected" + " bitmap format " + BitsPerPixel + " bpp"); } #if XDEBUG bytesSavedByCropping += BitmapData.Length - newBB.BitmapData.Length; ++texturesCropped; #endif return(newBB); }
/// <summary> /// Remaps all pixels in the specified sequence state's frame that have /// a hue value of OriginalHue ± 45 (and a lightness less than 340) to the /// new values specified by <c>changes</c> /// </summary> /// <param name="srcFrame"> /// the animation frame is frame is to be remapped /// </param> /// <param name="changes"> /// a ColorRemapInfo specifying the destination color range /// </param> /// <returns> /// A remapped copy of the frame, or the original frame if nothing /// is to be done /// </returns> /// <remarks> /// This is an O(width*height) operation - and pixel operations are slow! /// If no changes are specified in <paramref name="changes" />, or if /// only a hue change with the original hue and new hue being the same is /// specified, the source frame is returned. /// </remarks> public static AnimationFrame Remap(AnimationFrame srcFrame, ColorRemapInfo changes) { AnimationFrame destFrame = new AnimationFrame(srcFrame); // if nothing has to be changed, just use the original BB if (!changes.SetSaturation && changes.DiffLightness == 0 && (!changes.SetHue || changes.NewHue == OriginalHue)) { destFrame.BitmapBuilder = srcFrame.BitmapBuilder; return(destFrame); } BitmapBuilder src = srcFrame.BitmapBuilder; BitmapBuilder dest = new BitmapBuilder(src.BitsPerPixel, src.Width, src.Height); destFrame.BitmapBuilder = dest; // choose a new key color to make sure our remapped values don't end up transparent // these are empirical values that seem to work well, but can be changed if any problems arise ushort newRawKeyColor = 0x7A38; Color newKeyColor = Color.FromArgb(247, 140, 198); if (changes.SetHue && (changes.NewHue > 220 || changes.NewHue < 60)) { newRawKeyColor = 0x47D8; newKeyColor = Color.FromArgb(140, 247, 198); } System.Diagnostics.Debug.Assert(newRawKeyColor == ColorToShort(newKeyColor)); byte newKeyColorL = (byte)(newRawKeyColor & 0xFF); byte newKeyColorH = (byte)(newRawKeyColor >> 8); destFrame.SetKeyColor(newRawKeyColor, newKeyColor); switch (src.BitsPerPixel) { // X1R5G5B5 little endian 16 bit color // (as found in most animations) case 16: { uint lineSize; if ((src.Width & 1U) == 0) { lineSize = src.Width; } else { lineSize = src.Width + 1; } uint padding = (lineSize - src.Width) << 1; // TODO: finish optimizing Remap // copy everything that's not image data //Array.Copy(src.BitmapData, dest.BitmapData, (int)src.DataLocation); long i = src.DataLocation; long copyStart = 0; for (int y = 0; y < src.Height; ++y, i += padding) { for (int x = 0; x < src.Width; ++x, i += 2) { ushort c = (ushort)(src.BitmapData[i] + (src.BitmapData[i + 1] << 8)); if (c == srcFrame.RawKeyColor) { if (i > copyStart) { } // Array.Copy(src.BitmapData, (int)copyStart, dest.BitmapData, (int)copyStart, (int)(i - copyStart)); copyStart = i + 2; dest.BitmapData[i] = newKeyColorL; dest.BitmapData[i + 1] = newKeyColorH; continue; } int R = (c >> 10) << 3, G = ((c >> 5) & 0x1F) << 3, B = (c & 0x1F) << 3; //int R = (c >> 7) & ~7, // G = (c >> 2) & 0xF8, // B = (c & 0x1F) << 3; R += (int)Math.Ceiling(R * 6.0f / 239.0f); G += (int)Math.Ceiling(G * 6.0f / 239.0f); B += (int)Math.Ceiling(B * 6.0f / 239.0f); int h, s, l; RGBToHSL(R, G, B, out h, out s, out l); // apply changes to all green color if (h > OriginalHue - 45 && h < OriginalHue + 45 && l < 340) { if (i > copyStart) { } // Array.Copy(src.BitmapData, (int)copyStart, dest.BitmapData, (int)copyStart, (int)(i - copyStart)); copyStart = i + 2; if (changes.SetHue) { h += changes.NewHue - OriginalHue; if (h < 0) { h += 360; } } if (changes.SetSaturation) { s = changes.NewSaturation; } l += changes.DiffLightness; if (l < 0) { l = 0; } else if (l > 359) { l = 359; } ushort to = ColorToShort(ColorFromHSL(h, s, l)); byte toL = (byte)(to & 0xFF); byte toH = (byte)(to >> 8); dest.BitmapData[i] = toL; dest.BitmapData[i + 1] = toH; } else { dest.BitmapData[i] = src.BitmapData[i]; dest.BitmapData[i + 1] = src.BitmapData[i + 1]; } } } break; } // images that are not 16 bpp are not supported and it is unlikely // they ever will, as the original graphics use only 16 bpp, and // new ones will use completely different formats (and probably 32 bpp) default: throw new FormatException("Cannot remap unexpected" + " bitmap format " + src.BitsPerPixel + " bpp"); } return(destFrame); }