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 */
46enum
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 */
72INLINE unsigned
73ditherColor(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*/
92INLINE rgbquad_t
93blendRGB(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
110INLINE rgbquad_t
111premultiplyRGBA(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 */
138INLINE void
139putRGBADither(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 */
209INLINE rgbquad_t
210getRGBA(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 */
286INLINE void
287fillLine(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 */
299INLINE int
300getMaskShift(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