mirror of https://github.com/HandBrake/HandBrake
235 lines
8.3 KiB
Objective-C
235 lines
8.3 KiB
Objective-C
/* HBColorUtilities.m $
|
|
|
|
This file is part of the HandBrake source code.
|
|
Homepage: <http://handbrake.fr/>.
|
|
It may be used under the terms of the GNU General Public License. */
|
|
|
|
#import "HBImageUtilities.h"
|
|
#import <CoreVideo/CoreVideo.h>
|
|
#include "handbrake/handbrake.h"
|
|
|
|
CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, CGFloat thumbnailHeight)
|
|
{
|
|
// Create the bitmap context
|
|
CGContextRef context = NULL;
|
|
void * bitmapData;
|
|
int bitmapByteCount;
|
|
int bitmapBytesPerRow;
|
|
|
|
// Get image width, height. We'll use the entire image.
|
|
int width = (CGFloat)CGImageGetWidth(image) / (CGFloat)CGImageGetHeight(image) * thumbnailHeight;
|
|
int height = thumbnailHeight;
|
|
|
|
// Declare the number of bytes per row. Each pixel in the bitmap in this
|
|
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
|
|
// alpha.
|
|
bitmapBytesPerRow = (width * 4);
|
|
bitmapByteCount = (bitmapBytesPerRow * height);
|
|
|
|
// Allocate memory for image data. This is the destination in memory
|
|
// where any drawing to the bitmap context will be rendered.
|
|
bitmapData = malloc(bitmapByteCount);
|
|
if (bitmapData == NULL)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
|
|
// per component. Regardless of what the source image format is
|
|
// (CMYK, Grayscale, and so on) it will be converted over to the format
|
|
// specified here by CGBitmapContextCreate.
|
|
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
|
|
context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
|
|
colorspace, (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
|
|
|
|
if (context == NULL)
|
|
{
|
|
// error creating context
|
|
return nil;
|
|
}
|
|
|
|
// Draw the image to the bitmap context. Once we draw, the memory
|
|
// allocated for the context for rendering will then contain the
|
|
// raw image data in the specified color space.
|
|
CGContextDrawImage(context, CGRectMake(0,0,width, height), image);
|
|
|
|
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
|
CGContextRelease(context);
|
|
free(bitmapData);
|
|
|
|
return imgRef;
|
|
}
|
|
|
|
|
|
CGImageRef CGImageRotated(CGImageRef imgRef, CGFloat angle, BOOL flipped) CF_RETURNS_RETAINED
|
|
{
|
|
CGFloat angleInRadians = angle * (M_PI / 180);
|
|
CGFloat width = CGImageGetWidth(imgRef);
|
|
CGFloat height = CGImageGetHeight(imgRef);
|
|
|
|
CGRect imgRect = CGRectMake(0, 0, width, height);
|
|
CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
|
|
CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
|
|
|
|
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imgRef);
|
|
CGContextRef bmContext = CGBitmapContextCreate(NULL,
|
|
(size_t)rotatedRect.size.width,
|
|
(size_t)rotatedRect.size.height,
|
|
8,
|
|
0,
|
|
colorSpace,
|
|
(CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
|
|
CGContextSetAllowsAntialiasing(bmContext, FALSE);
|
|
CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
|
|
|
|
// Rotate
|
|
CGContextTranslateCTM(bmContext,
|
|
+ (rotatedRect.size.width / 2),
|
|
+ (rotatedRect.size.height / 2));
|
|
CGContextRotateCTM(bmContext, -angleInRadians);
|
|
CGContextTranslateCTM(bmContext,
|
|
- (rotatedRect.size.width / 2),
|
|
- (rotatedRect.size.height / 2));
|
|
|
|
// Flip
|
|
if (flipped)
|
|
{
|
|
CGAffineTransform flipHorizontal = CGAffineTransformMake(-1, 0, 0, 1, floor(rotatedRect.size.width), 0);
|
|
CGContextConcatCTM(bmContext, flipHorizontal);
|
|
}
|
|
|
|
CGContextDrawImage(bmContext,
|
|
CGRectMake((rotatedRect.size.width - width)/2.0f,
|
|
(rotatedRect.size.height - height)/2.0f,
|
|
width,
|
|
height),
|
|
imgRef);
|
|
|
|
CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
|
|
CFRelease(bmContext);
|
|
|
|
return rotatedImage;
|
|
}
|
|
|
|
CGColorSpaceRef copyColorSpace(int primaries, int transfer, int matrix)
|
|
{
|
|
CFStringRef primariesValue = NULL;
|
|
switch (primaries)
|
|
{
|
|
case HB_COLR_PRI_EBUTECH:
|
|
primariesValue = kCVImageBufferColorPrimaries_EBU_3213;
|
|
break;
|
|
|
|
case HB_COLR_PRI_SMPTEC:
|
|
primariesValue = kCVImageBufferColorPrimaries_SMPTE_C;
|
|
break;
|
|
|
|
case HB_COLR_PRI_BT2020:
|
|
primariesValue = kCVImageBufferColorPrimaries_ITU_R_2020;
|
|
break;
|
|
|
|
case HB_COLR_PRI_BT709:
|
|
default:
|
|
primariesValue = kCVImageBufferColorPrimaries_ITU_R_709_2;
|
|
}
|
|
|
|
CFStringRef transferValue = NULL;
|
|
CFNumberRef gammaValue = NULL;
|
|
switch (transfer)
|
|
{
|
|
case HB_COLR_TRA_GAMMA22:
|
|
{
|
|
transferValue = kCVImageBufferTransferFunction_UseGamma;
|
|
Float32 gamma = 2.2;
|
|
gammaValue = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma);
|
|
break;
|
|
}
|
|
|
|
case HB_COLR_TRA_GAMMA28:
|
|
{
|
|
transferValue = kCVImageBufferTransferFunction_UseGamma;
|
|
Float32 gamma = 2.8;
|
|
gammaValue = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma);
|
|
break;
|
|
}
|
|
|
|
case HB_COLR_TRA_SMPTE240M:
|
|
transferValue = kCVImageBufferTransferFunction_SMPTE_240M_1995;
|
|
break;
|
|
|
|
case HB_COLR_TRA_LINEAR:
|
|
if (@available(macOS 10.14, *)) {
|
|
transferValue = kCVImageBufferTransferFunction_Linear;
|
|
break;
|
|
}
|
|
|
|
case HB_COLR_TRA_IEC61966_2_1:
|
|
transferValue = kCVImageBufferTransferFunction_sRGB;
|
|
break;
|
|
|
|
case HB_COLR_TRA_BT2020_10:
|
|
case HB_COLR_TRA_BT2020_12:
|
|
transferValue = kCVImageBufferTransferFunction_ITU_R_2020;
|
|
break;
|
|
|
|
case HB_COLR_TRA_SMPTEST2084:
|
|
transferValue = kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ;
|
|
break;
|
|
|
|
case HB_COLR_TRA_ARIB_STD_B67:
|
|
transferValue = kCVImageBufferTransferFunction_ITU_R_2100_HLG;
|
|
break;
|
|
|
|
case HB_COLR_TRA_SMPTE428:
|
|
transferValue = kCVImageBufferTransferFunction_SMPTE_ST_428_1;
|
|
break;
|
|
|
|
case HB_COLR_TRA_BT709:
|
|
default:
|
|
transferValue = kCVImageBufferTransferFunction_ITU_R_709_2;
|
|
}
|
|
|
|
CFStringRef matrixValue = NULL;
|
|
switch (matrix)
|
|
{
|
|
case HB_COLR_MAT_SMPTE170M:
|
|
matrixValue = kCVImageBufferYCbCrMatrix_ITU_R_601_4;
|
|
break;
|
|
|
|
case HB_COLR_MAT_SMPTE240M:
|
|
matrixValue = kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
|
|
break;
|
|
|
|
case HB_COLR_MAT_BT2020_NCL:
|
|
case HB_COLR_MAT_BT2020_CL:
|
|
matrixValue = kCVImageBufferYCbCrMatrix_ITU_R_2020;
|
|
break;
|
|
|
|
case HB_COLR_MAT_BT709:
|
|
default:
|
|
matrixValue = kCVImageBufferYCbCrMatrix_ITU_R_709_2;
|
|
}
|
|
|
|
CGColorSpaceRef colorSpace = NULL;
|
|
if (transferValue == kCVImageBufferTransferFunction_UseGamma)
|
|
{
|
|
const void *keys[4] = { kCVImageBufferColorPrimariesKey, kCVImageBufferTransferFunctionKey, kCVImageBufferYCbCrMatrixKey, kCVImageBufferGammaLevelKey };
|
|
const void *values[4] = { primariesValue, transferValue, matrixValue, gammaValue };
|
|
CFDictionaryRef attachments = CFDictionaryCreate(NULL, keys, values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
|
|
colorSpace = CVImageBufferCreateColorSpaceFromAttachments(attachments);
|
|
CFRelease(attachments);
|
|
}
|
|
else
|
|
{
|
|
const void *keys[3] = { kCVImageBufferColorPrimariesKey, kCVImageBufferTransferFunctionKey, kCVImageBufferYCbCrMatrixKey };
|
|
const void *values[3] = { primariesValue, transferValue, matrixValue };
|
|
CFDictionaryRef attachments = CFDictionaryCreate(NULL, keys, values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
|
|
colorSpace = CVImageBufferCreateColorSpaceFromAttachments(attachments);
|
|
CFRelease(attachments);
|
|
}
|
|
|
|
return colorSpace;
|
|
}
|