| 1 | // tPaletteImage.cpp  |
| 2 | //  |
| 3 | // A simple palettized image. Comprised of Width x Height pixel data storing indexes into a palette. The palette is  |
| 4 | // simply an array of tPixels (RGB). Index resolution is determined by the pixel format (1 to 8 bits). The number of  |
| 5 | // palette entries (colours) is 2 ^ the index-resolution.  |
| 6 | //  |
| 7 | // Copyright (c) 2022, 2024 Tristan Grimmer.  |
| 8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
| 9 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
| 10 | //  |
| 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
| 12 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
| 13 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
| 14 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
| 15 | // PERFORMANCE OF THIS SOFTWARE.  |
| 16 |   |
| 17 | #include <Foundation/tAssert.h>  |
| 18 | #include <Foundation/tStandard.h>  |
| 19 | #include <Foundation/tBitArray.h>  |
| 20 | #include "Image/tPaletteImage.h"  |
| 21 | namespace tImage  |
| 22 | {  |
| 23 |   |
| 24 |   |
| 25 | bool tPaletteImage::Set(const tPaletteImage& src)  |
| 26 | {  |
| 27 | if (&src == this)  |
| 28 | return true;  |
| 29 |   |
| 30 | Clear();  |
| 31 | if (!src.IsValid())  |
| 32 | return false;  |
| 33 |   |
| 34 | PixelFormat = src.PixelFormat;  |
| 35 | Width = src.Width;  |
| 36 | Height = src.Height;  |
| 37 |   |
| 38 | int dataSize = src.GetDataSize();  |
| 39 | tAssert((dataSize > 0) && src.PixelData);  |
| 40 | PixelData = new uint8[dataSize];  |
| 41 | tStd::tMemcpy(dest: PixelData, src: src.PixelData, numBytes: dataSize);  |
| 42 |   |
| 43 | int palSize = src.GetPaletteSize();  |
| 44 | tAssert((palSize > 0) && src.Palette);  |
| 45 | Palette = new tColour3b[palSize];  |
| 46 | tStd::tMemcpy(dest: Palette, src: src.Palette, numBytes: palSize*sizeof(tColour3b));  |
| 47 |   |
| 48 | return true;  |
| 49 | }  |
| 50 |   |
| 51 |   |
| 52 | bool tPaletteImage::Set(tPixelFormat fmt, int width, int height)  |
| 53 | {  |
| 54 | Clear();  |
| 55 | if (!tIsPaletteFormat(format: fmt) || (width <= 0) || (height <= 0))  |
| 56 | return false;  |
| 57 |   |
| 58 | PixelFormat = fmt;  |
| 59 | Width = width;  |
| 60 | Height = height;  |
| 61 |   |
| 62 | int dataSize = GetDataSize();  |
| 63 | tAssert(dataSize > 0);  |
| 64 | PixelData = new uint8[dataSize];  |
| 65 | tStd::tMemset(dest: PixelData, val: 0, numBytes: dataSize);  |
| 66 |   |
| 67 | int palSize = GetPaletteSize();  |
| 68 | tAssert(palSize > 0);  |
| 69 | Palette = new tColour3b[palSize];  |
| 70 | tStd::tMemset(dest: Palette, val: 0, numBytes: palSize*sizeof(tColour3b));  |
| 71 |   |
| 72 | return true;  |
| 73 | }  |
| 74 |   |
| 75 |   |
| 76 | bool tPaletteImage::Set(tPixelFormat fmt, int width, int height, const uint8* pixelData, const tColour3b* palette)  |
| 77 | {  |
| 78 | Clear();  |
| 79 | if (!tIsPaletteFormat(format: fmt) || (width <= 0) || (height <= 0) || !pixelData || !palette)  |
| 80 | return false;  |
| 81 |   |
| 82 | PixelFormat = fmt;  |
| 83 | Width = width;  |
| 84 | Height = height;  |
| 85 |   |
| 86 | int dataSize = GetDataSize();  |
| 87 | tAssert(dataSize > 0);  |
| 88 | PixelData = new uint8[dataSize];  |
| 89 | tStd::tMemcpy(dest: PixelData, src: pixelData, numBytes: dataSize);  |
| 90 |   |
| 91 | int palSize = GetPaletteSize();  |
| 92 | tAssert(palSize > 0);  |
| 93 | Palette = new tColour3b[palSize];  |
| 94 | tStd::tMemcpy(dest: Palette, src: palette, numBytes: palSize*sizeof(tColour3b));  |
| 95 |   |
| 96 | return true;  |
| 97 | }  |
| 98 |   |
| 99 |   |
| 100 | bool tPaletteImage::Set(tPixelFormat fmt, int width, int height, const tPixel4b* pixels, tQuantize::Method quantMethod)  |
| 101 | {  |
| 102 | Clear();  |
| 103 | if (!tIsPaletteFormat(format: fmt) || (width <= 0) || (height <= 0) || !pixels)  |
| 104 | return false;  |
| 105 |   |
| 106 | tPixel3b* rgbPixels = new tPixel3b[width*height];  |
| 107 | for (int i = 0; i < width*height; i++)  |
| 108 | {  |
| 109 | rgbPixels[i].R = pixels[i].R;  |
| 110 | rgbPixels[i].G = pixels[i].G;  |
| 111 | rgbPixels[i].B = pixels[i].B;  |
| 112 | }  |
| 113 | bool success = Set(fmt, width, height, pixels: rgbPixels, quantMethod);  |
| 114 |   |
| 115 | delete[] rgbPixels;  |
| 116 | return success;  |
| 117 | }  |
| 118 |   |
| 119 |   |
| 120 | bool tPaletteImage::Set(tPixelFormat fmt, int width, int height, const tPixel3b* pixels, tQuantize::Method quantMethod)  |
| 121 | {  |
| 122 | Clear();  |
| 123 | if (!tIsPaletteFormat(format: fmt) || (width <= 0) || (height <= 0) || !pixels)  |
| 124 | return false;  |
| 125 |   |
| 126 | PixelFormat = fmt;  |
| 127 | Width = width;  |
| 128 | Height = height;  |
| 129 | int numColours = GetPaletteSize();  |
| 130 | Palette = new tColour3b[numColours];  |
| 131 | int dataSize = GetDataSize();  |
| 132 | PixelData = new uint8[dataSize];  |
| 133 |   |
| 134 | uint8* indices = new uint8[width*height];  |
| 135 |   |
| 136 | // Step 1. Call quantize. Populates the palette and the indices.  |
| 137 | switch (quantMethod)  |
| 138 | {  |
| 139 | case tQuantize::Method::Fixed:  |
| 140 | tQuantizeFixed::QuantizeImage(numColours, width, height, pixels, destPalette: Palette, destIndices: indices);  |
| 141 | break;  |
| 142 |   |
| 143 | case tQuantize::Method::Spatial:  |
| 144 | tQuantizeSpatial::QuantizeImage(numColours, width, height, pixels, destPalette: Palette, destIndices: indices);  |
| 145 | break;  |
| 146 |   |
| 147 | case tQuantize::Method::Neu:  |
| 148 | tQuantizeNeu::QuantizeImage(numColours, width, height, pixels, destPalette: Palette, destIndices: indices);  |
| 149 | break;  |
| 150 |   |
| 151 | case tQuantize::Method::Wu:  |
| 152 | tQuantizeWu::QuantizeImage(numColours, width, height, pixels, destPalette: Palette, destIndices: indices);  |
| 153 | break;  |
| 154 |   |
| 155 | default:  |
| 156 | delete[] indices;  |
| 157 | Clear();  |
| 158 | return false;  |
| 159 | }  |
| 160 |   |
| 161 | // Step 2. Populate PixelData from indices.  |
| 162 | int bpp = tGetBitsPerPixel(fmt);  |
| 163 | int numBits = Width*Height*bpp;  |
| 164 | int bitIndex = 0;  |
| 165 | tBitArray8 bitArray(PixelData, numBits, true);  |
| 166 | for (int y = 0; y < Height; y++)  |
| 167 | {  |
| 168 | for (int x = 0; x < Width; x++)  |
| 169 | {  |
| 170 | bitArray.SetBits(n: bitIndex, c: bpp, v: indices[x + y*Width]);  |
| 171 | bitIndex += bpp;  |
| 172 | }  |
| 173 | }  |
| 174 |   |
| 175 | delete[] indices;  |
| 176 | return true;  |
| 177 | }  |
| 178 |   |
| 179 |   |
| 180 | bool tPaletteImage::Get(tPixel4b* pixels)  |
| 181 | {  |
| 182 | if (!IsValid() || !pixels)  |
| 183 | return false;  |
| 184 |   |
| 185 | int bpp = tGetBitsPerPixel(PixelFormat);  |
| 186 | int numBits = Width*Height*bpp;  |
| 187 | int bitIndex = 0;  |
| 188 | tBitArray8 bitArray(PixelData, numBits, true);  |
| 189 | for (int y = 0; y < Height; y++)  |
| 190 | {  |
| 191 | for (int x = 0; x < Width; x++)  |
| 192 | {  |
| 193 | uint8 palIdx = bitArray.GetBits(n: bitIndex, c: bpp);  |
| 194 | tColour3b& colour = Palette[palIdx];  |
| 195 | pixels[x + y*Width].Set(r: colour.R, g: colour.G, b: colour.B);  |
| 196 | bitIndex += bpp;  |
| 197 | }  |
| 198 | }  |
| 199 |   |
| 200 | return true;  |
| 201 | }  |
| 202 |   |
| 203 |   |
| 204 | bool tPaletteImage::Get(tPixel3b* pixels)  |
| 205 | {  |
| 206 | if (!IsValid() || !pixels)  |
| 207 | return false;  |
| 208 |   |
| 209 | int bpp = tGetBitsPerPixel(PixelFormat);  |
| 210 | int numBits = Width*Height*bpp;  |
| 211 | int bitIndex = 0;  |
| 212 | tBitArray8 bitArray(PixelData, numBits, true);  |
| 213 | for (int y = 0; y < Height; y++)  |
| 214 | {  |
| 215 | for (int x = 0; x < Width; x++)  |
| 216 | {  |
| 217 | uint8 palIdx = bitArray.GetBits(n: bitIndex, c: bpp);  |
| 218 | tColour3b& colour = Palette[palIdx];  |
| 219 | pixels[x + y*Width].Set(r: colour.R, g: colour.G, b: colour.B);  |
| 220 | bitIndex += bpp;  |
| 221 | }  |
| 222 | }  |
| 223 |   |
| 224 | return true;  |
| 225 | }  |
| 226 |   |
| 227 |   |
| 228 | int tPaletteImage::GetDataSize() const  |
| 229 | {  |
| 230 | int numBits = Width*Height*tGetBitsPerPixel(PixelFormat);  |
| 231 | int numBytes = (numBits + 7) / 8;  |
| 232 | return numBytes;  |
| 233 | }  |
| 234 |   |
| 235 |   |
| 236 | int tPaletteImage::GetPaletteSize() const  |
| 237 | {  |
| 238 | return tMath::tPow2(n: tGetBitsPerPixel(PixelFormat));  |
| 239 | }  |
| 240 |   |
| 241 |   |
| 242 | }  |
| 243 | |