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             *(int *) 0 = 0;    /* crash */
 193             break;
 194         case 2:
 195             PUT(word_t, ptr, value);
 196             break;
 197         case 1:
 198             PUT(byte_t, ptr, value);
 199             break;
 200         }
 201     }
 202 }
 203 
 204 /* load a single pixel color value and un-convert it to rgbquad according to visual format */
 205 INLINE rgbquad_t
 206 getRGBA(void *ptr, ImageFormat * format)
 207 {
 208     /*
 209        FIXME: color is not un-alpha-premultiplied on get
 210        this is not required by current code, but it makes the implementation inconsistent
 211        i.e. put(get) will not work right for alpha-premultiplied images */
 212 
 213     /* get the value basing on depth and byte order */
 214     rgbquad_t value = 0;
 215 
 216     switch (format->byteOrder) {
 217     case BYTE_ORDER_LSBFIRST:
 218         switch (format->depthBytes) {
 219         case 4:
 220             value |= GET(byte_t, ptr);
 221             value <<= 8;
 222             INCP(byte_t, ptr);
 223         case 3:
 224             value |= GET(byte_t, ptr);
 225             value <<= 8;
 226             INCP(byte_t, ptr);
 227         case 2:
 228             value |= GET(byte_t, ptr);
 229             value <<= 8;
 230             INCP(byte_t, ptr);
 231         case 1:
 232             value |= GET(byte_t, ptr);
 233         }
 234         break;
 235     case BYTE_ORDER_MSBFIRST:
 236         switch (format->depthBytes) {   /* lack of *break*'s is intentional */
 237         case 4:
 238             value |= (GET(byte_t, ptr) << 24);
 239             INCP(byte_t, ptr);
 240         case 3:
 241             value |= (GET(byte_t, ptr) << 16);
 242             INCP(byte_t, ptr);
 243         case 2:
 244             value |= (GET(byte_t, ptr) << 8);
 245             INCP(byte_t, ptr);
 246         case 1:
 247             value |= GET(byte_t, ptr);
 248         }
 249         break;
 250     case BYTE_ORDER_NATIVE:
 251         switch (format->depthBytes) {
 252         case 4:
 253             value = GET(rgbquad_t, ptr);
 254             break;
 255         case 3:                /* not supported, LSB or MSB should always be specified */
 256             *(int *) 0 = 0;
 257             break;
 258         case 2:
 259             value = (rgbquad_t) GET(word_t, ptr);
 260             break;
 261         case 1:
 262             value = (rgbquad_t) GET(byte_t, ptr);
 263             break;
 264         }
 265         break;
 266     }
 267     /* now un-convert the value */
 268     if (format->colorMap) {
 269         if (value == format->transparentColor)
 270             return 0;
 271         else
 272             return format->colorMap[value];
 273     }
 274     else {
 275         return UNCONVCOMP(value, format, 0) | UNCONVCOMP(value, format, 1) |
 276             UNCONVCOMP(value, format, 2) | UNCONVCOMP(value, format, 3) |
 277             format->fixedBits;
 278     }
 279 }
 280 
 281 /* fill the line with the specified color according to visual format */
 282 INLINE void
 283 fillLine(rgbquad_t color, void *pDst, int incDst, int n,
 284         ImageFormat * dstFormat, int row, int col)
 285 {
 286     int i;
 287 
 288     for (i = 0; i < n; ++i) {
 289         putRGBADither(color, pDst, dstFormat, row, col++);
 290         INCPN(byte_t, pDst, incDst);
 291     }
 292 }
 293 
 294 /* find the shift for specified mask, also verify the mask is valid */
 295 INLINE int
 296 getMaskShift(rgbquad_t mask, int *pShift, int *pnumBits)
 297 {
 298     int shift = 0, numBits = 0;
 299 
 300     /* check the mask is not empty */
 301     if (!mask)
 302         return 0;
 303     /* calculate the shift */
 304     while ((mask & 1) == 0) {
 305         ++shift;
 306         mask >>= 1;
 307     }
 308     /* check the mask is contigious */
 309     if ((mask & (mask + 1)) != 0)
 310         return 0;
 311     /* calculate the number of bits */
 312     do {
 313         ++numBits;
 314         mask >>= 1;
 315     } while ((mask & 1) != 0);
 316     *pShift = shift;
 317     *pnumBits = numBits;
 318     return 1;
 319 }
 320 
 321 #endif