From d6750818fcf957a5d529d6a160efe1199e7bbc46 Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Tue, 21 Jul 2015 10:08:29 -0700 Subject: [PATCH] More work on pixel operations --- lime/graphics/utils/ImageDataUtil.hx | 14 +- .../include/graphics/utils/ImageDataUtil.h | 2 +- project/include/math/color/RGBA.h | 7 + project/src/ExternalInterface.cpp | 29 ++- project/src/graphics/utils/ImageDataUtil.cpp | 227 +++++++++++------- 5 files changed, 173 insertions(+), 106 deletions(-) diff --git a/lime/graphics/utils/ImageDataUtil.hx b/lime/graphics/utils/ImageDataUtil.hx index dbab59695..df59562b3 100644 --- a/lime/graphics/utils/ImageDataUtil.hx +++ b/lime/graphics/utils/ImageDataUtil.hx @@ -154,9 +154,9 @@ class ImageDataUtil { public static function copyPixels (image:Image, sourceImage:Image, sourceRect:Rectangle, destPoint:Vector2, alphaImage:Image = null, alphaPoint:Vector2 = null, mergeAlpha:Bool = false):Void { - //#if ((cpp || neko) && !disable_cffi) - //if (!System.disableCFFI) lime_image_data_util_copy_pixels (image, sourceImage, sourceRect, destPoint, mergeAlpha); else - //#end + #if ((cpp || neko) && !disable_cffi) + if (!System.disableCFFI) lime_image_data_util_copy_pixels (image, sourceImage, sourceRect, destPoint, alphaImage, alphaPoint, mergeAlpha); else + #end { var sourceData = sourceImage.buffer.data; @@ -317,9 +317,9 @@ class ImageDataUtil { var data = image.buffer.data; if (data == null) return; - //#if ((cpp || neko) && !disable_cffi) - //if (!System.disableCFFI) lime_image_data_util_fill_rect (image, rect, rgba); else - //#end + #if ((cpp || neko) && !disable_cffi) + if (!System.disableCFFI) lime_image_data_util_fill_rect (image, rect, fillColor); else + #end { var format = image.buffer.format; @@ -1089,7 +1089,7 @@ class ImageDataUtil { #if (cpp || neko || nodejs) private static var lime_image_data_util_color_transform = System.load ("lime", "lime_image_data_util_color_transform", 3); private static var lime_image_data_util_copy_channel = System.load ("lime", "lime_image_data_util_copy_channel", -1); - 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_copy_pixels = System.load ("lime", "lime_image_data_util_copy_pixels", -1); 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", 4); diff --git a/project/include/graphics/utils/ImageDataUtil.h b/project/include/graphics/utils/ImageDataUtil.h index e67ec5299..21d72a786 100644 --- a/project/include/graphics/utils/ImageDataUtil.h +++ b/project/include/graphics/utils/ImageDataUtil.h @@ -22,7 +22,7 @@ namespace lime { 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 CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, Image* alphaImage, Vector2* alphaPoint, bool mergeAlpha); static void FillRect (Image* image, Rectangle* rect, int color); static void FloodFill (Image* image, int x, int y, int color); static void GetPixels (Image* image, Rectangle* rect, PixelFormat format, Bytes* pixels); diff --git a/project/include/math/color/RGBA.h b/project/include/math/color/RGBA.h index fc96ccee3..02a6f0f8e 100644 --- a/project/include/math/color/RGBA.h +++ b/project/include/math/color/RGBA.h @@ -189,6 +189,13 @@ namespace lime { } + inline bool operator == (RGBA& rgba) { + + return (a == rgba.a && r == rgba.r && g == rgba.g && b == rgba.b); + + } + + unsigned char r; unsigned char g; unsigned char b; diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp index 764b204d0..d4dded491 100644 --- a/project/src/ExternalInterface.cpp +++ b/project/src/ExternalInterface.cpp @@ -532,13 +532,28 @@ namespace lime { } - value lime_image_data_util_copy_pixels (value image, value sourceImage, value sourceRect, value destPoint, value mergeAlpha) { + value lime_image_data_util_copy_pixels (value *arg, int nargs) { + + enum { image, sourceImage, sourceRect, destPoint, alphaImage, alphaPoint, mergeAlpha }; + + Image _image = Image (arg[image]); + Image _sourceImage = Image (arg[sourceImage]); + Rectangle _sourceRect = Rectangle (arg[sourceRect]); + Vector2 _destPoint = Vector2 (arg[destPoint]); + + if (val_is_null (arg[alphaImage])) { + + ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, 0, 0, val_bool (arg[mergeAlpha])); + + } else { + + Image _alphaImage = Image (arg[alphaImage]); + Vector2 _alphaPoint = Vector2 (arg[alphaPoint]); + + ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, &_alphaImage, &_alphaPoint, val_bool (arg[mergeAlpha])); + + } - Image _image = Image (image); - Image _sourceImage = Image (sourceImage); - Rectangle _sourceRect = Rectangle (sourceRect); - Vector2 _destPoint = Vector2 (destPoint); - ImageDataUtil::CopyPixels (&_image, &_sourceImage, &_sourceRect, &_destPoint, val_bool (mergeAlpha)); return alloc_null (); } @@ -1153,7 +1168,7 @@ namespace lime { DEFINE_PRIM (lime_gamepad_get_device_name, 1); DEFINE_PRIM (lime_image_data_util_color_transform, 3); DEFINE_PRIM_MULT (lime_image_data_util_copy_channel); - DEFINE_PRIM (lime_image_data_util_copy_pixels, 5); + DEFINE_PRIM_MULT (lime_image_data_util_copy_pixels); 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, 4); diff --git a/project/src/graphics/utils/ImageDataUtil.cpp b/project/src/graphics/utils/ImageDataUtil.cpp index d301cee22..50246c792 100644 --- a/project/src/graphics/utils/ImageDataUtil.cpp +++ b/project/src/graphics/utils/ImageDataUtil.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace lime { @@ -25,8 +26,7 @@ namespace lime { colorMatrix->GetGreenTable (greenTable); colorMatrix->GetBlueTable (blueTable); - int row; - int offset; + int row, offset; RGBA pixel; for (int y = 0; y < dataView.height; y++) { @@ -61,10 +61,8 @@ namespace lime { bool srcPremultiplied = sourceImage->buffer->premultiplied; bool destPremultiplied = image->buffer->premultiplied; - int srcPosition; - int destPosition; - RGBA srcPixel; - RGBA destPixel; + int srcPosition, destPosition; + RGBA srcPixel, destPixel; unsigned char value = 0; for (int y = 0; y < destView.height; y++) { @@ -107,35 +105,36 @@ namespace lime { } - void ImageDataUtil::CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, bool mergeAlpha) { - - int rowOffset = int (destPoint->y + image->offsetY - sourceRect->y - sourceImage->offsetY); - int columnOffset = int (destPoint->x + image->offsetX - sourceRect->x - sourceImage->offsetY); + void ImageDataUtil::CopyPixels (Image* image, Image* sourceImage, Rectangle* sourceRect, Vector2* destPoint, Image* alphaImage, Vector2* alphaPoint, bool mergeAlpha) { uint8_t* sourceData = (uint8_t*)sourceImage->buffer->data->Data (); - int sourceStride = sourceImage->buffer->Stride (); - int sourceOffset = 0; + uint8_t* destData = (uint8_t*)image->buffer->data->Data (); - uint8_t* data = (uint8_t*)image->buffer->data->Data (); - int stride = image->buffer->Stride (); - int offset = 0; + ImageDataView sourceView = ImageDataView (sourceImage, sourceRect); + ImageDataView destView = ImageDataView (image, &Rectangle (destPoint->x, destPoint->y, sourceView.width, sourceView.height)); - int rows = sourceRect->y + sourceRect->height + sourceImage->offsetY; - int columns = sourceRect->x + sourceRect->width + sourceImage->offsetX; + PixelFormat sourceFormat = sourceImage->buffer->format; + PixelFormat destFormat = image->buffer->format; + bool sourcePremultiplied = sourceImage->buffer->premultiplied; + bool destPremultiplied = image->buffer->premultiplied; - if (!mergeAlpha || !sourceImage->buffer->transparent) { + int sourcePosition, destPosition; + RGBA sourcePixel; + + if (!mergeAlpha || !sourceImage->transparent) { - for (int row = sourceRect->y + sourceImage->offsetY; row < rows; row++) { + for (int y = 0; y < destView.height; y++) { - for (int column = sourceRect->x + sourceImage->offsetX; column < columns; column++) { + sourcePosition = sourceView.Row (y); + destPosition = destView.Row (y); + + for (int x = 0; x < destView.width; x++) { - sourceOffset = (row * sourceStride) + (column * 4); - offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); + sourcePixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied); - data[offset] = sourceData[sourceOffset]; - data[offset + 1] = sourceData[sourceOffset + 1]; - data[offset + 2] = sourceData[sourceOffset + 2]; - data[offset + 3] = sourceData[sourceOffset + 3]; + sourcePosition += 4; + destPosition += 4; } @@ -143,27 +142,94 @@ namespace lime { } else { - float sourceAlpha; - float destAlpha; - float outA; - float oneMinusSourceAlpha; + float sourceAlpha, destAlpha, oneMinusSourceAlpha, blendAlpha; + RGBA destPixel; - for (int row = sourceRect->y + sourceImage->offsetY; row < rows; row++) { + if (alphaImage == 0) { - for (int column = sourceRect->x + sourceImage->offsetX; column < columns; column++) { + for (int y = 0; y < destView.height; y++) { - sourceOffset = (row * sourceStride) + (column * 4); - offset = ((row + rowOffset) * stride) + ((column + columnOffset) * 4); + sourcePosition = sourceView.Row (y); + destPosition = destView.Row (y); - sourceAlpha = sourceData[sourceOffset + 3] / 255.0; - destAlpha = data[offset + 3] / 255.0; - oneMinusSourceAlpha = (1 - sourceAlpha); + for (int x = 0; x < destView.width; x++) { + + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); + + sourceAlpha = sourcePixel.a / 255.0; + destAlpha = destPixel.a / 255.0; + oneMinusSourceAlpha = 1 - sourceAlpha; + blendAlpha = sourceAlpha + (destAlpha * oneMinusSourceAlpha); + + if (blendAlpha == 0) { + + destPixel.Set (0, 0, 0, 0); + + } else { + + destPixel.r = __clamp[int (0.5 + (sourcePixel.r * sourceAlpha + destPixel.r * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.g = __clamp[int (0.5 + (sourcePixel.g * sourceAlpha + destPixel.g * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.b = __clamp[int (0.5 + (sourcePixel.b * sourceAlpha + destPixel.b * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.a = __clamp[int (0.5 + blendAlpha * 255.0)]; + + } + + destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied); + + sourcePosition += 4; + destPosition += 4; + + } - outA = sourceAlpha + destAlpha * oneMinusSourceAlpha; - data[offset + 0] = __clamp[int (0.5 + ((sourceData[sourceOffset + 0] * sourceAlpha + data[offset + 0] * destAlpha * oneMinusSourceAlpha) / outA))]; - data[offset + 1] = __clamp[int (0.5 + ((sourceData[sourceOffset + 1] * sourceAlpha + data[offset + 1] * destAlpha * oneMinusSourceAlpha) / outA))]; - data[offset + 2] = __clamp[int (0.5 + ((sourceData[sourceOffset + 2] * sourceAlpha + data[offset + 2] * destAlpha * oneMinusSourceAlpha) / outA))]; - data[offset + 3] = __clamp[int (0.5 + (outA * 255.0))]; + } + + } else { + + uint8_t* alphaData = (uint8_t*)alphaImage->buffer->data->Data (); + PixelFormat alphaFormat = alphaImage->buffer->format; + bool alphaPremultiplied = alphaImage->buffer->premultiplied; + + ImageDataView alphaView = ImageDataView (alphaImage, &Rectangle (alphaPoint->x, alphaPoint->y, destView.width, destView.height)); + int alphaPosition; + RGBA alphaPixel; + + for (int y = 0; y < alphaView.height; y++) { + + sourcePosition = sourceView.Row (y); + destPosition = destView.Row (y); + alphaPosition = alphaView.Row (y); + + for (int x = 0; x < alphaView.width; x++) { + + sourcePixel.ReadUInt8 (sourceData, sourcePosition, sourceFormat, sourcePremultiplied); + destPixel.ReadUInt8 (destData, destPosition, destFormat, destPremultiplied); + alphaPixel.ReadUInt8 (alphaData, alphaPosition, alphaFormat, alphaPremultiplied); + + sourceAlpha = alphaPixel.a / 0xFF; + destAlpha = destPixel.a / 0xFF; + oneMinusSourceAlpha = 1 - sourceAlpha; + blendAlpha = sourceAlpha + (destAlpha * oneMinusSourceAlpha); + + if (blendAlpha == 0) { + + destPixel.Set (0, 0, 0, 0); + + } else { + + destPixel.r = __clamp[int (0.5 + (sourcePixel.r * sourceAlpha + destPixel.r * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.g = __clamp[int (0.5 + (sourcePixel.g * sourceAlpha + destPixel.g * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.b = __clamp[int (0.5 + (sourcePixel.b * sourceAlpha + destPixel.b * destAlpha * oneMinusSourceAlpha) / blendAlpha)]; + destPixel.a = __clamp[int (0.5 + blendAlpha * 255.0)]; + + } + + destPixel.WriteUInt8 (destData, destPosition, destFormat, destPremultiplied); + + sourcePosition += 4; + destPosition += 4; + + } } @@ -176,44 +242,21 @@ namespace lime { void ImageDataUtil::FillRect (Image* image, Rectangle* rect, int color) { - int* data = (int*)image->buffer->data->Data (); + uint8_t* data = (uint8_t*)image->buffer->data->Data (); + PixelFormat format = image->buffer->format; + bool premultiplied = image->buffer->premultiplied; - if (rect->width == image->buffer->width && rect->height == image->buffer->height && rect->x == 0 && rect->y == 0 && image->offsetX == 0 && image->offsetY == 0) { + ImageDataView dataView = ImageDataView (image, rect); + int row; + RGBA fillColor (color); + + for (int y = 0; y < dataView.height; y++) { - int length = image->buffer->width * image->buffer->height; + row = dataView.Row (y); - if (color == 0 || color == 0xFFFFFFFF || (color & 0xFF == (color >> 8) & 0xFF && (color >> 8) & 0xFF == (color >> 16) & 0xFF && (color >> 16) & 0xFF == (color >> 24) & 0xFF)) { + for (int x = 0; x < dataView.width; x++) { - memset ((uint8_t*)data, color & 0xFF, length * 4); - - } else { - - for (int i = 0; i < length; i++) { - - data[i] = color; - - } - - } - - } else { - - int stride = image->buffer->width; - int offset; - - int rowStart = int (rect->y + image->offsetY); - int rowEnd = int (rect->y + rect->height + image->offsetY); - int columnStart = int (rect->x + image->offsetX); - int columnEnd = int (rect->x + rect->width + image->offsetX); - - for (int row = rowStart; row < rowEnd; row++) { - - for (int column = columnStart; column < columnEnd; column++) { - - offset = (row * stride) + (column); - data[offset] = color; - - } + fillColor.WriteUInt8 (data, row + (x * 4), format, premultiplied); } @@ -225,19 +268,22 @@ namespace lime { void ImageDataUtil::FloodFill (Image* image, int x, int y, int color) { uint8_t* data = (uint8_t*)image->buffer->data->Data (); + PixelFormat format = image->buffer->format; + bool premultiplied = image->buffer->premultiplied; - int offset = (((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4)); - uint8_t hitColorR = data[offset + 0]; - uint8_t hitColorG = data[offset + 1]; - uint8_t hitColorB = data[offset + 2]; - uint8_t hitColorA = image->transparent ? data[offset + 3] : 0xFF; + RGBA fillColor (color); - uint8_t r = (color >> 24) & 0xFF; - uint8_t g = (color >> 16) & 0xFF; - uint8_t b = (color >> 8) & 0xFF; - uint8_t a = image->transparent ? color & 0xFF : 0xFF; + RGBA hitColor; + hitColor.ReadUInt8 (data, ((y + image->offsetY) * (image->buffer->width * 4)) + ((x + image->offsetX) * 4), format, premultiplied); - if (hitColorR == r && hitColorG == g && hitColorB == b && hitColorA == a) return; + if (!image->transparent) { + + fillColor.a = 0xFF; + hitColor.a = 0xFF; + + } + + if (fillColor == hitColor) return; int dx[4] = { 0, -1, 1, 0 }; int dy[4] = { -1, 0, 0, 1 }; @@ -252,6 +298,7 @@ namespace lime { queue.push_back (y); int curPointX, curPointY, i, nextPointX, nextPointY, nextPointOffset; + RGBA readColor; while (queue.size () > 0) { @@ -270,13 +317,11 @@ namespace lime { } nextPointOffset = (nextPointY * image->width + nextPointX) * 4; + readColor.ReadUInt8 (data, nextPointOffset, format, premultiplied); - if (data[nextPointOffset + 0] == hitColorR && data[nextPointOffset + 1] == hitColorG && data[nextPointOffset + 2] == hitColorB && data[nextPointOffset + 3] == hitColorA) { + if (readColor == hitColor) { - data[nextPointOffset + 0] = r; - data[nextPointOffset + 1] = g; - data[nextPointOffset + 2] = b; - data[nextPointOffset + 3] = a; + fillColor.WriteUInt8 (data, nextPointOffset, format, premultiplied); queue.push_back (nextPointX); queue.push_back (nextPointY);