/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/LFPQuantizer.cpp
Line | Count | Source |
1 | | // ========================================================== |
2 | | // LFPQuantizer class implementation |
3 | | // |
4 | | // Design and implementation by |
5 | | // - Carsten Klein (cklein05@users.sourceforge.net) |
6 | | // |
7 | | // This file is part of FreeImage 3 |
8 | | // |
9 | | // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
10 | | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
11 | | // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
12 | | // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
13 | | // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
14 | | // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
15 | | // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
16 | | // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
17 | | // THIS DISCLAIMER. |
18 | | // |
19 | | // Use at your own risk! |
20 | | // ========================================================== |
21 | | |
22 | | #include "Quantizers.h" |
23 | | #include "FreeImage.h" |
24 | | #include "Utilities.h" |
25 | | |
26 | | LFPQuantizer::LFPQuantizer(unsigned PaletteSize) : |
27 | 0 | m_size(0), m_limit(PaletteSize), m_index(0) { |
28 | 0 | m_map = new MapEntry[MAP_SIZE]; |
29 | 0 | memset(m_map, 0xFF, MAP_SIZE * sizeof(MapEntry)); |
30 | 0 | } |
31 | | |
32 | 0 | LFPQuantizer::~LFPQuantizer() { |
33 | 0 | delete[] m_map; |
34 | 0 | } |
35 | | |
36 | 0 | FIBITMAP* LFPQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette) { |
37 | |
|
38 | 0 | if (ReserveSize > 0 && ReservePalette != NULL) { |
39 | 0 | AddReservePalette(ReservePalette, ReserveSize); |
40 | 0 | } |
41 | |
|
42 | 0 | const unsigned width = FreeImage_GetWidth(dib); |
43 | 0 | const unsigned height = FreeImage_GetHeight(dib); |
44 | |
|
45 | 0 | FIBITMAP *dib8 = FreeImage_Allocate(width, height, 8); |
46 | 0 | if (dib8 == NULL) { |
47 | 0 | return NULL; |
48 | 0 | } |
49 | | |
50 | 0 | const unsigned src_pitch = FreeImage_GetPitch(dib); |
51 | 0 | const unsigned dst_pitch = FreeImage_GetPitch(dib8); |
52 | |
|
53 | 0 | const BYTE * const src_bits = FreeImage_GetBits(dib); |
54 | 0 | BYTE * const dst_bits = FreeImage_GetBits(dib8); |
55 | |
|
56 | 0 | unsigned last_color = -1; |
57 | 0 | int last_index = 0; |
58 | |
|
59 | 0 | if (FreeImage_GetBPP(dib) == 24) { |
60 | | |
61 | | // Getting the source pixel as an unsigned int is much faster than |
62 | | // working with FI_RGBA_xxx and shifting. However, this may fail |
63 | | // for the very last pixel, since its rgbReserved member (alpha) |
64 | | // may actually point to an address beyond the bitmap's memory. So, |
65 | | // we do not process the last scanline in the first loop. |
66 | | |
67 | | // Process all but the last scanline. |
68 | 0 | for (unsigned y = 0; y < height - 1; ++y) { |
69 | 0 | BYTE *dst_line = dst_bits + y * dst_pitch; |
70 | 0 | const BYTE *src_line = src_bits + y * src_pitch; |
71 | 0 | for (unsigned x = 0; x < width; ++x) { |
72 | 0 | const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; |
73 | 0 | if (color != last_color) { |
74 | 0 | last_color = color; |
75 | 0 | last_index = GetIndexForColor(color); |
76 | 0 | if (last_index == -1) { |
77 | 0 | FreeImage_Unload(dib8); |
78 | 0 | return NULL; |
79 | 0 | } |
80 | 0 | } |
81 | 0 | dst_line[x] = last_index; |
82 | 0 | src_line += 3; |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | // Process all but the last pixel of the last scanline. |
87 | 0 | BYTE *dst_line = dst_bits + (height - 1) * dst_pitch; |
88 | 0 | const BYTE *src_line = src_bits + (height - 1) * src_pitch; |
89 | 0 | for (unsigned x = 0; x < width - 1; ++x) { |
90 | 0 | const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; |
91 | 0 | if (color != last_color) { |
92 | 0 | last_color = color; |
93 | 0 | last_index = GetIndexForColor(color); |
94 | 0 | if (last_index == -1) { |
95 | 0 | FreeImage_Unload(dib8); |
96 | 0 | return NULL; |
97 | 0 | } |
98 | 0 | } |
99 | 0 | dst_line[x] = last_index; |
100 | 0 | src_line += 3; |
101 | 0 | } |
102 | | |
103 | | // Process the last pixel (src_line should already point to it). |
104 | 0 | const unsigned color = 0 | src_line[FI_RGBA_BLUE] << FI_RGBA_BLUE_SHIFT |
105 | 0 | | src_line[FI_RGBA_GREEN] << FI_RGBA_GREEN_SHIFT |
106 | 0 | | src_line[FI_RGBA_RED] << FI_RGBA_RED_SHIFT; |
107 | 0 | if (color != last_color) { |
108 | 0 | last_color = color; |
109 | 0 | last_index = GetIndexForColor(color); |
110 | 0 | if (last_index == -1) { |
111 | 0 | FreeImage_Unload(dib8); |
112 | 0 | return NULL; |
113 | 0 | } |
114 | 0 | } |
115 | 0 | dst_line[width - 1] = last_index; |
116 | |
|
117 | 0 | } else { |
118 | 0 | for (unsigned y = 0; y < height; ++y) { |
119 | 0 | BYTE *dst_line = dst_bits + y * dst_pitch; |
120 | 0 | const BYTE *src_line = src_bits + y * src_pitch; |
121 | 0 | for (unsigned x = 0; x < width; ++x) { |
122 | 0 | const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; |
123 | 0 | if (color != last_color) { |
124 | 0 | last_color = color; |
125 | 0 | last_index = GetIndexForColor(color); |
126 | 0 | if (last_index == -1) { |
127 | 0 | FreeImage_Unload(dib8); |
128 | 0 | return NULL; |
129 | 0 | } |
130 | 0 | } |
131 | 0 | dst_line[x] = last_index; |
132 | 0 | src_line += 4; |
133 | 0 | } |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 0 | WritePalette(FreeImage_GetPalette(dib8)); |
138 | 0 | return dib8; |
139 | 0 | } |
140 | | |
141 | | /** |
142 | | * Returns the palette index of the specified color. Tries to put the |
143 | | * color into the map, if it's not already present in the map. In that |
144 | | * case, a new index is used for the color. Returns -1, if adding the |
145 | | * color would exceed the desired maximum number of colors in the |
146 | | * palette. |
147 | | * @param color the color to get the index from |
148 | | * @return the palette index of the specified color or -1, if there |
149 | | * is no space left in the palette |
150 | | */ |
151 | 0 | inline int LFPQuantizer::GetIndexForColor(unsigned color) { |
152 | 0 | unsigned bucket = hash(color) & (MAP_SIZE - 1); |
153 | 0 | while (m_map[bucket].color != color) { |
154 | 0 | if (m_map[bucket].color == EMPTY_BUCKET) { |
155 | 0 | if (m_size == m_limit) { |
156 | 0 | return -1; |
157 | 0 | } |
158 | 0 | m_map[bucket].color = color; |
159 | 0 | m_map[bucket].index = m_index++; |
160 | 0 | ++m_size; |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | bucket = (bucket + 1) % MAP_SIZE; |
164 | 0 | } |
165 | 0 | return m_map[bucket].index; |
166 | 0 | } |
167 | | |
168 | | /** |
169 | | * Adds the specified number of entries of the specified reserve |
170 | | * palette to the newly created palette. |
171 | | * @param *palette a pointer to the reserve palette to copy from |
172 | | * @param size the number of entries to copy |
173 | | */ |
174 | 0 | void LFPQuantizer::AddReservePalette(const void *palette, unsigned size) { |
175 | 0 | if (size > MAX_SIZE) { |
176 | 0 | size = MAX_SIZE; |
177 | 0 | } |
178 | 0 | unsigned *ppal = (unsigned *) palette; |
179 | 0 | const unsigned offset = m_limit - size; |
180 | 0 | for (unsigned i = 0; i < size; ++i) { |
181 | 0 | const unsigned color = *ppal++; |
182 | 0 | const unsigned index = i + offset; |
183 | 0 | unsigned bucket = hash(color) & (MAP_SIZE - 1); |
184 | 0 | while((m_map[bucket].color != EMPTY_BUCKET) && (m_map[bucket].color != color)) { |
185 | 0 | bucket = (bucket + 1) % MAP_SIZE; |
186 | 0 | } |
187 | 0 | if(m_map[bucket].color != color) { |
188 | 0 | m_map[bucket].color = color; |
189 | 0 | m_map[bucket].index = index; |
190 | 0 | } |
191 | 0 | } |
192 | 0 | m_size += size; |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * Copies the newly created palette into the specified destination |
197 | | * palette. Although unused palette entries are not overwritten in |
198 | | * the destination palette, it is assumed to have space for at |
199 | | * least 256 entries. |
200 | | * @param palette a pointer to the destination palette |
201 | | */ |
202 | 0 | void LFPQuantizer::WritePalette(void *palette) { |
203 | 0 | for (unsigned i = 0; i < MAP_SIZE; ++i) { |
204 | 0 | if (m_map[i].color != EMPTY_BUCKET) { |
205 | 0 | ((unsigned *) palette)[m_map[i].index] = m_map[i].color; |
206 | 0 | } |
207 | 0 | } |
208 | 0 | } |