-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBCImageUtilities.m
418 lines (304 loc) · 13.3 KB
/
BCImageUtilities.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
/*
* BCImageUtilities.c
* MindsEye
*
* Created by Tom Houpt on 12/8/15.
* Copyright 2012 BehavioralCybernetics LLC. All rights reserved.
*
*/
#include "BCImageUtilities.h"
/** create an RGBA bitmap context of the given dimensions
with kCGColorSpaceGenericRGB and 8 bits per component
*/
CGContextRef CreateBitmapContext (NSInteger pixelsWide,
NSInteger pixelsHigh)
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
size_t bitmapByteCount;
size_t bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4);// 1
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2
bitmapData = malloc( bitmapByteCount );// 3
if (bitmapData == NULL) {
fprintf (stderr, "Memory not allocated!");
}
else {
context = CGBitmapContextCreate (bitmapData,// 4
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
// from apple docs:
// The constants for specifying the alpha channel information are declared with the CGImageAlphaInfo type but can be passed to this parameter [as CGBitmapInfo] safely
}
CGColorSpaceRelease( colorSpace );// 6
if (context== NULL) {
free (bitmapData);// 5
fprintf (stderr, "Context not created!");
}
return context;// 7
}
unsigned char *bitmapDataFromImage(CGImageRef sourceImage, unsigned long *bufferSize) {
// 1. get the size info from the sourceImage
// 2. set bytesPerPixel and bitmap buffer count based on 4-byte RGBA
// 3. Create a buffer for the bitmap and an RGBA CGContext
// 4. Draw the sourceImage into the context
// 5. get a pointer to the bitmap data of the context
CGSize size;
size_t bytesPerPixel;
size_t bytesPerRow;
size_t bitmapByteCount;
// size_t bitsPerComponent;
// 1. get the size info from the sourceImage
size.height = CGImageGetHeight(sourceImage);
size.width = CGImageGetWidth(sourceImage);
// 2. set bytesPerPixel and bitmap buffer count based on 4-byte RGBA
// 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.
bytesPerPixel = 4; // RGBA
bytesPerRow = (size_t)size.width * bytesPerPixel;
bitmapByteCount = (size_t)size.height * bytesPerRow;
// bitsPerComponent = 8;
// see Technical Q&A QA1509: Getting the pixel data from a CGImage object
// either use a data provider, or draw into a bitmap context
// the data provider method leaves the image in the internal CGImage format, which may be weird,
// e.g, jpeg or kept as 1-byte grayscale....
// the bitmap context will be in specified format, e.g. RGBA 8 bit,
// "with the caveat that alpha information from the image will be multiplied into the color components."
// 3. Create a buffer for the bitmap and an RGBA CGContext
// Create the bitmap context
unsigned char *sourceBitmapData = malloc(bitmapByteCount);
CGContextRef cgctx = createRGBABitmapContextFromImage(sourceImage, sourceBitmapData);
if (cgctx == NULL) {
// error creating context
free(sourceBitmapData);
return NULL;
}
// Get image width, height. We'll use the entire image.
CGRect rect = {{0,0},{size.width,size.height}};
// 4. Draw the sourceImage into the context
// 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(cgctx, rect, sourceImage);
// 5. get a pointer to the bitmap data of the context
// Now we can get a pointer to the image data associated with the bitmap
// context.
// this should be the same thing as sourceBitmapData allocated above....
sourceBitmapData = CGBitmapContextGetData (cgctx);
if (NULL != sourceBitmapData) {
(*bufferSize) = bitmapByteCount;
}
return sourceBitmapData;
}
CGContextRef createRGBABitmapContextFromImage(CGImageRef sourceImage, unsigned char *sourceBitmapData) {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
CGSize size;
size_t bytesPerPixel;
size_t bytesPerRow;
// size_t bitmapByteCount;
size_t bitsPerComponent;
// 1. get the size info from the sourceImage
size.height = CGImageGetHeight(sourceImage);
size.width = CGImageGetWidth(sourceImage);
// 2. set bytesPerPixel and bitmap buffer count based on 4-byte RGBA
// 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.
bytesPerPixel = 4; // RGBA
bytesPerRow = (size_t)size.width * bytesPerPixel;
// bitmapByteCount = (size_t)size.height * bytesPerRow;
bitsPerComponent = 8;
// Use the generic RGB color space.
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
if (colorSpace == NULL) {
NSLog(@"CreateRGBABitmapContext: Error allocating color space\n");
return NULL;
}
// Create the bitmap context. We want pre-multiplied RGBA, 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.
context = CGBitmapContextCreate (sourceBitmapData,
(size_t)size.width,
(size_t)size.height,
bitsPerComponent,
bytesPerRow,
colorSpace,
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
// from apple docs:
// The constants for specifying the alpha channel information are declared with the CGImageAlphaInfo type but can be passed to this parameter [as CGBitmapInfo] safely
if (context == NULL) {
NSLog(@"Context not created!");
}
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}
CGImageRef makeImageFromBitmap(CGContextRef cgctx) {
// get a CGImage from the CGContext
CGImageRef destinationImage = CGBitmapContextCreateImage(cgctx);
return destinationImage;
}
NSImage *DragImageWithText(NSImage *theImage, NSString *theText) {
NSSize size = [theText sizeWithAttributes:nil];
size.width += [theImage size].width + 6;
if (size.height < [theImage size].height) {
size.height = [theImage size].height;
}
NSImage *theDragImage = [[NSImage alloc] initWithSize:size];
[theDragImage lockFocus];
[theImage drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0,0,[theImage size].width,[theImage size].height) operation:NSCompositingOperationSourceOver fraction:1.0];
[theText drawAtPoint:NSMakePoint([theImage size].width + 6,4) withAttributes:nil];
[theDragImage unlockFocus];
return theDragImage;
}
BOOL IsImageFile(NSString*filePath) {
BOOL isImageFile = NO;
LSItemInfoRecord info;
CFStringRef uti = NULL;
CFArrayRef supportedTypes = NULL;
BOOL itsAFile = NO;
NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
if (fileAttribs) {
// Check for packages.
if ([NSFileTypeDirectory isEqualTo:[fileAttribs objectForKey:NSFileType]]) {
if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filePath] == NO){
itsAFile = YES; // If it is a file, it's OK to add.
}
}
else {
itsAFile = YES; // It is a file, so it's OK to add.
}
}
if (!itsAFile) {return NO;}
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (__bridge CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
if (LSCopyItemInfoForURL(url, kLSRequestExtension | kLSRequestTypeCreator, &info) == noErr) {
// Obtain the UTI using the file information.
// If there is a file extension, get the UTI.
if (info.extension != NULL) {
uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, info.extension, kUTTypeData);
CFRelease(info.extension);
}
// No UTI yet
if (uti == NULL) {
// If there is an OSType, get the UTI.
CFStringRef typeString = UTCreateStringForOSType(info.filetype);
if ( typeString != NULL) {
uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, typeString, kUTTypeData);
CFRelease(typeString);
}
}
// Verify that this is a file that the ImageIO framework supports.
if (uti != NULL) {
supportedTypes = CGImageSourceCopyTypeIdentifiers();
CFIndex i, typeCount = CFArrayGetCount(supportedTypes);
for (i = 0; i < typeCount; i++) {
if (UTTypeConformsTo(uti, (CFStringRef)CFArrayGetValueAtIndex(supportedTypes, i))) {
isImageFile = YES;
break;
}
}
}
}
if (uti != NULL) {
CFRelease(uti);
}
if (url != NULL) {
CFRelease(url);
}
if (supportedTypes != NULL) {
CFRelease(supportedTypes);
}
return isImageFile;
}
/** save the given CGImage to a TIFF file with filename at path
*/
void SaveImageToTIFF(CGImageRef imageRef, NSString *path,CFMutableDictionaryRef tiffProperties) {
BOOL make_default_properties_dictionary = NO;
// moved TIFF properties up to controller...
if (nil == tiffProperties) {
make_default_properties_dictionary = YES;
int32_t compression = NSTIFFCompressionLZW; // non-lossy LZW compression
tiffProperties = CFDictionaryCreateMutable(nil,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(tiffProperties,
kCGImagePropertyTIFFCompression,
CFNumberCreate(NULL, kCFNumberIntType, &compression));
}
CFMutableDictionaryRef mSaveMetaAndOpts = CFDictionaryCreateMutable(nil,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(mSaveMetaAndOpts,
kCGImagePropertyTIFFDictionary,
tiffProperties);
NSURL *outURL = [NSURL fileURLWithPath:path];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL (CFBridgingRetain(outURL),
kUTTypeTIFF,
1,
NULL);
CGImageDestinationAddImage(destination,
imageRef,
mSaveMetaAndOpts);
CGImageDestinationSetProperties(destination, mSaveMetaAndOpts);
CGImageDestinationFinalize(destination);
CFRelease(destination);
if (make_default_properties_dictionary) {
CFRelease(tiffProperties);
}
CFRelease(mSaveMetaAndOpts);
// NOTE: do we need to release outURL because it was cast to CFBridgingRetain
}
CGImageRef CreatePNGImageRefFromBundle (const char *imageName)
{
CGImageRef image;
CGDataProviderRef provider;
CFStringRef name;
CFURLRef url;
CFBundleRef mainBundle = CFBundleGetMainBundle();
// Get the URL to the bundle resource.
name = CFStringCreateWithCString (NULL, imageName, kCFStringEncodingUTF8);
url = CFBundleCopyResourceURL(mainBundle, name, CFSTR("png"), NULL);
CFRelease(name);
// Create the data provider object
provider = CGDataProviderCreateWithURL (url);
CFRelease (url);
// Create the image object from that provider.
image = CGImageCreateWithPNGDataProvider (provider, NULL, true,
kCGRenderingIntentDefault);
CGDataProviderRelease (provider);
return (image);
}
double calcFocusMetric(CGImageRef theCGImage) {
NSData *jpeg = JPEGDataFromCGImage(theCGImage,0.25);
return [jpeg length];
}
NSData *JPEGDataFromCGImage(CGImageRef image, CGFloat compressionQuality) {
NSMutableData *jpegData = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)jpegData, kUTTypeJPEG, 1, NULL);
if (!destination) {
NSLog(@"Failed to create CGImageDestination for JPEG.");
return nil;
}
// Define the options for the JPEG
NSDictionary *properties = @{(__bridge NSString *)kCGImageDestinationLossyCompressionQuality: @(compressionQuality)};
CGImageDestinationAddImage(destination, image, (__bridge CFDictionaryRef)properties);
// Finalize the destination to write the image data
if (!CGImageDestinationFinalize(destination)) {
NSLog(@"Failed to write JPEG image.");
CFRelease(destination);
return nil;
}
CFRelease(destination);
return [jpegData copy];
}