From 8680d42d254903a2e592c7369800251b8c0b5d1f Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Tue, 14 Apr 2015 16:15:32 -0700 Subject: [PATCH] Add more CFFI methods, improve use within the tools (force color order) --- lime/graphics/format/BMP.hx | 2 +- lime/graphics/utils/ImageDataUtil.hx | 735 +++++++++--------- lime/system/System.hx | 2 +- lime/tools/helpers/IconHelper.hx | 2 +- project/include/graphics/PixelFormat.h | 19 + .../include/graphics/utils/ImageDataUtil.h | 11 +- project/src/ExternalInterface.cpp | 39 + project/src/graphics/utils/ImageDataUtil.cpp | 151 ++++ 8 files changed, 607 insertions(+), 354 deletions(-) create mode 100644 project/include/graphics/PixelFormat.h diff --git a/lime/graphics/format/BMP.hx b/lime/graphics/format/BMP.hx index eb7ab47e8..e3cf0d6d2 100644 --- a/lime/graphics/format/BMP.hx +++ b/lime/graphics/format/BMP.hx @@ -94,7 +94,7 @@ class BMP { } - var pixels = image.getPixels (new Rectangle (0, 0, image.width, image.height)); + var pixels = image.getPixels (new Rectangle (0, 0, image.width, image.height), ARGB); var a, r, g, b; switch (type) { diff --git a/lime/graphics/utils/ImageDataUtil.hx b/lime/graphics/utils/ImageDataUtil.hx index 7530101f7..c237d281d 100644 --- a/lime/graphics/utils/ImageDataUtil.hx +++ b/lime/graphics/utils/ImageDataUtil.hx @@ -52,44 +52,45 @@ class ImageDataUtil { if (data == null) return; #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_color_transform (image, rect, colorMatrix); - #else - - var stride = image.buffer.width * 4; - var offset:Int; - - var rowStart = Std.int (rect.top + image.offsetY); - var rowEnd = Std.int (rect.bottom + image.offsetY); - var columnStart = Std.int (rect.left + image.offsetX); - var columnEnd = Std.int (rect.right + image.offsetX); - - var r, g, b, a, ex = 0; - - for (row in rowStart...rowEnd) { + if (!System.disableCFFI) lime_image_data_util_color_transform (image, rect, colorMatrix); else + #end + { - for (column in columnStart...columnEnd) { + var stride = image.buffer.width * 4; + var offset:Int; + + var rowStart = Std.int (rect.top + image.offsetY); + var rowEnd = Std.int (rect.bottom + image.offsetY); + var columnStart = Std.int (rect.left + image.offsetX); + var columnEnd = Std.int (rect.right + image.offsetX); + + var r, g, b, a, ex = 0; + + for (row in rowStart...rowEnd) { - offset = (row * stride) + (column * 4); - - a = Std.int ((data[offset + 3] * colorMatrix.alphaMultiplier) + colorMatrix.alphaOffset); - ex = a > 0xFF ? a - 0xFF : 0; - b = Std.int ((data[offset + 2] * colorMatrix.blueMultiplier) + colorMatrix.blueOffset + ex); - ex = b > 0xFF ? b - 0xFF : 0; - g = Std.int ((data[offset + 1] * colorMatrix.greenMultiplier) + colorMatrix.greenOffset + ex); - ex = g > 0xFF ? g - 0xFF : 0; - r = Std.int ((data[offset] * colorMatrix.redMultiplier) + colorMatrix.redOffset + ex); - - data[offset] = r > 0xFF ? 0xFF : r; - data[offset + 1] = g > 0xFF ? 0xFF : g; - data[offset + 2] = b > 0xFF ? 0xFF : b; - data[offset + 3] = a > 0xFF ? 0xFF : a; + for (column in columnStart...columnEnd) { + + offset = (row * stride) + (column * 4); + + a = Std.int ((data[offset + 3] * colorMatrix.alphaMultiplier) + colorMatrix.alphaOffset); + ex = a > 0xFF ? a - 0xFF : 0; + b = Std.int ((data[offset + 2] * colorMatrix.blueMultiplier) + colorMatrix.blueOffset + ex); + ex = b > 0xFF ? b - 0xFF : 0; + g = Std.int ((data[offset + 1] * colorMatrix.greenMultiplier) + colorMatrix.greenOffset + ex); + ex = g > 0xFF ? g - 0xFF : 0; + r = Std.int ((data[offset] * colorMatrix.redMultiplier) + colorMatrix.redOffset + ex); + + data[offset] = r > 0xFF ? 0xFF : r; + data[offset + 1] = g > 0xFF ? 0xFF : g; + data[offset + 2] = b > 0xFF ? 0xFF : b; + data[offset + 3] = a > 0xFF ? 0xFF : a; + + } } } - #end - image.dirty = true; } @@ -121,46 +122,47 @@ class ImageDataUtil { if (srcData == null || destData == null) return; #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_copy_channel (image, sourceImage, sourceRect, destPoint, srcIdx, destIdx); - #else - - var srcStride = Std.int (sourceImage.buffer.width * 4); - var srcPosition = Std.int (((sourceRect.x + sourceImage.offsetX) * 4) + (srcStride * (sourceRect.y + sourceImage.offsetY)) + srcIdx); - var srcRowOffset = srcStride - Std.int (4 * (sourceRect.width + sourceImage.offsetX)); - var srcRowEnd = Std.int (4 * (sourceRect.x + sourceImage.offsetX + sourceRect.width)); - var srcData = sourceImage.buffer.data; - - var destStride = Std.int (image.buffer.width * 4); - var destPosition = Std.int (((destPoint.x + image.offsetX) * 4) + (destStride * (destPoint.y + image.offsetY)) + destIdx); - var destRowOffset = destStride - Std.int (4 * (sourceRect.width + image.offsetX)); - var destRowEnd = Std.int (4 * (destPoint.x + image.offsetX + sourceRect.width)); - var destData = image.buffer.data; - - var length = Std.int (sourceRect.width * sourceRect.height); - - for (i in 0...length) { + if (!System.disableCFFI) lime_image_data_util_copy_channel (image, sourceImage, sourceRect, destPoint, srcIdx, destIdx); else + #end + { - destData[destPosition] = srcData[srcPosition]; + var srcStride = Std.int (sourceImage.buffer.width * 4); + var srcPosition = Std.int (((sourceRect.x + sourceImage.offsetX) * 4) + (srcStride * (sourceRect.y + sourceImage.offsetY)) + srcIdx); + var srcRowOffset = srcStride - Std.int (4 * (sourceRect.width + sourceImage.offsetX)); + var srcRowEnd = Std.int (4 * (sourceRect.x + sourceImage.offsetX + sourceRect.width)); + var srcData = sourceImage.buffer.data; - srcPosition += 4; - destPosition += 4; + var destStride = Std.int (image.buffer.width * 4); + var destPosition = Std.int (((destPoint.x + image.offsetX) * 4) + (destStride * (destPoint.y + image.offsetY)) + destIdx); + var destRowOffset = destStride - Std.int (4 * (sourceRect.width + image.offsetX)); + var destRowEnd = Std.int (4 * (destPoint.x + image.offsetX + sourceRect.width)); + var destData = image.buffer.data; - if ((srcPosition % srcStride) > srcRowEnd) { + var length = Std.int (sourceRect.width * sourceRect.height); + + for (i in 0...length) { - srcPosition += srcRowOffset; + destData[destPosition] = srcData[srcPosition]; - } - - if ((destPosition % destStride) > destRowEnd) { + srcPosition += 4; + destPosition += 4; - destPosition += destRowOffset; + if ((srcPosition % srcStride) > srcRowEnd) { + + srcPosition += srcRowOffset; + + } + + if ((destPosition % destStride) > destRowEnd) { + + destPosition += destRowOffset; + + } } } - #end - image.dirty = true; } @@ -181,66 +183,69 @@ class ImageDataUtil { } #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_copy_pixels (image, sourceImage, sourceRect, destPoint, mergeAlpha); - #else - - var rowOffset = Std.int (destPoint.y + image.offsetY - sourceRect.y - sourceImage.offsetY); - var columnOffset = Std.int (destPoint.x + image.offsetX - sourceRect.x - sourceImage.offsetY); - - var sourceData = sourceImage.buffer.data; - var sourceStride = sourceImage.buffer.width * 4; - var sourceOffset:Int = 0; - - var data = image.buffer.data; - var stride = image.buffer.width * 4; - var offset:Int = 0; - - if (!mergeAlpha || !sourceImage.transparent) { + if (!System.disableCFFI) lime_image_data_util_copy_pixels (image, sourceImage, sourceRect, destPoint, mergeAlpha); else + #end + { - //#if (!js && !flash) - //if (sourceRect.width == image.width && sourceRect.height == image.height && image.width == sourceImage.width && image.height == sourceImage.height && sourceRect.x == 0 && sourceRect.y == 0 && destPoint.x == 0 && destPoint.y == 0) { - // - //image.buffer.data.buffer.blit (0, sourceImage.buffer.data.buffer, 0, Std.int (sourceRect.width * sourceRect.height) * 4); - //return; - // - //} - //#end + var rowOffset = Std.int (destPoint.y + image.offsetY - sourceRect.y - sourceImage.offsetY); + var columnOffset = Std.int (destPoint.x + image.offsetX - sourceRect.x - sourceImage.offsetY); - for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { + var sourceData = sourceImage.buffer.data; + var sourceStride = sourceImage.buffer.width * 4; + var sourceOffset:Int = 0; + + var data = image.buffer.data; + var stride = image.buffer.width * 4; + var offset:Int = 0; + + if (!mergeAlpha || !sourceImage.transparent) { - for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + //#if (!js && !flash) + //if (sourceRect.width == image.width && sourceRect.height == image.height && image.width == sourceImage.width && image.height == sourceImage.height && sourceRect.x == 0 && sourceRect.y == 0 && destPoint.x == 0 && destPoint.y == 0) { + // + //image.buffer.data.buffer.blit (0, sourceImage.buffer.data.buffer, 0, Std.int (sourceRect.width * sourceRect.height) * 4); + //return; + // + //} + //#end + + for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { - sourceOffset = (row * sourceStride) + (column * 4); - offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); - - data[offset] = sourceData[sourceOffset]; - data[offset + 1] = sourceData[sourceOffset + 1]; - data[offset + 2] = sourceData[sourceOffset + 2]; - data[offset + 3] = sourceData[sourceOffset + 3]; + for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + + sourceOffset = (row * sourceStride) + (column * 4); + offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + + data[offset] = sourceData[sourceOffset]; + data[offset + 1] = sourceData[sourceOffset + 1]; + data[offset + 2] = sourceData[sourceOffset + 2]; + data[offset + 3] = sourceData[sourceOffset + 3]; + + } } - } - - } else { - - var sourceAlpha:Float; - var oneMinusSourceAlpha:Float; - - for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { + } else { - for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + var sourceAlpha:Float; + var oneMinusSourceAlpha:Float; + + for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { - sourceOffset = (row * sourceStride) + (column * 4); - offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); - - sourceAlpha = sourceData[sourceOffset + 3] / 255; - oneMinusSourceAlpha = (1 - sourceAlpha); - - data[offset] = __clamp[Std.int (sourceData[sourceOffset] + (data[offset] * oneMinusSourceAlpha))]; - data[offset + 1] = __clamp[Std.int (sourceData[sourceOffset + 1] + (data[offset + 1] * oneMinusSourceAlpha))]; - data[offset + 2] = __clamp[Std.int (sourceData[sourceOffset + 2] + (data[offset + 2] * oneMinusSourceAlpha))]; - data[offset + 3] = __clamp[Std.int (sourceData[sourceOffset + 3] + (data[offset + 3] * oneMinusSourceAlpha))]; + for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + + sourceOffset = (row * sourceStride) + (column * 4); + offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + + sourceAlpha = sourceData[sourceOffset + 3] / 255; + oneMinusSourceAlpha = (1 - sourceAlpha); + + data[offset] = __clamp[Std.int (sourceData[sourceOffset] + (data[offset] * oneMinusSourceAlpha))]; + data[offset + 1] = __clamp[Std.int (sourceData[sourceOffset + 1] + (data[offset + 1] * oneMinusSourceAlpha))]; + data[offset + 2] = __clamp[Std.int (sourceData[sourceOffset + 2] + (data[offset + 2] * oneMinusSourceAlpha))]; + data[offset + 3] = __clamp[Std.int (sourceData[sourceOffset + 3] + (data[offset + 3] * oneMinusSourceAlpha))]; + + } } @@ -248,8 +253,6 @@ class ImageDataUtil { } - #end - image.dirty = true; } @@ -281,62 +284,63 @@ class ImageDataUtil { if (data == null) return; #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_fill_rect (image, rect, rgba); - #else - - if (rect.width == image.buffer.width && rect.height == image.buffer.height && rect.x == 0 && rect.y == 0 && image.offsetX == 0 && image.offsetY == 0) { + if (!System.disableCFFI) lime_image_data_util_fill_rect (image, rect, rgba); else + #end + { - var length = image.buffer.width * image.buffer.height; - - var j = 0; - for (i in 0...length) { + if (rect.width == image.buffer.width && rect.height == image.buffer.height && rect.x == 0 && rect.y == 0 && image.offsetX == 0 && image.offsetY == 0) { - j = i * 4; + var length = image.buffer.width * image.buffer.height; - #if js - data[j + 0] = r; - data[j + 1] = g; - data[j + 2] = b; - data[j + 3] = a; - #else - data.setUInt32 (j, rgba); - #end - - } - - } else { - - var stride = image.buffer.width * 4; - var offset:Int; - - var rowStart = Std.int (rect.y + image.offsetY); - var rowEnd = Std.int (rect.bottom + image.offsetY); - var columnStart = Std.int (rect.x + image.offsetX); - var columnEnd = Std.int (rect.right + image.offsetX); - - for (row in rowStart...rowEnd) { - - for (column in columnStart...columnEnd) { + var j = 0; + for (i in 0...length) { - offset = (row * stride) + (column * 4); + j = i * 4; #if js - data[offset] = r; - data[offset + 1] = g; - data[offset + 2] = b; - data[offset + 3] = a; + data[j + 0] = r; + data[j + 1] = g; + data[j + 2] = b; + data[j + 3] = a; #else - data.setUInt32 (offset, rgba); + data.setUInt32 (j, rgba); #end } + } else { + + var stride = image.buffer.width * 4; + var offset:Int; + + var rowStart = Std.int (rect.y + image.offsetY); + var rowEnd = Std.int (rect.bottom + image.offsetY); + var columnStart = Std.int (rect.x + image.offsetX); + var columnEnd = Std.int (rect.right + image.offsetX); + + for (row in rowStart...rowEnd) { + + for (column in columnStart...columnEnd) { + + offset = (row * stride) + (column * 4); + + #if js + data[offset] = r; + data[offset + 1] = g; + data[offset + 2] = b; + data[offset + 3] = a; + #else + data.setUInt32 (offset, rgba); + #end + + } + + } + } } - #end - image.dirty = true; } @@ -350,61 +354,64 @@ class ImageDataUtil { if (format == ARGB) color = ((color & 0xFFFFFF) << 8) | ((color >> 24) & 0xFF); #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_flood_fill (image, x, y, color); - #else - - var offset = (((y + image.offsetY) * (image.buffer.width * 4)) + ((x + image.offsetX) * 4)); - var hitColorR = data[offset + 0]; - var hitColorG = data[offset + 1]; - var hitColorB = data[offset + 2]; - var hitColorA = image.transparent ? data[offset + 3] : 0xFF; - - var r = (color >> 24) & 0xFF; - var g = (color >> 16) & 0xFF; - var b = (color >> 8) & 0xFF; - var a = image.transparent ? color & 0xFF : 0xFF; - - if (hitColorR == r && hitColorG == g && hitColorB == b && hitColorA == a) return; - - var dx = [ 0, -1, 1, 0 ]; - var dy = [ -1, 0, 0, 1 ]; - - var minX = -image.offsetX; - var minY = -image.offsetY; - var maxX = minX + image.width; - var maxY = minY + image.height; - - var queue = new Array (); - queue.push (x); - queue.push (y); - - while (queue.length > 0) { + if (!System.disableCFFI) lime_image_data_util_flood_fill (image, x, y, color); else + #end + { - var curPointY = queue.pop (); - var curPointX = queue.pop (); + var offset = (((y + image.offsetY) * (image.buffer.width * 4)) + ((x + image.offsetX) * 4)); + var hitColorR = data[offset + 0]; + var hitColorG = data[offset + 1]; + var hitColorB = data[offset + 2]; + var hitColorA = image.transparent ? data[offset + 3] : 0xFF; - for (i in 0...4) { + var r = (color >> 24) & 0xFF; + var g = (color >> 16) & 0xFF; + var b = (color >> 8) & 0xFF; + var a = image.transparent ? color & 0xFF : 0xFF; + + if (hitColorR == r && hitColorG == g && hitColorB == b && hitColorA == a) return; + + var dx = [ 0, -1, 1, 0 ]; + var dy = [ -1, 0, 0, 1 ]; + + var minX = -image.offsetX; + var minY = -image.offsetY; + var maxX = minX + image.width; + var maxY = minY + image.height; + + var queue = new Array (); + queue.push (x); + queue.push (y); + + while (queue.length > 0) { - var nextPointX = curPointX + dx[i]; - var nextPointY = curPointY + dy[i]; + var curPointY = queue.pop (); + var curPointX = queue.pop (); - if (nextPointX < minX || nextPointY < minY || nextPointX >= maxX || nextPointY >= maxY) { + for (i in 0...4) { - continue; + var nextPointX = curPointX + dx[i]; + var nextPointY = curPointY + dy[i]; - } - - var nextPointOffset = (nextPointY * image.width + nextPointX) * 4; - - if (data[nextPointOffset + 0] == hitColorR && data[nextPointOffset + 1] == hitColorG && data[nextPointOffset + 2] == hitColorB && data[nextPointOffset + 3] == hitColorA) { + if (nextPointX < minX || nextPointY < minY || nextPointX >= maxX || nextPointY >= maxY) { + + continue; + + } - data[nextPointOffset + 0] = r; - data[nextPointOffset + 1] = g; - data[nextPointOffset + 2] = b; - data[nextPointOffset + 3] = a; + var nextPointOffset = (nextPointY * image.width + nextPointX) * 4; - queue.push (nextPointX); - queue.push (nextPointY); + if (data[nextPointOffset + 0] == hitColorR && data[nextPointOffset + 1] == hitColorG && data[nextPointOffset + 2] == hitColorB && data[nextPointOffset + 3] == hitColorA) { + + data[nextPointOffset + 0] = r; + data[nextPointOffset + 1] = g; + data[nextPointOffset + 2] = b; + data[nextPointOffset + 3] = a; + + queue.push (nextPointX); + queue.push (nextPointY); + + } } @@ -412,8 +419,6 @@ class ImageDataUtil { } - #end - image.dirty = true; } @@ -486,6 +491,8 @@ class ImageDataUtil { public static function getPixels (image:Image, rect:Rectangle, format:PixelFormat):ByteArray { + if (image.buffer.data == null) return null; + var length = Std.int (rect.width * rect.height); #if flash @@ -495,71 +502,81 @@ class ImageDataUtil { byteArray.position = 0; #end - //#if (!js && !flash) - //if (rect.width == image.width && rect.height == image.height && rect.x == 0 && rect.y == 0) { - // - //byteArray.blit (0, image.buffer.data.buffer, 0, length * 4); - //return byteArray; - // - //} - //#end + Sys.println ("SDFKJ"); + Sys.println (System.disableCFFI); - // TODO: optimize if the rect is the same as the full buffer size - - var srcData = image.buffer.data; - var srcStride = Std.int (image.buffer.width * 4); - var srcPosition = Std.int ((rect.x * 4) + (srcStride * rect.y)); - var srcRowOffset = srcStride - Std.int (4 * rect.width); - var srcRowEnd = Std.int (4 * (rect.x + rect.width)); - - #if js - byteArray.length = length * 4; + #if ((cpp || neko) && !disable_cffi) + if (!System.disableCFFI) lime_image_data_util_get_pixels (image, rect, format == ARGB ? 1 : 0); else #end - - if (format == ARGB) { + { - for (i in 0...length) { + //#if (!js && !flash) + //if (rect.width == image.width && rect.height == image.height && rect.x == 0 && rect.y == 0) { + // + //byteArray.blit (0, image.buffer.data.buffer, 0, length * 4); + //return byteArray; + // + //} + //#end + + // TODO: optimize if the rect is the same as the full buffer size + + var srcData = image.buffer.data; + var srcStride = Std.int (image.buffer.width * 4); + var srcPosition = Std.int ((rect.x * 4) + (srcStride * rect.y)); + var srcRowOffset = srcStride - Std.int (4 * rect.width); + var srcRowEnd = Std.int (4 * (rect.x + rect.width)); + + #if js + byteArray.length = length * 4; + #end + + if (format == ARGB) { - #if flash - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - #else - byteArray.__set (i * 4 + 1, srcData[srcPosition++]); - byteArray.__set (i * 4 + 2, srcData[srcPosition++]); - byteArray.__set (i * 4 + 3, srcData[srcPosition++]); - byteArray.__set (i * 4, srcData[srcPosition++]); - #end - - if ((srcPosition % srcStride) > srcRowEnd) { + for (i in 0...length) { - srcPosition += srcRowOffset; + #if flash + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + #else + byteArray.__set (i * 4 + 1, srcData[srcPosition++]); + byteArray.__set (i * 4 + 2, srcData[srcPosition++]); + byteArray.__set (i * 4 + 3, srcData[srcPosition++]); + byteArray.__set (i * 4, srcData[srcPosition++]); + #end + + if ((srcPosition % srcStride) > srcRowEnd) { + + srcPosition += srcRowOffset; + + } } - } - - } else { - - for (i in 0...length) { + } else { - #if flash - // TODO - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - byteArray.writeByte (srcData[srcPosition++]); - #else - byteArray.__set (i * 4, srcData[srcPosition++]); - byteArray.__set (i * 4 + 1, srcData[srcPosition++]); - byteArray.__set (i * 4 + 2, srcData[srcPosition++]); - byteArray.__set (i * 4 + 3, srcData[srcPosition++]); - #end - - if ((srcPosition % srcStride) > srcRowEnd) { + for (i in 0...length) { - srcPosition += srcRowOffset; + #if flash + // TODO + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + byteArray.writeByte (srcData[srcPosition++]); + #else + byteArray.__set (i * 4, srcData[srcPosition++]); + byteArray.__set (i * 4 + 1, srcData[srcPosition++]); + byteArray.__set (i * 4 + 2, srcData[srcPosition++]); + byteArray.__set (i * 4 + 3, srcData[srcPosition++]); + #end + + if ((srcPosition % srcStride) > srcRowEnd) { + + srcPosition += srcRowOffset; + + } } @@ -575,28 +592,37 @@ class ImageDataUtil { public static function merge (image:Image, sourceImage:Image, sourceRect:Rectangle, destPoint:Vector2, redMultiplier:Int, greenMultiplier:Int, blueMultiplier:Int, alphaMultiplier:Int):Void { - var rowOffset = Std.int (destPoint.y + image.offsetY - sourceRect.y - sourceImage.offsetY); - var columnOffset = Std.int (destPoint.x + image.offsetX - sourceRect.x - sourceImage.offsetY); + if (image.buffer.data == null || sourceImage.buffer.data == null) return; - var sourceData = sourceImage.buffer.data; - var sourceStride = sourceImage.buffer.width * 4; - var sourceOffset:Int = 0; - - var data = image.buffer.data; - var stride = image.buffer.width * 4; - var offset:Int = 0; - - for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { + #if ((cpp || neko) && !disable_cffi) + if (!System.disableCFFI) lime_image_data_util_merge (image, sourceImage, sourceRect, destPoint, redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier); else + #end + { - for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + var rowOffset = Std.int (destPoint.y + image.offsetY - sourceRect.y - sourceImage.offsetY); + var columnOffset = Std.int (destPoint.x + image.offsetX - sourceRect.x - sourceImage.offsetY); + + var sourceData = sourceImage.buffer.data; + var sourceStride = sourceImage.buffer.width * 4; + var sourceOffset:Int = 0; + + var data = image.buffer.data; + var stride = image.buffer.width * 4; + var offset:Int = 0; + + for (row in Std.int (sourceRect.top + sourceImage.offsetY)...Std.int (sourceRect.bottom + sourceImage.offsetY)) { - sourceOffset = (row * sourceStride) + (column * 4); - offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); - - data[offset] = Std.int (((sourceData[offset] * redMultiplier) + (data[offset] * (256 - redMultiplier))) / 256); - data[offset + 1] = Std.int (((sourceData[offset + 1] * greenMultiplier) + (data[offset + 1] * (256 - greenMultiplier))) / 256); - data[offset + 2] = Std.int (((sourceData[offset + 2] * blueMultiplier) + (data[offset + 2] * (256 - blueMultiplier))) / 256); - data[offset + 3] = Std.int (((sourceData[offset + 3] * alphaMultiplier) + (data[offset + 3] * (256 - alphaMultiplier))) / 256); + for (column in Std.int (sourceRect.left + sourceImage.offsetX)...Std.int (sourceRect.right + sourceImage.offsetX)) { + + sourceOffset = (row * sourceStride) + (column * 4); + offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + + data[offset] = Std.int (((sourceData[offset] * redMultiplier) + (data[offset] * (256 - redMultiplier))) / 256); + data[offset + 1] = Std.int (((sourceData[offset + 1] * greenMultiplier) + (data[offset + 1] * (256 - greenMultiplier))) / 256); + data[offset + 2] = Std.int (((sourceData[offset + 2] * blueMultiplier) + (data[offset + 2] * (256 - blueMultiplier))) / 256); + data[offset + 3] = Std.int (((sourceData[offset + 3] * alphaMultiplier) + (data[offset + 3] * (256 - alphaMultiplier))) / 256); + + } } @@ -613,25 +639,26 @@ class ImageDataUtil { if (data == null) return; #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_multiply_alpha (image); - #else - - var index, a16; - var length = Std.int (data.length / 4); - - for (i in 0...length) { + if (!System.disableCFFI) lime_image_data_util_multiply_alpha (image); else + #end + { - index = i * 4; + var index, a16; + var length = Std.int (data.length / 4); - a16 = __alpha16[data[index + 3]]; - data[index] = (data[index] * a16) >> 16; - data[index + 1] = (data[index + 1] * a16) >> 16; - data[index + 2] = (data[index + 2] * a16) >> 16; + for (i in 0...length) { + + index = i * 4; + + a16 = __alpha16[data[index + 3]]; + data[index] = (data[index] * a16) >> 16; + data[index + 1] = (data[index + 1] * a16) >> 16; + data[index + 2] = (data[index + 2] * a16) >> 16; + + } } - #end - image.buffer.premultiplied = true; image.dirty = true; @@ -641,52 +668,60 @@ class ImageDataUtil { public static function resize (image:Image, newWidth:Int, newHeight:Int):Void { var buffer = image.buffer; + if (buffer.width == newWidth && buffer.height == newHeight) return; var newBuffer = new ImageBuffer (new UInt8Array (newWidth * newHeight * 4), newWidth, newHeight); - var imageWidth = image.width; - var imageHeight = image.height; - - var data = image.data; - var newData = newBuffer.data; - var sourceIndex:Int, sourceIndexX:Int, sourceIndexY:Int, sourceIndexXY:Int, index:Int; - var sourceX:Int, sourceY:Int; - var u:Float, v:Float, uRatio:Float, vRatio:Float, uOpposite:Float, vOpposite:Float; - - for (y in 0...newHeight) { + #if ((cpp || neko) && !disable_cffi) + if (!System.disableCFFI) lime_image_data_util_resize (image, newBuffer, newWidth, newHeight); else + #end + { - for (x in 0...newWidth) { + var imageWidth = image.width; + var imageHeight = image.height; + + var data = image.data; + var newData = newBuffer.data; + var sourceIndex:Int, sourceIndexX:Int, sourceIndexY:Int, sourceIndexXY:Int, index:Int; + var sourceX:Int, sourceY:Int; + var u:Float, v:Float, uRatio:Float, vRatio:Float, uOpposite:Float, vOpposite:Float; + + for (y in 0...newHeight) { - u = ((x + 0.5) / newWidth) * imageWidth - 0.5; - v = ((y + 0.5) / newHeight) * imageHeight - 0.5; - - sourceX = Std.int (u); - sourceY = Std.int (v); - - sourceIndex = (sourceY * imageWidth + sourceX) * 4; - sourceIndexX = (sourceX < imageWidth - 1) ? sourceIndex + 4 : sourceIndex; - sourceIndexY = (sourceY < imageHeight - 1) ? sourceIndex + (imageWidth * 4) : sourceIndex; - sourceIndexXY = (sourceIndexX != sourceIndex) ? sourceIndexY + 4 : sourceIndexY; - - index = (y * newWidth + x) * 4; - - uRatio = u - sourceX; - vRatio = v - sourceY; - uOpposite = 1 - uRatio; - vOpposite = 1 - vRatio; - - newData[index] = Std.int ((data[sourceIndex] * uOpposite + data[sourceIndexX] * uRatio) * vOpposite + (data[sourceIndexY] * uOpposite + data[sourceIndexXY] * uRatio) * vRatio); - newData[index + 1] = Std.int ((data[sourceIndex + 1] * uOpposite + data[sourceIndexX + 1] * uRatio) * vOpposite + (data[sourceIndexY + 1] * uOpposite + data[sourceIndexXY + 1] * uRatio) * vRatio); - newData[index + 2] = Std.int ((data[sourceIndex + 2] * uOpposite + data[sourceIndexX + 2] * uRatio) * vOpposite + (data[sourceIndexY + 2] * uOpposite + data[sourceIndexXY + 2] * uRatio) * vRatio); - - // Maybe it would be better to not weigh colors with an alpha of zero, but the below should help prevent black fringes caused by transparent pixels made visible - - if (data[sourceIndexX + 3] == 0 || data[sourceIndexY + 3] == 0 || data[sourceIndexXY + 3] == 0) { + for (x in 0...newWidth) { - newData[index + 3] = 0; + u = ((x + 0.5) / newWidth) * imageWidth - 0.5; + v = ((y + 0.5) / newHeight) * imageHeight - 0.5; - } else { + sourceX = Std.int (u); + sourceY = Std.int (v); - newData[index + 3] = data[sourceIndex + 3]; + sourceIndex = (sourceY * imageWidth + sourceX) * 4; + sourceIndexX = (sourceX < imageWidth - 1) ? sourceIndex + 4 : sourceIndex; + sourceIndexY = (sourceY < imageHeight - 1) ? sourceIndex + (imageWidth * 4) : sourceIndex; + sourceIndexXY = (sourceIndexX != sourceIndex) ? sourceIndexY + 4 : sourceIndexY; + + index = (y * newWidth + x) * 4; + + uRatio = u - sourceX; + vRatio = v - sourceY; + uOpposite = 1 - uRatio; + vOpposite = 1 - vRatio; + + newData[index] = Std.int ((data[sourceIndex] * uOpposite + data[sourceIndexX] * uRatio) * vOpposite + (data[sourceIndexY] * uOpposite + data[sourceIndexXY] * uRatio) * vRatio); + newData[index + 1] = Std.int ((data[sourceIndex + 1] * uOpposite + data[sourceIndexX + 1] * uRatio) * vOpposite + (data[sourceIndexY + 1] * uOpposite + data[sourceIndexXY + 1] * uRatio) * vRatio); + newData[index + 2] = Std.int ((data[sourceIndex + 2] * uOpposite + data[sourceIndexX + 2] * uRatio) * vOpposite + (data[sourceIndexY + 2] * uOpposite + data[sourceIndexXY + 2] * uRatio) * vRatio); + + // Maybe it would be better to not weigh colors with an alpha of zero, but the below should help prevent black fringes caused by transparent pixels made visible + + if (data[sourceIndexX + 3] == 0 || data[sourceIndexY + 3] == 0 || data[sourceIndexXY + 3] == 0) { + + newData[index + 3] = 0; + + } else { + + newData[index + 3] = data[sourceIndex + 3]; + + } } @@ -694,7 +729,7 @@ class ImageDataUtil { } - buffer.data = newData; + buffer.data = newBuffer.data; buffer.width = newWidth; buffer.height = newHeight; @@ -864,32 +899,33 @@ class ImageDataUtil { if (data == null) return; #if ((cpp || neko) && !disable_cffi) - lime_image_data_util_unmultiply_alpha (image); - #else - - var index, a, unmultiply; - var length = Std.int (data.length / 4); - - for (i in 0...length) { + if (!System.disableCFFI) lime_image_data_util_unmultiply_alpha (image); else + #end + { - index = i * 4; + var index, a, unmultiply; + var length = Std.int (data.length / 4); - a = data[index + 3]; - - if (a != 0) { + for (i in 0...length) { - unmultiply = 255.0 / a; + index = i * 4; - data[index] = __clamp[Std.int (data[index] * unmultiply)]; - data[index + 1] = __clamp[Std.int (data[index + 1] * unmultiply)]; - data[index + 2] = __clamp[Std.int (data[index + 2] * unmultiply)]; + a = data[index + 3]; + + if (a != 0) { + + unmultiply = 255.0 / a; + + data[index] = __clamp[Std.int (data[index] * unmultiply)]; + data[index + 1] = __clamp[Std.int (data[index + 1] * unmultiply)]; + data[index + 2] = __clamp[Std.int (data[index + 2] * unmultiply)]; + + } } } - #end - image.buffer.premultiplied = false; image.dirty = true; @@ -909,7 +945,10 @@ class ImageDataUtil { private static var lime_image_data_util_copy_pixels = System.load ("lime", "lime_image_data_util_copy_pixels", 5); private static var lime_image_data_util_fill_rect = System.load ("lime", "lime_image_data_util_fill_rect", 3); private static var lime_image_data_util_flood_fill = System.load ("lime", "lime_image_data_util_flood_fill", 4); + private static var lime_image_data_util_get_pixels = System.load ("lime", "lime_image_data_util_get_pixels", 3); + private static var lime_image_data_util_merge = System.load ("lime", "lime_image_data_util_merge", -1); private static var lime_image_data_util_multiply_alpha = System.load ("lime", "lime_image_data_util_multiply_alpha", 1); + private static var lime_image_data_util_resize = System.load ("lime", "lime_image_data_util_resize", 4); private static var lime_image_data_util_unmultiply_alpha = System.load ("lime", "lime_image_data_util_unmultiply_alpha", 1); #end diff --git a/lime/system/System.hx b/lime/system/System.hx index 87de600a3..fa4732445 100644 --- a/lime/system/System.hx +++ b/lime/system/System.hx @@ -29,7 +29,7 @@ class System { public static var applicationDirectory (get, null):String; public static var applicationStorageDirectory (get, null):String; public static var desktopDirectory (get, null):String; - public static var disableCFFI:Bool; + public static var disableCFFI:Bool = #if (disable_cffi || macro || !cpp || !neko || !nodejs) true #else false #end; public static var documentsDirectory (get, null):String; public static var fontsDirectory (get, null):String; public static var userDirectory (get, null):String; diff --git a/lime/tools/helpers/IconHelper.hx b/lime/tools/helpers/IconHelper.hx index f30ade332..6f8e7983d 100644 --- a/lime/tools/helpers/IconHelper.hx +++ b/lime/tools/helpers/IconHelper.hx @@ -116,7 +116,7 @@ class IconHelper { for (c in 0...4) out.writeByte (code.charCodeAt(c)); var n = s * s; - var pixels = image.getPixels (new Rectangle (0, 0, s, s)); + var pixels = image.getPixels (new Rectangle (0, 0, s, s), ARGB); var bytes_r = packBits (pixels, 1, s * s); var bytes_g = packBits (pixels, 2, s * s); diff --git a/project/include/graphics/PixelFormat.h b/project/include/graphics/PixelFormat.h new file mode 100644 index 000000000..101dcdab8 --- /dev/null +++ b/project/include/graphics/PixelFormat.h @@ -0,0 +1,19 @@ +#ifndef LIME_GRAPHICS_PIXEL_FORMAT_H +#define LIME_GRAPHICS_PIXEL_FORMAT_H + + +namespace lime { + + + enum PixelFormat { + + RGBA, + ARGB + + }; + + +} + + +#endif \ No newline at end of file diff --git a/project/include/graphics/utils/ImageDataUtil.h b/project/include/graphics/utils/ImageDataUtil.h index 1a6456865..1be0de46a 100644 --- a/project/include/graphics/utils/ImageDataUtil.h +++ b/project/include/graphics/utils/ImageDataUtil.h @@ -4,10 +4,12 @@ #include #include +#include #include #include #include #include +#include namespace lime { @@ -18,13 +20,16 @@ namespace lime { public: - static void ColorTransform (Image *image, Rectangle *rect, ColorMatrix *ColorMatrix); + static void ColorTransform (Image* image, Rectangle* rect, ColorMatrix* ColorMatrix); static void CopyChannel (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int srcChannel, int destChannel); static void CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, bool mergeAlpha); static void FillRect (Image* image, Rectangle* rect, int color); static void FloodFill (Image* image, int x, int y, int color); - static void MultiplyAlpha (Image *image); - static void UnmultiplyAlpha (Image *image); + static void GetPixels (Image* image, Rectangle* rect, PixelFormat format, ByteArray* pixels); + static void Merge (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int redMultiplier, int greenMultiplier, int blueMultiplier, int alphaMultiplier); + static void MultiplyAlpha (Image* image); + static void Resize (Image* image, ImageBuffer* buffer, int width, int height); + static void UnmultiplyAlpha (Image* image); }; diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp index 9ffb46456..6a8e4223a 100644 --- a/project/src/ExternalInterface.cpp +++ b/project/src/ExternalInterface.cpp @@ -504,6 +504,32 @@ namespace lime { } + value lime_image_data_util_get_pixels (value image, value rect, value format) { + + Image _image = Image (image); + Rectangle _rect = Rectangle (rect); + PixelFormat _format = (PixelFormat)val_int (format); + ByteArray pixels = ByteArray (); + ImageDataUtil::GetPixels (&_image, &_rect, _format, &pixels); + return pixels.mValue; + + } + + + value lime_image_data_util_merge (value *arg, int nargs) { + + enum { image, sourceImage, sourceRect, destPoint, redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier }; + + Image _image = Image (arg[image]); + Image _sourceImage = Image (arg[sourceImage]); + Rectangle _sourceRect = Rectangle (arg[sourceRect]); + Vector2 _destPoint = Vector2 (arg[destPoint]); + ImageDataUtil::Merge (&_image, &_sourceImage, &_sourceRect, &_destPoint, val_int (arg[redMultiplier]), val_int (arg[greenMultiplier]), val_int (arg[blueMultiplier]), val_int (arg[alphaMultiplier])); + return alloc_null (); + + } + + value lime_image_data_util_multiply_alpha (value image) { Image _image = Image (image); @@ -513,6 +539,16 @@ namespace lime { } + value lime_image_data_util_resize (value image, value buffer, value width, value height) { + + Image _image = Image (image); + ImageBuffer _buffer = ImageBuffer (buffer); + ImageDataUtil::Resize (&_image, &_buffer, val_int (width), val_int (height)); + return alloc_null (); + + } + + value lime_image_data_util_unmultiply_alpha (value image) { Image _image = Image (image); @@ -918,7 +954,10 @@ namespace lime { DEFINE_PRIM (lime_image_data_util_copy_pixels, 5); DEFINE_PRIM (lime_image_data_util_fill_rect, 3); DEFINE_PRIM (lime_image_data_util_flood_fill, 4); + DEFINE_PRIM (lime_image_data_util_get_pixels, 3); + DEFINE_PRIM_MULT (lime_image_data_util_merge); DEFINE_PRIM (lime_image_data_util_multiply_alpha, 1); + DEFINE_PRIM (lime_image_data_util_resize, 4); DEFINE_PRIM (lime_image_data_util_unmultiply_alpha, 1); DEFINE_PRIM (lime_image_encode, 3); DEFINE_PRIM (lime_image_load, 1); diff --git a/project/src/graphics/utils/ImageDataUtil.cpp b/project/src/graphics/utils/ImageDataUtil.cpp index eb076b85b..fee50cb6d 100644 --- a/project/src/graphics/utils/ImageDataUtil.cpp +++ b/project/src/graphics/utils/ImageDataUtil.cpp @@ -296,6 +296,100 @@ namespace lime { } + void ImageDataUtil::GetPixels (Image* image, Rectangle* rect, PixelFormat format, ByteArray* pixels) { + + int length = int (rect->width * rect->height); + pixels->Resize (length * 4); + + if (format == RGBA && rect->width == image->buffer->width && rect->height == image->buffer->height && rect->x == 0 && rect->y == 0) { + + memcpy (pixels->Bytes (), image->buffer->data->Bytes (), image->buffer->data->Size ()); + return; + + } + + uint8_t* data = (uint8_t*)pixels->Bytes (); + uint8_t* srcData = (uint8_t*)image->buffer->data->Bytes (); + + int srcStride = int (image->buffer->width * 4); + int srcPosition = int ((rect->x * 4) + (srcStride * rect->y)); + int srcRowOffset = srcStride - int (4 * rect->width); + int srcRowEnd = int (4 * (rect->x + rect->width)); + + if (format == ARGB) { + + for (int i = 0; i < length; i++) { + + data[i * 4 + 1] = srcData[srcPosition++]; + data[i * 4 + 2] = srcData[srcPosition++]; + data[i * 4 + 3] = srcData[srcPosition++]; + data[i * 4] = srcData[srcPosition++]; + + if ((srcPosition % srcStride) > srcRowEnd) { + + srcPosition += srcRowOffset; + + } + + } + + } else { + + for (int i = 0; i < length; i++) { + + data[i * 4] = srcData[srcPosition++]; + data[i * 4 + 1] = srcData[srcPosition++]; + data[i * 4 + 2] = srcData[srcPosition++]; + data[i * 4 + 3] = srcData[srcPosition++]; + + if ((srcPosition % srcStride) > srcRowEnd) { + + srcPosition += srcRowOffset; + + } + + } + + } + + } + + + void ImageDataUtil::Merge (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, int redMultiplier, int greenMultiplier, int blueMultiplier, int alphaMultiplier) { + + int rowOffset = int (destPoint->y + image->offsetY - sourceRect->y - sourceImage->offsetY); + int columnOffset = int (destPoint->x + image->offsetX - sourceRect->x - sourceImage->offsetY); + + uint8_t* sourceData = (uint8_t*)sourceImage->buffer->data->Bytes (); + int sourceStride = sourceImage->buffer->width * 4; + int sourceOffset = 0; + + uint8_t* data = (uint8_t*)image->buffer->data->Bytes (); + int stride = image->buffer->width * 4; + int offset = 0; + + int rowEnd = int (sourceRect->y + sourceRect->height + sourceImage->offsetY); + int columnEnd = int (sourceRect->x + sourceRect->width + sourceImage->offsetX); + + for (int row = int (sourceRect->y + sourceImage->offsetY); row < rowEnd; row++) { + + for (int column = int (sourceRect->x + sourceImage->offsetX); column < columnEnd; column++) { + + sourceOffset = (row * sourceStride) + (column * 4); + offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + + data[offset] = int (((sourceData[offset] * redMultiplier) + (data[offset] * (256 - redMultiplier))) / 256); + data[offset + 1] = int (((sourceData[offset + 1] * greenMultiplier) + (data[offset + 1] * (256 - greenMultiplier))) / 256); + data[offset + 2] = int (((sourceData[offset + 2] * blueMultiplier) + (data[offset + 2] * (256 - blueMultiplier))) / 256); + data[offset + 3] = int (((sourceData[offset + 3] * alphaMultiplier) + (data[offset + 3] * (256 - alphaMultiplier))) / 256); + + } + + } + + } + + void ImageDataUtil::MultiplyAlpha (Image* image) { int a16 = 0; @@ -315,6 +409,63 @@ namespace lime { } + void ImageDataUtil::Resize (Image* image, ImageBuffer* buffer, int newWidth, int newHeight) { + + int imageWidth = image->width; + int imageHeight = image->height; + + uint8_t* data = (uint8_t*)image->buffer->data->Bytes (); + uint8_t* newData = (uint8_t*)buffer->data->Bytes (); + + int sourceIndex, sourceIndexX, sourceIndexY, sourceIndexXY, index; + int sourceX, sourceY; + float u, v, uRatio, vRatio, uOpposite, vOpposite; + + for (int y = 0; y < newHeight; y++) { + + for (int x = 0; x < newWidth; x++) { + + u = ((x + 0.5) / newWidth) * imageWidth - 0.5; + v = ((y + 0.5) / newHeight) * imageHeight - 0.5; + + sourceX = int (u); + sourceY = int (v); + + sourceIndex = (sourceY * imageWidth + sourceX) * 4; + sourceIndexX = (sourceX < imageWidth - 1) ? sourceIndex + 4 : sourceIndex; + sourceIndexY = (sourceY < imageHeight - 1) ? sourceIndex + (imageWidth * 4) : sourceIndex; + sourceIndexXY = (sourceIndexX != sourceIndex) ? sourceIndexY + 4 : sourceIndexY; + + index = (y * newWidth + x) * 4; + + uRatio = u - sourceX; + vRatio = v - sourceY; + uOpposite = 1 - uRatio; + vOpposite = 1 - vRatio; + + newData[index] = int ((data[sourceIndex] * uOpposite + data[sourceIndexX] * uRatio) * vOpposite + (data[sourceIndexY] * uOpposite + data[sourceIndexXY] * uRatio) * vRatio); + newData[index + 1] = int ((data[sourceIndex + 1] * uOpposite + data[sourceIndexX + 1] * uRatio) * vOpposite + (data[sourceIndexY + 1] * uOpposite + data[sourceIndexXY + 1] * uRatio) * vRatio); + newData[index + 2] = int ((data[sourceIndex + 2] * uOpposite + data[sourceIndexX + 2] * uRatio) * vOpposite + (data[sourceIndexY + 2] * uOpposite + data[sourceIndexXY + 2] * uRatio) * vRatio); + + // Maybe it would be better to not weigh colors with an alpha of zero, but the below should help prevent black fringes caused by transparent pixels made visible + + if (data[sourceIndexX + 3] == 0 || data[sourceIndexY + 3] == 0 || data[sourceIndexXY + 3] == 0) { + + newData[index + 3] = 0; + + } else { + + newData[index + 3] = data[sourceIndex + 3]; + + } + + } + + } + + } + + void ImageDataUtil::UnmultiplyAlpha (Image* image) { int length = image->buffer->data->Size () / 4;