/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.opengl;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
public class ImageDataUtil {
/**
* Alpha mode, values 0 - 255 specify global alpha level
*/
static final int
ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
/**
* Data types (internal)
*/
private static final int
// direct / true color formats with arbitrary masks & shifts
TYPE_GENERIC_8 = 0,
TYPE_GENERIC_16_MSB = 1,
TYPE_GENERIC_16_LSB = 2,
TYPE_GENERIC_24 = 3,
TYPE_GENERIC_32_MSB = 4,
TYPE_GENERIC_32_LSB = 5,
// palette indexed color formats
TYPE_INDEX_8 = 6,
TYPE_INDEX_4 = 7,
TYPE_INDEX_2 = 8,
TYPE_INDEX_1_MSB = 9,
TYPE_INDEX_1_LSB = 10;
/**
* Byte and bit order constants.
*/
static final int LSB_FIRST = 0;
static final int MSB_FIRST = 1;
/**
* Blit operation bits to be OR'ed together to specify the desired operation.
*/
static final int
BLIT_SRC = 1, // copy source directly, else applies logic operations
BLIT_ALPHA = 2, // enable alpha blending
BLIT_DITHER = 4; // enable dithering in low color modes
/**
* Arbitrary channel width data to 8-bit conversion table.
*/
static final byte[][] ANY_TO_EIGHT = new byte[9][];
static {
for (int b = 0; b < 9; ++b) {
byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
if (b == 0) continue;
int inc = 0;
for (int bit = 0x10000; (bit >>= b) != 0;) inc |= bit;
for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8);
}
}
/**
* Blits a direct palette image into a direct palette image.
* <p>
* Note: When the source and destination depth, order and masks
* are pairwise equal and the blitter operation is BLIT_SRC,
* the masks are ignored. Hence when not changing the image
* data format, 0 may be specified for the masks.
* </p>
*
* @param op the blitter operation: a combination of BLIT_xxx flags
* (see BLIT_xxx constants)
* @param srcData the source byte array containing image data
* @param srcDepth the source depth: one of 8, 16, 24, 32
* @param srcStride the source number of bytes per line
* @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
* ignored if srcDepth is not 16 or 32
* @param srcX the top-left x-coord of the source blit region
* @param srcY the top-left y-coord of the source blit region
* @param srcWidth the width of the source blit region
* @param srcHeight the height of the source blit region
* @param srcRedMask the source red channel mask
* @param srcGreenMask the source green channel mask
* @param srcBlueMask the source blue channel mask
* @param alphaMode the alpha blending or mask mode, may be
* an integer 0-255 for global alpha; ignored if BLIT_ALPHA
* not specified in the blitter operations
* (see ALPHA_MODE_xxx constants)
* @param alphaData the alpha blending or mask data, varies depending
* on the value of alphaMode and sometimes ignored
* @param alphaStride the alpha data number of bytes per line
* @param alphaX the top-left x-coord of the alpha blit region
* @param alphaY the top-left y-coord of the alpha blit region
* @param destData the destination byte array containing image data
* @param destDepth the destination depth: one of 8, 16, 24, 32
* @param destStride the destination number of bytes per line
* @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
* ignored if destDepth is not 16 or 32
* @param destX the top-left x-coord of the destination blit region
* @param destY the top-left y-coord of the destination blit region
* @param destWidth the width of the destination blit region
* @param destHeight the height of the destination blit region
* @param destRedMask the destination red channel mask
* @param destGreenMask the destination green channel mask
* @param destBlueMask the destination blue channel mask
* @param flipX if true the resulting image is flipped along the vertical axis
* @param flipY if true the resulting image is flipped along the horizontal axis
*/
static void blit(int op,
byte[] srcData, int srcDepth, int srcStride, int srcOrder,
int srcX, int srcY, int srcWidth, int srcHeight,
int srcRedMask, int srcGreenMask, int srcBlueMask,
int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
byte[] destData, int destDepth, int destStride, int destOrder,
int destX, int destY, int destWidth, int destHeight,
int destRedMask, int destGreenMask, int destBlueMask,
boolean flipX, boolean flipY) {
if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
// these should be supplied as params later
final int srcAlphaMask = 0, destAlphaMask = 0;
/*** Prepare scaling data ***/
final int dwm1 = destWidth - 1;
final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
final int dhm1 = destHeight - 1;
final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
/*** Prepare source-related data ***/
final int sbpp, stype;
switch (srcDepth) {
case 8:
sbpp = 1;
stype = TYPE_GENERIC_8;
break;
case 16:
sbpp = 2;
stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
break;
case 24:
sbpp = 3;
stype = TYPE_GENERIC_24;
break;
case 32:
sbpp = 4;
stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
break;
default:
//throw new IllegalArgumentException("Invalid source type");
return;
}
int spr = srcY * srcStride + srcX * sbpp;
/*** Prepare destination-related data ***/
final int dbpp, dtype;
switch (destDepth) {
case 8:
dbpp = 1;
dtype = TYPE_GENERIC_8;
break;
case 16:
dbpp = 2;
dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
break;
case 24:
dbpp = 3;
dtype = TYPE_GENERIC_24;
break;
case 32:
dbpp = 4;
dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
break;
default:
//throw new IllegalArgumentException("Invalid destination type");
return;
}
int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
final int dprxi = (flipX) ? -dbpp : dbpp;
final int dpryi = (flipY) ? -destStride : destStride;
/*** Prepare special processing data ***/
int apr;
if ((op & BLIT_ALPHA) != 0) {
switch (alphaMode) {
case ALPHA_MASK_UNPACKED:
case ALPHA_CHANNEL_SEPARATE:
if (alphaData == null) alphaMode = 0x10000;
apr = alphaY * alphaStride + alphaX;
break;
case ALPHA_MASK_PACKED:
if (alphaData == null) alphaMode = 0x10000;
alphaStride <<= 3;
apr = alphaY * alphaStride + alphaX;
break;
case ALPHA_MASK_INDEX:
//throw new IllegalArgumentException("Invalid alpha type");
return;
case ALPHA_MASK_RGB:
if (alphaData == null) alphaMode = 0x10000;
apr = 0;
break;
default:
alphaMode = (alphaMode << 16) / 255; // prescale
case ALPHA_CHANNEL_SOURCE:
apr = 0;
break;
}
} else {
alphaMode = 0x10000;
apr = 0;
}
/*** Blit ***/
int dp = dpr;
int sp = spr;
if ((alphaMode == 0x10000) && (stype == dtype) &&
(srcRedMask == destRedMask) && (srcGreenMask == destGreenMask) &&
(srcBlueMask == destBlueMask) && (srcAlphaMask == destAlphaMask)) {
/*** Fast blit (straight copy) ***/
switch (sbpp) {
case 1:
for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
destData[dp] = srcData[sp];
sp += (sfx >>> 16);
}
}
break;
case 2:
for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
destData[dp] = srcData[sp];
destData[dp + 1] = srcData[sp + 1];
sp += (sfx >>> 16) * 2;
}
}
break;
case 3:
for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
destData[dp] = srcData[sp];
destData[dp + 1] = srcData[sp + 1];
destData[dp + 2] = srcData[sp + 2];
sp += (sfx >>> 16) * 3;
}
}
break;
case 4:
for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
destData[dp] = srcData[sp];
destData[dp + 1] = srcData[sp + 1];
destData[dp + 2] = srcData[sp + 2];
destData[dp + 3] = srcData[sp + 3];
sp += (sfx >>> 16) * 4;
}
}
break;
}
return;
}
/*** Comprehensive blit (apply transformations) ***/
final int srcRedShift = getChannelShift(srcRedMask);
final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
final int srcGreenShift = getChannelShift(srcGreenMask);
final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
final int srcBlueShift = getChannelShift(srcBlueMask);
final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
final int srcAlphaShift = getChannelShift(srcAlphaMask);
final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
final int destRedShift = getChannelShift(destRedMask);
final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
final int destRedPreShift = 8 - destRedWidth;
final int destGreenShift = getChannelShift(destGreenMask);
final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
final int destGreenPreShift = 8 - destGreenWidth;
final int destBlueShift = getChannelShift(destBlueMask);
final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
final int destBluePreShift = 8 - destBlueWidth;
final int destAlphaShift = getChannelShift(destAlphaMask);
final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
final int destAlphaPreShift = 8 - destAlphaWidth;
int ap = apr, alpha = alphaMode;
int r = 0, g = 0, b = 0, a = 0;
int rq = 0, gq = 0, bq = 0, aq = 0;
for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
sp = spr += (sfy >>> 16) * srcStride,
ap = apr += (sfy >>> 16) * alphaStride,
sfy = (sfy & 0xffff) + sfyi,
dp = dpr += dpryi) {
for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
dp += dprxi,
sfx = (sfx & 0xffff) + sfxi) {
/*** READ NEXT PIXEL ***/
switch (stype) {
case TYPE_GENERIC_8: {
final int data = srcData[sp] & 0xff;
sp += (sfx >>> 16);
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_16_MSB: {
final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
sp += (sfx >>> 16) * 2;
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_16_LSB: {
final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
sp += (sfx >>> 16) * 2;
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_24: {
final int data = (( ((srcData[sp] & 0xff) << 8) |
(srcData[sp + 1] & 0xff)) << 8) |
(srcData[sp + 2] & 0xff);
sp += (sfx >>> 16) * 3;
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_32_MSB: {
final int data = (( (( ((srcData[sp] & 0xff) << 8) |
(srcData[sp + 1] & 0xff)) << 8) |
(srcData[sp + 2] & 0xff)) << 8) |
(srcData[sp + 3] & 0xff);
sp += (sfx >>> 16) * 4;
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_32_LSB: {
final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
(srcData[sp + 2] & 0xff)) << 8) |
(srcData[sp + 1] & 0xff)) << 8) |
(srcData[sp] & 0xff);
sp += (sfx >>> 16) * 4;
r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
} break;
}
/*** DO SPECIAL PROCESSING IF REQUIRED ***/
switch (alphaMode) {
case ALPHA_CHANNEL_SEPARATE:
alpha = ((alphaData[ap] & 0xff) << 16) / 255;
ap += (sfx >> 16);
break;
case ALPHA_CHANNEL_SOURCE:
alpha = (a << 16) / 255;
break;
case ALPHA_MASK_UNPACKED:
alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
ap += (sfx >> 16);
break;
case ALPHA_MASK_PACKED:
alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
ap += (sfx >> 16);
break;
case ALPHA_MASK_RGB:
alpha = 0x10000;
for (int i = 0; i < alphaData.length; i += 3) {
if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) {
alpha = 0x0000;
break;
}
}
break;
}
if (alpha != 0x10000) {
if (alpha == 0x0000) continue;
switch (dtype) {
case TYPE_GENERIC_8: {
final int data = destData[dp] & 0xff;
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_16_MSB: {
final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_16_LSB: {
final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_24: {
final int data = (( ((destData[dp] & 0xff) << 8) |
(destData[dp + 1] & 0xff)) << 8) |
(destData[dp + 2] & 0xff);
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_32_MSB: {
final int data = (( (( ((destData[dp] & 0xff) << 8) |
(destData[dp + 1] & 0xff)) << 8) |
(destData[dp + 2] & 0xff)) << 8) |
(destData[dp + 3] & 0xff);
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
case TYPE_GENERIC_32_LSB: {
final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
(destData[dp + 2] & 0xff)) << 8) |
(destData[dp + 1] & 0xff)) << 8) |
(destData[dp] & 0xff);
rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
} break;
}
// Perform alpha blending
a = aq + ((a - aq) * alpha >> 16);
r = rq + ((r - rq) * alpha >> 16);
g = gq + ((g - gq) * alpha >> 16);
b = bq + ((b - bq) * alpha >> 16);
}
/*** WRITE NEXT PIXEL ***/
final int data =
(r >>> destRedPreShift << destRedShift) |
(g >>> destGreenPreShift << destGreenShift) |
(b >>> destBluePreShift << destBlueShift) |
(a >>> destAlphaPreShift << destAlphaShift);
switch (dtype) {
case TYPE_GENERIC_8: {
destData[dp] = (byte) data;
} break;
case TYPE_GENERIC_16_MSB: {
destData[dp] = (byte) (data >>> 8);
destData[dp + 1] = (byte) (data & 0xff);
} break;
case TYPE_GENERIC_16_LSB: {
destData[dp] = (byte) (data & 0xff);
destData[dp + 1] = (byte) (data >>> 8);
} break;
case TYPE_GENERIC_24: {
destData[dp] = (byte) (data >>> 16);
destData[dp + 1] = (byte) (data >>> 8);
destData[dp + 2] = (byte) (data & 0xff);
} break;
case TYPE_GENERIC_32_MSB: {
destData[dp] = (byte) (data >>> 24);
destData[dp + 1] = (byte) (data >>> 16);
destData[dp + 2] = (byte) (data >>> 8);
destData[dp + 3] = (byte) (data & 0xff);
} break;
case TYPE_GENERIC_32_LSB: {
destData[dp] = (byte) (data & 0xff);
destData[dp + 1] = (byte) (data >>> 8);
destData[dp + 2] = (byte) (data >>> 16);
destData[dp + 3] = (byte) (data >>> 24);
} break;
}
}
}
}
/**
* Computes the required channel shift from a mask.
*/
static int getChannelShift(int mask) {
if (mask == 0) return 0;
int i;
for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) {
mask >>>= 1;
}
return i;
}
/**
* Computes the required channel width (depth) from a mask.
*/
static int getChannelWidth(int mask, int shift) {
if (mask == 0) return 0;
int i;
mask >>>= shift;
for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) {
mask >>>= 1;
}
return i - shift;
}
public static ImageData convertImageData (ImageData source) {
PaletteData palette = new PaletteData (0xff0000, 0xff00, 0xff);
ImageData newSource = new ImageData (source.width, source.height, 24, palette);
ImageDataUtil.blit (
1,
source.data,
source.depth,
source.bytesPerLine,
(source.depth != 16) ? MSB_FIRST : LSB_FIRST,
0,
0,
source.width,
source.height,
source.palette.redMask,
source.palette.greenMask,
source.palette.blueMask,
255,
null,
0,
0,
0,
newSource.data,
newSource.depth,
newSource.bytesPerLine,
(newSource.depth != 16) ? MSB_FIRST : LSB_FIRST,
0,
0,
newSource.width,
newSource.height,
newSource.palette.redMask,
newSource.palette.greenMask,
newSource.palette.blueMask,
false,
true);
return newSource;
}
}