/* * Copyright 2019 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Diagnostics; using CommonUtil; namespace PluginCommon { /// /// Bitmap with 8-bit palette indices, for use with visualization generators. /// /// /// The bitmap is initially filled with color index 0. /// [Serializable] public class VisBitmap8 : IVisualization2d { public const int MAX_DIMENSION = 4096; // IVisualization2d public int Width { get; private set; } // IVisualization2d public int Height { get; private set; } private byte[] mData; private int[] mPalette; private int mMaxColorIndex; /// /// Constructor. /// /// Bitmap width, in pixels. /// Bitmap height, in pixels. public VisBitmap8(int width, int height) { if (width <= 0 || width > MAX_DIMENSION || height <= 0 || height > MAX_DIMENSION) { throw new ArgumentException("Bad bitmap width/height " + width + "," + height); } Width = width; Height = height; mData = new byte[width * height]; mPalette = new int[256]; // entries initialize to 0, i.e. transparent black mMaxColorIndex = 0; } //[Obsolete("use GetPixelIndex()")] public int GetPixel(int x, int y) { byte pix = mData[x + y * Width]; return mPalette[pix]; } /// /// Gets the color index for a single pixel. /// /// X coordinate. /// Y coordinate. /// Color index. public byte GetPixelIndex(int x, int y) { return mData[x + y * Width]; } /// /// Sets the color for a single pixel. /// /// X coordinate. /// Y coordinate. /// Color index. public void SetPixelIndex(int x, int y, byte colorIndex) { if (x < 0 || x >= Width || y < 0 || y >= Height) { throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width + " height=" + Height + ")"); } if (colorIndex < 0 || colorIndex >= mMaxColorIndex) { throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" + mMaxColorIndex + ")"); } mData[x + y * Width] = colorIndex; } /// /// Sets the color for all pixels. /// /// Color index. public void SetAllPixelIndices(byte colorIndex) { if (colorIndex < 0 || colorIndex >= mMaxColorIndex) { throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" + mMaxColorIndex + ")"); } for (int i = 0; i < mData.Length; i++) { mData[i] = colorIndex; } } // IVisualization2d public byte[] GetPixels() { // TODO: remap any duplicate colors to reduce size of GIF return mData; } // IVisualization2d public int[] GetPalette() { // TODO: remove any duplicate colors to reduce size of GIF int[] pal = new int[mMaxColorIndex]; for (int i = 0; i < mMaxColorIndex; i++) { pal[i] = mPalette[i]; } return pal; } /// /// Adds a new color to the palette. If the color already exists, the call has no /// effect. /// /// 32-bit ARGB color value. //[Obsolete("use SetColor()")] public void AddColor(int color) { if (mMaxColorIndex == 256) { Debug.WriteLine("Palette is full"); return; } // I'm expecting palettes to only have a few colors, so O(n^2) is fine for now. for (int i = 0; i < mMaxColorIndex; i++) { if (mPalette[i] == color) { Debug.WriteLine("Color " + color.ToString("x6") + " already exists in palette (" + i + ")"); return; } } mPalette[mMaxColorIndex++] = color; } /// /// Adds a new color to the palette. If the color already exists, the call has no /// effect. /// /// Alpha value. /// Red value. /// Green value. /// Blue value. //[Obsolete("use SetColor()")] public void AddColor(byte a, byte r, byte g, byte b) { AddColor(Util.MakeARGB(a, r, g, b)); } /// /// Sets the Nth entry in the color palette. /// /// /// The size of the color palette will expand to hold the largest index. For best /// results, start with index 0 and count up, avoiding duplicates. /// /// Palette index, 0-255. /// Alpha value. /// Red value. /// Green value. /// Blue value. public void SetColor(int index, byte a, byte r, byte g, byte b) { if (index < 0 || index > 255) { throw new ArgumentException("Invalid index: " + index); } mPalette[index] = Util.MakeARGB(a, r, g, b); if (index >= mMaxColorIndex) { mMaxColorIndex = index + 1; } } /// /// Draws an 8x8 character cell on the bitmap. /// /// /// Might want a way to specify that the background shouldn't be drawn at all. /// /// Bitmap to draw on. /// Character to draw. /// X coord of upper-left pixel. /// Y coord of upper-left pixel. /// Foreground color index. /// Background color index. public static void DrawChar(VisBitmap8 vb, char ch, int xc, int yc, byte foreColor, byte backColor) { int origXc = xc; int[] charBits = Font8x8.GetBitData(ch); for (int row = 0; row < 8; row++) { int rowBits = charBits[row]; for (int col = 7; col >= 0; col--) { if ((rowBits & (1 << col)) != 0) { vb.SetPixelIndex(xc, yc, foreColor); } else { vb.SetPixelIndex(xc, yc, backColor); } xc++; } xc = origXc; yc++; } } // Character cell dimensions. public static int CharWidth { get { return 8; } } public static int CharHeight { get { return 8; } } } }