| /* |
| * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #ifndef SPLASHSCREEN_GFX_IMPL_H |
| #define SPLASHSCREEN_GFX_IMPL_H |
| |
| #include "splashscreen_gfx.h" |
| |
| /* here come some very simple macros */ |
| |
| /* advance a pointer p by sizeof(type)*n bytes */ |
| #define INCPN(type,p,n) ((p) = (type*)(p)+(n)) |
| |
| /* advance a pointer by sizeof(type) */ |
| #define INCP(type,p) INCPN(type,(p),1) |
| |
| /* store a typed value to pointed location */ |
| #define PUT(type,p,v) (*(type*)(p) = (type)(v)) |
| |
| /* load a typed value from pointed location */ |
| #define GET(type,p) (*(type*)p) |
| |
| /* same as cond<0?-1:0 */ |
| enum |
| { |
| IFNEG_SHIFT_BITS = sizeof(int) * 8 - 1 |
| }; |
| |
| #define IFNEG(cond) ((int)(cond)>>IFNEG_SHIFT_BITS) |
| |
| /* same as cond<0?n1:n2 */ |
| #define IFNEGPOS(cond,n1,n2) ((IFNEG(cond)&(n1))|((~IFNEG(cond))&(n2))) |
| |
| /* value shifted left by n bits, negative n is allowed */ |
| #define LSHIFT(value,n) IFNEGPOS((n),(value)>>-(n),(value)<<(n)) |
| |
| /* value shifted right by n bits, negative n is allowed */ |
| #define RSHIFT(value,n) IFNEGPOS(n,(value)<<-(n),(value)>>(n)) |
| |
| /* converts a single i'th component to the specific format defined by format->shift[i] and format->mask[i] */ |
| #define CONVCOMP(quad,format,i) \ |
| (LSHIFT((quad),(format)->shift[i])&(format)->mask[i]) |
| |
| /* extracts the component defined by format->shift[i] and format->mask[i] from a specific-format value */ |
| #define UNCONVCOMP(value,format,i) \ |
| (RSHIFT((value)&(format)->mask[i],(format)->shift[i])) |
| |
| /* dithers the color using the dither matrices and colormap from format |
| indices to dither matrices are passed as arguments */ |
| INLINE unsigned |
| ditherColor(rgbquad_t value, ImageFormat * format, int row, int col) |
| { |
| int blue = QUAD_BLUE(value); |
| int green = QUAD_GREEN(value); |
| int red = QUAD_RED(value); |
| |
| blue = format->dithers[0].colorTable[blue + |
| format->dithers[0].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
| green = format->dithers[1].colorTable[green + |
| format->dithers[1].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
| red = format->dithers[2].colorTable[red + |
| format->dithers[2].matrix[col & DITHER_MASK][row & DITHER_MASK]]; |
| return red + green + blue; |
| } |
| |
| /* blend (lerp between) two rgb quads |
| src and dst alpha is ignored |
| the algorithm: src*alpha+dst*(1-alpha)=(src-dst)*alpha+dst, rb and g are done separately |
| it's possible to verify that it's almost accurate indeed */ |
| |
| INLINE rgbquad_t |
| blendRGB(rgbquad_t dst, rgbquad_t src, rgbquad_t alpha) |
| { |
| const rgbquad_t dstrb = dst & 0xFF00FF; |
| const rgbquad_t dstg = dst & 0xFF00; |
| const rgbquad_t srcrb = src & 0xFF00FF; |
| const rgbquad_t srcg = src & 0xFF00; |
| |
| rgbquad_t drb = srcrb - dstrb; |
| rgbquad_t dg = srcg - dstg; |
| |
| alpha += 1; |
| |
| drb *= alpha; |
| dg *= alpha; |
| drb >>= 8; |
| dg >>= 8; |
| |
| return ((drb + dstrb) & 0xFF00FF) | ((dg + dstg) & 0xFF00); |
| } |
| |
| /* scales rgb quad by alpha. basically similar to what's above. src alpha is retained. |
| used for premultiplying alpha |
| |
| btw: braindead MSVC6 generates _three_ mul instructions for this function */ |
| |
| INLINE rgbquad_t |
| premultiplyRGBA(rgbquad_t src) |
| { |
| rgbquad_t srb = src & 0xFF00FF; |
| rgbquad_t sg = src & 0xFF00; |
| rgbquad_t alpha = src >> QUAD_ALPHA_SHIFT; |
| |
| alpha += 1; |
| |
| srb *= alpha; |
| sg *= alpha; |
| srb >>= 8; |
| sg >>= 8; |
| |
| return (src & 0xFF000000) | (srb & 0xFF00FF) | (sg & 0xFF00); |
| } |
| |
| /* The functions below are inherently ineffective, but the performance seems to be |
| more or less adequate for the case of splash screens. They can be optimized later |
| if needed. The idea of optimization is to provide inlineable form of putRGBADither and |
| getRGBA at least for certain most frequently used visuals. Something like this is |
| done in Java 2D ("loops"). This would be possible with C++ templates, but making it |
| clean for C would require ugly preprocessor tricks. Leaving it out for later. |
| */ |
| |
| /* convert a single pixel color value from rgbquad according to visual format |
| and place it to pointed location |
| ordered dithering used when necessary */ |
| INLINE void |
| putRGBADither(rgbquad_t value, void *ptr, ImageFormat * format, |
| int row, int col) |
| { |
| if (format->premultiplied) { |
| value = premultiplyRGBA(value); |
| } |
| if (format->dithers) { |
| value = format->colorIndex[ditherColor(value, format, row, col)]; |
| } |
| else { |
| value = CONVCOMP(value, format, 0) | CONVCOMP(value, format, 1) | |
| CONVCOMP(value, format, 2) | CONVCOMP(value, format, 3); |
| } |
| switch (format->byteOrder) { |
| case BYTE_ORDER_LSBFIRST: |
| switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
| case 4: |
| PUT(byte_t, ptr, value & 0xff); |
| value >>= 8; |
| INCP(byte_t, ptr); |
| case 3: |
| PUT(byte_t, ptr, value & 0xff); |
| value >>= 8; |
| INCP(byte_t, ptr); |
| case 2: |
| PUT(byte_t, ptr, value & 0xff); |
| value >>= 8; |
| INCP(byte_t, ptr); |
| case 1: |
| PUT(byte_t, ptr, value & 0xff); |
| } |
| break; |
| case BYTE_ORDER_MSBFIRST: |
| switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
| case 4: |
| PUT(byte_t, ptr, (value >> 24) & 0xff); |
| INCP(byte_t, ptr); |
| case 3: |
| PUT(byte_t, ptr, (value >> 16) & 0xff); |
| INCP(byte_t, ptr); |
| case 2: |
| PUT(byte_t, ptr, (value >> 8) & 0xff); |
| INCP(byte_t, ptr); |
| case 1: |
| PUT(byte_t, ptr, value & 0xff); |
| } |
| break; |
| case BYTE_ORDER_NATIVE: |
| switch (format->depthBytes) { |
| case 4: |
| PUT(rgbquad_t, ptr, value); |
| break; |
| case 3: /* not supported, LSB or MSB should always be specified */ |
| *(int *) 0 = 0; /* crash */ |
| break; |
| case 2: |
| PUT(word_t, ptr, value); |
| break; |
| case 1: |
| PUT(byte_t, ptr, value); |
| break; |
| } |
| } |
| } |
| |
| /* load a single pixel color value and un-convert it to rgbquad according to visual format */ |
| INLINE rgbquad_t |
| getRGBA(void *ptr, ImageFormat * format) |
| { |
| /* |
| FIXME: color is not un-alpha-premultiplied on get |
| this is not required by current code, but it makes the implementation inconsistent |
| i.e. put(get) will not work right for alpha-premultiplied images */ |
| |
| /* get the value basing on depth and byte order */ |
| rgbquad_t value = 0; |
| |
| switch (format->byteOrder) { |
| case BYTE_ORDER_LSBFIRST: |
| switch (format->depthBytes) { |
| case 4: |
| value |= GET(byte_t, ptr); |
| value <<= 8; |
| INCP(byte_t, ptr); |
| case 3: |
| value |= GET(byte_t, ptr); |
| value <<= 8; |
| INCP(byte_t, ptr); |
| case 2: |
| value |= GET(byte_t, ptr); |
| value <<= 8; |
| INCP(byte_t, ptr); |
| case 1: |
| value |= GET(byte_t, ptr); |
| } |
| break; |
| case BYTE_ORDER_MSBFIRST: |
| switch (format->depthBytes) { /* lack of *break*'s is intentional */ |
| case 4: |
| value |= (GET(byte_t, ptr) << 24); |
| INCP(byte_t, ptr); |
| case 3: |
| value |= (GET(byte_t, ptr) << 16); |
| INCP(byte_t, ptr); |
| case 2: |
| value |= (GET(byte_t, ptr) << 8); |
| INCP(byte_t, ptr); |
| case 1: |
| value |= GET(byte_t, ptr); |
| } |
| break; |
| case BYTE_ORDER_NATIVE: |
| switch (format->depthBytes) { |
| case 4: |
| value = GET(rgbquad_t, ptr); |
| break; |
| case 3: /* not supported, LSB or MSB should always be specified */ |
| *(int *) 0 = 0; |
| break; |
| case 2: |
| value = (rgbquad_t) GET(word_t, ptr); |
| break; |
| case 1: |
| value = (rgbquad_t) GET(byte_t, ptr); |
| break; |
| } |
| break; |
| } |
| /* now un-convert the value */ |
| if (format->colorMap) { |
| if (value == format->transparentColor) |
| return 0; |
| else |
| return format->colorMap[value]; |
| } |
| else { |
| return UNCONVCOMP(value, format, 0) | UNCONVCOMP(value, format, 1) | |
| UNCONVCOMP(value, format, 2) | UNCONVCOMP(value, format, 3) | |
| format->fixedBits; |
| } |
| } |
| |
| /* fill the line with the specified color according to visual format */ |
| INLINE void |
| fillLine(rgbquad_t color, void *pDst, int incDst, int n, |
| ImageFormat * dstFormat, int row, int col) |
| { |
| int i; |
| |
| for (i = 0; i < n; ++i) { |
| putRGBADither(color, pDst, dstFormat, row, col++); |
| INCPN(byte_t, pDst, incDst); |
| } |
| } |
| |
| /* find the shift for specified mask, also verify the mask is valid */ |
| INLINE int |
| getMaskShift(rgbquad_t mask, int *pShift, int *pnumBits) |
| { |
| int shift = 0, numBits = 0; |
| |
| /* check the mask is not empty */ |
| if (!mask) |
| return 0; |
| /* calculate the shift */ |
| while ((mask & 1) == 0) { |
| ++shift; |
| mask >>= 1; |
| } |
| /* check the mask is contigious */ |
| if ((mask & (mask + 1)) != 0) |
| return 0; |
| /* calculate the number of bits */ |
| do { |
| ++numBits; |
| mask >>= 1; |
| } while ((mask & 1) != 0); |
| *pShift = shift; |
| *pnumBits = numBits; |
| return 1; |
| } |
| |
| #endif |