mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
2165 lines
100 KiB
Objective-C
2165 lines
100 KiB
Objective-C
#import "TGImageBlur.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
|
|
#import <Accelerate/Accelerate.h>
|
|
|
|
#import "UIImage+TG.h"
|
|
#import "TGStaticBackdropImageData.h"
|
|
|
|
#define TG_USE_NEON 0
|
|
|
|
#if TG_USE_NEON
|
|
# import <arm_neon.h>
|
|
#endif
|
|
|
|
static inline uint64_t get_colors (const uint8_t *p) {
|
|
return p[0] + (p[1] << 16) + ((uint64_t)p[2] << 32);
|
|
}
|
|
|
|
static void fastBlur (int imageWidth, int imageHeight, int imageStride, void *pixels)
|
|
{
|
|
uint8_t *pix = (uint8_t *)pixels;
|
|
const int w = imageWidth;
|
|
const int h = imageHeight;
|
|
const int stride = imageStride;
|
|
const int radius = 3;
|
|
const int r1 = radius + 1;
|
|
const int div = radius * 2 + 1;
|
|
|
|
if (radius > 15 || div >= w || div >= h)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64_t rgb[imageStride * imageHeight];
|
|
|
|
int x, y, i;
|
|
|
|
int yw = 0;
|
|
const int we = w - r1;
|
|
for (y = 0; y < h; y++) {
|
|
uint64_t cur = get_colors (&pix[yw]);
|
|
uint64_t rgballsum = -radius * cur;
|
|
uint64_t rgbsum = cur * ((r1 * (r1 + 1)) >> 1);
|
|
|
|
for (i = 1; i <= radius; i++) {
|
|
uint64_t cur = get_colors (&pix[yw + i * 4]);
|
|
rgbsum += cur * (r1 - i);
|
|
rgballsum += cur;
|
|
}
|
|
|
|
x = 0;
|
|
|
|
#define update(start, middle, end) \
|
|
rgb[y * w + x] = (rgbsum >> 4) & 0x00FF00FF00FF00FF; \
|
|
\
|
|
rgballsum += get_colors (&pix[yw + (start) * 4]) - \
|
|
2 * get_colors (&pix[yw + (middle) * 4]) + \
|
|
get_colors (&pix[yw + (end) * 4]); \
|
|
rgbsum += rgballsum; \
|
|
x++; \
|
|
|
|
while (x < r1) {
|
|
update (0, x, x + r1);
|
|
}
|
|
while (x < we) {
|
|
update (x - r1, x, x + r1);
|
|
}
|
|
while (x < w) {
|
|
update (x - r1, x, w - 1);
|
|
}
|
|
#undef update
|
|
|
|
yw += stride;
|
|
}
|
|
|
|
const int he = h - r1;
|
|
for (x = 0; x < w; x++) {
|
|
uint64_t rgballsum = -radius * rgb[x];
|
|
uint64_t rgbsum = rgb[x] * ((r1 * (r1 + 1)) >> 1);
|
|
for (i = 1; i <= radius; i++) {
|
|
rgbsum += rgb[i * w + x] * (r1 - i);
|
|
rgballsum += rgb[i * w + x];
|
|
}
|
|
|
|
y = 0;
|
|
int yi = x * 4;
|
|
|
|
#define update(start, middle, end) \
|
|
int64_t res = rgbsum >> 4; \
|
|
pix[yi] = (uint8_t)res; \
|
|
pix[yi + 1] = (uint8_t)(res >> 16); \
|
|
pix[yi + 2] = (uint8_t)(res >> 32); \
|
|
\
|
|
rgballsum += rgb[x + (start) * w] - \
|
|
2 * rgb[x + (middle) * w] + \
|
|
rgb[x + (end) * w]; \
|
|
rgbsum += rgballsum; \
|
|
y++; \
|
|
yi += stride;
|
|
|
|
while (y < r1) {
|
|
update (0, y, y + r1);
|
|
}
|
|
while (y < he) {
|
|
update (y - r1, y, y + r1);
|
|
}
|
|
while (y < h) {
|
|
update (y - r1, y, h - 1);
|
|
}
|
|
#undef update
|
|
}
|
|
}
|
|
|
|
static void fastBlurMore (int imageWidth, int imageHeight, int imageStride, void *pixels)
|
|
{
|
|
uint8_t *pix = (uint8_t *)pixels;
|
|
const int w = imageWidth;
|
|
const int h = imageHeight;
|
|
const int stride = imageStride;
|
|
const int radius = 7;
|
|
const int r1 = radius + 1;
|
|
const int div = radius * 2 + 1;
|
|
|
|
if (radius > 15 || div >= w || div >= h)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64_t *rgb = malloc(imageStride * imageHeight * sizeof(uint64_t));
|
|
|
|
int x, y, i;
|
|
|
|
int yw = 0;
|
|
const int we = w - r1;
|
|
for (y = 0; y < h; y++) {
|
|
uint64_t cur = get_colors (&pix[yw]);
|
|
uint64_t rgballsum = -radius * cur;
|
|
uint64_t rgbsum = cur * ((r1 * (r1 + 1)) >> 1);
|
|
|
|
for (i = 1; i <= radius; i++) {
|
|
uint64_t cur = get_colors (&pix[yw + i * 4]);
|
|
rgbsum += cur * (r1 - i);
|
|
rgballsum += cur;
|
|
}
|
|
|
|
x = 0;
|
|
|
|
#define update(start, middle, end) \
|
|
rgb[y * w + x] = (rgbsum >> 6) & 0x00FF00FF00FF00FF; \
|
|
\
|
|
rgballsum += get_colors (&pix[yw + (start) * 4]) - \
|
|
2 * get_colors (&pix[yw + (middle) * 4]) + \
|
|
get_colors (&pix[yw + (end) * 4]); \
|
|
rgbsum += rgballsum; \
|
|
x++; \
|
|
|
|
while (x < r1) {
|
|
update (0, x, x + r1);
|
|
}
|
|
while (x < we) {
|
|
update (x - r1, x, x + r1);
|
|
}
|
|
while (x < w) {
|
|
update (x - r1, x, w - 1);
|
|
}
|
|
#undef update
|
|
|
|
yw += stride;
|
|
}
|
|
|
|
const int he = h - r1;
|
|
for (x = 0; x < w; x++) {
|
|
uint64_t rgballsum = -radius * rgb[x];
|
|
uint64_t rgbsum = rgb[x] * ((r1 * (r1 + 1)) >> 1);
|
|
for (i = 1; i <= radius; i++) {
|
|
rgbsum += rgb[i * w + x] * (r1 - i);
|
|
rgballsum += rgb[i * w + x];
|
|
}
|
|
|
|
y = 0;
|
|
int yi = x * 4;
|
|
|
|
#define update(start, middle, end) \
|
|
int64_t res = rgbsum >> 6; \
|
|
pix[yi] = (uint8_t)res; \
|
|
pix[yi + 1] = (uint8_t)(res >> 16); \
|
|
pix[yi + 2] = (uint8_t)(res >> 32); \
|
|
\
|
|
rgballsum += rgb[x + (start) * w] - \
|
|
2 * rgb[x + (middle) * w] + \
|
|
rgb[x + (end) * w]; \
|
|
rgbsum += rgballsum; \
|
|
y++; \
|
|
yi += stride;
|
|
|
|
while (y < r1) {
|
|
update (0, y, y + r1);
|
|
}
|
|
while (y < he) {
|
|
update (y - r1, y, y + r1);
|
|
}
|
|
while (y < h) {
|
|
update (y - r1, y, h - 1);
|
|
}
|
|
#undef update
|
|
}
|
|
|
|
free(rgb);
|
|
}
|
|
|
|
static void matrixMul(CGFloat *a, CGFloat *b, CGFloat *result)
|
|
{
|
|
for (int i = 0; i != 4; ++i)
|
|
{
|
|
for (int j = 0; j != 4; ++j)
|
|
{
|
|
CGFloat sum = 0;
|
|
for (int k = 0; k != 4; ++k)
|
|
{
|
|
sum += a[i + k * 4] * b[k + j * 4];
|
|
}
|
|
result[i + j * 4] = sum;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline CGSize fitSize(CGSize size, CGSize maxSize)
|
|
{
|
|
if (size.width < 1)
|
|
size.width = 1;
|
|
if (size.height < 1)
|
|
size.height = 1;
|
|
|
|
if (size.width > maxSize.width)
|
|
{
|
|
size.height = CGFloor((size.height * maxSize.width / size.width));
|
|
size.width = maxSize.width;
|
|
}
|
|
if (size.height > maxSize.height)
|
|
{
|
|
size.width = CGFloor((size.width * maxSize.height / size.height));
|
|
size.height = maxSize.height;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static void computeImageVariance(uint8_t *memory, int width, int height, int stride, float *outVariance, float *outLuminance, float *outRealLuminance)
|
|
{
|
|
uint32_t rnSum = 0;
|
|
uint32_t gnSum = 0;
|
|
uint32_t bnSum = 0;
|
|
|
|
uint64_t rnSumSq = 0;
|
|
uint64_t gnSumSq = 0;
|
|
uint64_t bnSumSq = 0;
|
|
|
|
uint32_t luminanceSum = 0;
|
|
//uint64_t luminanceSumSq = 0;
|
|
|
|
/*float rSum = 0.0f;
|
|
float gSum = 0.0f;
|
|
float bSum = 0.0f;
|
|
|
|
float rSumSq = 0.0f;
|
|
float gSumSq = 0.0f;
|
|
float bSumSq = 0.0f;*/
|
|
|
|
uint32_t histogram[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
uint32_t color = *((uint32_t *)&memory[y * stride + x * 4]);
|
|
|
|
uint32_t r = (color >> 16) & 0xff;
|
|
uint32_t g = (color >> 8) & 0xff;
|
|
uint32_t b = color & 0xff;
|
|
|
|
uint32_t pixelLuminance = (uint8_t)((r * 299 + g * 587 + b * 114) / 1000);
|
|
histogram[(pixelLuminance * 9 / 255) % 10]++;
|
|
|
|
luminanceSum += pixelLuminance;
|
|
//luminanceSumSq += pixelLuminance * pixelLuminance;
|
|
|
|
rnSum += r;
|
|
gnSum += g;
|
|
bnSum += b;
|
|
|
|
rnSumSq += r * r;
|
|
gnSumSq += g * g;
|
|
bnSumSq += b * b;
|
|
|
|
/*rSum += r / 255.0f;
|
|
gSum += g / 255.0f;
|
|
bSum += b / 255.0f;
|
|
|
|
rSumSq += (r / 255.0f) * (r / 255.0f);
|
|
gSumSq += (g / 255.0f) * (g / 255.0f);
|
|
bSumSq += (b / 255.0f) * (b / 255.0f);*/
|
|
}
|
|
}
|
|
|
|
int n = width * height;
|
|
|
|
/*float rVariance = (rSumSq - (rSum * rSum) / n) / (n);
|
|
float gVariance = (gSumSq - (gSum * gSum) / n) / (n);
|
|
float bVariance = (bSumSq - (bSum * bSum) / n) / (n);*/
|
|
|
|
float rnVariance = ((uint64_t)((rnSumSq / 255) - ((uint64_t)rnSum * (uint64_t)rnSum / 255) / n)) / (255.0f * n);
|
|
float gnVariance = ((uint64_t)((gnSumSq / 255) - ((uint64_t)gnSum * (uint64_t)gnSum / 255) / n)) / (255.0f * n);
|
|
float bnVariance = ((uint64_t)((bnSumSq / 255) - ((uint64_t)bnSum * (uint64_t)bnSum / 255) / n)) / (255.0f * n);
|
|
|
|
float variance = rnVariance + gnVariance + bnVariance;
|
|
if (outVariance != NULL)
|
|
*outVariance = variance;
|
|
|
|
//float luminanceVariance = ((uint64_t)((luminanceSumSq / 255) - ((uint64_t)luminanceSum * (uint64_t)luminanceSum / 255) / n)) / (255.0f * n);
|
|
|
|
float floatHistogram[10];
|
|
|
|
float norm = (float)(width * height);
|
|
|
|
float n0 = 0.0f;
|
|
float n1 = 0.0f;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
floatHistogram[i] = histogram[i] / norm;
|
|
|
|
if (i <= 6)
|
|
n0 += floatHistogram[i];
|
|
else
|
|
n1 += floatHistogram[i];
|
|
}
|
|
|
|
//TGLog(@"histogram: [%f %f %f %f %f %f %f %f %f %f]", floatHistogram[0], floatHistogram[1], floatHistogram[2], floatHistogram[3], floatHistogram[4], floatHistogram[5], floatHistogram[6], floatHistogram[7], floatHistogram[8], floatHistogram[9]);
|
|
|
|
if (outLuminance != NULL)
|
|
*outLuminance = n0 < n1 ? 0.95f : 0.5f;
|
|
|
|
if (outRealLuminance != NULL)
|
|
*outRealLuminance = (luminanceSum / (norm * 255.0f));
|
|
}
|
|
|
|
static void fastScaleImage(uint8_t *sourceMemory, int sourceWidth, int sourceHeight, int sourceStride, uint8_t *targetMemory, int targetWidth, int targetHeight, int targetStride, CGRect sourceRectInTargetSpace)
|
|
{
|
|
int imageX = MIN(0, (int)sourceRectInTargetSpace.origin.x);
|
|
int imageY = MIN(0, (int)sourceRectInTargetSpace.origin.y);
|
|
int imageWidth = (int)sourceRectInTargetSpace.size.width;
|
|
int imageHeight = (int)sourceRectInTargetSpace.size.height;
|
|
|
|
for (int y = 0; y < targetHeight; y++)
|
|
{
|
|
for (int x = 0; x < targetWidth; x++)
|
|
{
|
|
int sourceY = (y - imageY) * sourceHeight / imageHeight;
|
|
int sourceX = (x - imageX) * sourceWidth / imageWidth;
|
|
|
|
if (sourceX >= 0 && sourceY >= 0 && sourceX < sourceWidth && sourceY < sourceHeight)
|
|
{
|
|
uint32_t color = *((uint32_t *)&sourceMemory[sourceY * sourceStride + sourceX * 4]);
|
|
*((uint32_t *)&targetMemory[y * targetStride + x * 4]) = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint32_t TGImageAverageColor(void *memory, const unsigned int width, const unsigned int height, const unsigned int stride)
|
|
{
|
|
int32_t av0 = 0;
|
|
int32_t av1 = 0;
|
|
int32_t av2 = 0;
|
|
|
|
for (unsigned int y = 0; y < height; y++)
|
|
{
|
|
for (unsigned int x = 0; x < width; x++)
|
|
{
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
av0 += pixel & 0xff;
|
|
av1 += (pixel >> 8) & 0xff;
|
|
av2 += (pixel >> 16) & 0xff;
|
|
}
|
|
}
|
|
|
|
uint32_t norm = (width * height);
|
|
av0 = av0 / norm;
|
|
av1 = av1 / norm;
|
|
av2 = av2 / norm;
|
|
|
|
return 0xff000000 | av0 | (av1 << 8) | (av2 << 16);
|
|
}
|
|
|
|
static inline uint32_t alphaComposePremultipliedPixels(uint32_t a, uint32_t b)
|
|
{
|
|
uint32_t a0 = ((a >> 24) & 0xff);
|
|
uint32_t a1 = ((b >> 24) & 0xff);
|
|
|
|
uint32_t r0 = (a >> 16) & 0xff;
|
|
uint32_t g0 = (a >> 8) & 0xff;
|
|
uint32_t b0 = a & 0xff;
|
|
|
|
uint32_t r1 = (b >> 16) & 0xff;
|
|
uint32_t g1 = (b >> 8) & 0xff;
|
|
uint32_t b1 = b & 0xff;
|
|
|
|
uint32_t ta = ((a0 * a0) >> 8) + ((a1 * (255 - ((a0 * a0) >> 8))) >> 8);
|
|
uint32_t tr = ((r0 * a0) >> 8) + ((r1 * (255 - ((a0 * a0) >> 8))) >> 8);
|
|
uint32_t tg = ((g0 * a0) >> 8) + ((g1 * (255 - ((a0 * a0) >> 8))) >> 8);
|
|
uint32_t tb = ((b0 * a0) >> 8) + ((b1 * (255 - ((a0 * a0) >> 8))) >> 8);
|
|
|
|
return (ta << 24) | (tr << 16) | (tg << 8) | tb;
|
|
}
|
|
|
|
static inline uint32_t premultipliedPixel(uint32_t rgb, uint32_t alpha)
|
|
{
|
|
uint32_t r = (((rgb >> 16) & 0xff) * alpha) >> 8;
|
|
uint32_t g = (((rgb >> 8) & 0xff) * alpha) >> 8;
|
|
uint32_t b = ((rgb & 0xff) * alpha) >> 8;
|
|
|
|
return (alpha << 24) | (r << 16) | (g << 8) | b;
|
|
}
|
|
|
|
static void addAttachmentImageCorners(void *memory, const unsigned int width, const unsigned int height, const unsigned int stride)
|
|
{
|
|
const int scale = TGIsRetina() ? 2 : 1;
|
|
|
|
const int shadowSize = 1;
|
|
const int strokeWidth = scale >= 2 ? 3 : 2;
|
|
const int radius = 13 * scale;
|
|
|
|
const int contextWidth = radius * 2 + shadowSize * 2 + strokeWidth * 2;
|
|
const int contextHeight = radius * 2 + shadowSize * 2 + strokeWidth * 2;
|
|
const int contextStride = (4 * contextWidth + 15) & (~15);
|
|
|
|
static uint8_t *contextMemory = NULL;
|
|
static uint8_t *alphaMemory = NULL;
|
|
|
|
const uint32_t shadowColorRaw = 0x44000000;
|
|
const uint32_t shadowColorArgb = premultipliedPixel(shadowColorRaw & 0xffffff, ((shadowColorRaw >> 24) & 0xff) - 30);
|
|
static uint32_t strokeColorArgb = 0xffffffff;
|
|
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
contextMemory = malloc(contextStride * contextHeight);
|
|
memset(contextMemory, 0, contextStride * contextHeight);
|
|
|
|
alphaMemory = malloc(contextStride * contextHeight);
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
CGContextRef targetContext = CGBitmapContextCreate(contextMemory, contextWidth, contextHeight, 8, contextStride, colorSpace, bitmapInfo);
|
|
CFRelease(colorSpace);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor blackColor].CGColor);
|
|
CGContextFillEllipseInRect(targetContext, CGRectMake(shadowSize + strokeWidth / 2.0f, shadowSize + strokeWidth / 2.0f, contextWidth - (shadowSize + strokeWidth / 2.0f) * 2.0f, contextHeight - (shadowSize + strokeWidth / 2.0f) * 2.0f));
|
|
|
|
memcpy(alphaMemory, contextMemory, contextStride * contextHeight);
|
|
|
|
memset(contextMemory, 0, contextStride * contextHeight);
|
|
|
|
CGContextSetStrokeColorWithColor(targetContext, UIColorRGBA(shadowColorRaw, ((shadowColorRaw >> 24) & 0xff) / 255.0f).CGColor);
|
|
CGContextSetLineWidth(targetContext, shadowSize);
|
|
CGContextStrokeEllipseInRect(targetContext, CGRectMake(shadowSize / 2.0f, shadowSize / 2.0f, contextWidth - shadowSize, contextHeight - shadowSize));
|
|
CGContextStrokeEllipseInRect(targetContext, CGRectMake(shadowSize / 2.0f + 0.5f, shadowSize / 2.0f - 0.5f, contextWidth - shadowSize, contextHeight - shadowSize));
|
|
|
|
CGContextSetStrokeColorWithColor(targetContext, UIColorRGBA(shadowColorRaw, (((shadowColorRaw >> 24) & 0xff) / 255.0f) * 0.5f).CGColor);
|
|
CGContextStrokeEllipseInRect(targetContext, CGRectMake(shadowSize / 2.0f - 0.2f, shadowSize / 2.0f + 0.2f, contextWidth - shadowSize, contextHeight - shadowSize));
|
|
|
|
CGContextSetStrokeColorWithColor(targetContext, UIColorRGB(strokeColorArgb).CGColor);
|
|
CGContextSetLineWidth(targetContext, strokeWidth);
|
|
CGContextStrokeEllipseInRect(targetContext, CGRectMake(shadowSize + strokeWidth / 2.0f, shadowSize + strokeWidth / 2.0f, contextWidth - (shadowSize + strokeWidth / 2.0f) * 2.0f, contextHeight - (shadowSize + strokeWidth / 2.0f) * 2.0f));
|
|
|
|
CGContextSetStrokeColorWithColor(targetContext, UIColorRGBA(strokeColorArgb, 0.4f).CGColor);
|
|
CGContextStrokeEllipseInRect(targetContext, CGRectMake(shadowSize + strokeWidth / 2.0f + 0.5f, shadowSize + strokeWidth / 2.0f - 0.5f, contextWidth - (shadowSize + strokeWidth / 2.0f) * 2.0f, contextHeight - (shadowSize + strokeWidth / 2.0f) * 2.0f));
|
|
|
|
CFRelease(targetContext);
|
|
});
|
|
|
|
const unsigned int radiusWithPadding = contextWidth / 2;
|
|
const unsigned int rightRadius = width - radiusWithPadding;
|
|
|
|
for (unsigned int y = 0; y < radiusWithPadding; y++)
|
|
{
|
|
for (unsigned int x = 0; x < radiusWithPadding; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[y * contextStride + x * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[y * contextStride + x * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (int y = 0; y < shadowSize; y++)
|
|
{
|
|
for (unsigned int x = radiusWithPadding; x < rightRadius; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = shadowColorArgb;
|
|
}
|
|
}
|
|
|
|
for (int y = shadowSize; y < shadowSize + strokeWidth; y++)
|
|
{
|
|
for (unsigned int x = radiusWithPadding; x < rightRadius; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = strokeColorArgb;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = 0; y < radiusWithPadding; y++)
|
|
{
|
|
for (unsigned int x = rightRadius; x < width; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[y * contextStride + (width - 1 - x) * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[y * contextStride + (width - 1 - x) * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = radiusWithPadding; y < height - radiusWithPadding; y++)
|
|
{
|
|
for (int x = 0; x < shadowSize; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = shadowColorArgb;
|
|
}
|
|
|
|
for (int x = shadowSize; x < shadowSize + strokeWidth; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = strokeColorArgb;
|
|
}
|
|
|
|
for (unsigned int x = width - shadowSize - strokeWidth; x < width - shadowSize; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = strokeColorArgb;
|
|
}
|
|
|
|
for (unsigned int x = width - shadowSize; x < width; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = shadowColorArgb;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - radiusWithPadding; y < height; y++)
|
|
{
|
|
for (unsigned int x = 0; x < radiusWithPadding; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[(height - 1 - y) * contextStride + x * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[(height - 1 - y) * contextStride + x * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - shadowSize - strokeWidth; y < height - shadowSize; y++)
|
|
{
|
|
for (unsigned int x = radiusWithPadding; x < rightRadius; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = strokeColorArgb;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - shadowSize; y < height; y++)
|
|
{
|
|
for (unsigned int x = radiusWithPadding; x < rightRadius; x++)
|
|
{
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = shadowColorArgb;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - radiusWithPadding; y < height; y++)
|
|
{
|
|
for (unsigned int x = width - radiusWithPadding; x < width; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[(height - 1 - y) * contextStride + (width - 1 - x) * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[(height - 1 - y) * contextStride + (width - 1 - x) * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TGAddImageCorners(void *memory, const unsigned int width, const unsigned int height, const unsigned int stride, int radius)
|
|
{
|
|
const int scale = TGIsRetina() ? 2 : 1;
|
|
|
|
const int contextWidth = radius * scale;
|
|
const int contextHeight = radius * scale;
|
|
const int contextStride = (4 * contextWidth + 15) & (~15);
|
|
|
|
uint8_t *contextMemory = NULL;
|
|
uint8_t *alphaMemory = NULL;
|
|
|
|
{
|
|
contextMemory = malloc(contextStride * contextHeight);
|
|
memset(contextMemory, 0, contextStride * contextHeight);
|
|
|
|
alphaMemory = malloc(contextStride * contextHeight);
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
CGContextRef targetContext = CGBitmapContextCreate(contextMemory, contextWidth, contextHeight, 8, contextStride, colorSpace, bitmapInfo);
|
|
CFRelease(colorSpace);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor blackColor].CGColor);
|
|
CGContextFillEllipseInRect(targetContext, CGRectMake(0.0f, 0.0f, contextWidth, contextHeight));
|
|
|
|
memcpy(alphaMemory, contextMemory, contextStride * contextHeight);
|
|
|
|
memset(contextMemory, 0, contextStride * contextHeight);
|
|
|
|
CFRelease(targetContext);
|
|
}
|
|
|
|
const unsigned int radiusWithPadding = contextWidth / 2;
|
|
const unsigned int rightRadius = width - radiusWithPadding;
|
|
|
|
for (unsigned int y = 0; y < radiusWithPadding; y++)
|
|
{
|
|
for (unsigned int x = 0; x < radiusWithPadding; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[y * contextStride + x * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[y * contextStride + x * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = 0; y < radiusWithPadding; y++)
|
|
{
|
|
for (unsigned int x = rightRadius; x < width; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[y * contextStride + (width - 1 - x) * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[y * contextStride + (width - 1 - x) * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - radiusWithPadding; y < height; y++)
|
|
{
|
|
for (unsigned int x = 0; x < radiusWithPadding; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[(height - 1 - y) * contextStride + x * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[(height - 1 - y) * contextStride + x * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
for (unsigned int y = height - radiusWithPadding; y < height; y++)
|
|
{
|
|
for (unsigned int x = width - radiusWithPadding; x < width; x++)
|
|
{
|
|
uint32_t alpha = alphaMemory[(height - 1 - y) * contextStride + (width - 1 - x) * 4 + 3];
|
|
uint32_t pixel = *((uint32_t *)(&memory[y * stride + x * 4]));
|
|
|
|
pixel = (alpha << 24) | (((((pixel >> 16) & 0xff) * alpha) >> 8) << 16) | (((((pixel >> 8) & 0xff) * alpha) >> 8) << 8) | (((((pixel >> 0) & 0xff) * alpha) >> 8) << 0);
|
|
pixel = alphaComposePremultipliedPixels(*((uint32_t *)&contextMemory[(height - 1 - y) * contextStride + (width - 1 - x) * 4]), pixel);
|
|
*((uint32_t *)(&memory[y * stride + x * 4])) = pixel;
|
|
}
|
|
}
|
|
|
|
free(alphaMemory);
|
|
free(contextMemory);
|
|
}
|
|
|
|
static int16_t *brightenMatrix(int32_t *outDivisor)
|
|
{
|
|
static int16_t saturationMatrix[16];
|
|
static const int32_t divisor = 256;
|
|
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
CGFloat s = 2.6f;
|
|
CGFloat offset = 0.02f;
|
|
CGFloat factor = 1.3f;
|
|
CGFloat satMatrix[] = {
|
|
0.0722f + 0.9278f * s, 0.0722f - 0.0722f * s, 0.0722f - 0.0722f * s, 0,
|
|
0.7152f - 0.7152f * s, 0.7152f + 0.2848f * s, 0.7152f - 0.7152f * s, 0,
|
|
0.2126f - 0.2126f * s, 0.2126f - 0.2126f * s, 0.2126f + 0.7873f * s, 0,
|
|
0.0f, 0.0f, 0.0f, 1,
|
|
};
|
|
CGFloat contrastMatrix[] = {
|
|
factor, 0.0f, 0.0f, 0.0f,
|
|
0.0f, factor, 0.0f, 0.0f,
|
|
0.0f, 0.0f, factor, 0.0f,
|
|
offset, offset, offset, 1.0f
|
|
};
|
|
CGFloat colorMatrix[16];
|
|
matrixMul(satMatrix, contrastMatrix, colorMatrix);
|
|
|
|
NSUInteger matrixSize = sizeof(colorMatrix) / sizeof(colorMatrix[0]);
|
|
for (NSUInteger i = 0; i < matrixSize; ++i) {
|
|
saturationMatrix[i] = (int16_t)CGRound(colorMatrix[i] * divisor);
|
|
}
|
|
});
|
|
|
|
if (outDivisor != NULL)
|
|
*outDivisor = divisor;
|
|
|
|
return saturationMatrix;
|
|
}
|
|
|
|
static int16_t *lightBrightenMatrix(int32_t *outDivisor)
|
|
{
|
|
static int16_t saturationMatrix[16];
|
|
static const int32_t divisor = 256;
|
|
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
CGFloat s = 1.8f;
|
|
CGFloat offset = 0.02f;
|
|
CGFloat factor = 1.1f;
|
|
CGFloat satMatrix[] = {
|
|
0.0722f + 0.9278f * s, 0.0722f - 0.0722f * s, 0.0722f - 0.0722f * s, 0,
|
|
0.7152f - 0.7152f * s, 0.7152f + 0.2848f * s, 0.7152f - 0.7152f * s, 0,
|
|
0.2126f - 0.2126f * s, 0.2126f - 0.2126f * s, 0.2126f + 0.7873f * s, 0,
|
|
0.0f, 0.0f, 0.0f, 1,
|
|
};
|
|
CGFloat contrastMatrix[] = {
|
|
factor, 0.0f, 0.0f, 0.0f,
|
|
0.0f, factor, 0.0f, 0.0f,
|
|
0.0f, 0.0f, factor, 0.0f,
|
|
offset, offset, offset, 1.0f
|
|
};
|
|
CGFloat colorMatrix[16];
|
|
matrixMul(satMatrix, contrastMatrix, colorMatrix);
|
|
|
|
NSUInteger matrixSize = sizeof(colorMatrix) / sizeof(colorMatrix[0]);
|
|
for (NSUInteger i = 0; i < matrixSize; ++i) {
|
|
saturationMatrix[i] = (int16_t)CGRound(colorMatrix[i] * divisor);
|
|
}
|
|
});
|
|
|
|
if (outDivisor != NULL)
|
|
*outDivisor = divisor;
|
|
|
|
return saturationMatrix;
|
|
}
|
|
|
|
static int16_t *secretMatrix(int32_t *outDivisor)
|
|
{
|
|
static int16_t saturationMatrix[16];
|
|
static const int32_t divisor = 256;
|
|
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
CGFloat s = 1.6f;
|
|
CGFloat offset = 0.0f;
|
|
CGFloat factor = 1.3f;
|
|
CGFloat satMatrix[] = {
|
|
0.0722f + 0.9278f * s, 0.0722f - 0.0722f * s, 0.0722f - 0.0722f * s, 0,
|
|
0.7152f - 0.7152f * s, 0.7152f + 0.2848f * s, 0.7152f - 0.7152f * s, 0,
|
|
0.2126f - 0.2126f * s, 0.2126f - 0.2126f * s, 0.2126f + 0.7873f * s, 0,
|
|
0.0f, 0.0f, 0.0f, 1,
|
|
};
|
|
CGFloat contrastMatrix[] = {
|
|
factor, 0.0f, 0.0f, 0.0f,
|
|
0.0f, factor, 0.0f, 0.0f,
|
|
0.0f, 0.0f, factor, 0.0f,
|
|
offset, offset, offset, 1.0f
|
|
};
|
|
CGFloat colorMatrix[16];
|
|
matrixMul(satMatrix, contrastMatrix, colorMatrix);
|
|
|
|
NSUInteger matrixSize = sizeof(colorMatrix) / sizeof(colorMatrix[0]);
|
|
for (NSUInteger i = 0; i < matrixSize; ++i) {
|
|
saturationMatrix[i] = (int16_t)CGRound(colorMatrix[i] * divisor);
|
|
}
|
|
});
|
|
|
|
if (outDivisor != NULL)
|
|
*outDivisor = divisor;
|
|
|
|
return saturationMatrix;
|
|
}
|
|
|
|
UIImage *TGAverageColorImage(UIColor *color)
|
|
{
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 1.0f), true, 1.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
CGContextSetFillColorWithColor(context, [color CGColor]);
|
|
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 1.0f));
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGAverageColorRoundImage(UIColor *color, CGSize size)
|
|
{
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
CGContextSetFillColorWithColor(context, [color CGColor]);
|
|
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 1.0f));
|
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGAverageColorAttachmentImage(UIColor *color, bool attachmentBorder)
|
|
{
|
|
return TGAverageColorAttachmentWithCornerRadiusImage(color, attachmentBorder, 13);
|
|
}
|
|
|
|
UIImage *TGAverageColorAttachmentWithCornerRadiusImage(UIColor *color, bool attachmentBorder, int cornerRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize size = CGSizeMake(36.0f, 36.0f);
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale)};
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, scale, scale);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
CGContextSetBlendMode(targetContext, kCGBlendModeCopy);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [color CGColor]);
|
|
CGContextFillRect(targetContext, CGRectMake(0.0f, 0.0f, targetContextSize.width, targetContextSize.height));
|
|
|
|
if (attachmentBorder)
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (unsigned int)targetBytesPerRow);
|
|
else
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(cornerRadius * scale));
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp] stretchableImageWithLeftCapWidth:(int)(targetContextSize.width / scale / 2) topCapHeight:(int)(targetContextSize.height / scale / 2)];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
/*CGFloat matrix[16];
|
|
int32_t divisor = 256;
|
|
int16_t *integerMatrix = brightenMatrix(&divisor);
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
matrix[i] = integerMatrix[i] / (CGFloat)divisor;
|
|
}
|
|
|
|
CGFloat vector[4];
|
|
[color getRed:&vector[3] green:&vector[2] blue:&vector[1] alpha:&vector[0]];
|
|
|
|
CGFloat resultColor[4];
|
|
matrixVectorMul(matrix, vector, resultColor);
|
|
|
|
UIImage *genericCircleImage = nil;
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(50.0f, 50.0f), false, scale);
|
|
CGContextRef circleContext = UIGraphicsGetCurrentContext();
|
|
CGContextSetFillColorWithColor(circleContext, [[UIColor alloc] initWithRed:vector[3] green:vector[2] blue:vector[1] alpha:vector[0]].CGColor);
|
|
CGContextFillEllipseInRect(circleContext, CGRectMake(0.0f, 0.0f, 50.0f, 50.0f));
|
|
genericCircleImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
[image setActionCircleImage:genericCircleImage];*/
|
|
|
|
return image;
|
|
}
|
|
|
|
static void modifyAndBlurImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride, bool strongBlur, int16_t *matrix)
|
|
{
|
|
unsigned int tempWidth = width / 6;
|
|
unsigned int tempHeight = height / 6;
|
|
unsigned int tempStride = ((4 * tempWidth + 15) & (~15));
|
|
void *tempPixels = malloc(tempStride * tempHeight);
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = width;
|
|
srcBuffer.height = height;
|
|
srcBuffer.rowBytes = stride;
|
|
srcBuffer.data = pixels;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = tempWidth;
|
|
dstBuffer.height = tempHeight;
|
|
dstBuffer.rowBytes = tempStride;
|
|
dstBuffer.data = tempPixels;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
fastBlurMore(tempWidth, tempHeight, tempStride, tempPixels);
|
|
if (strongBlur)
|
|
fastBlur(tempWidth, tempHeight, tempStride, tempPixels);
|
|
|
|
int32_t divisor = 256;
|
|
vImageMatrixMultiply_ARGB8888(&dstBuffer, &dstBuffer, matrix, divisor, NULL, NULL, kvImageDoNotTile);
|
|
vImageScale_ARGB8888(&dstBuffer, &srcBuffer, NULL, kvImageDoNotTile);
|
|
|
|
free(tempPixels);
|
|
}
|
|
|
|
static void modifyImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride, int16_t *matrix)
|
|
{
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = width;
|
|
dstBuffer.height = height;
|
|
dstBuffer.rowBytes = stride;
|
|
dstBuffer.data = pixels;
|
|
|
|
int32_t divisor = 256;
|
|
vImageMatrixMultiply_ARGB8888(&dstBuffer, &dstBuffer, matrix, divisor, NULL, NULL, kvImageDoNotTile);
|
|
}
|
|
|
|
static void brightenAndBlurImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride, bool strongBlur)
|
|
{
|
|
modifyAndBlurImage(pixels, width, height, stride, strongBlur, brightenMatrix(NULL));
|
|
}
|
|
|
|
static void brightenImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride)
|
|
{
|
|
modifyImage(pixels, width, height, stride, lightBrightenMatrix(NULL));
|
|
}
|
|
|
|
TGStaticBackdropAreaData *createImageBackdropArea(uint8_t *sourceImageMemory, int sourceImageWidth, int sourceImageHeight, int sourceImageStride, CGSize originalSize, CGRect sourceImageRect)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
const struct { int width, height; } contextSize = { (int)(sourceImageRect.size.width / 2), (int)(sourceImageRect.size.height / 2) };
|
|
size_t bytesPerRow = ((4 * (int)contextSize.width) + 15) & (~15);
|
|
|
|
CGFloat scalingFactor = contextSize.width / sourceImageRect.size.width;
|
|
|
|
void *memory = malloc((int)(bytesPerRow * contextSize.height));
|
|
memset(memory, 0x00, (int)(bytesPerRow * contextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
CGContextRef context = CGBitmapContextCreate(memory, (int)contextSize.width, (int)contextSize.height, 8, bytesPerRow, colorSpace, bitmapInfo);
|
|
CGColorSpaceRelease(colorSpace);
|
|
UIGraphicsPushContext(context);
|
|
|
|
CGContextTranslateCTM(context, contextSize.width / 2.0f, contextSize.height / 2.0f);
|
|
CGContextScaleCTM(context, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(context, -contextSize.width / 2.0f, -contextSize.height / 2.0f);
|
|
|
|
CGRect imageRect = CGRectMake(-sourceImageRect.origin.x * scalingFactor, -sourceImageRect.origin.y * scalingFactor, originalSize.width * scalingFactor, originalSize.height * scalingFactor);
|
|
|
|
float luminance = 0.0f;
|
|
float realLuminance = 0.0f;
|
|
float variance = 0.0f;
|
|
|
|
if (sourceImageMemory != NULL)
|
|
{
|
|
fastScaleImage(sourceImageMemory, sourceImageWidth, sourceImageHeight, sourceImageStride, memory, contextSize.width, contextSize.height, (int)bytesPerRow, imageRect);
|
|
}
|
|
|
|
/*if (luminance > 0.8f)
|
|
modifyAndBlurImage(memory, contextSize.width, contextSize.height, bytesPerRow, false, brightenTimestampMatrix(NULL));
|
|
else
|
|
modifyAndBlurImage(memory, contextSize.width, contextSize.height, bytesPerRow, false, darkenTimestampMatrix(NULL));*/
|
|
|
|
fastBlur(contextSize.width, contextSize.height, (int)bytesPerRow, memory);
|
|
fastBlur(contextSize.width, contextSize.height, (int)bytesPerRow, memory);
|
|
computeImageVariance(memory, contextSize.width, contextSize.height, (int)bytesPerRow, &variance, &luminance, &realLuminance);
|
|
|
|
if ((variance >= 0.009f && realLuminance > 0.7f) || variance >= 0.05f)
|
|
{
|
|
uint32_t color = TGImageAverageColor(memory, contextSize.width, contextSize.height, (int)bytesPerRow);
|
|
//color = 0xff00ffff;
|
|
CGContextSetFillColorWithColor(context, UIColorRGBA(color, 0.7f).CGColor);
|
|
CGContextFillRect(context, CGRectMake(0, 0, contextSize.width, contextSize.height));
|
|
|
|
uint32_t r = (color >> 16) & 0xff;
|
|
uint32_t g = (color >> 8) & 0xff;
|
|
uint32_t b = color & 0xff;
|
|
|
|
uint32_t pixelLuminance = (uint8_t)((r * 299 + g * 587 + b * 114) / 1000);
|
|
luminance = pixelLuminance / 255.0f;
|
|
if (luminance < 0.85f)
|
|
luminance = 0.0f;
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(context);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
UIGraphicsPopContext();
|
|
CFRelease(context);
|
|
free(memory);
|
|
|
|
TGStaticBackdropAreaData *backdropArea = [[TGStaticBackdropAreaData alloc] initWithBackground:image mappedRect:CGRectMake(sourceImageRect.origin.x / originalSize.width, sourceImageRect.origin.y / originalSize.height, sourceImageRect.size.width / originalSize.width, sourceImageRect.size.height / originalSize.height)];
|
|
backdropArea.luminance = luminance;
|
|
|
|
return backdropArea;
|
|
}
|
|
|
|
TGStaticBackdropAreaData *createTimestampBackdropArea(uint8_t *sourceImageMemory, int sourceImageWidth, int sourceImageHeight, int sourceImageStride, CGSize originalSize)
|
|
{
|
|
const int extraRadius = 0.0f;
|
|
const struct { int width, height; } unscaledSize = { 84 + extraRadius * 2, 18 };
|
|
const struct { int right, bottom; } padding = { 6 - extraRadius, 6 };
|
|
|
|
return createImageBackdropArea(sourceImageMemory, sourceImageWidth, sourceImageHeight, sourceImageStride, originalSize, CGRectMake(originalSize.width - padding.right - unscaledSize.width, originalSize.height - padding.bottom - unscaledSize.height, unscaledSize.width, unscaledSize.height));
|
|
}
|
|
|
|
TGStaticBackdropAreaData *createAdditionalDataBackdropArea(uint8_t *sourceImageMemory, int sourceImageWidth, int sourceImageHeight, int sourceImageStride, CGSize originalSize)
|
|
{
|
|
const int extraRadius = 0.0f;
|
|
const struct { int width, height; } unscaledSize = { 160 + extraRadius * 2, 18 };
|
|
const struct { int left, top; } padding = { 6 - extraRadius, 6 };
|
|
|
|
return createImageBackdropArea(sourceImageMemory, sourceImageWidth, sourceImageHeight, sourceImageStride, originalSize, CGRectMake(padding.left, padding.top, unscaledSize.width, unscaledSize.height));
|
|
}
|
|
|
|
UIImage *TGBlurredAttachmentImage(UIImage *source, CGSize size, uint32_t *averageColor, bool attachmentBorder)
|
|
{
|
|
return TGBlurredAttachmentWithCornerRadiusImage(source, size, averageColor, attachmentBorder, 14);
|
|
}
|
|
|
|
UIImage *TGBlurredAttachmentWithCornerRadiusImage(UIImage *source, CGSize size, uint32_t *averageColor, bool attachmentBorder, int cornerRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize fittedSize = fitSize(size, CGSizeMake(90, 90));
|
|
|
|
CGFloat actionCircleDiameter = 50.0f;
|
|
|
|
const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height };
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale)};
|
|
const struct { int width, height; } actionCircleContextSize = { (int)(actionCircleDiameter * scale), (int)(actionCircleDiameter * scale) };
|
|
|
|
size_t blurredBytesPerRow = ((4 * (int)blurredContextSize.width) + 15) & (~15);
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
size_t actionCircleBytesPerRow = ((4 * (int)actionCircleContextSize.width) + 15) & (~15);
|
|
|
|
void *blurredMemory = malloc((int)(blurredBytesPerRow * blurredContextSize.height));
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(blurredContext);
|
|
CGContextTranslateCTM(blurredContext, blurredContextSize.width / 2.0f, blurredContextSize.height / 2.0f);
|
|
CGContextScaleCTM(blurredContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(blurredContext, -blurredContextSize.width / 2.0f, -blurredContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(blurredContext, kCGInterpolationLow);
|
|
[source drawInRect:CGRectMake(0, 0, blurredContextSize.width, blurredContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
fastBlur((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(blurredMemory, blurredContextSize.width, blurredContextSize.height, (int)blurredBytesPerRow);
|
|
}
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = blurredContextSize.width;
|
|
srcBuffer.height = blurredContextSize.height;
|
|
srcBuffer.rowBytes = blurredBytesPerRow;
|
|
srcBuffer.data = blurredMemory;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = targetContextSize.width;
|
|
dstBuffer.height = targetContextSize.height;
|
|
dstBuffer.rowBytes = targetBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
CGContextRelease(blurredContext);
|
|
free(blurredMemory);
|
|
|
|
UIGraphicsPushContext(actionCircleContext);
|
|
CGContextTranslateCTM(actionCircleContext, actionCircleContextSize.width / 2.0f, actionCircleContextSize.height / 2.0f);
|
|
CGContextScaleCTM(actionCircleContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(actionCircleContext, -actionCircleContextSize.width / 2.0f, -actionCircleContextSize.height / 2.0f);
|
|
|
|
CGContextSetInterpolationQuality(actionCircleContext, kCGInterpolationLow);
|
|
CGContextSetBlendMode(actionCircleContext, kCGBlendModeCopy);
|
|
|
|
[source drawInRect:CGRectMake((actionCircleContextSize.width - targetContextSize.width) / 2.0f, (actionCircleContextSize.height - targetContextSize.height) / 2.0f, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
brightenAndBlurImage(actionCircleMemory, actionCircleContextSize.width, actionCircleContextSize.height, (int)actionCircleBytesPerRow, scale < 2);
|
|
|
|
CGContextBeginPath(actionCircleContext);
|
|
CGContextAddRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextAddEllipseInRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextClosePath(actionCircleContext);
|
|
|
|
CGContextSetFillColorWithColor(actionCircleContext, [UIColor clearColor].CGColor);
|
|
CGContextEOFillPath(actionCircleContext);
|
|
|
|
CGImageRef actionCircleBitmapImage = CGBitmapContextCreateImage(actionCircleContext);
|
|
UIImage *actionCircleImage = [[UIImage alloc] initWithCGImage:actionCircleBitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(actionCircleBitmapImage);
|
|
|
|
CGContextRelease(actionCircleContext);
|
|
free(actionCircleMemory);
|
|
|
|
TGStaticBackdropAreaData *timestampBackdropArea = createTimestampBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
TGStaticBackdropAreaData *additionalDataBackdropArea = createAdditionalDataBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
|
|
if (attachmentBorder)
|
|
{
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
}
|
|
else
|
|
{
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(cornerRadius * scale));
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
TGStaticBackdropImageData *backdropData = [[TGStaticBackdropImageData alloc] init];
|
|
[backdropData setBackdropArea:[[TGStaticBackdropAreaData alloc] initWithBackground:actionCircleImage] forKey:TGStaticBackdropMessageActionCircle];
|
|
|
|
[backdropData setBackdropArea:timestampBackdropArea forKey:TGStaticBackdropMessageTimestamp];
|
|
[backdropData setBackdropArea:additionalDataBackdropArea forKey:TGStaticBackdropMessageAdditionalData];
|
|
|
|
[image setStaticBackdropImageData:backdropData];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGSecretBlurredAttachmentImage(UIImage *source, CGSize size, uint32_t *averageColor, bool attachmentBorder)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize fittedSize = fitSize(size, CGSizeMake(40, 40));
|
|
|
|
CGFloat actionCircleDiameter = 50.0f;
|
|
|
|
const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height };
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale)};
|
|
const struct { int width, height; } actionCircleContextSize = { (int)(actionCircleDiameter * scale), (int)(actionCircleDiameter * scale) };
|
|
|
|
size_t blurredBytesPerRow = ((4 * (int)blurredContextSize.width) + 15) & (~15);
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
size_t actionCircleBytesPerRow = ((4 * (int)actionCircleContextSize.width) + 15) & (~15);
|
|
|
|
void *blurredMemory = malloc((int)(blurredBytesPerRow * blurredContextSize.height));
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(blurredContext);
|
|
CGContextTranslateCTM(blurredContext, blurredContextSize.width / 2.0f, blurredContextSize.height / 2.0f);
|
|
CGContextScaleCTM(blurredContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(blurredContext, -blurredContextSize.width / 2.0f, -blurredContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(blurredContext, kCGInterpolationLow);
|
|
[source drawInRect:CGRectMake(0, 0, blurredContextSize.width, blurredContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
fastBlurMore((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
fastBlurMore((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
fastBlurMore((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
|
|
int32_t divisor = 256;
|
|
vImage_Buffer dstBuffer1;
|
|
dstBuffer1.width = (int)blurredContextSize.width;
|
|
dstBuffer1.height = (int)blurredContextSize.height;
|
|
dstBuffer1.rowBytes = blurredBytesPerRow;
|
|
dstBuffer1.data = blurredMemory;
|
|
vImageMatrixMultiply_ARGB8888(&dstBuffer1, &dstBuffer1, secretMatrix(NULL), divisor, NULL, NULL, kvImageDoNotTile);
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(blurredMemory, blurredContextSize.width, blurredContextSize.height, (int)blurredBytesPerRow);
|
|
}
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = blurredContextSize.width;
|
|
srcBuffer.height = blurredContextSize.height;
|
|
srcBuffer.rowBytes = blurredBytesPerRow;
|
|
srcBuffer.data = blurredMemory;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = targetContextSize.width;
|
|
dstBuffer.height = targetContextSize.height;
|
|
dstBuffer.rowBytes = targetBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
CGContextRelease(blurredContext);
|
|
free(blurredMemory);
|
|
|
|
UIGraphicsPushContext(actionCircleContext);
|
|
CGContextTranslateCTM(actionCircleContext, actionCircleContextSize.width / 2.0f, actionCircleContextSize.height / 2.0f);
|
|
CGContextScaleCTM(actionCircleContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(actionCircleContext, -actionCircleContextSize.width / 2.0f, -actionCircleContextSize.height / 2.0f);
|
|
|
|
CGContextSetInterpolationQuality(actionCircleContext, kCGInterpolationLow);
|
|
CGContextSetBlendMode(actionCircleContext, kCGBlendModeCopy);
|
|
|
|
[source drawInRect:CGRectMake((actionCircleContextSize.width - targetContextSize.width) / 2.0f, (actionCircleContextSize.height - targetContextSize.height) / 2.0f, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
brightenAndBlurImage(actionCircleMemory, actionCircleContextSize.width, actionCircleContextSize.height, (int)actionCircleBytesPerRow, scale < 2);
|
|
|
|
CGContextBeginPath(actionCircleContext);
|
|
CGContextAddRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextAddEllipseInRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextClosePath(actionCircleContext);
|
|
|
|
CGContextSetFillColorWithColor(actionCircleContext, [UIColor clearColor].CGColor);
|
|
CGContextEOFillPath(actionCircleContext);
|
|
|
|
CGImageRef actionCircleBitmapImage = CGBitmapContextCreateImage(actionCircleContext);
|
|
UIImage *actionCircleImage = [[UIImage alloc] initWithCGImage:actionCircleBitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(actionCircleBitmapImage);
|
|
|
|
CGContextRelease(actionCircleContext);
|
|
free(actionCircleMemory);
|
|
|
|
TGStaticBackdropAreaData *timestampBackdropArea = createTimestampBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
TGStaticBackdropAreaData *additionalDataBackdropArea = createAdditionalDataBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
|
|
if (attachmentBorder) {
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
} else {
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(13 * scale));
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
TGStaticBackdropImageData *backdropData = [[TGStaticBackdropImageData alloc] init];
|
|
[backdropData setBackdropArea:[[TGStaticBackdropAreaData alloc] initWithBackground:actionCircleImage] forKey:TGStaticBackdropMessageActionCircle];
|
|
|
|
[backdropData setBackdropArea:timestampBackdropArea forKey:TGStaticBackdropMessageTimestamp];
|
|
[backdropData setBackdropArea:additionalDataBackdropArea forKey:TGStaticBackdropMessageAdditionalData];
|
|
|
|
[image setStaticBackdropImageData:backdropData];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGBlurredFileImage(UIImage *source, CGSize size, uint32_t *averageColor, int borderRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize fittedSize = fitSize(size, CGSizeMake(90, 90));
|
|
|
|
CGFloat actionCircleDiameter = 50.0f;
|
|
|
|
const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height };
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale)};
|
|
const struct { int width, height; } actionCircleContextSize = { (int)(actionCircleDiameter * scale), (int)(actionCircleDiameter * scale) };
|
|
|
|
size_t blurredBytesPerRow = ((4 * (int)blurredContextSize.width) + 15) & (~15);
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
size_t actionCircleBytesPerRow = ((4 * (int)actionCircleContextSize.width) + 15) & (~15);
|
|
|
|
void *blurredMemory = malloc((int)(blurredBytesPerRow * blurredContextSize.height));
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(blurredContext);
|
|
CGContextTranslateCTM(blurredContext, blurredContextSize.width / 2.0f, blurredContextSize.height / 2.0f);
|
|
CGContextScaleCTM(blurredContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(blurredContext, -blurredContextSize.width / 2.0f, -blurredContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(blurredContext, kCGInterpolationLow);
|
|
[source drawInRect:CGRectMake(0, 0, blurredContextSize.width, blurredContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
fastBlur((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(blurredMemory, blurredContextSize.width, blurredContextSize.height, (int)blurredBytesPerRow);
|
|
}
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = blurredContextSize.width;
|
|
srcBuffer.height = blurredContextSize.height;
|
|
srcBuffer.rowBytes = blurredBytesPerRow;
|
|
srcBuffer.data = blurredMemory;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = targetContextSize.width;
|
|
dstBuffer.height = targetContextSize.height;
|
|
dstBuffer.rowBytes = targetBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
CGContextRelease(blurredContext);
|
|
free(blurredMemory);
|
|
|
|
UIGraphicsPushContext(actionCircleContext);
|
|
CGContextTranslateCTM(actionCircleContext, actionCircleContextSize.width / 2.0f, actionCircleContextSize.height / 2.0f);
|
|
CGContextScaleCTM(actionCircleContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(actionCircleContext, -actionCircleContextSize.width / 2.0f, -actionCircleContextSize.height / 2.0f);
|
|
|
|
CGContextSetInterpolationQuality(actionCircleContext, kCGInterpolationLow);
|
|
CGContextSetBlendMode(actionCircleContext, kCGBlendModeCopy);
|
|
|
|
[source drawInRect:CGRectMake((actionCircleContextSize.width - targetContextSize.width) / 2.0f, (actionCircleContextSize.height - targetContextSize.height) / 2.0f, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
brightenAndBlurImage(actionCircleMemory, actionCircleContextSize.width, actionCircleContextSize.height, (int)actionCircleBytesPerRow, scale < 2);
|
|
|
|
CGContextBeginPath(actionCircleContext);
|
|
CGContextAddRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextAddEllipseInRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextClosePath(actionCircleContext);
|
|
|
|
CGContextSetFillColorWithColor(actionCircleContext, [UIColor clearColor].CGColor);
|
|
CGContextEOFillPath(actionCircleContext);
|
|
|
|
CGImageRef actionCircleBitmapImage = CGBitmapContextCreateImage(actionCircleContext);
|
|
UIImage *actionCircleImage = [[UIImage alloc] initWithCGImage:actionCircleBitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(actionCircleBitmapImage);
|
|
|
|
CGContextRelease(actionCircleContext);
|
|
free(actionCircleMemory);
|
|
|
|
TGStaticBackdropAreaData *timestampBackdropArea = createTimestampBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
TGStaticBackdropAreaData *additionalDataBackdropArea = createAdditionalDataBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
|
|
if (borderRadius != 0)
|
|
{
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(borderRadius * scale));
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
TGStaticBackdropImageData *backdropData = [[TGStaticBackdropImageData alloc] init];
|
|
[backdropData setBackdropArea:[[TGStaticBackdropAreaData alloc] initWithBackground:actionCircleImage] forKey:TGStaticBackdropMessageActionCircle];
|
|
|
|
[backdropData setBackdropArea:timestampBackdropArea forKey:TGStaticBackdropMessageTimestamp];
|
|
[backdropData setBackdropArea:additionalDataBackdropArea forKey:TGStaticBackdropMessageAdditionalData];
|
|
|
|
[image setStaticBackdropImageData:backdropData];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGBlurredAlphaImage(UIImage *source, CGSize size)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize fittedSize = fitSize(size, CGSizeMake(90, 90));
|
|
|
|
const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height };
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale)};
|
|
|
|
size_t blurredBytesPerRow = ((4 * (int)blurredContextSize.width) + 15) & (~15);
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *blurredMemory = malloc((int)(blurredBytesPerRow * blurredContextSize.height));
|
|
memset(blurredMemory, 0, blurredBytesPerRow * blurredContextSize.height);
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(blurredContext);
|
|
CGContextTranslateCTM(blurredContext, blurredContextSize.width / 2.0f, blurredContextSize.height / 2.0f);
|
|
CGContextScaleCTM(blurredContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(blurredContext, -blurredContextSize.width / 2.0f, -blurredContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(blurredContext, kCGInterpolationLow);
|
|
[source drawInRect:CGRectMake(6, 6, blurredContextSize.width - 12, blurredContextSize.height - 12) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = blurredContextSize.width;
|
|
srcBuffer.height = blurredContextSize.height;
|
|
srcBuffer.rowBytes = blurredBytesPerRow;
|
|
srcBuffer.data = blurredMemory;
|
|
|
|
{
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = blurredContextSize.width;
|
|
dstBuffer.height = blurredContextSize.height;
|
|
dstBuffer.rowBytes = blurredBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
int boxSize = (int)(0.02f * 100);
|
|
boxSize = boxSize - (boxSize % 2) + 1;
|
|
|
|
vImageBoxConvolve_ARGB8888(&srcBuffer, &dstBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
|
|
vImageBoxConvolve_ARGB8888(&dstBuffer, &srcBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
|
|
}
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = targetContextSize.width;
|
|
dstBuffer.height = targetContextSize.height;
|
|
dstBuffer.rowBytes = targetBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
CGContextRelease(blurredContext);
|
|
free(blurredMemory);
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
[image setExtendedEdgeInsets:UIEdgeInsetsMake(12.0f, 12.0f, 12.0f, 12.0f)];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGBlurredRectangularImage(UIImage *source, CGSize size, CGSize renderSize, uint32_t *averageColor, void (^pixelProcessingBlock)(void *, int, int, int))
|
|
{
|
|
CGSize fittedSize = fitSize(size, CGSizeMake(90, 90));
|
|
CGSize fittedRenderSize = CGSizeMake(fittedSize.width / size.width * renderSize.width, fittedSize.height / size.height * renderSize.height);
|
|
|
|
const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height };
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width), (int)(size.height)};
|
|
|
|
size_t blurredBytesPerRow = ((4 * (int)blurredContextSize.width) + 15) & (~15);
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *blurredMemory = malloc((int)(blurredBytesPerRow * blurredContextSize.height));
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(blurredContext);
|
|
CGContextTranslateCTM(blurredContext, blurredContextSize.width / 2.0f, blurredContextSize.height / 2.0f);
|
|
CGContextScaleCTM(blurredContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(blurredContext, -blurredContextSize.width / 2.0f, -blurredContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(blurredContext, kCGInterpolationLow);
|
|
[source drawInRect:CGRectMake((blurredContextSize.width - fittedRenderSize.width) / 2.0f, (blurredContextSize.height - fittedRenderSize.height) / 2.0f, fittedRenderSize.width, fittedRenderSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
fastBlur((int)blurredContextSize.width, (int)blurredContextSize.height, (int)blurredBytesPerRow, blurredMemory);
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(blurredMemory, blurredContextSize.width, blurredContextSize.height, (int)blurredBytesPerRow);
|
|
}
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = blurredContextSize.width;
|
|
srcBuffer.height = blurredContextSize.height;
|
|
srcBuffer.rowBytes = blurredBytesPerRow;
|
|
srcBuffer.data = blurredMemory;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = targetContextSize.width;
|
|
dstBuffer.height = targetContextSize.height;
|
|
dstBuffer.rowBytes = targetBytesPerRow;
|
|
dstBuffer.data = targetMemory;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
CGContextRelease(blurredContext);
|
|
free(blurredMemory);
|
|
|
|
if (pixelProcessingBlock)
|
|
{
|
|
pixelProcessingBlock(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGLoadedAttachmentImage(UIImage *source, CGSize size, uint32_t *averageColor, bool attachmentBorder)
|
|
{
|
|
return TGLoadedAttachmentWithCornerRadiusImage(source, size, averageColor, attachmentBorder, 14);
|
|
}
|
|
|
|
UIImage *TGLoadedAttachmentWithCornerRadiusImage(UIImage *source, CGSize size, uint32_t *averageColor, bool attachmentBorder, int cornerRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGFloat actionCircleDiameter = 50.0f;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
const struct { int width, height; } actionCircleContextSize = { (int)(actionCircleDiameter * scale), (int)(actionCircleDiameter * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
size_t actionCircleBytesPerRow = ((4 * (int)actionCircleContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(actionCircleContext);
|
|
CGContextTranslateCTM(actionCircleContext, actionCircleContextSize.width / 2.0f, actionCircleContextSize.height / 2.0f);
|
|
CGContextScaleCTM(actionCircleContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(actionCircleContext, -actionCircleContextSize.width / 2.0f, -actionCircleContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(actionCircleContext, kCGInterpolationLow);
|
|
CGContextSetBlendMode(actionCircleContext, kCGBlendModeCopy);
|
|
|
|
[source drawInRect:CGRectMake((actionCircleContextSize.width - targetContextSize.width) / 2.0f, (actionCircleContextSize.height - targetContextSize.height) / 2.0f, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
brightenAndBlurImage(actionCircleMemory, actionCircleContextSize.width, actionCircleContextSize.height, (int)actionCircleBytesPerRow, true);
|
|
|
|
CGContextBeginPath(actionCircleContext);
|
|
CGContextAddRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextAddEllipseInRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextClosePath(actionCircleContext);
|
|
|
|
CGContextSetFillColorWithColor(actionCircleContext, [UIColor clearColor].CGColor);
|
|
CGContextEOFillPath(actionCircleContext);
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
CGImageRef actionCircleBitmapImage = CGBitmapContextCreateImage(actionCircleContext);
|
|
UIImage *actionCircleImage = [[UIImage alloc] initWithCGImage:actionCircleBitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(actionCircleBitmapImage);
|
|
|
|
CGContextRelease(actionCircleContext);
|
|
free(actionCircleMemory);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
[source drawInRect:CGRectMake(0, 0, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
}
|
|
|
|
TGStaticBackdropAreaData *timestampBackdropArea = createTimestampBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
TGStaticBackdropAreaData *additionalDataBackdropArea = createAdditionalDataBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
|
|
if (attachmentBorder)
|
|
{
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
}
|
|
else
|
|
{
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(cornerRadius * scale));
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
TGStaticBackdropImageData *backdropData = [[TGStaticBackdropImageData alloc] init];
|
|
[backdropData setBackdropArea:[[TGStaticBackdropAreaData alloc] initWithBackground:actionCircleImage] forKey:TGStaticBackdropMessageActionCircle];
|
|
|
|
[backdropData setBackdropArea:timestampBackdropArea forKey:TGStaticBackdropMessageTimestamp];
|
|
[backdropData setBackdropArea:additionalDataBackdropArea forKey:TGStaticBackdropMessageAdditionalData];
|
|
|
|
[image setStaticBackdropImageData:backdropData];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGAnimationFrameAttachmentImage(UIImage *source, CGSize size, CGSize renderSize)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
renderSize.width *= scale;
|
|
renderSize.height *= scale;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor blackColor].CGColor);
|
|
CGContextFillRect(targetContext, CGRectMake(0, 0, targetContextSize.width, targetContextSize.height));
|
|
CGRect imageRect = CGRectMake((targetContextSize.width - renderSize.width) / 2.0f, (targetContextSize.height - renderSize.height) / 2.0f, renderSize.width, renderSize.height);
|
|
[source drawInRect:imageRect blendMode:kCGBlendModeNormal alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGLoadedFileImage(UIImage *source, CGSize size, uint32_t *averageColor, int borderRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGFloat actionCircleDiameter = 50.0f;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
const struct { int width, height; } actionCircleContextSize = { (int)(actionCircleDiameter * scale), (int)(actionCircleDiameter * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
size_t actionCircleBytesPerRow = ((4 * (int)actionCircleContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
memset(targetMemory, 0xff, targetBytesPerRow * targetContextSize.height);
|
|
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
|
|
memset(actionCircleMemory, 0xff, actionCircleBytesPerRow * actionCircleContextSize.height);
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(actionCircleContext);
|
|
CGContextTranslateCTM(actionCircleContext, actionCircleContextSize.width / 2.0f, actionCircleContextSize.height / 2.0f);
|
|
CGContextScaleCTM(actionCircleContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(actionCircleContext, -actionCircleContextSize.width / 2.0f, -actionCircleContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(actionCircleContext, kCGInterpolationLow);
|
|
CGContextSetBlendMode(actionCircleContext, kCGBlendModeCopy);
|
|
|
|
[source drawInRect:CGRectMake((actionCircleContextSize.width - targetContextSize.width) / 2.0f, (actionCircleContextSize.height - targetContextSize.height) / 2.0f, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
brightenAndBlurImage(actionCircleMemory, actionCircleContextSize.width, actionCircleContextSize.height, (int)actionCircleBytesPerRow, true);
|
|
|
|
CGContextBeginPath(actionCircleContext);
|
|
CGContextAddRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextAddEllipseInRect(actionCircleContext, CGRectMake(0.0f, 0.0f, actionCircleContextSize.width, actionCircleContextSize.height));
|
|
CGContextClosePath(actionCircleContext);
|
|
|
|
CGContextSetFillColorWithColor(actionCircleContext, [UIColor clearColor].CGColor);
|
|
CGContextEOFillPath(actionCircleContext);
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
CGImageRef actionCircleBitmapImage = CGBitmapContextCreateImage(actionCircleContext);
|
|
UIImage *actionCircleImage = [[UIImage alloc] initWithCGImage:actionCircleBitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(actionCircleBitmapImage);
|
|
|
|
CGContextRelease(actionCircleContext);
|
|
free(actionCircleMemory);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
[source drawInRect:CGRectMake(0, 0, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
if (averageColor != NULL)
|
|
*averageColor = TGImageAverageColor(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
|
|
if (borderRadius != 0)
|
|
{
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, (int)(borderRadius * scale));
|
|
}
|
|
|
|
TGStaticBackdropAreaData *timestampBackdropArea = createTimestampBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
TGStaticBackdropAreaData *additionalDataBackdropArea = createAdditionalDataBackdropArea(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, CGSizeMake(size.width, size.height));
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
TGStaticBackdropImageData *backdropData = [[TGStaticBackdropImageData alloc] init];
|
|
[backdropData setBackdropArea:[[TGStaticBackdropAreaData alloc] initWithBackground:actionCircleImage] forKey:TGStaticBackdropMessageActionCircle];
|
|
|
|
[backdropData setBackdropArea:timestampBackdropArea forKey:TGStaticBackdropMessageTimestamp];
|
|
[backdropData setBackdropArea:additionalDataBackdropArea forKey:TGStaticBackdropMessageAdditionalData];
|
|
|
|
[image setStaticBackdropImageData:backdropData];
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGReducedAttachmentImage(UIImage *source, CGSize originalSize, bool attachmentBorder)
|
|
{
|
|
return TGReducedAttachmentWithCornerRadiusImage(source, originalSize, attachmentBorder, 14);
|
|
}
|
|
|
|
UIImage *TGReducedAttachmentWithCornerRadiusImage(UIImage *source, CGSize originalSize, bool attachmentBorder, int cornerRadius)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
CGSize size = CGSizeMake(CGFloor(originalSize.width * 0.4f), CGFloor(originalSize.height * 0.4f));
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
const struct { int width, height; } targetContextOriginalSize = { (int)(originalSize.width * scale), (int)(originalSize.height * scale) };
|
|
|
|
CGFloat padding = 32.0f;
|
|
CGFloat scaledWidth = targetContextOriginalSize.width / ((targetContextOriginalSize.width - padding * 2.0f) / (targetContextSize.width - padding * 2.0f));
|
|
CGFloat scaledHeight = targetContextOriginalSize.height / ((targetContextOriginalSize.height - padding * 2.0f) / (targetContextSize.height - padding * 2.0f));
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
|
|
CGContextSetInterpolationQuality(targetContext, kCGInterpolationMedium);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor grayColor].CGColor);
|
|
CGContextFillRect(targetContext, CGRectMake(0.0f, 0.0, targetContextSize.width, targetContextSize.height));
|
|
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(0.0f, 0.0f, padding, padding));
|
|
[source drawInRect:CGRectMake(0, 0, targetContextOriginalSize.width, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(targetContextSize.width - padding, 0.0f, padding, padding));
|
|
[source drawInRect:CGRectMake(targetContextSize.width - targetContextOriginalSize.width, 0, targetContextOriginalSize.width, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(0.0f, targetContextSize.height - padding, padding, padding));
|
|
[source drawInRect:CGRectMake(0, targetContextSize.height - targetContextOriginalSize.height, targetContextOriginalSize.width, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(targetContextSize.width - padding, targetContextSize.height - padding, padding, padding));
|
|
[source drawInRect:CGRectMake(targetContextSize.width - targetContextOriginalSize.width, targetContextSize.height - targetContextOriginalSize.height, targetContextOriginalSize.width, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(padding, 0.0f, targetContextSize.width - padding * 2, padding));
|
|
[source drawInRect:CGRectMake((targetContextSize.width - scaledWidth) / 2.0f, 0, scaledWidth, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(padding, targetContextSize.height - padding, targetContextSize.width - padding * 2, padding));
|
|
[source drawInRect:CGRectMake((targetContextSize.width - scaledWidth) / 2.0f, targetContextSize.height - targetContextOriginalSize.height, scaledWidth, targetContextOriginalSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(0.0f, padding, padding, targetContextSize.height - padding * 2));
|
|
[source drawInRect:CGRectMake(0, (targetContextSize.height - scaledHeight) / 2.0f, targetContextOriginalSize.width, scaledHeight) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextSaveGState(targetContext);
|
|
CGContextClipToRect(targetContext, CGRectMake(targetContextSize.width - padding, padding, padding, targetContextSize.height - padding * 2));
|
|
[source drawInRect:CGRectMake(targetContextSize.width - targetContextOriginalSize.width, (targetContextSize.height - scaledHeight) / 2.0f, targetContextOriginalSize.width, scaledHeight) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
CGContextRestoreGState(targetContext);
|
|
|
|
CGContextClipToRect(targetContext, CGRectMake(padding, padding, targetContextSize.width - padding * 2, targetContextSize.height - padding * 2));
|
|
[source drawInRect:CGRectMake((targetContextSize.width - scaledWidth) / 2.0f, (targetContextSize.height - scaledHeight) / 2.0f, scaledWidth, scaledHeight) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
if (attachmentBorder)
|
|
addAttachmentImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
else
|
|
{
|
|
TGAddImageCorners(targetMemory, targetContextSize.width, targetContextSize.height, (int)(int)targetBytesPerRow, (int)(cornerRadius * scale));
|
|
}
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
//return image;
|
|
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(padding / scale, padding / scale, padding / scale, padding / scale) resizingMode:UIImageResizingModeStretch];
|
|
}
|
|
|
|
UIImage *TGBlurredBackgroundImage(UIImage *source, CGSize size)
|
|
{
|
|
CGFloat scale = source.scale;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
[source drawInRect:CGRectMake(0, 0, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
brightenAndBlurImage(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, false);
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGRoundImage(UIImage *source, CGSize size)
|
|
{
|
|
CGFloat scale = TGIsRetina() ? 2.0f : 1.0f;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
memset(targetMemory, 0, (int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
|
|
CGContextBeginPath(targetContext);
|
|
CGContextAddEllipseInRect(targetContext, CGRectMake(0.0f, 0.0f, targetContextSize.width, targetContextSize.height));
|
|
CGContextClip(targetContext);
|
|
|
|
[source drawInRect:CGRectMake(0, 0, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
void TGPlainImageAverageColor(UIImage *source, uint32_t *averageColor)
|
|
{
|
|
CGFloat scale = source.scale;
|
|
CGSize size = source.size;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
[source drawInRect:CGRectMake(0, 0, targetContextSize.width, targetContextSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
if (averageColor != NULL)
|
|
*averageColor = TGImageAverageColor(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
}
|
|
|
|
static void lightBlurImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride)
|
|
{
|
|
unsigned int tempWidth = width / 6;
|
|
unsigned int tempHeight = height / 6;
|
|
unsigned int tempStride = ((4 * tempWidth + 15) & (~15));
|
|
void *tempPixels = malloc(tempStride * tempHeight);
|
|
|
|
vImage_Buffer srcBuffer;
|
|
srcBuffer.width = width;
|
|
srcBuffer.height = height;
|
|
srcBuffer.rowBytes = stride;
|
|
srcBuffer.data = pixels;
|
|
|
|
vImage_Buffer dstBuffer;
|
|
dstBuffer.width = tempWidth;
|
|
dstBuffer.height = tempHeight;
|
|
dstBuffer.rowBytes = tempStride;
|
|
dstBuffer.data = tempPixels;
|
|
|
|
vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageDoNotTile);
|
|
|
|
fastBlur(tempWidth, tempHeight, tempStride, tempPixels);
|
|
|
|
vImageScale_ARGB8888(&dstBuffer, &srcBuffer, NULL, kvImageDoNotTile);
|
|
|
|
free(tempPixels);
|
|
}
|
|
|
|
UIImage *TGCropBackdropImage(UIImage *source, CGSize size)
|
|
{
|
|
CGFloat scale = source.scale;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor blackColor].CGColor);
|
|
CGContextFillRect(targetContext, CGRectMake(0, 0, targetContextSize.width, targetContextSize.height));
|
|
|
|
CGSize halfSize = CGSizeMake(CGFloor(size.width * scale / 2.0f), CGFloor(size.height * scale / 2.0f));
|
|
[source drawInRect:CGRectMake((targetContextSize.width - halfSize.width) / 2, (targetContextSize.height - halfSize.height) / 2, halfSize.width, halfSize.height) blendMode:kCGBlendModeNormal alpha:0.6f];
|
|
UIGraphicsPopContext();
|
|
|
|
lightBlurImage(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
UIImage *TGCameraPositionSwitchImage(UIImage *source, CGSize size)
|
|
{
|
|
return TGBlurredRectangularImage(source, size, size, NULL, nil);
|
|
}
|
|
|
|
UIImage *TGCameraModeSwitchImage(UIImage *source, CGSize size)
|
|
{
|
|
return TGBlurredRectangularImage(source, size, size, NULL, nil);
|
|
}
|
|
|
|
UIImage *TGScaleAndCropImageToPixelSize(UIImage *source, CGSize size, CGSize renderSize, uint32_t *averageColor, void (^pixelProcessingBlock)(void *, int, int, int))
|
|
{
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width), (int)(size.height)};
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(targetContext, kCGInterpolationMedium);
|
|
[source drawInRect:CGRectMake((size.width - renderSize.width) / 2.0f, (size.height - renderSize.height) / 2.0f, renderSize.width, renderSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
UIGraphicsPopContext();
|
|
|
|
if (averageColor != NULL)
|
|
{
|
|
*averageColor = TGImageAverageColor(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
}
|
|
|
|
if (pixelProcessingBlock)
|
|
pixelProcessingBlock(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
return image;
|
|
}
|
|
|
|
NSArray *TGBlurredBackgroundImages(UIImage *source, CGSize sourceSize)
|
|
{
|
|
CGSize size = TGFitSize(sourceSize, CGSizeMake(220, 220));
|
|
CGSize renderSize = size;
|
|
|
|
const struct { int width, height; } targetContextSize = { (int)(size.width), (int)(size.height)};
|
|
|
|
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
|
|
|
|
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
UIGraphicsPushContext(targetContext);
|
|
|
|
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
|
|
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
|
|
CGContextSetInterpolationQuality(targetContext, kCGInterpolationMedium);
|
|
|
|
[source drawInRect:CGRectMake((size.width - renderSize.width) / 2.0f, (size.height - renderSize.height) / 2.0f, renderSize.width, renderSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
|
|
fastBlurMore(targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, targetMemory);
|
|
fastBlurMore(targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, targetMemory);
|
|
|
|
brightenImage(targetMemory, targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow);
|
|
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *foregroundImage = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
[source drawInRect:CGRectMake((size.width - renderSize.width) / 2.0f, (size.height - renderSize.height) / 2.0f, renderSize.width, renderSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
|
|
|
|
fastBlurMore(targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, targetMemory);
|
|
fastBlurMore(targetContextSize.width, targetContextSize.height, (int)targetBytesPerRow, targetMemory);
|
|
|
|
CGContextSetFillColorWithColor(targetContext, [UIColor colorWithWhite:0.0f alpha:0.5f].CGColor);
|
|
CGContextFillRect(targetContext, CGRectMake(0.0f, 0.0f, targetContextSize.width, targetContextSize.height));
|
|
|
|
bitmapImage = CGBitmapContextCreateImage(targetContext);
|
|
UIImage *backgroundImage = [[UIImage alloc] initWithCGImage:bitmapImage];
|
|
CGImageRelease(bitmapImage);
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
CGContextRelease(targetContext);
|
|
free(targetMemory);
|
|
|
|
NSMutableArray *array = [[NSMutableArray alloc] init];
|
|
[array addObject:backgroundImage];
|
|
[array addObject:foregroundImage];
|
|
return array;
|
|
}
|