/* * Copyright 2006, Haiku. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus */ #ifndef BLENDING_H #define BLENDING_H #include // faster bit error version: //#define INT_MULT(a, b, t) ((t) = (a) * (b), ((((t) >> 8) + (t)) >> 8)) // correct version #define INT_MULT(a, b, t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) #define INT_LERP(p, q, a, t) ((p) + INT_MULT(a, ((q) - (p)), t)) #define INT_PRELERP(p, q, a, t) ((p) + (q) - INT_MULT(a, p, t)) #define GAMMA_BLEND 1 #if GAMMA_BLEND extern uint16* kGammaTable; extern uint8* kInverseGammaTable; void init_gamma_blending(); void uninit_gamma_blending(); // blend inline void blend_gamma(uint16 b1, uint16 b2, uint16 b3, uint8 ba, // bottom components uint16 t1, uint16 t2, uint16 t3, uint8 ta, // top components uint8* d1, uint8* d2, uint8* d3, uint8* da) // dest components { if (ba == 255) { uint32 destAlpha = 255 - ta; *d1 = kInverseGammaTable[(b1 * destAlpha + t1 * ta) / 255]; *d2 = kInverseGammaTable[(b2 * destAlpha + t2 * ta) / 255]; *d3 = kInverseGammaTable[(b3 * destAlpha + t3 * ta) / 255]; *da = 255; } else { uint8 alphaRest = 255 - ta; uint32 alphaTemp = (65025 - alphaRest * (255 - ba)); uint32 alphaDest = ba * alphaRest; uint32 alphaSrc = 255 * ta; *d1 = kInverseGammaTable[(b1 * alphaDest + t1 * alphaSrc) / alphaTemp]; *d2 = kInverseGammaTable[(b2 * alphaDest + t2 * alphaSrc) / alphaTemp]; *d3 = kInverseGammaTable[(b3 * alphaDest + t3 * alphaSrc) / alphaTemp]; *da = alphaTemp / 255; } } // blend inline void blend(uint8 b1, uint8 b2, uint8 b3, uint8 ba, // bottom components uint8 t1, uint8 t2, uint8 t3, uint8 ta, // top components uint8* d1, uint8* d2, uint8* d3, uint8* da) // dest components { // convert to linear rgb uint16 gt1 = kGammaTable[t1]; uint16 gt2 = kGammaTable[t2]; uint16 gt3 = kGammaTable[t3]; uint16 gb1 = kGammaTable[b1]; uint16 gb2 = kGammaTable[b2]; uint16 gb3 = kGammaTable[b3]; blend_gamma(gb1, gb2, gb3, ba, gt1, gt2, gt3, ta, d1, d2, d3, da); } // convert_to_gamma // // converted value will be gamma corrected in the range [0...2550] // and can be passed on to the other functions that take uint16 components uint16 convert_to_gamma(uint8 value); // blend_colors_copy inline void blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, uint8 c1, uint8 c2, uint8 c3, uint16 gc1, uint16 gc2, uint16 gc3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { dest[0] = c1; dest[1] = c2; dest[2] = c3; dest[3] = alpha; } else { // only bottom components need to be gamma corrected uint16 gb1 = kGammaTable[bottom[0]]; uint16 gb2 = kGammaTable[bottom[1]]; uint16 gb3 = kGammaTable[bottom[2]]; blend_gamma(gb1, gb2, gb3, bottom[3], gc1, gc2, gc3, alpha, &dest[0], &dest[1], &dest[2], &dest[3]); } } else { *((uint32*)dest) = *((uint32*)bottom); } } // blend_colors inline void blend_colors(uint8* bottom, uint8 alpha, uint8 c1, uint8 c2, uint8 c3, uint16 gc1, uint16 gc2, uint16 gc3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { bottom[0] = c1; bottom[1] = c2; bottom[2] = c3; bottom[3] = alpha; } else { // only bottom components need to be gamma corrected uint16 gb1 = kGammaTable[bottom[0]]; uint16 gb2 = kGammaTable[bottom[1]]; uint16 gb3 = kGammaTable[bottom[2]]; blend_gamma(gb1, gb2, gb3, bottom[3], gc1, gc2, gc3, alpha, &bottom[0], &bottom[1], &bottom[2], &bottom[3]); } } } // blend_colors_copy inline void blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, uint8 c1, uint8 c2, uint8 c3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { dest[0] = c1; dest[1] = c2; dest[2] = c3; dest[3] = alpha; } else { blend(bottom[0], bottom[1], bottom[2], bottom[3], c1, c2, c3, alpha, &dest[0], &dest[1], &dest[2], &dest[3]); } } else { *((uint32*)dest) = *((uint32*)bottom); } } // blend_colors inline void blend_colors(uint8* bottom, uint8 alpha, uint8 c1, uint8 c2, uint8 c3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { bottom[0] = c1; bottom[1] = c2; bottom[2] = c3; bottom[3] = alpha; } else { blend(bottom[0], bottom[1], bottom[2], bottom[3], c1, c2, c3, alpha, &bottom[0], &bottom[1], &bottom[2], &bottom[3]); } } } // blend_colors inline void blend_colors(uint8* bottom, uint8* source, uint8 alphaOverride) { uint8 alpha = (source[3] * alphaOverride) / 255; if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { bottom[0] = source[0]; bottom[1] = source[1]; bottom[2] = source[2]; bottom[3] = alpha; } else { blend(bottom[0], bottom[1], bottom[2], bottom[3], source[0], source[1], source[2], alpha, &bottom[0], &bottom[1], &bottom[2], &bottom[3]); } } } // blend_colors inline void blend_colors(uint8* bottom, uint8* source) { if (source[3] > 0) { if (bottom[3] == 0 || source[3] == 255) { bottom[0] = source[0]; bottom[1] = source[1]; bottom[2] = source[2]; bottom[3] = source[3]; } else { blend(bottom[0], bottom[1], bottom[2], bottom[3], source[0], source[1], source[2], source[3], &bottom[0], &bottom[1], &bottom[2], &bottom[3]); } } } // blend_colors_copy inline void blend_colors_copy(uint8* dest, uint8* bottom, uint8* top) { if (bottom[3] == 0 || top[3] == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { blend(bottom[0], bottom[1], bottom[2], bottom[3], top[0], top[1], top[2], top[3], &dest[0], &dest[1], &dest[2], &dest[3]); } } // blend_pixels inline void blend_pixels(uint8* bottom, uint8* top, uint8 alpha) { if (alpha > 0) { if (alpha == 255) { bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2]; bottom[3] = top[3]; } else { // convert to linear rgb uint16 t1 = kGammaTable[top[0]]; uint16 t2 = kGammaTable[top[1]]; uint16 t3 = kGammaTable[top[2]]; uint16 b1 = kGammaTable[bottom[0]]; uint16 b2 = kGammaTable[bottom[1]]; uint16 b3 = kGammaTable[bottom[2]]; uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; uint8 invAlpha = 255 - mergeAlpha; bottom[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; bottom[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; bottom[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } } // blend_pixels_copy inline void blend_pixels_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alpha) { if (alpha > 0) { if (alpha == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { // convert to linear rgb uint16 t1 = kGammaTable[top[0]]; uint16 t2 = kGammaTable[top[1]]; uint16 t3 = kGammaTable[top[2]]; uint16 b1 = kGammaTable[bottom[0]]; uint16 b2 = kGammaTable[bottom[1]]; uint16 b3 = kGammaTable[bottom[2]]; uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; uint8 invAlpha = 255 - mergeAlpha; dest[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; dest[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; dest[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } else { dest[0] = bottom[0]; dest[1] = bottom[1]; dest[2] = bottom[2]; dest[3] = bottom[3]; } } // blend_pixels_overlay inline void blend_pixels_overlay(uint8* bottom, uint8* top, uint8 alphaOverride) { uint8 alpha = (top[3] * alphaOverride) / 255; if (alpha > 0) { if (alpha == 255) { bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2]; bottom[3] = top[3]; } else { // convert to linear rgb uint16 t1 = kGammaTable[top[0]]; uint16 t2 = kGammaTable[top[1]]; uint16 t3 = kGammaTable[top[2]]; uint16 b1 = kGammaTable[bottom[0]]; uint16 b2 = kGammaTable[bottom[1]]; uint16 b3 = kGammaTable[bottom[2]]; uint8 mergeAlpha = bottom[3] ? alpha : 255; uint8 invAlpha = 255 - mergeAlpha; bottom[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; bottom[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; bottom[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } } // blend_pixels_overlay_copy inline void blend_pixels_overlay_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alphaOverride) { uint8 alpha = (top[3] * alphaOverride) / 255; if (alpha > 0) { if (alpha == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { // convert to linear rgb uint16 t1 = kGammaTable[top[0]]; uint16 t2 = kGammaTable[top[1]]; uint16 t3 = kGammaTable[top[2]]; uint16 b1 = kGammaTable[bottom[0]]; uint16 b2 = kGammaTable[bottom[1]]; uint16 b3 = kGammaTable[bottom[2]]; uint8 mergeAlpha = bottom[3] ? alpha : 255; uint8 invAlpha = 255 - mergeAlpha; dest[0] = kInverseGammaTable[(b1 * invAlpha + t1 * mergeAlpha) / 255]; dest[1] = kInverseGammaTable[(b2 * invAlpha + t2 * mergeAlpha) / 255]; dest[2] = kInverseGammaTable[(b3 * invAlpha + t3 * mergeAlpha) / 255]; dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } else { dest[0] = bottom[0]; dest[1] = bottom[1]; dest[2] = bottom[2]; dest[3] = bottom[3]; } } /////////////////////////////////////////////////////////////////////////////////////////////////////// #else // GAMMA_BLEND // blend_colors_copy inline void blend_colors_copy(uint8* bottom, uint8 alpha, uint8* dest, uint8 c1, uint8 c2, uint8 c3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { dest[0] = c1; dest[1] = c2; dest[2] = c3; dest[3] = alpha; } else { if (bottom[3] == 255) { uint32 destAlpha = 255 - alpha; dest[0] = (uint8)((bottom[0] * destAlpha + c1 * alpha) / 255); dest[1] = (uint8)((bottom[1] * destAlpha + c2 * alpha) / 255); dest[2] = (uint8)((bottom[2] * destAlpha + c3 * alpha) / 255); dest[3] = 255; } else { uint8 alphaRest = 255 - alpha; uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); uint32 alphaDest = bottom[3] * alphaRest; uint32 alphaSrc = 255 * alpha; dest[0] = (bottom[0] * alphaDest + c1 * alphaSrc) / alphaTemp; dest[1] = (bottom[1] * alphaDest + c2 * alphaSrc) / alphaTemp; dest[2] = (bottom[2] * alphaDest + c3 * alphaSrc) / alphaTemp; dest[3] = alphaTemp / 255; } } } else { *((uint32*)dest) = *((uint32*)bottom); } } // blend_colors inline void blend_colors(uint8* bottom, uint8 alpha, uint8 c1, uint8 c2, uint8 c3) { if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { bottom[0] = c1; bottom[1] = c2; bottom[2] = c3; bottom[3] = alpha; } else { if (bottom[3] == 255) { uint32 destAlpha = 255 - alpha; bottom[0] = (uint8)((bottom[0] * destAlpha + c1 * alpha) / 255); bottom[1] = (uint8)((bottom[1] * destAlpha + c2 * alpha) / 255); bottom[2] = (uint8)((bottom[2] * destAlpha + c3 * alpha) / 255); } else { uint8 alphaRest = 255 - alpha; uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); uint32 alphaDest = bottom[3] * alphaRest; uint32 alphaSrc = 255 * alpha; bottom[0] = (bottom[0] * alphaDest + c1 * alphaSrc) / alphaTemp; bottom[1] = (bottom[1] * alphaDest + c2 * alphaSrc) / alphaTemp; bottom[2] = (bottom[2] * alphaDest + c3 * alphaSrc) / alphaTemp; bottom[3] = alphaTemp / 255; } } } } // blend_colors inline void blend_colors(uint8* bottom, uint8* source, uint8 alphaOverride) { uint8 alpha = (source[3] * alphaOverride) / 255; if (alpha > 0) { if (bottom[3] == 0 || alpha == 255) { bottom[0] = source[0]; bottom[1] = source[1]; bottom[2] = source[2]; bottom[3] = alpha; } else { if (bottom[3] == 255) { uint32 destAlpha = 255 - alpha; bottom[0] = (uint8)((bottom[0] * destAlpha + source[0] * alpha) / 255); bottom[1] = (uint8)((bottom[1] * destAlpha + source[1] * alpha) / 255); bottom[2] = (uint8)((bottom[2] * destAlpha + source[2] * alpha) / 255); } else { uint8 alphaRest = 255 - alpha; uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); uint32 alphaDest = bottom[3] * alphaRest; uint32 alphaSrc = 255 * alpha; bottom[0] = (bottom[0] * alphaDest + source[0] * alphaSrc) / alphaTemp; bottom[1] = (bottom[1] * alphaDest + source[1] * alphaSrc) / alphaTemp; bottom[2] = (bottom[2] * alphaDest + source[2] * alphaSrc) / alphaTemp; bottom[3] = alphaTemp / 255; } } } } // blend_colors inline void blend_colors(uint8* bottom, uint8* source) { if (source[3] > 0) { if (bottom[3] == 0 || source[3] == 255) { bottom[0] = source[0]; bottom[1] = source[1]; bottom[2] = source[2]; bottom[3] = source[3]; } else { if (bottom[3] == 255) { uint32 destAlpha = 255 - source[3]; bottom[0] = (uint8)((bottom[0] * destAlpha + source[0] * source[3]) / 255); bottom[1] = (uint8)((bottom[1] * destAlpha + source[1] * source[3]) / 255); bottom[2] = (uint8)((bottom[2] * destAlpha + source[2] * source[3]) / 255); } else { uint8 alphaRest = 255 - source[3]; uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); uint32 alphaDest = bottom[3] * alphaRest; uint32 alphaSrc = 255 * source[3]; bottom[0] = (bottom[0] * alphaDest + source[0] * alphaSrc) / alphaTemp; bottom[1] = (bottom[1] * alphaDest + source[1] * alphaSrc) / alphaTemp; bottom[2] = (bottom[2] * alphaDest + source[2] * alphaSrc) / alphaTemp; bottom[3] = alphaTemp / 255; } } } } // blend_colors_copy inline void blend_colors_copy(uint8* dest, uint8* bottom, uint8* top) { if (bottom[3] == 0 || top[3] == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { if (bottom[3] == 255) { uint32 destAlpha = 255 - top[3]; dest[0] = (uint8)((bottom[0] * destAlpha + top[0] * top[3]) / 255); dest[1] = (uint8)((bottom[1] * destAlpha + top[1] * top[3]) / 255); dest[2] = (uint8)((bottom[2] * destAlpha + top[2] * top[3]) / 255); dest[3] = 255; } else { uint8 alphaRest = 255 - top[3]; uint32 alphaTemp = (65025 - alphaRest * (255 - bottom[3])); uint32 alphaDest = bottom[3] * alphaRest; uint32 alphaSrc = 255 * top[3]; dest[0] = (bottom[0] * alphaDest + top[0] * alphaSrc) / alphaTemp; dest[1] = (bottom[1] * alphaDest + top[1] * alphaSrc) / alphaTemp; dest[2] = (bottom[2] * alphaDest + top[2] * alphaSrc) / alphaTemp; dest[3] = alphaTemp / 255; } } } // blend_pixels inline void blend_pixels(uint8* bottom, uint8* top, uint8 alpha) { if (alpha > 0) { if (alpha == 255) { bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2]; bottom[3] = top[3]; } else { uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; uint8 invAlpha = 255 - mergeAlpha; bottom[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; bottom[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; bottom[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } } // blend_pixels_copy inline void blend_pixels_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alpha) { if (alpha > 0) { if (alpha == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { uint8 mergeAlpha = bottom[3] ? (top[3] * alpha) / 255 : 255; uint8 invAlpha = 255 - mergeAlpha; dest[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; dest[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; dest[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } else { dest[0] = bottom[0]; dest[1] = bottom[1]; dest[2] = bottom[2]; dest[3] = bottom[3]; } } // blend_pixels_overlay inline void blend_pixels_overlay(uint8* bottom, uint8* top, uint8 alphaOverride) { uint8 alpha = (top[3] * alphaOverride) / 255; if (alpha > 0) { if (alpha == 255) { bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2]; bottom[3] = top[3]; } else { uint8 mergeAlpha = bottom[3] ? alpha : 255; uint8 invAlpha = 255 - mergeAlpha; bottom[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; bottom[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; bottom[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; bottom[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } } // blend_pixels_overlay_copy inline void blend_pixels_overlay_copy(uint8* bottom, uint8* top, uint8* dest, uint8 alphaOverride) { uint8 alpha = (top[3] * alphaOverride) / 255; if (alpha > 0) { if (alpha == 255) { dest[0] = top[0]; dest[1] = top[1]; dest[2] = top[2]; dest[3] = top[3]; } else { uint8 mergeAlpha = bottom[3] ? alpha : 255; uint8 invAlpha = 255 - mergeAlpha; dest[0] = (bottom[0] * invAlpha + top[0] * mergeAlpha) / 255; dest[1] = (bottom[1] * invAlpha + top[1] * mergeAlpha) / 255; dest[2] = (bottom[2] * invAlpha + top[2] * mergeAlpha) / 255; dest[3] = (bottom[3] * (255 - alpha) + top[3] * alpha) / 255; } } else { dest[0] = bottom[0]; dest[1] = bottom[1]; dest[2] = bottom[2]; dest[3] = bottom[3]; } } #endif // GAMMA_BLEND #endif // BLENDING_H