1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/BeefySysLib/img/ImgEffects.cpp
2025-01-26 07:04:26 -08:00

2856 lines
76 KiB
C++

#include "ImgEffects.h"
#include "ImageData.h"
#include <math.h>
#include "util/CatmullRom.h"
#include "util/Vector.h"
#include <fstream>
#include "PSDReader.h"
#include "ImageUtils.h"
#include "ImageAdjustments.h"
#include "util/PerfTimer.h"
USING_NS_BF;
static inline float BFRound(float val)
{
if (val < 0)
return (float) (int) (val - 0.5f);
else
return (float) (int) (val + 0.5f);
}
static uint16* CreateContourDataTable(ImageCurve* contour, float range = 1.0f)
{
uint16* contourData = new uint16[CONTOUR_DATA_SIZE];
for (int i = 0; i < CONTOUR_DATA_SIZE; i++)
{
float aX = i / (float) (CONTOUR_DATA_SIZE - 1);
float tableX = std::min(1.0f, aX / range);
float yVal = contour->GetVal(tableX * 255.0f) / 255.0f;
contourData[i] = BFClamp((int) (yVal * (GRADIENT_DATA_SIZE - 1) + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
}
return contourData;
}
static uint32* CreateGradientDataTable(ImageGradient* colorGradient)
{
uint32* gradientData = new uint32[GRADIENT_DATA_SIZE];
for (int i = 0; i < GRADIENT_DATA_SIZE; i++)
{
float yVal = i / (float) (GRADIENT_DATA_SIZE - 1);
uint32 color = 0;
for (int colorIdx = 0; colorIdx < 4; colorIdx++)
{
float gradientPos = 1.0f - yVal;
if ((colorIdx == 3) && (colorGradient[colorIdx].mPoints.size() > 2))
{
// Strange effect where alpha gradient never fully reaches end when there's more than one stop
gradientPos = std::min(gradientPos, 0.95f);
}
color |= (colorGradient[colorIdx].GetVal(gradientPos * colorGradient[colorIdx].mXSize)) << (colorIdx * 8);
}
gradientData[i] = color;
}
return gradientData;
}
ImageCurvePoint::ImageCurvePoint()
{
mSpline = NULL;
mIsSplineOwner = false;
}
ImageCurvePoint::~ImageCurvePoint()
{
if (mIsSplineOwner)
mSpline = NULL;
}
void ImageCurve::Init()
{
CubicFuncSpline* curSpline = NULL;
for (int i = 0; i < (int) mPoints.size(); i++)
{
ImageCurvePoint* pt = &mPoints[i];
if (pt->mIsCorner)
{
if (curSpline != NULL)
{
curSpline->AddPt(pt->mX, pt->mY);
curSpline = NULL;
}
}
else
{
if (curSpline == NULL)
{
curSpline = new CubicFuncSpline();
pt->mIsSplineOwner = true;
}
pt->mSpline = curSpline;
curSpline->AddPt(pt->mX, pt->mY);
}
}
/*{
std::fstream stream("c:\\temp\\curve.csv", std::ios::out);
for (int i = 0; i < (int) 255; i++)
{
float aVal = GetVal(i);
stream << aVal << std::endl;
}
BF_ASSERT(stream.is_open());
stream.flush();
}
_asm nop;*/
}
float ImageCurve::GetVal(float x)
{
ImageCurvePoint* prevPt = NULL;
for (int i = 0; i < (int) mPoints.size(); i++)
{
ImageCurvePoint* nextPt = &mPoints[i];
if (x == nextPt->mX)
return nextPt->mY;
if ((x <= nextPt->mX) && (prevPt != NULL))
{
if (prevPt->mSpline != NULL)
return prevPt->mSpline->Evaluate(x);
//return BFClamp(prevPt->mSpline->Evaluate(x), 0.0f, 255.0f);
return prevPt->mY +
((x - prevPt->mX) / (nextPt->mX - prevPt->mX)) * (nextPt->mY - prevPt->mY);
}
prevPt = nextPt;
}
return mPoints.back().mY;
}
bool ImageCurve::IsDefault()
{
return (mPoints.size() == 2) && (mPoints[0].mX == 0) && (mPoints[0].mY == 0) && (mPoints[1].mX == 255.0f) && (mPoints[1].mY == 255.0f);
}
static uint32 LerpColor(uint32 color1, uint32 color2, float pct)
{
if (color1 == color2)
return color1;
uint32 a = (uint32)(pct * 256.0f);
uint32 oma = 256 - a;
uint32 color =
(((((color1 & 0x000000FF) * oma) + ((color2 & 0x000000FF) * a)) >> 8) & 0x000000FF) |
(((((color1 & 0x0000FF00) * oma) + ((color2 & 0x0000FF00) * a)) >> 8) & 0x0000FF00) |
(((((color1 & 0x00FF0000) * oma) + ((color2 & 0x00FF0000) * a)) >> 8) & 0x00FF0000) |
(((((color1 >> 24) & 0xFF) * oma) + (((color2 >> 24) & 0xFF) * a) & 0x0000FF00) << 16);
return color;
}
int ImageGradient::GetVal(float x)
{
if (mSpline.mInputPoints.size() == 0)
{
int n = (int)mPoints.size() - 1;
mSpline.AddPt((float) mPoints[0].mValue);
for (int i = 0; i < (int) mPoints.size(); i++)
mSpline.AddPt((float) mPoints[i].mValue);
mSpline.AddPt((float) mPoints[n].mValue);
/*{
std::fstream stream("c:\\temp\\curve.csv", std::ios::out);
for (int i = 0; i < (int) 256; i++)
{
int aVal = GetVal((float) i * 4096 / 256);
stream << aVal << std::endl;
}
BF_ASSERT(stream.is_open());
stream.flush();
}
_asm nop;*/
}
for (int i = 0; i < (int) mPoints.size(); i++)
{
if (x < mPoints[i].mX)
{
if (i == 0)
return mPoints[i].mValue;
float prevVal = (float) mPoints[i - 1].mValue;
float nextVal = (float) mPoints[i].mValue;
float aPct = (x - mPoints[i - 1].mX) / (mPoints[i].mX - mPoints[i - 1].mX);
float linearVal = (prevVal * (1.0f - aPct)) + (nextVal * aPct);
if ((mSmoothness == 0) || (mPoints.size() <= 2))
return (int) (linearVal + 0.5f);
float curveVal = mSpline.Evaluate(i + aPct);
float result = (curveVal * mSmoothness) + (linearVal * (1.0f - mSmoothness));
if (prevVal < nextVal)
result = BFClamp(result, prevVal, nextVal);
else
result = BFClamp(result, nextVal, prevVal);
return (int) (result + 0.5f);
}
}
return mPoints.back().mValue;
}
ImageEffects::ImageEffects()
{
mSwapImages[0] = NULL;
mSwapImages[1] = NULL;
}
ImageEffects::~ImageEffects()
{
for (int i = 0; i < (int) mImageEffectVector.size(); i++)
delete mImageEffectVector[i];
}
ImageData* ImageEffects::GetDestImage(ImageData* usingImage)
{
if ((mSwapImages[0] != usingImage) && (mSwapImages[0] != NULL))
return mSwapImages[0];
if ((mSwapImages[1] != usingImage) && (mSwapImages[1] != NULL))
return mSwapImages[1];
ImageData* anImage = new ImageData();
anImage->mStride = usingImage->mStride;
anImage->mWidth = usingImage->mWidth;
anImage->mHeight = usingImage->mHeight;
anImage->mBits = new uint32[anImage->mWidth*anImage->mHeight];
if (mSwapImages[0] == NULL)
mSwapImages[0] = anImage;
else
mSwapImages[1] = anImage;
return anImage;
}
#define BOXBLUR_IN(x) (((x < 0) || (x >= width)) ? edgeValue : in[inIndex + x])
static void BoxBlur(uint32* in, uint32* out, int width, int height, float radius, uint32 edgeValue)
{
AutoPerf gPerf("ImgEffects - BoxBlur");
int widthMinus1 = width-1;
int r = (int)radius;
int tableSize = 2*r+1;
float frac = radius - r;
int a = (int) (frac * 256);
int oma = 256 - a;
int div = (2*r+1)*256 + a*2;
int inIndex = 0;
if (radius > 1)
{
for ( int y = 0; y < height; y++ )
{
int outIndex = y;
uint32 ta = 0;
for ( int i = -r; i <= r; i++ )
{
int rgb = BOXBLUR_IN(i);
ta += rgb * 256;
}
ta += a * BOXBLUR_IN(-r-1);
ta += a * BOXBLUR_IN(r+1);
for ( int x = 0; x < width; x++ )
{
out[outIndex] = ta / div;
uint32 r1Value = edgeValue;
uint32 r2Value = edgeValue;
int r1 = x+r+1;
int r2 = r1+1;
if (r2 < width)
{
r1Value = in[inIndex + r1];
r2Value = in[inIndex + r2];
}
else if (r1 < width)
{
r1Value = in[inIndex + r1];
}
uint32 l1Value = edgeValue;
uint32 l2Value = edgeValue;
int l1 = x-r-1;
int l2 = l1+1;
if (l1 >= 0)
{
l1Value = in[inIndex + l1];
l2Value = in[inIndex + l2];
}
else if (l2 >= 0)
{
l2Value = in[inIndex + l2];
}
int rgbL = (l1Value * a) + (l2Value * oma);
int rgbR = (r1Value * oma) + (r2Value * a);
ta += rgbR;
ta -= rgbL;
outIndex += height;
}
inIndex += width;
}
}
else
{
for ( int y = 0; y < height; y++ )
{
int outIndex = y;
for ( int x = 0; x < width; x++ )
{
int r = x+1;
if (r > widthMinus1)
r = widthMinus1;
int l = x-1;
if (l < 0)
l = 0;
int rgbL = in[inIndex+l] * a;
int rgbR = in[inIndex+r] * a;
int rgbM = in[inIndex+x] * 256;
out[outIndex] = (rgbL + rgbM + rgbR) / div;
outIndex += height;
}
inIndex += width;
}
}
}
float GetSoftBlurRadius(float origRadius, float radiusLeft)
{
//float radiusOffset = 1.90f - 0.7f * std::min(1.0f, origRadius / 10);
//return radiusLeft - radiusOffset;
if (radiusLeft == 0)
return 0;
float radiusOffset = 1.2f;
float blurRadius = radiusLeft - radiusOffset;
if (radiusLeft <= 2)
blurRadius = 0.5f;
return blurRadius;
}
float GetSoftBlurRadius(float origRadius)
{
return GetSoftBlurRadius(origRadius, origRadius);
}
void SoftBlurInit(ImageData* inImage, ImageData* outImage, bool invert)
{
uint32* in = inImage->mBits;
uint32* out = outImage->mBits;
int iw = inImage->mWidth;
int ih = inImage->mHeight;
int ow = outImage->mWidth;
int oh = outImage->mHeight;
int ox = inImage->mX - outImage->mX;
int oy = inImage->mY - outImage->mY;
if (!invert)
{
for (int y = 0; y < oh; y++)
for (int x = 0; x < ow; x++)
out[x+y*ow] = 0;
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
out[(x+ox)+(y+oy)*ow] = anAlpha * 256;
}
}
}
else
{
for (int y = 0; y < oh; y++)
for (int x = 0; x < ow; x++)
out[x+y*ow] = 255*256;
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
out[(x+ox)+(y+oy)*ow] = (255 - (anAlpha)) * 256;
}
}
}
}
void SoftBlur(uint32* data, int w, int h, float radius, uint32 defaultValue)
{
uint32* tempBuffer = new uint32[w*h];
int expandedX = 0;
int expandedY = 0;
int itrCount = (radius < 0.9f) ? 1 : 2;
float d = radius / itrCount;
if (d > 0)
{
for (int i = 0; i < itrCount; i++)
{
BoxBlur(data, tempBuffer, w, h, d, defaultValue);
BoxBlur(tempBuffer, data, h, w, d, defaultValue);
}
}
delete [] tempBuffer;
}
static int gAlphaCheckOfs [][2] =
{{-1, -1}, {0, -1}, {1, -1},
{-1, 0}, {1, 0},
{-1, 1}, {0, 1}, {1, 1}};
static int gAlphaScaleVals [] =
{358, 254, 358,
254, 256,
358, 254, 358};
static int gKernelOfsFwd[][2] =
{ {-1, -2}, {1, -2},
{-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1},
{-1, 0}};
static int gKernelValsFwd[] =
{567, 567,
567, 358, 254, 358, 567,
254};
static int gKernelOfsRev[][2] =
{
{1, 0},
{-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1},
{-1, 2}, {1, 2}};
static int gKernelValsRev[] =
{ 254,
567, 358, 254, 358, 567,
567, 567};
static void ChamferedDistanceTransformInit(ImageData* inImage, ImageData* outImage, bool invert, int softSize = 0)
{
AutoPerf gPerf("ImgEffects - ChamferedDistanceTransformInit");
uint32* in = inImage->mBits;
uint32* out = outImage->mBits;
int iw = inImage->mWidth;
int ih = inImage->mHeight;
int ow = outImage->mWidth;
int oh = outImage->mHeight;
int ox = inImage->mX - outImage->mX;
int oy = inImage->mY - outImage->mY;
int inf = (ow+oh)*564;
int alphaCheckCount = sizeof(gAlphaScaleVals) / sizeof(int);
if (!invert)
{
for (int y = 0; y < oh; y++)
for (int x = 0; x < ow; x++)
out[x+y*ow] = inf;
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
if (anAlpha != 0)
{
int aVal = (255 - anAlpha);
out[(x+ox)+(y+oy)*ow] = aVal + aVal / 32;
}
else
out[(x+ox)+(y+oy)*ow] = inf;
}
}
}
else
{
for (int y = 0; y < oh; y++)
for (int x = 0; x < ow; x++)
out[x+y*ow] = 0;
if (softSize != 0)
{
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
int aVal;
if (anAlpha > 128)
aVal = (anAlpha - 128) * (softSize + 1)*2;
else
aVal = 0;
out[(x+ox)+(y+oy)*ow] = aVal;
}
}
}
else
{
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
if (anAlpha == 255)
{
out[(x+ox)+(y+oy)*ow] = inf;
}
else
{
out[(x+ox)+(y+oy)*ow] = anAlpha + anAlpha / 32;
}
}
}
}
}
}
static void ChamferedDistanceTransformSlow(uint32* out, int width, int height, int startX, int startY, int endX, int endY)
{
AutoPerf gPerf("ImgEffects - ChamferedDistanceTransform");
int kernelCountFwd = sizeof(gKernelValsFwd) / sizeof(int);
int kernelCountRev = sizeof(gKernelValsRev) / sizeof(int);
for (int y = startY; y < endY; y++)
{
for (int x = startX; x < endX; x++)
{
for (int kernIdx = 0; kernIdx < kernelCountFwd; kernIdx++)
{
int cx = BFClamp(x + gKernelOfsFwd[kernIdx][0], 0, width - 1);
int cy = BFClamp(y + gKernelOfsFwd[kernIdx][1], 0, height - 1);
int aVal = out[cx+cy*width] + gKernelValsFwd[kernIdx];
if (aVal < (int) out[x+y*width])
out[x+y*width] = aVal;
}
}
}
for (int y = endY - 1; y >= startY; y--)
{
for (int x = endX - 1; x >= startX; x--)
{
for (int kernIdx = 0; kernIdx < kernelCountRev; kernIdx++)
{
int cx = BFClamp(x + gKernelOfsRev[kernIdx][0], 0, width - 1);
int cy = BFClamp(y + gKernelOfsRev[kernIdx][1], 0, height - 1);
int aVal = out[cx+cy*width] + gKernelValsRev[kernIdx];
if (aVal < (int) out[x+y*width])
out[x+y*width] = aVal;
}
}
}
}
static void ChamferedDistanceTransform(uint32* out, int width, int height)
{
AutoPerf gPerf("ImgEffects - ChamferedDistanceTransform");
if ((width < 4) || (height < 4))
{
ChamferedDistanceTransformSlow(out, width, height, 0, 0, width, height);
return;
}
// Do 2 pixel border (where we have to clamp)
ChamferedDistanceTransformSlow(out, width, height, 0, 0, width, 2);
ChamferedDistanceTransformSlow(out, width, height, 0, height-2, width, 2);
ChamferedDistanceTransformSlow(out, width, height, 0, 2, 2, height - 2);
ChamferedDistanceTransformSlow(out, width, height, width - 2, 2, 2, height - 2);
// Do inner region (no clamping)
int kernelCountFwd = sizeof(gKernelValsFwd) / sizeof(int);
int kernelCountRev = sizeof(gKernelValsRev) / sizeof(int);
int aStartX = 2;
int aStartY = 2;
int aEndX = width - 2;
int aEndY = height - 2;
for (int y = aStartY; y < aEndY; y++)
{
for (int x = aStartX; x < aEndX; x++)
{
for (int kernIdx = 0; kernIdx < kernelCountFwd; kernIdx++)
{
int cx = x + gKernelOfsFwd[kernIdx][0];
int cy = y + gKernelOfsFwd[kernIdx][1];
int aVal = out[cx+cy*width] + gKernelValsFwd[kernIdx];
if (aVal < (int) out[x+y*width])
out[x+y*width] = aVal;
}
}
}
for (int y = aEndY - 1; y >= aStartY; y--)
{
for (int x = aEndX - 1; x >= aStartX; x--)
{
for (int kernIdx = 0; kernIdx < kernelCountRev; kernIdx++)
{
int cx = x + gKernelOfsRev[kernIdx][0];
int cy = y + gKernelOfsRev[kernIdx][1];
int aVal = out[cx+cy*width] + gKernelValsRev[kernIdx];
if (aVal < (int) out[x+y*width])
out[x+y*width] = aVal;
}
}
}
}
#define IN_PIXEL(xval,yval) in[(xval) + (yval)*width]
#define IN_PIXEL_YX(yval,xval) in[(xval) + (yval)*width]
static void CreateNormalMap(uint32* in, uint32* out, int width, int height, float depth, Vector3* dotVector)
{
AutoPerf gPerf("ImgEffects - CreateNormalMap");
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// surrounding pixels
const uint32 topLeft = IN_PIXEL_YX(std::max(y - 1, 0), std::max(x - 1, 0));
const uint32 top = IN_PIXEL_YX(std::max(y - 1, 0), x);
const uint32 topRight = IN_PIXEL_YX(std::max(y - 1, 0), std::min(x + 1, width - 1));
const uint32 right = IN_PIXEL_YX(y, std::min(x + 1, width - 1));
const uint32 bottomRight = IN_PIXEL_YX(std::min(y + 1, height - 1), std::min(x + 1, width - 1));
const uint32 bottom = IN_PIXEL_YX(std::min(y + 1, height - 1), x);
const uint32 bottomLeft = IN_PIXEL_YX(std::min(y + 1, height - 1), std::max(x - 1, 0));
const uint32 left = IN_PIXEL_YX(y, std::max(x - 1, 0));
// their intensities
const float tl = (float)(topLeft);
const float t = (float)(top);
const float tr = (float)(topRight);
const float r = (float)(right);
const float br = (float)(bottomRight);
const float b = (float)(bottom);
const float bl = (float)(bottomLeft);
const float l = (float) (left);
// sobel filter
float dX = (tr + 2.0f * r + br) - (tl + 2.0f * l + bl);
float dY = (bl + 2.0f * b + br) - (tl + 2.0f * t + tr);
float dZ = depth * 10 * 0x100;
float len = sqrt(dX*dX + dY*dY + dZ*dZ);
dX /= len;
dY /= len;
dZ /= len;
if (dotVector != NULL)
{
Vector3 aNorm(dX, dY, dZ);
float dot = Vector3::Dot(*dotVector, aNorm);
out[x+y*width] = (uint32) (0xFF00 * dot) + 0x100000;
}
else
{
out[x+y*width] =
((int) (dX * 0x7F + 0x80 + 0.5f)) |
((int) (dY * 0x7F + 0x80 + 0.5f) << 8) |
((int) (dZ * 0xFF) << 16) |
0xFF000000;
}
}
}
}
static void AntiAliasIndices(uint32* in, int width, int height)
{
int w = width;
int h = height;
for (int y = 0; y < h - 1; y++)
{
for (int x = 0; x < w - 1; x++)
{
int val1 = in[x+y*w];
int val2 = in[(x+1)+y*w];
int val3 = in[x+(y+1)*w];
int val4 = in[(x+1)+(y+1)*w];
in[x+y*w] = ((val1 * 114) + (val2 * 57) + (val3 * 57) + (val4 * 28)) / 256;
}
}
}
static inline void BlendColors(PackedColor* destColor, PackedColor* underColor, PackedColor* overColor)
{
int a = 255 - overColor->a;
int oma = 255 - a;
destColor->r = ((underColor->r * a) + (overColor->r * oma)) / 255;
destColor->g = ((underColor->g * a) + (overColor->g * oma)) / 255;
destColor->b = ((underColor->b * a) + (overColor->b * oma)) / 255;
}
template <class CompareFunctor>
void ApplyBlendingRange(ImageData* origImage, ImageData* destImage, ImageData* checkImage, int rangeStart, int rangeEnd, CompareFunctor functor)
{
if ((rangeStart == 0) && (rangeEnd == 0xFF))
return;
int aStartX = std::max(checkImage->mX, origImage->mX);
int aStartY = std::max(checkImage->mY, origImage->mY);
int aEndX = std::min(checkImage->mX + checkImage->mWidth, origImage->mX + origImage->mWidth);
int aEndY = std::min(checkImage->mY + checkImage->mHeight, origImage->mY + origImage->mHeight);
if (rangeStart <= rangeEnd)
{
for (int y = aStartY; y < aEndY; y++)
{
PackedColor* aDestColor = (PackedColor*) (destImage->mBits + (aStartX - destImage->mX) + ((y - destImage->mY) * destImage->mWidth));
PackedColor* origColor = (PackedColor*) (origImage->mBits + (aStartX - origImage->mX) + ((y - origImage->mY) * origImage->mWidth));
PackedColor* checkColor = (PackedColor*) (checkImage->mBits + (aStartX - checkImage->mX) + ((y - checkImage->mY) * checkImage->mWidth));
for (int x = aStartX; x < aEndX; x++)
{
if ((functor(*checkColor) < rangeStart) || (functor(*checkColor) > rangeEnd))
*aDestColor = *origColor;
aDestColor++;
origColor++;
checkColor++;
}
}
}
else // Inverted range
{
for (int y = aStartY; y < aEndY; y++)
{
PackedColor* aDestColor = (PackedColor*) (destImage->mBits + (aStartX - destImage->mX) + ((y - destImage->mY) * destImage->mWidth));
PackedColor* origColor = (PackedColor*) (origImage->mBits + (aStartX - origImage->mX) + ((y - origImage->mY) * origImage->mWidth));
PackedColor* checkColor = (PackedColor*) (checkImage->mBits + (aStartX - checkImage->mX) + ((y - checkImage->mY) * checkImage->mWidth));
for (int x = aStartX; x < aEndX; x++)
{
if ((functor(*checkColor) < rangeStart) && (functor(*checkColor) > rangeEnd))
*aDestColor = *origColor;
aDestColor++;
origColor++;
checkColor++;
}
}
}
}
ImageData* ImageEffects::FlattenInto(ImageData* dest, PSDLayerInfo* srcLayer, ImageData* srcImage, ImageData* knockoutBottom, ImageData* insideImage)
{
AutoPerf gPerf("ImageEffects::FlattenInto");
bool hasComplexBlending = srcLayer->mBlendMode != 'norm';
BF_ASSERT((dest == NULL) || (!dest->mAlphaPremultiplied));
BF_ASSERT(!srcLayer->mAlphaPremultiplied);
ImageData* aSrcImage = srcImage;
if (srcLayer->mImageAdjustment != NULL)
{
if (((srcLayer->mLayerMask != NULL) && (srcLayer->mLayerMaskEnabled)) || (srcLayer->mVectorMask != NULL))
{
if (srcLayer->mWidth == 0)
{
aSrcImage = new ImageData();
if (srcLayer->mLayerMaskDefault == 255)
aSrcImage->CreateNew(0, 0, srcLayer->mPSDReader->mWidth, srcLayer->mPSDReader->mHeight, false);
else
aSrcImage->CreateNew(srcLayer->mLayerMaskX, srcLayer->mLayerMaskY, srcLayer->mLayerMaskWidth, srcLayer->mLayerMaskHeight, false);
aSrcImage->Fill(0xFFFFFFFF);
srcLayer->ApplyMask(aSrcImage);
}
}
else
{
// No mask - just directly apply the effects
ImageData* aDestImage = srcLayer->mImageAdjustment->CreateAdjustedImage(srcLayer, dest);
ImageEffectCtx aCtx;
aCtx.mOrigImage = NULL;
aCtx.mBlendX = dest->mX;
aCtx.mBlendY = dest->mY;
aCtx.mBlendWidth = dest->mWidth;
aCtx.mBlendHeight = dest->mHeight;
aCtx.mInnerImage = NULL;
aCtx.mOuterImage = NULL;
aCtx.mLayerInfo = srcLayer;
aCtx.mLayerImage = NULL;
return aDestImage;
}
}
int borderSize = 0;
bool hasInnerEffect = false;
bool hasOuterEffect = false;
bool needsOrigBits = false;
for (int i = 0; i < (int) mImageEffectVector.size(); i++)
{
BaseImageEffect* anEffect = mImageEffectVector[i];
borderSize = std::max(borderSize, anEffect->GetNeededBorderSize());
int mixType = anEffect->GetMixType();
hasInnerEffect |= (mixType == IMAGEMIX_INNER) || (mixType == IMAGEMIX_OVER);
hasOuterEffect |= (mixType == IMAGEMIX_OUTER) || (mixType == IMAGEMIX_OVER);
needsOrigBits |= anEffect->NeedsOrigBits(this);
hasComplexBlending |= anEffect->mBlendMode != 'Nrml';
}
bool hasBlendingRanges =
(srcLayer->mBlendingRangeSourceStart != 0x00000000) ||
(srcLayer->mBlendingRangeSourceEnd != 0xFFFFFFFF) ||
(srcLayer->mBlendingRangeDestStart != 0x00000000) ||
(srcLayer->mBlendingRangeDestEnd != 0xFFFFFFFF);
bool doPostOpacity = hasComplexBlending || hasOuterEffect;
uint8* aMask = NULL;
//hasInnerEffect = true;
if (insideImage != NULL)
hasInnerEffect = true;
hasInnerEffect = true;
hasOuterEffect = true;
needsOrigBits |= srcLayer->mLayerMaskHidesEffects;
if (!srcLayer->mTransparencyShapesLayer)
hasOuterEffect = false;
int minDestX = aSrcImage->mX - borderSize;
int maxDestX = aSrcImage->mX + aSrcImage->mWidth + borderSize;
int minDestY = aSrcImage->mY - borderSize;
int maxDestY = aSrcImage->mY + aSrcImage->mHeight + borderSize;
if (dest != NULL)
{
minDestX = std::min(dest->mX, minDestX);
maxDestX = std::max(dest->mX + dest->mWidth, maxDestX);
minDestY = std::min(dest->mY, minDestY);
maxDestY = std::max(dest->mY + dest->mHeight, maxDestY);
}
ImageEffectCtx aCtx;
aCtx.mOrigImage = NULL;
aCtx.mBlendX = minDestX;
aCtx.mBlendY = minDestY;
aCtx.mBlendWidth = maxDestX - minDestX;
aCtx.mBlendHeight = maxDestY - minDestY;
aCtx.mInnerImage = NULL;
aCtx.mOuterImage = NULL;
aCtx.mLayerInfo = srcLayer;
aCtx.mLayerImage = aSrcImage;
ImageData* aDestImage = NULL;
int minCopyX = aSrcImage->mX - borderSize;
int maxCopyX = aSrcImage->mX + aSrcImage->mWidth + borderSize;
int minCopyY = aSrcImage->mY - borderSize;
int maxCopyY = aSrcImage->mY + aSrcImage->mHeight + borderSize;
// This gets set if we need the 'dest' initialized with bits
bool needsDestInit = (!hasOuterEffect) || (!hasInnerEffect);
for (int i = 0; i < 4; i++)
{
bool needed = ((i == 0) && (hasInnerEffect)) || ((i == 1) && (hasOuterEffect)) ||
((i == 2) && (needsOrigBits)) || ((i == 3) && (needsDestInit));
if (!needed)
continue;
ImageData* effectImage = new ImageData();
effectImage->mX = aCtx.mBlendX;
effectImage->mY = aCtx.mBlendY;
effectImage->CreateNew(aCtx.mBlendWidth, aCtx.mBlendHeight);
if (dest != NULL)
{
int minX = dest->mX;
int maxX = dest->mX + dest->mWidth;
int minY = dest->mY;
int maxY = dest->mY + dest->mHeight;
if ((i == 0) && (insideImage != NULL))
{
CopyImageBits(effectImage, insideImage);
}
else if ((i == 0) && (srcLayer->mKnockout == 1))
{
// Shallow knockout
if (knockoutBottom != NULL)
{
for (int y = knockoutBottom->mY; y < knockoutBottom->mY + knockoutBottom->mHeight; y++)
{
for (int x = knockoutBottom->mX; x < knockoutBottom->mX + knockoutBottom->mWidth; x++)
{
effectImage->mBits[(x - effectImage->mX) + (y - effectImage->mY)*effectImage->mWidth] =
knockoutBottom->mBits[(x - knockoutBottom->mX) + (y - knockoutBottom->mY)*knockoutBottom->mWidth];
}
}
}
}
else if ((i == 0) && (srcLayer->mKnockout == 2))
{
// Deep knockout - leave blank
}
else
{
for (int y = minY; y < maxY; y++)
{
for (int x = minX; x < maxX; x++)
{
effectImage->mBits[(x - effectImage->mX) + (y - effectImage->mY)*effectImage->mWidth] =
dest->mBits[(x - dest->mX) + (y - dest->mY)*dest->mWidth];
}
}
}
}
if (i == 0)
aCtx.mInnerImage = effectImage;
else if (i == 1)
aCtx.mOuterImage = effectImage;
else if (i == 2)
aCtx.mOrigImage = effectImage;
else
aDestImage = effectImage;
}
if (aDestImage == NULL)
{
aDestImage = new ImageData();
aDestImage->mX = aCtx.mBlendX;
aDestImage->mY = aCtx.mBlendY;
aDestImage->CreateNew(aCtx.mBlendWidth, aCtx.mBlendHeight);
}
if (aCtx.mInnerImage != NULL)
{
if (insideImage != NULL)
{
//BlendImage(aCtx.mInnerImage, aSrcImage, insideImage->mX - aCtx.mBlendX, insideImage->mY - aCtx.mBlendY, 1.0f, 'Nrml');
}
else if (srcLayer->mImageAdjustment != NULL)
{
srcLayer->mImageAdjustment->ApplyImageAdjustment(srcLayer, aCtx.mInnerImage);
CrossfadeImage(dest, aCtx.mInnerImage, srcLayer->mFillOpacity / 255.0f);
}
else
{
int blendMode = srcLayer->mBlendMode;
float anAlpha = srcLayer->mFillOpacity / 255.0f;
if (srcLayer->mBlendInteriorEffectsAsGroup)
anAlpha = 1.0f;
if (!doPostOpacity)
anAlpha *= (srcLayer->mOpacity / 255.0f);
BlendImage(aCtx.mInnerImage, aSrcImage, aSrcImage->mX - aCtx.mBlendX, aSrcImage->mY - aCtx.mBlendY, anAlpha, blendMode, true);
}
}
else
{
float anAlpha = srcLayer->mFillOpacity / 255.0f;
if (srcLayer->mBlendInteriorEffectsAsGroup)
anAlpha = 1.0f;
if (!doPostOpacity)
anAlpha *= (srcLayer->mOpacity / 255.0f);
BlendImage(aDestImage, aSrcImage, aSrcImage->mX - aCtx.mBlendX, aSrcImage->mY - aCtx.mBlendY, anAlpha, srcLayer->mBlendMode, aCtx.mOuterImage != NULL);
}
if ((srcLayer->mLayerMaskHidesEffects) && (srcLayer->mLayerMaskEnabled))
{
aMask = new uint8[aCtx.mBlendWidth * aCtx.mBlendHeight];
memset(aMask, srcLayer->mLayerMaskDefault, aCtx.mBlendWidth * aCtx.mBlendHeight);
ImageData* anImage = aCtx.mOrigImage;
int maskStartX = std::max(srcLayer->mLayerMaskX, anImage->mX);
int maskStartY = std::max(srcLayer->mLayerMaskY, anImage->mY);
int maskEndX = std::min(srcLayer->mLayerMaskX + srcLayer->mLayerMaskWidth, anImage->mX + anImage->mWidth);
int maskEndY = std::min(srcLayer->mLayerMaskY + srcLayer->mLayerMaskHeight, anImage->mY + anImage->mHeight);
for (int y = maskStartY; y < maskEndY; y++)
{
uint8* maskSrc = srcLayer->mLayerMask + (y - srcLayer->mLayerMaskY)*srcLayer->mLayerMaskWidth;
uint8* maskDest = aMask + (y - anImage->mY)*anImage->mWidth;
for (int x = maskStartX; x < maskEndX; x++)
maskDest[x - anImage->mX] = maskSrc[x - srcLayer->mLayerMaskX];
}
}
for (int i = 0; i < (int) mImageEffectVector.size(); i++)
{
BaseImageEffect* anEffect = mImageEffectVector[i];
if (!anEffect->mInitialized)
{
anEffect->Init();
anEffect->mInitialized = true;
}
AutoPerf gPerf("ImageEffects::FlattenInto ImageEffect");
anEffect->Apply(&aCtx);
}
// Did we create a temporary source image (for adjustment layers, primary)
if (aSrcImage != srcImage)
{
//delete aSrcImage;
//aSrcImage = srcImage;
// Add back in the destination alpha that was factored out during adjustment image creation
//MultiplyImageAlpha(aSrcImage, dest);
}
//aCtx.mOuterImage->mBits[0] = 0xFFFFFFFF;
//aCtx.mOuterImage->mBits[aCtx.mBlendWidth*aCtx.mBlendHeight-1] = 0xFFFFFFFF;
if (srcLayer->mBlendInteriorEffectsAsGroup)
{
for (int y = 0; y < aSrcImage->mHeight; y++)
{
PackedColor* srcColor = (PackedColor*) (aSrcImage->mBits + y*aSrcImage->mWidth);
for (int x = 0; x < aSrcImage->mWidth; x++)
{
srcColor->a = (srcColor->a * srcLayer->mFillOpacity) / 255;
srcColor++;
}
}
}
if (aCtx.mOuterImage != NULL)
{
//TODO: Do this right...
/*for (int x = 0; x < aSrcImage->mWidth; x++)
{
for (int y = 0; y < borderSize; y++)
aDestImage->mBits[x+y*aCtx.mBlendWidth] = aCtx.mOuterImage->mBits[x+y*aCtx.mBlendWidth];
for (int y = aCtx.mBlendHeight-borderSize; y < aCtx.mBlendHeight; y++)
aDestImage->mBits[x+y*aCtx.mBlendWidth] = aCtx.mOuterImage->mBits[x+y*aCtx.mBlendWidth];
}
for (int y = borderSize; y < aCtx.mBlendHeight-borderSize; y++)
{
for (int x = 0; x < borderSize; x++)
aDestImage->mBits[x+y*aCtx.mBlendWidth] = aCtx.mOuterImage->mBits[x+y*aCtx.mBlendWidth];
for (int x = aCtx.mBlendWidth-borderSize; x < aCtx.mBlendWidth; x++)
aDestImage->mBits[x+y*aCtx.mBlendWidth] = aCtx.mOuterImage->mBits[x+y*aCtx.mBlendWidth];
}*/
for (int y = aCtx.mBlendY; y < aCtx.mBlendY + aCtx.mBlendHeight; y++)
{
for (int x = aCtx.mBlendX; x < aCtx.mBlendX + aCtx.mBlendWidth; x++)
{
aDestImage->mBits[(x-aCtx.mBlendX)+(y-aCtx.mBlendY)*aCtx.mBlendWidth] =
aCtx.mOuterImage->mBits[(x-aCtx.mBlendX)+(y-aCtx.mBlendY)*aCtx.mBlendWidth];
}
}
/*int aSize = aCtx.mBlendWidth*aCtx.mBlendHeight;
for (int i = 0; i < aSize; i++)
aDestImage->mBits[i] = aCtx.mOuterImage->mBits[i];*/
if (aCtx.mInnerImage == NULL)
{
for (int y = 0; y < aSrcImage->mHeight; y++)
{
PackedColor* srcColor = (PackedColor*) (aSrcImage->mBits + y*aSrcImage->mWidth);
PackedColor* aDestColor = (PackedColor*) (aDestImage->mBits + borderSize + (y + borderSize) * aDestImage->mWidth);
PackedColor* effectOutsideColor = (PackedColor*) (aCtx.mOuterImage->mBits + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
for (int x = 0; x < aSrcImage->mWidth; x++)
{
int a = srcColor->a;
int oma = 255 - a;
int newDestAlpha = ((aDestColor->a * a) + (effectOutsideColor->a * oma)) / 255;
if (newDestAlpha != 0)
{
int ca = (255 * aDestColor->a * a) / (aDestColor->a * a + effectOutsideColor->a * oma);
int coma = 255 - ca;
aDestColor->a = newDestAlpha;
aDestColor->r = ((aDestColor->r * ca) + (effectOutsideColor->r * coma)) / 255;
aDestColor->g = ((aDestColor->g * ca) + (effectOutsideColor->g * coma)) / 255;
aDestColor->b = ((aDestColor->b * ca) + (effectOutsideColor->b * coma)) / 255;
}
srcColor++;
aDestColor++;
effectOutsideColor++;
}
}
}
}
if (aCtx.mInnerImage != NULL)
{
if (aCtx.mOuterImage != NULL)
{
if (aMask != NULL)
{
for (int y = 0; y < aSrcImage->mHeight; y++)
{
uint8* maskData = (aMask + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
PackedColor* srcColor = (PackedColor*) (aSrcImage->mBits + y*aSrcImage->mWidth);
PackedColor* aDestColor = (PackedColor*) (aDestImage->mBits + borderSize + (y + borderSize) * aDestImage->mWidth);
PackedColor* effectInsideColor = (PackedColor*) (aCtx.mInnerImage->mBits + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
PackedColor* effectOutsideColor = (PackedColor*) (aCtx.mOuterImage->mBits + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
PackedColor* origColor = (PackedColor*) (aCtx.mOrigImage->mBits + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
for (int x = 0; x < aSrcImage->mWidth; x++)
{
/*if ((aCtx.mBlendX + x == 60) && (aCtx.mBlendY + y == 60))
{
_asm nop;
}*/
uint32 insidePct = srcColor->a * (*maskData);
uint32 outsidePct = (255 - srcColor->a) * (*maskData);
uint32 origPct = 255 * (255 - *maskData);
uint32 insideContrib = effectInsideColor->a * insidePct;
uint32 outsideContrib = effectOutsideColor->a * outsidePct;
uint32 origContrib = origColor->a * origPct;
uint32 totalContrib = insideContrib + outsideContrib + origContrib;
int newDestAlpha = (insideContrib + outsideContrib + origContrib) / 255 / 255;
if (newDestAlpha != 0)
{
int ma = *maskData;
int moma = 255 - ma;
aDestColor->r = ((insideContrib * effectInsideColor->r) + (outsideContrib * effectOutsideColor->r) + (origContrib * origColor->r)) / totalContrib;
aDestColor->g = ((insideContrib * effectInsideColor->g) + (outsideContrib * effectOutsideColor->g) + (origContrib * origColor->g)) / totalContrib;
aDestColor->b = ((insideContrib * effectInsideColor->b) + (outsideContrib * effectOutsideColor->b) + (origContrib * origColor->b)) / totalContrib;
}
aDestColor->a = newDestAlpha;
origColor++;
maskData++;
srcColor++;
aDestColor++;
effectInsideColor++;
effectOutsideColor++;
}
}
}
else
{
for (int y = 0; y < aSrcImage->mHeight; y++)
{
int ctxOffset = (aSrcImage->mX - aCtx.mBlendX) + (aSrcImage->mY - aCtx.mBlendY + y)*aCtx.mBlendWidth;
PackedColor* srcColor = (PackedColor*) (aSrcImage->mBits + y*aSrcImage->mWidth);
PackedColor* aDestColor = (PackedColor*) (aDestImage->mBits + ctxOffset);
PackedColor* effectInsideColor = (PackedColor*) (aCtx.mInnerImage->mBits + ctxOffset);
PackedColor* effectOutsideColor = (PackedColor*) (aCtx.mOuterImage->mBits + ctxOffset);
for (int x = 0; x < aSrcImage->mWidth; x++)
{
if (srcColor->a == 0)
{
*aDestColor = *effectOutsideColor;
}
else
{
int a = srcColor->a;
int oma = 255 - a;
int newDestAlpha = ((effectInsideColor->a * a) + (effectOutsideColor->a * oma)) / 255;
if (newDestAlpha != 0)
{
int ca = (255 * effectInsideColor->a * a) / (effectInsideColor->a * a + effectOutsideColor->a * oma);
int coma = 255 - ca;
aDestColor->a = newDestAlpha;
aDestColor->r = ((effectInsideColor->r * ca) + (effectOutsideColor->r * coma)) / 255;
aDestColor->g = ((effectInsideColor->g * ca) + (effectOutsideColor->g * coma)) / 255;
aDestColor->b = ((effectInsideColor->b * ca) + (effectOutsideColor->b * coma)) / 255;
}
else
{
*aDestColor = *effectOutsideColor;
}
}
srcColor++;
aDestColor++;
effectInsideColor++;
effectOutsideColor++;
}
}
}
}
else
{
for (int y = 0; y < aSrcImage->mHeight; y++)
{
PackedColor* srcColor = (PackedColor*) (aSrcImage->mBits + y*aSrcImage->mWidth);
PackedColor* aDestColor = (PackedColor*) (aDestImage->mBits + borderSize + (y + borderSize) * aDestImage->mWidth);
PackedColor* effectInsideColor = (PackedColor*) (aCtx.mInnerImage->mBits + borderSize + ((y + borderSize) * aCtx.mBlendWidth));
for (int x = 0; x < aSrcImage->mWidth; x++)
{
//if (srcColor->a != 0)
{
int a = 255 - srcColor->a;
if (!srcLayer->mTransparencyShapesLayer)
a = 0;
int oma = 255 - a;
int newDestAlpha = ((aDestColor->a * a) + (effectInsideColor->a * oma)) / 255;
if (newDestAlpha != 0)
{
int ca = (255 * aDestColor->a * a) / (aDestColor->a * a + effectInsideColor->a * oma);
int coma = 255 - ca;
aDestColor->a = newDestAlpha;
aDestColor->r = ((aDestColor->r * ca) + (effectInsideColor->r * coma)) / 255;
aDestColor->g = ((aDestColor->g * ca) + (effectInsideColor->g * coma)) / 255;
aDestColor->b = ((aDestColor->b * ca) + (effectInsideColor->b * coma)) / 255;
}
}
srcColor++;
aDestColor++;
effectInsideColor++;
}
}
}
}
/*if (srcLayer->mImageAdjustment != NULL)
{
// Just use the "inner image" as the dest since there's no mask to apply
delete aDestImage;
aDestImage = aCtx.mInnerImage;
aCtx.mInnerImage = NULL;
}*/
if (hasBlendingRanges)
{
int aStartX = std::max(aSrcImage->mX, dest->mX);
int aStartY = std::max(aSrcImage->mY, dest->mY);
int aEndX = std::min(aSrcImage->mX + aSrcImage->mWidth, dest->mX + dest->mWidth);
int aEndY = std::min(aSrcImage->mY + aSrcImage->mHeight, dest->mY + dest->mHeight);
PackedColor* blendingRangeSourceStart = (PackedColor*) &(srcLayer->mBlendingRangeSourceStart);
PackedColor* blendingRangeSourceEnd = (PackedColor*) &(srcLayer->mBlendingRangeSourceEnd);
PackedColor* blendingRangeDestStart = (PackedColor*) &(srcLayer->mBlendingRangeDestStart);
PackedColor* blendingRangeDestEnd = (PackedColor*) &(srcLayer->mBlendingRangeDestEnd);
ApplyBlendingRange(dest, aDestImage, aSrcImage, blendingRangeSourceStart->r, blendingRangeSourceEnd->r, PackedColorGetR());
ApplyBlendingRange(dest, aDestImage, dest, blendingRangeDestStart->r, blendingRangeDestEnd->r, PackedColorGetR());
ApplyBlendingRange(dest, aDestImage, aSrcImage, blendingRangeSourceStart->g, blendingRangeSourceEnd->g, PackedColorGetG());
ApplyBlendingRange(dest, aDestImage, dest, blendingRangeDestStart->g, blendingRangeDestEnd->g, PackedColorGetG());
ApplyBlendingRange(dest, aDestImage, aSrcImage, blendingRangeSourceStart->b, blendingRangeSourceEnd->b, PackedColorGetB());
ApplyBlendingRange(dest, aDestImage, dest, blendingRangeDestStart->b, blendingRangeDestEnd->b, PackedColorGetB());
ApplyBlendingRange(dest, aDestImage, aSrcImage, blendingRangeSourceStart->a, blendingRangeSourceEnd->a, PackedColorGetGray());
ApplyBlendingRange(dest, aDestImage, dest, blendingRangeDestStart->a, blendingRangeDestEnd->a, PackedColorGetGray());
}
if (srcLayer->mChannelMask != 0xFFFFFFFF)
{
for (int i = 0; i < aDestImage->mWidth*aDestImage->mHeight; i++)
aDestImage->mBits[i] &= srcLayer->mChannelMask;
int aStartX = dest->mX;
int aStartY = dest->mY;
int aEndX = dest->mX + dest->mWidth;
int aEndY = dest->mY + dest->mHeight;
for (int y = aStartY; y < aEndY; y++)
{
uint32* aDestColor = aDestImage->mBits + (aStartX - aDestImage->mX) + ((y - aDestImage->mY) * aDestImage->mWidth);
uint32* origColor = dest->mBits + (aStartX - dest->mX) + ((y - dest->mY) * dest->mWidth);
for (int x = aStartX; x < aEndX; x++)
{
*aDestColor |= (*origColor & ~srcLayer->mChannelMask);
aDestColor++;
origColor++;
}
}
}
if ((srcLayer->mOpacity != 255) && (doPostOpacity))
CrossfadeImage(dest, aDestImage, srcLayer->mOpacity / 255.0f);
if (aSrcImage != srcImage)
{
// If we have a temporary source image (for an adjustment layer, for example)
delete aSrcImage;
}
delete aMask;
return aDestImage;
}
void ImageEffects::AddEffect(BaseImageEffect* effect)
{
mImageEffectVector.push_back(effect);
}
//
BaseImageEffect::BaseImageEffect()
{
mContourData = NULL;
mGradientData = NULL;
mInitialized = false;
}
BaseImageEffect::~BaseImageEffect()
{
delete mContourData;
delete mGradientData;
}
void BaseImageEffect::Init()
{
}
void BaseImageEffect::Apply(ImageEffectCtx* ctx)
{
ImageData* effectImage = new ImageData();
effectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
effectImage->mX = ctx->mBlendX;
effectImage->mY = ctx->mBlendY;
Apply(ctx->mLayerInfo, ctx->mLayerImage, effectImage);
int mixType = GetMixType();
if ((mixType == IMAGEMIX_INNER) || (mixType == IMAGEMIX_OVER))
BlendImage(ctx->mInnerImage, effectImage, 0, 0, (float) mOpacity / 100.0f, mBlendMode);
if ((mixType == IMAGEMIX_OUTER) || (mixType == IMAGEMIX_OVER))
BlendImage(ctx->mOuterImage, effectImage, 0, 0, (float) mOpacity / 100.0f, mBlendMode);
delete effectImage;
}
int BaseImageEffect::GetMixType() // Otherwise interior
{
return IMAGEMIX_INNER;
}
int BaseImageEffect::GetNeededBorderSize()
{
return 0;
}
bool BaseImageEffect::NeedsOrigBits(ImageEffects* effects)
{
return false;
}
void ImageShadowEffect::Init()
{
mContourData = CreateContourDataTable(&mContour, 1.0f);
}
void ImageShadowEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int w = destImageData->mWidth;
int h = destImageData->mHeight;
int aSize = w*h;
float spread = (float) mSpread / 100.0f;
float spreadSize = (float) (int) (mSize * spread + 0.5f);
int distTransSize = 0;
ImageData* tempImage = new ImageData();
tempImage->CreateNew(w, h);
tempImage->mX = destImageData->mX;
tempImage->mY = destImageData->mY;
float aRadius = (float) mSize;
float aRadiusLeft = (float) mSize;
bool isInside = GetMixType() == IMAGEMIX_INNER;
if (spreadSize != 0)
{
// Do distance transform first to "grow" area
float distLenOffset = spreadSize / 20.0f;
float distLen = spreadSize - 1.0f + distLenOffset;
ChamferedDistanceTransformInit(imageData, tempImage, isInside);
ChamferedDistanceTransform(tempImage->mBits, w, h);
for (int i = 0; i < aSize; i++)
{
float dist = tempImage->mBits[i] / 256.0f;
dist -= spreadSize;
if (dist < 0)
tempImage->mBits[i] = 0xFF00;
else if (dist < 1.0f)
tempImage->mBits[i] = BFClamp((int) ((1.0f - dist) * 255.0f * 256.0f), 0, 0xFF00);
else
tempImage->mBits[i] = 0;
}
aRadiusLeft -= spreadSize;
}
else
{
SoftBlurInit(imageData, tempImage, isInside);
}
float radiusOffset = 1.2f;
float blurRadius = aRadiusLeft - radiusOffset;
if (aRadiusLeft <= 2)
blurRadius = 0.5f;
if (aRadiusLeft != 0)
SoftBlur(tempImage->mBits, w, h, blurRadius, 0);
bool doFrontTaper = (!isInside) &&
(((mContour.GetVal(0) != 0) || (mNoise != 0)));
if ((mAntiAliased) && (!mContour.IsDefault()) && (!doFrontTaper))
AntiAliasIndices(tempImage->mBits, w, h);
memset(destImageData->mBits, 0, w*h*sizeof(uint32));
float noise = (float) mNoise / 100.0f;
float angle = mUseGlobalLight ? (float) layerInfo->mPSDReader->mGlobalAngle : (float) mLocalAngle;
angle *= BF_PI / 180.0f;
int ofsX = (int) (BFRound(-cos(angle) * (float) mDistance));
int ofsY = (int) (BFRound(sin(angle) * (float) mDistance));
int maxX = std::min(w, w + ofsX);
int maxY = std::min(h, h + ofsY);
for (int y = std::max(0, ofsY); y < maxY; y++)
{
for (int x = std::max(0, ofsX); x < maxX; x++)
{
int i = x+y*w;
PackedColor* aDestColor = (PackedColor*) (destImageData->mBits + i);
int blurVal = tempImage->mBits[(x-ofsX)+(y-ofsY)*w];
int idx = (blurVal * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
BF_ASSERT(idx >= 0);
BF_ASSERT(idx < CONTOUR_DATA_SIZE);
int gradientIdx = mContourData[idx];
BF_ASSERT(gradientIdx >= 0);
BF_ASSERT(gradientIdx < GRADIENT_DATA_SIZE);
destImageData->mBits[i] = ((gradientIdx * 255 / (GRADIENT_DATA_SIZE - 1)) << 24) | (mColor & 0x00FFFFFF);
//TODO: Apply 'front taper?'
if (mNoise > 0)
{
if (rand() % 5 < 4)
{
float pixNoise = 256 * noise;
if (aDestColor->a > 0)
{
float randPct = ((Rand() % 10000) / 5000.0f) - 1.0f;
aDestColor->a = BFClamp(aDestColor->a + (int) (randPct * pixNoise + 0.5f), 0, 255);
}
}
}
if (doFrontTaper)
{
float minAlpha = std::min(1.0f, (idx / 4095.0f) * 8.4f);
aDestColor->a = std::min((int) aDestColor->a, (int) (minAlpha * 255));
}
/*if (i % 7 == 0)
{
aDestColor->a = 255;
aDestColor->r = 255;
}*/
}
}
delete tempImage;
}
int ImageShadowEffect::GetNeededBorderSize()
{
return (int) mSize + (int) mDistance;
}
int ImageDropShadowEffect::GetMixType()
{
return IMAGEMIX_OUTER;
}
void ImageGlowEffect::Init()
{
CreateContourAndGradientData();
}
///
void ImageGlowEffect::CreateContourAndGradientData()
{
mContourData = CreateContourDataTable(&mContour, (float) mRange / 100.0f);
mGradientData = CreateGradientDataTable(mColorGradient);
}
int ImageGlowEffect::GetNeededBorderSize()
{
//TODO: Empirically we only need (mSize / 2) for soft inner glow...
return (int) mSize;
}
void ChokedPixelTransform(ImageData* src, ImageData* dest, float radius, float chokePct, bool invert, bool soften = false)
{
int w = dest->mWidth;
int h = dest->mHeight;
uint32* aDest = dest->mBits;
ChamferedDistanceTransformInit(src, dest, invert, soften ? (int) radius : 0);
ChamferedDistanceTransform(dest->mBits, w, h);
int aSize = w*h;
int chokePixels = (int) (radius * chokePct + 0.5f);
bool fullChoke = chokePixels == radius;
uint32* tempBuffer = new uint32[w*h];
uint32* exterior = dest->mBits;
uint32* anInterior = tempBuffer;
float rad = radius + 0.5f - chokePixels;
int inf = (int) (radius + 2) * 256;
if (soften)
{
uint32* in = src->mBits;
int iw = src->mWidth;
int ih = src->mHeight;
int ox = src->mX - dest->mX;
int oy = src->mY - dest->mY;
for (int y = 0; y < ih; y++)
{
for (int x = 0; x < iw; x++)
{
int anAlpha = in[x+y*iw]>>24;
int i = (x+ox)+(y+oy)*w;
float dist = aDest[i] / 256.0f;
dist -= chokePixels + 0.001f;
if (dist < 0)
anInterior[i] = 0;
else if (dist < 1.0f)
anInterior[i] = (int) (dist * 256);
else
anInterior[i] = (int) (dist * 256);
if (anAlpha <= 128)
exterior[i] = (int) ((128.0f - anAlpha) * (radius * 2 + 2) + 0.5f);
else
exterior[i] = 0;
}
}
inf = (int) (128.0f * (radius * 2 + 2) + 0.5f);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < oy; y++)
exterior[x+y*w] = inf;
for (int y = oy+ih; y < h; y++)
exterior[x+y*w] = inf;
}
for (int y = oy; y < oy+ih; y++)
{
for (int x = 0; x < ox; x++)
exterior[x+y*w] = inf;
for (int x = ox+iw; x < w; x++)
exterior[x+y*w] = inf;
}
}
else
{
for (int i = 0; i < aSize; i++)
{
float dist = aDest[i] / 256.0f;
dist -= chokePixels + 0.001f;
if (dist < 0)
{
exterior[i] = inf;
anInterior[i] = 0;
}
else if (dist < 1.0f)
{
exterior[i] = BFClamp((int) ((1.0f - dist) * 256.0f), 0, 0xFF);
anInterior[i] = (int) (dist * 256);
}
else
{
exterior[i] = 0;
anInterior[i] = (int) (dist * 256);
}
}
}
ChamferedDistanceTransform(exterior, w, h);
float interiorDiv = (rad + 0.5f);
float interiorOffset = 0;
float exteriorOffset = -255;
float exteriorDivide = (radius + 1.0f - chokePixels);
if (soften)
{
exteriorOffset = 0;
}
if (fullChoke)
{
for (int i = 0; i < aSize; i++)
aDest[i] = std::min((uint32)0xFF00, exterior[i] * 256);
}
else
{
for (int i = 0; i < aSize; i++)
{
uint32 alphaVal = (uint32) (std::max(0.0f, (int) exterior[i] + exteriorOffset ) * 256 / exteriorDivide);
alphaVal = std::min((uint32) 0xFF00, (uint32) (alphaVal * 0.5f + 0x7F00));
uint32 alphaVal2 = (uint32) (std::max(0.0f, (int) anInterior[i] + interiorOffset) * 256 / interiorDiv);
if (alphaVal <= 0x7F80)
alphaVal = (uint32) std::min((uint32) 0xFF00, (uint32) std::max(0.0f, (0xFF00 - (int) alphaVal2) * 0.5f));
aDest[i] = alphaVal;
BF_ASSERT(alphaVal <= 0xFF00);
}
}
delete [] tempBuffer;
}
void ImageOuterGlowEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int sw = imageData->mWidth;
int sh = imageData->mHeight;
int w = destImageData->mWidth;
int h = destImageData->mHeight;
ImageData* newImage = destImageData;
float radius = (float) mSize;
int aSize = w*h;
float spread = (float) mSpread / 100.0f;
float spreadSize = (float) (int) (mSize * spread + 0.5f);
int distTransSize = 0;
if (mTechnique == 'PrBL')
{
ChokedPixelTransform(imageData, newImage, radius, spread, false);
}
else if (mTechnique == 'SfBL')
{
radius = std::max(radius, 2.0f); // Radius of '1' gets bumped up to 2
float aRadiusLeft = radius;
if (spreadSize != 0)
{
// Do distance transform first to "grow" area
float distLenOffset = spreadSize / 20.0f;
float distLen = spreadSize - 1.0f + distLenOffset;
ChamferedDistanceTransformInit(imageData, newImage, false);
ChamferedDistanceTransform(newImage->mBits, w, h);
for (int i = 0; i < aSize; i++)
{
float dist = newImage->mBits[i] / 256.0f;
dist -= spreadSize;
if (dist < 0)
newImage->mBits[i] = 0xFF00;
else if (dist < 1.0f)
newImage->mBits[i] = BFClamp((int) ((1.0f - dist) * 255.0f * 256.0f), 0, 0xFF00);
else
newImage->mBits[i] = 0;
}
aRadiusLeft -= spreadSize;
}
else
{
SoftBlurInit(imageData, newImage, false);
}
float radiusOffset = 1.2f;
float blurRadius = aRadiusLeft - radiusOffset;
if (aRadiusLeft <= 2)
blurRadius = 0.5f;
if ((aRadiusLeft != 0) && (mSize != 0))
SoftBlur(newImage->mBits, w, h, blurRadius, 0);
}
//OutputDebugStrF("Contour Time: %d\r\n", timeGetTime() - tickStart);
bool doFrontTaper =
((mColorGradient[3].GetVal((1.0f - mContour.GetVal(0) / 255.0f) * mColorGradient[3].mXSize) != 0) || (mNoise != 0));
if ((mAntiAliased) && (!mContour.IsDefault()) && (!doFrontTaper))
AntiAliasIndices(newImage->mBits, w, h);
float noise = (float) mNoise / 100.0f;
float jitter = (float) mJitter / 100.0f;
for (int i = 0; i < aSize; i++)
{
PackedColor* srcColor = (PackedColor*) (imageData->mBits + i);
PackedColor* aDestColor = (PackedColor*) (newImage->mBits + i);
int blurVal = newImage->mBits[i];
int idx = (blurVal * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
BF_ASSERT(idx >= 0);
BF_ASSERT(idx < CONTOUR_DATA_SIZE);
int gradientIdx = mContourData[idx];
if ((jitter > 0) && (mHasGradient))
{
if (rand() % 5 < 4)
{
float idxJitter = GRADIENT_DATA_SIZE * jitter;
if (idx != 0)
{
float randPct = ((Rand() % 10000) / 5000.0f) - 1.0f;
gradientIdx = BFClamp(gradientIdx + (int) (randPct * idxJitter + 0.5f), 0, CONTOUR_DATA_SIZE - 1);
}
}
}
BF_ASSERT(gradientIdx >= 0);
BF_ASSERT(gradientIdx < GRADIENT_DATA_SIZE);
newImage->mBits[i] = mGradientData[gradientIdx];
if (mNoise > 0)
{
if (rand() % 5 < 4)
{
float pixNoise = 256 * noise;
if (aDestColor->a > 0)
{
float randPct = ((Rand() % 10000) / 5000.0f) - 1.0f;
aDestColor->a = BFClamp(aDestColor->a + (int) (randPct * pixNoise + 0.5f), 0, 255);
}
}
}
if (doFrontTaper)
{
float minAlpha = std::min(1.0f, (idx / 4095.0f) * 8.4f);
aDestColor->a = std::min(aDestColor->a, (uint8) (minAlpha * 255));
}
}
}
int ImageOuterGlowEffect::GetMixType()
{
return IMAGEMIX_OUTER;
}
///
void ImageInnerGlowEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int sw = destImageData->mWidth;
int sh = destImageData->mHeight;
int w = sw;
int h = sh;
ImageData* newImage = destImageData;
float radius = (float) mSize;
int aSize = w*h;
float choke = (float) mChoke / 100.0f;
float chokePixels = (float) (int) (choke * mSize + 0.5f);
bool fullChoke = chokePixels == mSize;
float rad = radius + 0.5f - chokePixels;
float inf = (radius + 1.5f) * 256;
if (mTechnique == 'PrBL')
{
ChokedPixelTransform(imageData, newImage, radius, choke, true);
}
else if (mTechnique == 'SfBL')
{
// Soft blur
float aRadiusLeft = radius;
if (chokePixels > 0)
{
aRadiusLeft -= chokePixels;
ChamferedDistanceTransformInit(imageData, newImage, true);
ChamferedDistanceTransform(newImage->mBits, w, h);
for (int i = 0; i < aSize; i++)
{
float dist = newImage->mBits[i] / 256.0f;
dist -= chokePixels + 0.001f;
/*if (((int) (imageData->mBits[i] >> 24) == 255) && (dist >= 0))
newImage->mBits[i] = std::max(0, 256 - (int) (dist * 256)) * 256;
else
newImage->mBits[i] = 255*256; */
if (dist < 0)
newImage->mBits[i] = 0xFF00;
else if (dist < 1.0f)
newImage->mBits[i] = BFClamp((int) ((1.0f - dist) * 255.0f * 256.0f), 0, 0xFF00);
else
newImage->mBits[i] = 0;
}
}
else
{
SoftBlurInit(imageData, newImage, true);
}
SoftBlur(newImage->mBits, w, h, GetSoftBlurRadius(radius, aRadiusLeft), 0xFF00);
}
//OutputDebugStrF("Contour Time: %d\r\n", timeGetTime() - tickStart);
if ((mAntiAliased) && (!mContour.IsDefault()))
AntiAliasIndices(newImage->mBits, w, h);
float noise = (float) mNoise / 100.0f;
float jitter = (float) mJitter / 100.0f;
for (int i = 0; i < aSize; i++)
{
PackedColor* aDestColor = (PackedColor*) (newImage->mBits + i);
int blurVal = newImage->mBits[i];
int idx = (blurVal * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
BF_ASSERT(idx >= 0);
BF_ASSERT(idx < CONTOUR_DATA_SIZE);
int gradientIdx = mContourData[idx];
if ((jitter > 0) && (mHasGradient))
{
if (rand() % 5 < 4)
{
float idxJitter = GRADIENT_DATA_SIZE * jitter;
if (idx != 0)
{
float randPct = ((Rand() % 10000) / 5000.0f) - 1.0f;
gradientIdx = BFClamp(gradientIdx + (int) (randPct * idxJitter + 0.5f), 0, CONTOUR_DATA_SIZE - 1);
}
}
}
BF_ASSERT(gradientIdx >= 0);
BF_ASSERT(gradientIdx < GRADIENT_DATA_SIZE);
newImage->mBits[i] = mGradientData[gradientIdx];
if (mIsCenter)
aDestColor->a = std::max(0, 255 - aDestColor->a);
if (mNoise > 0)
{
if (rand() % 5 < 4)
{
float pixNoise = 256 * noise;
if (aDestColor->a > 0)
{
float randPct = ((Rand() % 10000) / 5000.0f) - 1.0f;
aDestColor->a = BFClamp(aDestColor->a + (int) (randPct * pixNoise + 0.5f), 0, 255);
}
}
}
}
}
///
ImageBevelEffect::ImageBevelEffect()
{
mGlossContourData = NULL;
}
ImageBevelEffect::~ImageBevelEffect()
{
delete mGlossContourData;
}
void ImageBevelEffect::Init()
{
mGlossContourData = CreateContourDataTable(&mGlossContour);
if (mUseContour)
{
float aRange = (float) (mBevelContourRange / 100.0f);
mBevelContourData = new int32[CONTOUR_DATA_SIZE];
int minVal = 0;
for (int i = 0; i < CONTOUR_DATA_SIZE; i++)
{
float aX = i / (float) (CONTOUR_DATA_SIZE - 1);
aX = (aX - (1.0f - aRange)) / aRange;
float yVal = mBevelContour.GetVal(aX * 255.0f) / 255.0f;
int aVal = (int) (yVal * 0xFF00 + 0.5f);
minVal = std::min(minVal, aVal);
mBevelContourData[i] = aVal;
}
for (int i = 0; i < CONTOUR_DATA_SIZE; i++)
mBevelContourData[i] -= minVal;
}
}
void ImageBevelEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
}
int ImageBevelEffect::GetMixType()
{
if (mStyle == 'OtrB') // Outer bevel
return IMAGEMIX_OUTER;
if (mStyle == 'Embs') // Emboss
return IMAGEMIX_OVER;
if (mStyle == 'PlEb') // Pillow emboss
return IMAGEMIX_OVER;
return IMAGEMIX_INNER;
}
void ImageBevelEffect::Apply(int pass, int style, PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* hiliteImage, ImageData* shadowImage)
{
int w = hiliteImage->mWidth;
int h = hiliteImage->mHeight;
int aSize = w*h;
uint32* normalMap = new uint32[aSize];
PSDLayerInfo* aLayerInfo = layerInfo;
ImageData* anImageData = imageData;
ImageData* newImage = hiliteImage;
float aDepth = (float) mDepth / 100.0f;
float aRadius = (float) mSize;
float angle = mUseGlobalLight ? (float) aLayerInfo->mPSDReader->mGlobalAngle : (float) mLocalAngle;
float altitude = mUseGlobalLight ? (float) aLayerInfo->mPSDReader->mGlobalAltitude : (float) mLocalAltitude;
float lightAngle = angle * BF_PI / 180.0f;
float lightElevation = altitude * BF_PI / 180.0f;
bool doFlip = !mDirectionUp;
if (pass == 1)
doFlip = !doFlip;
if (doFlip)
lightAngle = lightAngle + BF_PI;
Vector3 shadowVec(cosf((float) lightElevation) * -cosf(lightAngle),
cosf(lightElevation) * sinf(lightAngle),
sinf(lightElevation));
bool doOuterTaper = (style == 'OtrB') || (style == 'Embs') || (style == 'PlEb');
float blurRadius = aRadius;
if ((style == 'Embs') || (style == 'PlEb'))
blurRadius = (float) (int) (blurRadius / 2 + 0.75f);
if (mTechnique == 'SfBL') // Technique:Smooth
{
SoftBlurInit(anImageData, newImage, false);
SoftBlur(newImage->mBits, w, h, blurRadius - 1.0f, 0);
for (int i = 0; i < aSize; i++)
{
uint32 aVal = newImage->mBits[i];
newImage->mBits[i] = std::min(0xFF00, (int) (aVal + 256 / blurRadius));
}
}
else
{
ChokedPixelTransform(anImageData, newImage, blurRadius, 0, true, mTechnique == 'Slmt');
for (int i = 0; i < aSize; i++)
{
uint32 aVal = newImage->mBits[i];
newImage->mBits[i] = 0xFF00 - aVal;
}
}
if (mUseContour)
{
for (int i = 0; i < aSize; i++)
{
uint32 aVal = newImage->mBits[i];
int idx = (aVal * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
newImage->mBits[i] = mBevelContourData[idx];
}
}
if (mUseTexture)
{
PSDPattern* pattern = aLayerInfo->mPSDReader->mPSDPatternMap[mTexture.mPatternName];
int32 texMultiply = (int32) (0x100 * mTextureDepth / 100.0f);
int32 texOffset = texMultiply * 255;
if (!mTextureInvert)
texMultiply = -texMultiply;
int mipLevel = 0;
float dU = 1.0f / (float) (mTexture.mScale / 100.0f);
float dV = dU;
float virtPW = (float) pattern->mWidth;
float virtPH = (float) pattern->mHeight;
while (dU > 1.0001f)
{
// Select next mip level
mipLevel++;
PSDPattern* aMip = pattern->GetNextMipLevel();
dU /= 2.0f;
dV /= 2.0f;
virtPW /= 2.0f;
virtPH /= 2.0f;
pattern = aMip;
}
int pw = pattern->mWidth;
int ph = pattern->mHeight;
float startU = (float) (newImage->mX - (int) BFRound((float) mTexture.mPhaseX)) * dU;
while (startU < 0)
startU += virtPW;
float startV = (float) (newImage->mY - (int) BFRound((float) mTexture.mPhaseY)) * dV;
if (startV < 0)
startV += virtPH;
float aU = startU;
float aV = startV;
for (int y = 0; y < h; y++)
{
aU = startU;
uint32* aBits = newImage->mBits + (y*w);
for (int x = 0; x < w; x++)
{
int u1 = ((int) aU) % pw;
int v1 = ((int) aV) % ph;
float ua = aU - (int) aU;
float va = aV - (int) aV;
int u2 = (u1 + 1) % pw;
int v2 = (v1 + 1) % ph;
int intensity = (int) ((
((float) pattern->mIntensityBits[u1+v1*pw] * (1.0f - ua) * (1.0f - va)) +
((float) pattern->mIntensityBits[u2+v1*pw] * ( ua) * (1.0f - va)) +
((float) pattern->mIntensityBits[u1+v2*pw] * (1.0f - ua) * ( va)) +
((float) pattern->mIntensityBits[u2+v2*pw] * ( ua) * ( va))
) * texMultiply + 0.5f);
*aBits = *aBits + intensity + texOffset;
aBits++;
aU += dU;
aU -= (int) (aU / virtPW) * virtPW;
}
aV += dV;
aV -= (int) (aV / virtPH) * virtPH;
}
}
CreateNormalMap(newImage->mBits, normalMap, w, h, 255.0f / blurRadius / aDepth * 0.80f, &shadowVec);
// Shadows need to be weightedly differently for 'softening' purposes
// This equation fits the proper curve, but I don't really understand why
float shadowWeighting = sin(lightElevation) / (1.0f - sin(lightElevation));
if ((mAntiAliased) && (!mGlossContour.IsDefault()))
AntiAliasIndices(normalMap, w, h);
for (int i = 0; i < aSize; i++)
{
float dot = (int) (normalMap[i] - 0x100000) / (float) 0xFF00;
float zeroPt = shadowVec.mZ;
//TODO: Cache gloss contour
float curvePos = mGlossContour.GetVal(255.0f * dot) / 255.0f;
curvePos -= zeroPt;
float curveIdx = 0;
if (curvePos > 0)
{
float hilitePct = curvePos / (1.0f - zeroPt);
curvePos = hilitePct;
curveIdx = curvePos;
}
else
{
float shadowPct = curvePos / zeroPt;
curvePos /= zeroPt;
}
float lightVal = curvePos;
lightVal = BFClamp(lightVal, -1.0f, 1.0f);
if (lightVal < 0)
lightVal *= shadowWeighting;
normalMap[i] = std::max((uint32) (0xFF00 * lightVal) + 0x100000, (uint32) 0);
}
float aSoften = (float) mSoften;
if (aSoften > 0)
{
float d = GetSoftBlurRadius(aSoften) / 2;
if (d > 0)
{
uint32* tempBuffer = newImage->mBits;
for (int i = 0; i < 2; i++)
{
BoxBlur(normalMap, tempBuffer, w, h, d, 0);
BoxBlur(tempBuffer, normalMap, h, w, d, 0);
}
}
}
for (int i = 0; i < aSize; i++)
{
float lightVal = (int) (normalMap[i] - 0x100000) / (float) 0xFF00;
if (lightVal < 0)
lightVal /= shadowWeighting;
if (doOuterTaper)
{
float taperVal = newImage->mBits[i] / 256.0f / 255.0f * 7.85f;
if (lightVal > 0)
lightVal = std::min(lightVal, taperVal);
else
lightVal = std::max(lightVal, -taperVal);
}
if (lightVal > 0)
{
hiliteImage->mBits[i] = ((int) (BFClamp(lightVal, 0.0f, 1.0f) * 255.0f + 0.5f) << 24) | (mHiliteColor & 0x00FFFFFF);
shadowImage->mBits[i] = 0;
}
else
{
hiliteImage->mBits[i] = 0;
shadowImage->mBits[i] = ((int) (BFClamp(-lightVal, 0.0f, 1.0f) * 255.0f + 0.5f) << 24) | (mShadowColor & 0x00FFFFFF);
}
}
delete [] normalMap;
}
void ImageBevelEffect::Apply(ImageEffectCtx* ctx)
{
if (mStyle == 'stro')
return;
ImageData* hiliteEffectImage = new ImageData();
hiliteEffectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
hiliteEffectImage->mX = ctx->mBlendX;
hiliteEffectImage->mY = ctx->mBlendY;
ImageData* shadowEffectImage = new ImageData();
shadowEffectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
shadowEffectImage->mX = ctx->mBlendX;
shadowEffectImage->mY = ctx->mBlendY;
// Pillow emboss takes two passes, the others only take one
for (int aPass = 0; aPass < 2; aPass++)
{
bool needsMorePasses = false;
Apply(aPass, mStyle, ctx->mLayerInfo, ctx->mLayerImage, hiliteEffectImage, shadowEffectImage);
if ((aPass == 0) && ((mStyle == 'Embs') || (mStyle == 'PlEb') || (mStyle == 'InrB') || (mStyle == 'PlEb'))) // Emboss or pillow emboss or inner
{
BlendImage(ctx->mInnerImage, hiliteEffectImage, 0, 0, (float) mHiliteOpacity / 100.0f, mHiliteMode);
BlendImage(ctx->mInnerImage, shadowEffectImage, 0, 0, (float) mShadowOpacity / 100.0f, mShadowMode);
needsMorePasses |= (mStyle == 'PlEb');
}
if ((mStyle == 'Embs') || (mStyle == 'OtrB') || ((mStyle == 'PlEb') && (aPass == 1))) // Emboss or pillow emboss or outer
{
BlendImage(ctx->mOuterImage, hiliteEffectImage, 0, 0, (float) mHiliteOpacity / 100.0f, mHiliteMode);
BlendImage(ctx->mOuterImage, shadowEffectImage, 0, 0, (float) mShadowOpacity / 100.0f, mShadowMode);
}
if (!needsMorePasses)
break;
}
delete shadowEffectImage;
delete hiliteEffectImage;
}
int ImageBevelEffect::GetNeededBorderSize()
{
return (int) mSize;
}
///
void ImageSatinEffect::Init()
{
mContourData = CreateContourDataTable(&mContour, 1.0f);
}
void ImageSatinEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int sw = destImageData->mWidth;
int sh = destImageData->mHeight;
int w = sw;
int h = sh;
ImageData* newImage = destImageData;
int aSize = w*h;
ImageData* tempImage = new ImageData();
tempImage->CreateNew(w, h);
SoftBlurInit(imageData, tempImage, false);
SoftBlur(tempImage->mBits, w, h, GetSoftBlurRadius((float) mSize), 0);
float angle = (float) mAngle * BF_PI / 180.0f;
int ofsx = (int) (BFRound(cosf(angle) * (float) mDistance));
int ofsy = (int) (BFRound(-sinf(angle) * (float) mDistance));
uint32* tempData = tempImage->mBits;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int x1 = BFClamp(x + ofsx, 0, w - 1);
int y1 = BFClamp(y + ofsy, 0, h - 1);
int idx1 = (tempData[x1+y1*w] * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
int val1 = mContourData[idx1] * 255 / (GRADIENT_DATA_SIZE - 1);
int x2 = BFClamp(x - ofsx, 0, w - 1);
int y2 = BFClamp(y - ofsy, 0, h - 1);
int idx2 = (tempData[x2+y2*w] * (CONTOUR_DATA_SIZE - 1) / 255) / 256;
int val2 = mContourData[idx2] * 255 / (GRADIENT_DATA_SIZE - 1);
uint32 aVal = (uint32) abs((int) val1 - (int) val2);
newImage->mBits[x+y*w] = aVal;
}
}
if ((mAntiAliased) && (!mContour.IsDefault()))
AntiAliasIndices(newImage->mBits, w, h);
if (mInvert)
{
for (int i = 0; i < aSize; i++)
newImage->mBits[i] = ((255 - newImage->mBits[i]) << 24) | (mColor & 0x00FFFFFF);
}
else
{
for (int i = 0; i < aSize; i++)
newImage->mBits[i] = (newImage->mBits[i] << 24) | (mColor & 0x00FFFFFF);
}
delete tempImage;
}
int ImageSatinEffect::GetMixType() // Default:Interior
{
return IMAGEMIX_INNER;
}
int ImageSatinEffect::GetNeededBorderSize()
{
return (int) mSize;
}
void ImageColorOverlayEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
mColorFill.Apply(layerInfo, imageData, destImageData);
}
///
void ImageColorFill::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int w = destImageData->mWidth;
int h = destImageData->mHeight;
int aSize = w*h;
for (int i = 0; i < aSize; i++)
destImageData->mBits[i] = mColor;
}
ImageGradientFill::ImageGradientFill()
{
mGradientData = NULL;
}
ImageGradientFill::~ImageGradientFill()
{
delete mGradientData;
}
void ImageGradientFill::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int w = destImageData->mWidth;
int h = destImageData->mHeight;
int iw = layerInfo->mWidth;
int ih = layerInfo->mHeight;
int ox = layerInfo->mX - destImageData->mX;
int oy = layerInfo->mY - destImageData->mY;
if (mGradientData == NULL)
{
mGradientData = CreateGradientDataTable(mColorGradient);
if (mReverse)
{
for (int i = 0; i < GRADIENT_DATA_SIZE / 2; i++)
{
int endIdx = (GRADIENT_DATA_SIZE - 1) - i;
uint32 swap = mGradientData[i];
mGradientData[i] = mGradientData[endIdx];
mGradientData[endIdx] = swap;
}
}
}
int minX = w;
int maxX = 0;
int minY = h;
int maxY = 0;
for (int y = 0; y < ih; y++)
{
uint32* checkBits = imageData->mBits + (y*iw);
bool hadPixel = false;
for (int x = 0; x < iw; x++)
{
if ((checkBits[x] >> 24) >= 128)
{
minX = std::min(minX, x + ox);
hadPixel = true;
break;
}
}
for (int x = iw - 1; x >= 0; x--)
{
if ((checkBits[x] >> 24) >= 128)
{
maxX = std::max(maxX, x + ox);
hadPixel = true;
break;
}
}
if (hadPixel)
{
minY = std::min(minY, y + oy);
maxY = std::max(maxY, y + oy);
}
}
int contentW = std::max(0, maxX - minX + 1);
int contentH = std::max(0, maxY - minY + 1);
float angleDeg = (float) mAngle;
// There seems to be some inaccuracy with photoshop with angles.
// There's a large gap between 180 degrees and 179 degrees than there should be, for example
// This is not yet simulated here. Offsets are also slightly effected.
float angle = BF_PI * angleDeg / 180.0f;
float matA = -cosf(angle);
float matB = sinf(angle);
float matC = sin(angle);
float matD = cos(angle);
float xOfs = 0;
float yOfs = 0;
float gradWidth;
float gradHeight;
float offsetScale = 1.0f;
float sizeBasis;
if (mAlignWithLayer)
{
gradWidth = (float) contentW;
gradHeight = (float) contentH;
xOfs = -(float)contentW/2 - minX;
yOfs = -(float)contentH/2 - minY;
}
else
{
gradWidth = (float) layerInfo->mPSDReader->mWidth;
gradHeight = (float) layerInfo->mPSDReader->mHeight;
xOfs = destImageData->mX - (float) gradWidth/2;
yOfs = destImageData->mY - (float) gradHeight/2;
}
xOfs -= ((float) mOffsetX / 100.0f) * gradWidth;
yOfs -= ((float) mOffsetY / 100.0f) * gradHeight;
float sizeBasisX = gradWidth / fabs(matA);
float sizeBasisY = gradHeight / fabs(matB);
sizeBasis = std::min(sizeBasisX, sizeBasisY);
float scale = (float) mScale / 100.0f;
float gradientScale = (float) (GRADIENT_DATA_SIZE - 1) / sizeBasis / scale * 2;
for (int y = 0; y < h; y++)
{
uint32* aBits = destImageData->mBits + (y*w);
for (int x = 0; x < w; x++)
{
float aX = (float) x + xOfs;
float aY = (float) y + yOfs;
float xRot = (aX * matA) + (aY * matB);
float yRot = (aX * matC) + (aY * matD);
int gradientIdx;
switch (mStyle)
{
case 'Lnr ':
gradientIdx = BFClamp((int) ((xRot/2) * gradientScale + GRADIENT_DATA_SIZE/2 + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
break;
case 'Rdl ':
{
float dist = sqrt(xRot*xRot + yRot * yRot);
gradientIdx = (GRADIENT_DATA_SIZE - 1) - BFClamp((int) ((dist) * gradientScale + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
}
break;
case 'Angl':
{
float ang = (atan2(yRot, xRot) / BF_PI / 2.0f) + 0.5f;
gradientIdx = BFClamp((int) ((ang) * (GRADIENT_DATA_SIZE - 1) + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
}
break;
case 'Rflc':
gradientIdx = (GRADIENT_DATA_SIZE - 1) - BFClamp((int) (fabs(xRot) * gradientScale + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
break;
case 'Dmnd':
gradientIdx = (GRADIENT_DATA_SIZE - 1) - BFClamp((int) ((fabs(xRot) + fabs(yRot)) * gradientScale + 0.5f), 0, GRADIENT_DATA_SIZE - 1);
break;
}
aBits[x] = mGradientData[gradientIdx];
}
}
}
void ImagePatternFill::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
PSDPattern* pattern = layerInfo->mPSDReader->mPSDPatternMap[mPatternName];
int mipLevel = 0;
int w = destImageData->mWidth;
int h = destImageData->mHeight;
float dU = 1.0f / (float) (mScale / 100.0f);
float dV = dU;
float virtPW = (float) pattern->mWidth;
float virtPH = (float) pattern->mHeight;
while ((dU > 1.0001f) && (pattern->mWidth >= 4) && (pattern->mHeight >= 4))
{
// Select next mip level
mipLevel++;
PSDPattern* aMip = pattern->GetNextMipLevel();
dU /= 2.0f;
dV /= 2.0f;
virtPW /= 2.0f;
virtPH /= 2.0f;
pattern = aMip;
}
int pw = pattern->mWidth;
int ph = pattern->mHeight;
float phaseX = (float) mPhaseX;
float phaseY = (float) mPhaseY;
if (mLinkWithLayer)
{
phaseX += (float) layerInfo->mRefX;
phaseY += (float) layerInfo->mRefY;
}
float startU = (float) (destImageData->mX - (int) BFRound((float) phaseX)) * dU;
while (startU < 0)
startU += virtPW;
float startV = (float) (destImageData->mY - (int) BFRound((float) phaseY)) * dV;
if (startV < 0)
startV += virtPH;
float aU = startU;
float aV = startV;
for (int y = 0; y < h; y++)
{
aU = startU;
uint32* aBits = destImageData->mBits + (y*w);
for (int x = 0; x < w; x++)
{
int u1 = ((int) aU) % pw;
int v1 = ((int) aV) % ph;
float ua = aU - (int) aU;
float va = aV - (int) aV;
int u2 = (u1 + 1) % pw;
int v2 = (v1 + 1) % ph;
uint32 aColor1 = pattern->mBits[u1+v1*pw];
uint32 aColor2 = pattern->mBits[u2+v1*pw];
uint32 color3 = pattern->mBits[u1+v2*pw];
uint32 color4 = pattern->mBits[u2+v2*pw];
uint32 a1 = (int) ((1.0f - ua) * (1.0f - va) * 256 + 0.5f);
uint32 a2 = (int) (( ua) * (1.0f - va) * 256 + 0.5f);
uint32 a3 = (int) ((1.0f - ua) * ( va) * 256 + 0.5f);
uint32 a4 = (int) (( ua) * ( va) * 256 + 0.5f);
*aBits =
(((((aColor1 & 0x00FF00FF) * a1) + ((aColor2 & 0x00FF00FF) * a2) +
((color3 & 0x00FF00FF) * a3) + ((color4 & 0x00FF00FF) * a4)) >> 8) & 0x00FF00FF) |
(((((aColor1 >> 8) & 0x00FF00FF) * a1) + (((aColor2 >> 8) & 0x00FF00FF) * a2) +
(((color3 >> 8) & 0x00FF00FF) * a3) + (((color4 >> 8) & 0x00FF00FF) * a4)) & 0xFF00FF00);
//*aBits = aColor1;
aBits++;
aU += dU;
aU -= (int) (aU / virtPW) * virtPW;
}
aV += dV;
aV -= (int) (aV / virtPH) * virtPH;
}
}
void ImagePatternOverlayEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
mPattern.Apply(layerInfo, imageData, destImageData);
}
void ImageGradientOverlayEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
mGradientFill.Apply(layerInfo, imageData, destImageData);
}
void ImageStrokeEffect::Apply(PSDLayerInfo* layerInfo, ImageData* imageData, ImageData* destImageData)
{
int w = destImageData->mWidth;
int h = destImageData->mHeight;
int aSize = w*h;
ImageData* tempImage = new ImageData();
tempImage->CreateNew(w, h);
tempImage->mX = destImageData->mX;
tempImage->mY = destImageData->mY;
float aRadius = (float) mSize;
float blurRadius = aRadius;
if (mPosition == 'CtrF')
blurRadius /= 2;
uint32* aDest = tempImage->mBits;
ChamferedDistanceTransformInit(imageData, tempImage, false, 0);
ChamferedDistanceTransform(aDest, w, h);
uint32* tempBuffer = destImageData->mBits;//new uint32[w*h];
uint32* exterior = aDest;
uint32* anInterior = tempBuffer;
float rad = blurRadius;
int inf = (int) (blurRadius + 2) * 256;
for (int i = 0; i < aSize; i++)
{
float dist = aDest[i] / 256.0f;
dist -= 0.001f;
if (dist < 0)
{
exterior[i] = inf;
anInterior[i] = 0;
}
else if (dist < 1.0f)
{
exterior[i] = BFClamp((int) ((1.0f - dist) * 256.0f), 0, 0xFF);
anInterior[i] = (int) (dist * 256);
}
else
{
exterior[i] = 0;
anInterior[i] = (int) (dist * 256);
}
}
ChamferedDistanceTransform(exterior, w, h);
for (int i = 0; i < aSize; i++)
{
float distInterior = (anInterior[i] / 256.0f) - rad;
float distExterior = (exterior[i] / 256.0f) - rad;
float maxDist = std::max(distInterior, distExterior);
if (maxDist < 0)
aDest[i] = 0xFF000000;
else if (maxDist < 1.0f)
aDest[i] = ((int) (255.0f * (1.0f - maxDist))) << 24;
else
aDest[i] = 0;
}
tempImage->mX = destImageData->mX;
tempImage->mY = destImageData->mY;
if (mFillType == 'Ptrn')
mPatternFill.Apply(layerInfo, imageData, destImageData);
else if (mFillType == 'GrFl')
mGradientFill.Apply(layerInfo, imageData, destImageData);
else
mColorFill.Apply(layerInfo, imageData, destImageData);
for (int i = 0; i < aSize; i++)
{
uint32 srcAlpha = tempImage->mBits[i] >> 24;
uint32 destAlpha = destImageData->mBits[i] >> 24;
destImageData->mBits[i] = ((int) (srcAlpha * destAlpha / 255 + 0.5f) << 24) | (destImageData->mBits[i] & 0x00FFFFFF);
}
delete tempImage;
}
void ImageStrokeEffect::Apply(ImageEffectCtx* ctx)
{
// Try to find a bevel effect with a 'Stroke Emboss' style
ImageBevelEffect* bevelEffect = NULL;
for (int effectIdx = 0; effectIdx < (int) ctx->mLayerInfo->mImageEffects->mImageEffectVector.size(); effectIdx++)
{
BaseImageEffect* anEffect = ctx->mLayerInfo->mImageEffects->mImageEffectVector[effectIdx];
bevelEffect = dynamic_cast<ImageBevelEffect*>(anEffect);
if ((bevelEffect != NULL) && (bevelEffect->mStyle == 'stro'))
break;
bevelEffect = NULL;
}
if ((mOpacity == 100.0) && (bevelEffect == NULL))
{
BaseImageEffect::Apply(ctx);
return;
}
ImageData* effectImage = new ImageData();
effectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
effectImage->mX = ctx->mBlendX;
effectImage->mY = ctx->mBlendY;
Apply(ctx->mLayerInfo, ctx->mLayerImage, effectImage);
float opacity = (float) mOpacity / 100.0f;
ImageData* mixImage = CreateResizedImageUnion(ctx->mOrigImage, effectImage->mX, effectImage->mY, effectImage->mWidth, effectImage->mHeight);
BlendImage(mixImage, effectImage, effectImage->mX - mixImage->mX, effectImage->mY - mixImage->mY, opacity, mBlendMode, true);
if (bevelEffect != NULL)
{
ImageData* hiliteEffectImage = new ImageData();
hiliteEffectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
hiliteEffectImage->mX = ctx->mBlendX;
hiliteEffectImage->mY = ctx->mBlendY;
ImageData* shadowEffectImage = new ImageData();
shadowEffectImage->CreateNew(ctx->mBlendWidth, ctx->mBlendHeight);
shadowEffectImage->mX = ctx->mBlendX;
shadowEffectImage->mY = ctx->mBlendY;
int aStyle = 'Embs';
if (mPosition == 'OutF')
aStyle = 'OtrB';
else if (mPosition == 'InsF')
aStyle = 'InrB';
bevelEffect->Apply(0, aStyle, ctx->mLayerInfo, ctx->mLayerImage, hiliteEffectImage, shadowEffectImage);
BlendImage(mixImage, hiliteEffectImage, hiliteEffectImage->mX - mixImage->mX, hiliteEffectImage->mY - mixImage->mY, (float) bevelEffect->mHiliteOpacity / 100.0f, bevelEffect->mHiliteMode);
BlendImage(mixImage, shadowEffectImage, shadowEffectImage->mX - mixImage->mX, shadowEffectImage->mY - mixImage->mY, (float) bevelEffect->mShadowOpacity / 100.0f, bevelEffect->mShadowMode);
delete shadowEffectImage;
delete hiliteEffectImage;
}
int mixType = GetMixType();
if ((mixType == IMAGEMIX_INNER) || (mixType == IMAGEMIX_OVER))
BlendImagesTogether(ctx->mInnerImage, mixImage, effectImage);
if ((mixType == IMAGEMIX_OUTER) || (mixType == IMAGEMIX_OVER))
BlendImagesTogether(ctx->mOuterImage, mixImage, effectImage);
delete mixImage;
delete effectImage;
}
int ImageStrokeEffect::GetMixType() // Default:Interior
{
if (mPosition == 'OutF') // Outside
return IMAGEMIX_OUTER;
if (mPosition == 'InsF') // Inside
return IMAGEMIX_INNER;
if (mPosition == 'CtrF') // Inside
return IMAGEMIX_OVER;
return IMAGEMIX_INNER;
}
int ImageStrokeEffect::GetNeededBorderSize()
{
return (int)(mSize + 2.5);
}
bool ImageStrokeEffect::NeedsOrigBits(ImageEffects* effects)
{
ImageBevelEffect* bevelEffect = NULL;
for (int effectIdx = 0; effectIdx < (int) effects->mImageEffectVector.size(); effectIdx++)
{
BaseImageEffect* anEffect = effects->mImageEffectVector[effectIdx];
bevelEffect = dynamic_cast<ImageBevelEffect*>(anEffect);
if ((bevelEffect != NULL) && (bevelEffect->mStyle == 'stro'))
return true;
}
return mOpacity != 100.0;
}