|
|
|
|
@@ -2,6 +2,7 @@
|
|
|
|
|
#include <math/color/RGBA.h>
|
|
|
|
|
#include <system/System.h>
|
|
|
|
|
#include <utils/QuickVec.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|