1 | /* |
2 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Oracle designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Oracle in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 | * or visit www.oracle.com if you need additional information or have any |
23 | * questions. |
24 | */ |
25 | |
26 | #ifndef SPLASHSCREEN_GFX_IMPL_H |
27 | #define SPLASHSCREEN_GFX_IMPL_H |
28 | |
29 | #include "splashscreen_gfx.h" |
30 | |
31 | /* here come some very simple macros */ |
32 | |
33 | /* advance a pointer p by sizeof(type)*n bytes */ |
34 | #define INCPN(type,p,n) ((p) = (type*)(p)+(n)) |
35 | |
36 | /* advance a pointer by sizeof(type) */ |
37 | #define INCP(type,p) INCPN(type,(p),1) |
38 | |
39 | /* store a typed value to pointed location */ |
40 | #define PUT(type,p,v) (*(type*)(p) = (type)(v)) |
41 | |
42 | /* load a typed value from pointed location */ |
43 | #define GET(type,p) (*(type*)p) |
44 | |
45 | /* same as cond<0?-1:0 */ |
46 | enum |
47 | { |
48 | IFNEG_SHIFT_BITS = sizeof(int) * 8 - 1 |
49 | }; |
50 | |
51 | #define IFNEG(cond) ((int)(cond)>>IFNEG_SHIFT_BITS) |
52 | |
53 | /* same as cond<0?n1:n2 */ |
54 | #define IFNEGPOS(cond,n1,n2) ((IFNEG(cond)&(n1))|((~IFNEG(cond))&(n2))) |
55 | |
56 | /* value shifted left by n bits, negative n is allowed */ |
57 | #define LSHIFT(value,n) IFNEGPOS((n),(value)>>-(n),(value)<<(n)) |
58 | |
59 | /* value shifted right by n bits, negative n is allowed */ |
60 | #define RSHIFT(value,n) IFNEGPOS(n,(value)<<-(n),(value)>>(n)) |
61 | |
62 | /* converts a single i'th component to the specific format defined by format->shift[i] and format->mask[i] */ |
63 | #define CONVCOMP(quad,format,i) \ |
64 | (LSHIFT((quad),(format)->shift[i])&(format)->mask[i]) |
65 | |
66 | /* extracts the component defined by format->shift[i] and format->mask[i] from a specific-format value */ |
67 | #define UNCONVCOMP(value,format,i) \ |
68 | (RSHIFT((value)&(format)->mask[i],(format)->shift[i])) |
69 | |
70 | /* dithers the color using the dither matrices and colormap from format |
71 | indices to dither matrices are passed as arguments */ |
72 | INLINE unsigned |
73 | ditherColor(rgbquad_t value, ImageFormat * format, int row, int col) |
74 | { |
75 | int blue = QUAD_BLUE(value); |
76 | int green = QUAD_GREEN(value); |
77 | int red = QUAD_RED(value); |
78 | |
79 | blue = format->dithers[0].colorTable[blue + |
80 | format->dithers[0].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
81 | green = format->dithers[1].colorTable[green + |
82 | format->dithers[1].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
83 | red = format->dithers[2].colorTable[red + |
84 | format->dithers[2].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
85 | return red + green + blue; |
86 | } |
87 | |
88 | /* blend (lerp between) two rgb quads |
89 | src and dst alpha is ignored |
90 | the algorithm: src*alpha+dst*(1-alpha)=(src-dst)*alpha+dst, rb and g are done separately |
91 | */ |
92 | INLINE rgbquad_t |
93 | blendRGB(rgbquad_t dst, rgbquad_t src, rgbquad_t alpha) |
94 | { |
95 | const rgbquad_t a = alpha; |
96 | const rgbquad_t a1 = 0xFF - alpha; |
97 | |
98 | return MAKE_QUAD( |
99 | (rgbquad_t)((QUAD_RED(src) * a + QUAD_RED(dst) * a1) / 0xFF), |
100 | (rgbquad_t)((QUAD_GREEN(src) * a + QUAD_GREEN(dst) * a1) / 0xFF), |
101 | (rgbquad_t)((QUAD_BLUE(src) * a + QUAD_BLUE(dst) * a1) / 0xFF), |
102 | 0); |
103 | } |
104 | |
105 | /* scales rgb quad by alpha. basically similar to what's above. src alpha is retained. |
106 | used for premultiplying alpha |
107 | |
108 | btw: braindead MSVC6 generates _three_ mul instructions for this function */ |
109 | |
110 | INLINE rgbquad_t |
111 | premultiplyRGBA(rgbquad_t src) |
112 | { |
113 | rgbquad_t srb = src & 0xFF00FF; |
114 | rgbquad_t sg = src & 0xFF00; |
115 | rgbquad_t alpha = src >> QUAD_ALPHA_SHIFT; |
116 | |
117 | alpha += 1; |
118 | |
119 | srb *= alpha; |
120 | sg *= alpha; |
121 | srb >>= 8; |
122 | sg >>= 8; |
123 | |
124 | return (src & 0xFF000000) | (srb & 0xFF00FF) | (sg & 0xFF00); |
125 | } |
126 | |
127 | /* The functions below are inherently ineffective, but the performance seems to be |
128 | more or less adequate for the case of splash screens. They can be optimized later |
129 | if needed. The idea of optimization is to provide inlineable form of putRGBADither and |
130 | getRGBA at least for certain most frequently used visuals. Something like this is |
131 | done in Java 2D ("loops"). This would be possible with C++ templates, but making it |
132 | clean for C would require ugly preprocessor tricks. Leaving it out for later. |
133 | */ |
134 | |
135 | /* convert a single pixel color value from rgbquad according to visual format |
136 | and place it to pointed location |
137 | ordered dithering used when necessary */ |
138 | INLINE void |
139 | putRGBADither(rgbquad_t value, void *ptr, ImageFormat * format, |
140 | int row, int col) |
141 | { |
142 | if (format->premultiplied) { |
143 | value = premultiplyRGBA(value); |
144 | } |
145 | if (format->dithers) { |
146 | value = format->colorIndex[ditherColor(value, format, row, col)]; |
147 | } |
148 | else { |
149 | value = CONVCOMP(value, format, 0) | CONVCOMP(value, format, 1) | |
150 | CONVCOMP(value, format, 2) | CONVCOMP(value, format, 3); |
151 | } |
152 | switch (format->byteOrder) { |
153 | case BYTE_ORDER_LSBFIRST: |
154 | switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
155 | case 4: |
156 | PUT(byte_t, ptr, value & 0xff); |
157 | value >>= 8; |
158 | INCP(byte_t, ptr); |
159 | case 3: |
160 | PUT(byte_t, ptr, value & 0xff); |
161 | value >>= 8; |
162 | INCP(byte_t, ptr); |
163 | case 2: |
164 | PUT(byte_t, ptr, value & 0xff); |
165 | value >>= 8; |
166 | INCP(byte_t, ptr); |
167 | case 1: |
168 | PUT(byte_t, ptr, value & 0xff); |
169 | } |
170 | break; |
171 | case BYTE_ORDER_MSBFIRST: |
172 | switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
173 | case 4: |
174 | PUT(byte_t, ptr, (value >> 24) & 0xff); |
175 | INCP(byte_t, ptr); |
176 | case 3: |
177 | PUT(byte_t, ptr, (value >> 16) & 0xff); |
178 | INCP(byte_t, ptr); |
179 | case 2: |
180 | PUT(byte_t, ptr, (value >> 8) & 0xff); |
181 | INCP(byte_t, ptr); |
182 | case 1: |
183 | PUT(byte_t, ptr, value & 0xff); |
184 | } |
185 | break; |
186 | case BYTE_ORDER_NATIVE: |
187 | switch (format->depthBytes) { |
188 | case 4: |
189 | PUT(rgbquad_t, ptr, value); |
190 | break; |
191 | case 3: /* not supported, LSB or MSB should always be specified */ |
192 | PUT(byte_t, ptr, 0xff); /* Put a stub value */ |
193 | INCP(byte_t, ptr); |
194 | PUT(byte_t, ptr, 0xff); |
195 | INCP(byte_t, ptr); |
196 | PUT(byte_t, ptr, 0xff); |
197 | break; |
198 | case 2: |
199 | PUT(word_t, ptr, value); |
200 | break; |
201 | case 1: |
202 | PUT(byte_t, ptr, value); |
203 | break; |
204 | } |
205 | } |
206 | } |
207 | |
208 | /* load a single pixel color value and un-convert it to rgbquad according to visual format */ |
209 | INLINE rgbquad_t |
210 | getRGBA(void *ptr, ImageFormat * format) |
211 | { |
212 | /* |
213 | FIXME: color is not un-alpha-premultiplied on get |
214 | this is not required by current code, but it makes the implementation inconsistent |
215 | i.e. put(get) will not work right for alpha-premultiplied images */ |
216 | |
217 | /* get the value basing on depth and byte order */ |
218 | rgbquad_t value = 0; |
219 | |
220 | switch (format->byteOrder) { |
221 | case BYTE_ORDER_LSBFIRST: |
222 | switch (format->depthBytes) { |
223 | case 4: |
224 | value |= GET(byte_t, ptr); |
225 | value <<= 8; |
226 | INCP(byte_t, ptr); |
227 | case 3: |
228 | value |= GET(byte_t, ptr); |
229 | value <<= 8; |
230 | INCP(byte_t, ptr); |
231 | case 2: |
232 | value |= GET(byte_t, ptr); |
233 | value <<= 8; |
234 | INCP(byte_t, ptr); |
235 | case 1: |
236 | value |= GET(byte_t, ptr); |
237 | } |
238 | break; |
239 | case BYTE_ORDER_MSBFIRST: |
240 | switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
241 | case 4: |
242 | value |= (GET(byte_t, ptr) << 24); |
243 | INCP(byte_t, ptr); |
244 | case 3: |
245 | value |= (GET(byte_t, ptr) << 16); |
246 | INCP(byte_t, ptr); |
247 | case 2: |
248 | value |= (GET(byte_t, ptr) << 8); |
249 | INCP(byte_t, ptr); |
250 | case 1: |
251 | value |= GET(byte_t, ptr); |
252 | } |
253 | break; |
254 | case BYTE_ORDER_NATIVE: |
255 | switch (format->depthBytes) { |
256 | case 4: |
257 | value = GET(rgbquad_t, ptr); |
258 | break; |
259 | case 3: /* not supported, LSB or MSB should always be specified */ |
260 | value = 0xFFFFFFFF; /*return a stub value */ |
261 | break; |
262 | case 2: |
263 | value = (rgbquad_t) GET(word_t, ptr); |
264 | break; |
265 | case 1: |
266 | value = (rgbquad_t) GET(byte_t, ptr); |
267 | break; |
268 | } |
269 | break; |
270 | } |
271 | /* now un-convert the value */ |
272 | if (format->colorMap) { |
273 | if (value == format->transparentColor) |
274 | return 0; |
275 | else |
276 | return format->colorMap[value]; |
277 | } |
278 | else { |
279 | return UNCONVCOMP(value, format, 0) | UNCONVCOMP(value, format, 1) | |
280 | UNCONVCOMP(value, format, 2) | UNCONVCOMP(value, format, 3) | |
281 | format->fixedBits; |
282 | } |
283 | } |
284 | |
285 | /* fill the line with the specified color according to visual format */ |
286 | INLINE void |
287 | fillLine(rgbquad_t color, void *pDst, int incDst, int n, |
288 | ImageFormat * dstFormat, int row, int col) |
289 | { |
290 | int i; |
291 | |
292 | for (i = 0; i < n; ++i) { |
293 | putRGBADither(color, pDst, dstFormat, row, col++); |
294 | INCPN(byte_t, pDst, incDst); |
295 | } |
296 | } |
297 | |
298 | /* find the shift for specified mask, also verify the mask is valid */ |
299 | INLINE int |
300 | getMaskShift(rgbquad_t mask, int *pShift, int *pnumBits) |
301 | { |
302 | int shift = 0, numBits = 0; |
303 | |
304 | /* check the mask is not empty */ |
305 | if (!mask) |
306 | return 0; |
307 | /* calculate the shift */ |
308 | while ((mask & 1) == 0) { |
309 | ++shift; |
310 | mask >>= 1; |
311 | } |
312 | /* check the mask is contigious */ |
313 | if ((mask & (mask + 1)) != 0) |
314 | return 0; |
315 | /* calculate the number of bits */ |
316 | do { |
317 | ++numBits; |
318 | mask >>= 1; |
319 | } while ((mask & 1) != 0); |
320 | *pShift = shift; |
321 | *pnumBits = numBits; |
322 | return 1; |
323 | } |
324 | |
325 | #endif |
326 | |