Logo ROOT   6.18/05
Reference Guide
QuartzPixmap.mm
Go to the documentation of this file.
1// @(#)root/graf2d:$Id$
2// Author: Timur Pocheptsov 16/02/2012
3
4/*************************************************************************
5 * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12//#define NDEBUG
13
14#include <algorithm>
15#include <utility>
16#include <cassert>
17#include <cstddef>
18#include <limits>
19#include <new>
20
21#include "CocoaGuiTypes.h"
22#include "QuartzWindow.h"
23#include "QuartzPixmap.h"
24#include "QuartzUtils.h"
25#include "CocoaUtils.h"
26#include "X11Colors.h"
27
28namespace X11 = ROOT::MacOSX::X11;
29namespace Util = ROOT::MacOSX::Util;
30namespace Quartz = ROOT::Quartz;
31
32@implementation QuartzPixmap
33
34@synthesize fID;
35
36//______________________________________________________________________________
37- (id) initWithW : (unsigned) width H : (unsigned) height scaleFactor : (CGFloat) scaleFactor
38{
39 if (self = [super init]) {
40 fWidth = 0;
41 fHeight = 0;
42
43 if (![self resizeW : width H : height scaleFactor : scaleFactor]) {
44 [self release];
45 return nil;
46 }
47 }
48
49 return self;
50}
51
52//______________________________________________________________________________
53- (BOOL) resizeW : (unsigned) width H : (unsigned) height scaleFactor : (CGFloat) scaleFactor
54{
55 assert(width > 0 && "resizeW:H:, Pixmap width must be positive");
56 assert(height > 0 && "resizeW:H:, Pixmap height must be positive");
57
58 fScaleFactor = scaleFactor;
59
60 std::vector<unsigned char> memory;
61
62 const unsigned scaledW = width * fScaleFactor;
63 const unsigned scaledH = height * fScaleFactor;
64
65 try {
66 memory.resize(scaledW * scaledH * 4);//[0]
67 } catch (const std::bad_alloc &) {
68 NSLog(@"QuartzPixmap: -resizeW:H:, memory allocation failed");
69 return NO;
70 }
71
72 const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());//[1]
73 if (!colorSpace.Get()) {
74 NSLog(@"QuartzPixmap: -resizeW:H:, CGColorSpaceCreateDeviceRGB failed");
75 return NO;
76 }
77
78 Util::CFScopeGuard<CGContextRef> ctx(CGBitmapContextCreateWithData(&memory[0], scaledW, scaledH, 8,
79 scaledW * 4, colorSpace.Get(),
80 kCGImageAlphaPremultipliedLast, NULL, 0));
81 if (!ctx.Get()) {
82 NSLog(@"QuartzPixmap: -resizeW:H:, CGBitmapContextCreateWithData failed");
83 return NO;
84 }
85
86 //Now, apply scaling.
87
88 if (fScaleFactor > 1)
89 CGContextScaleCTM(ctx.Get(), fScaleFactor, fScaleFactor);
90
91 fContext.Reset(ctx.Release());
92
93 //sizes, data.
94 fWidth = width;
95 fHeight = height;
96 fData.swap(memory);
97
98 return YES;
99}
100
101//______________________________________________________________________________
102- (CGImageRef) createImageFromPixmap
103{
104 return [self createImageFromPixmap : X11::Rectangle(0, 0, fWidth, fHeight)];
105}
106
107//______________________________________________________________________________
108- (CGImageRef) createImageFromPixmap : (X11::Rectangle) cropArea
109{
110 //Crop area must be valid and adjusted by caller.
111
112 //This function is incorrect in a general case, it does not care about
113 //cropArea.fX and cropArea.fY, very sloppy implementation.
114
115 assert(cropArea.fX >= 0 && "createImageFromPixmap:, cropArea.fX is negative");
116 assert(cropArea.fY >= 0 && "createImageFromPixmap:, cropArea.fY is negative");
117 assert(cropArea.fWidth <= fWidth && "createImageFromPixmap:, bad cropArea.fWidth");
118 assert(cropArea.fHeight <= fHeight && "createImageFromPixmap:, bad cropArea.fHeight");
119
120 const unsigned scaledW = fWidth * fScaleFactor;
121 const unsigned scaledH = fHeight * fScaleFactor;
122
123
124 const Util::CFScopeGuard<CGDataProviderRef> provider(CGDataProviderCreateWithData(nullptr, &fData[0],
125 scaledW * scaledH * 4, nullptr));
126 if (!provider.Get()) {
127 NSLog(@"QuartzPixmap: -pixmapToImage, CGDataProviderCreateWithData failed");
128 return 0;
129 }
130
131 //RGB - this is only for TGCocoa::CreatePixmapFromData.
132 const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());
133 if (!colorSpace.Get()) {
134 NSLog(@"QuartzPixmap: -pixmapToImage, CGColorSpaceCreateDeviceRGB failed");
135 return 0;
136 }
137
138 //8 bits per component, 32 bits per pixel, 4 bytes per pixel, kCGImageAlphaLast:
139 //all values hardcoded for TGCocoa.
140 CGImageRef image = CGImageCreate(cropArea.fWidth * fScaleFactor, cropArea.fHeight * fScaleFactor,
141 8, 32, fWidth * 4 * fScaleFactor, colorSpace.Get(),
142 kCGImageAlphaPremultipliedLast, provider.Get(), 0,
143 false, kCGRenderingIntentDefault);
144
145 return image;
146}
147
148//______________________________________________________________________________
149- (BOOL) fIsPixmap
150{
151 return YES;
152}
153
154//______________________________________________________________________________
155- (BOOL) fIsOpenGLWidget
156{
157 return NO;
158}
159
160//______________________________________________________________________________
161- (CGFloat) fScaleFactor
162{
163 return fScaleFactor;
164}
165
166//______________________________________________________________________________
167- (CGContextRef) fContext
168{
169 assert(fContext.Get() != 0 && "fContext, called for bad pixmap");
170
171 return fContext.Get();
172}
173
174//______________________________________________________________________________
175- (unsigned) fWidth
176{
177 assert(fContext.Get() != 0 && "fWidth, called for bad pixmap");
178
179 return fWidth;
180}
181
182//______________________________________________________________________________
183- (unsigned) fHeight
184{
185 assert(fContext.Get() != 0 && "fHeight, called for bad pixmap");
186
187 return fHeight;
188}
189
190//______________________________________________________________________________
191- (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area
192 withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
193{
194 using namespace ROOT::MacOSX::X11;
195
196 //Check parameters.
197 assert(srcImage != nil && "copyImage:area:withMask:clipOrigin:toPoint:, srcImage parameter is nil");
198 assert(srcImage.fImage != nil && "copyImage:area:withMask:clipOrigin:toPoint:, srcImage.fImage is nil");
199
200 if (!AdjustCropArea(srcImage, area)) {
201 NSLog(@"QuartzPixmap: -copyImage:srcImage:area:withMask:clipOrigin"
202 ":toPoint, srcRect and copyRect do not intersect");
203 return;
204 }
205
206 CGImageRef subImage = 0;//RAII not really needed.
207 bool needSubImage = false;
208 if (area.fX || area.fY || area.fWidth != srcImage.fWidth || area.fHeight != srcImage.fHeight) {
209 needSubImage = true;
210 subImage = CreateSubImage(srcImage, area);
211 if (!subImage) {
212 NSLog(@"QuartzPixmap: -copyImage:area:withMask:clipOrigin:toPoint:, subimage creation failed");
213 return;
214 }
215 } else
216 subImage = srcImage.fImage;
217
218 //Save context state.
219 const Quartz::CGStateGuard stateGuard(fContext);
220
221 if (mask) {
222 assert(mask.fImage != nil && "copyImage:area:withMask:clipOrigin:toPoint, mask is not nil, but mask.fImage is nil");
223 assert(CGImageIsMask(mask.fImage) && "copyImage:area:withMask:clipOrigin:toPoint, mask.fImage is not a mask");
224 //TODO: fix the possible overflow? (though, who can have such images???)
225 clipXY.fY = LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
226 const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
227 CGContextClipToMask(fContext.Get(), clipRect, mask.fImage);
228 }
229
230 //TODO: fix the possible overflow? (though, who can have such images???)
231 dstPoint.fY = LocalYROOTToCocoa(self, dstPoint.fY + area.fHeight);
232 const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
233 CGContextDrawImage(fContext.Get(), imageRect, subImage);
234
235 if (needSubImage)
236 CGImageRelease(subImage);
237}
238
239//______________________________________________________________________________
240- (void) copyPixmap : (QuartzPixmap *) srcPixmap area : (X11::Rectangle) area
241 withMask : (QuartzImage *)mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint
242{
243 using namespace ROOT::MacOSX::X11;
244
245 assert(srcPixmap != nil &&
246 "copyPixmap:area:withMask:clipOrigin:toPoint, srcPixmap parameter is nil");
247
248 if (!AdjustCropArea(srcPixmap, area)) {
249 NSLog(@"QuartzPixmap: -copyPixmap:area:withMask:clipOrigin:"
250 "toPoint, srcRect and copyRect do not intersect");
251 return;
252 }
253
254 const Util::CFScopeGuard<CGImageRef> image([srcPixmap createImageFromPixmap : area]);
255 if (!image.Get())
256 return;
257
258 const Quartz::CGStateGuard stateGuard(fContext);
259
260 if (mask) {
261 assert(mask.fImage != nil &&
262 "copyPixmap:area:withMask:clipOrigin:toPoint, mask is not nil, but mask.fImage is nil");
263 assert(CGImageIsMask(mask.fImage) &&
264 "copyPixmap:area:withMask:clipOrigin:toPoint, mask.fImage is not a mask");
265 //TODO: fix the possible overflow? (though, who can have such images???)
266 clipXY.fY = LocalYROOTToCocoa(self, clipXY.fY + mask.fHeight);
267 const CGRect clipRect = CGRectMake(clipXY.fX, clipXY.fY, mask.fWidth, mask.fHeight);
268 CGContextClipToMask(fContext.Get(), clipRect, mask.fImage);
269 }
270
271 //TODO: fix the possible overflow? (though, who can have such images???)
272 dstPoint.fY = LocalYROOTToCocoa(self, dstPoint.fY + area.fHeight);
273 const CGRect imageRect = CGRectMake(dstPoint.fX, dstPoint.fY, area.fWidth, area.fHeight);
274 CGContextDrawImage(fContext.Get(), imageRect, image.Get());
275}
276
277//______________________________________________________________________________
278- (void) copy : (NSObject<X11Drawable> *) src area : (X11::Rectangle) area
279 withMask : (QuartzImage *)mask clipOrigin : (X11::Point) origin toPoint : (X11::Point) dstPoint
280{
281 assert(area.fWidth && area.fHeight &&
282 "copy:area:widthMask:clipOrigin:toPoint, empty area to copy");
283
284 if ([src isKindOfClass : [QuartzImage class]]) {
285 [self copyImage : (QuartzImage *)src area : area withMask : mask clipOrigin : origin toPoint : dstPoint];
286 } else if ([src isKindOfClass : [QuartzPixmap class]]) {
287 [self copyPixmap : (QuartzPixmap *)src area : area withMask : mask clipOrigin : origin toPoint : dstPoint];
288 } else
289 assert(0 && "Can copy only from pixmap or image");
290}
291
292//______________________________________________________________________________
293- (unsigned char *) readColorBits : (X11::Rectangle) area
294{
295 assert(area.fWidth && area.fHeight && "readColorBits:, empty area to copy");
296
297 if (!X11::AdjustCropArea(self, area)) {
298 NSLog(@"QuartzPixmap: readColorBits:intoBuffer:, src and copy area do not intersect");
299 return 0;
300 }
301
302 // Not std::vector, since we pass the ownership ...
303 unsigned char *buffer = 0;
304 try {
305 buffer = new unsigned char[area.fWidth * area.fHeight * 4]();
306 } catch (const std::bad_alloc &) {
307 NSLog(@"QuartzImage: -readColorBits:, memory allocation failed");
308 return 0;
309 }
310
312
313 if (fScaleFactor > 1) {
314 scaledPixmap.Reset([[QuartzPixmap alloc] initWithW : fWidth H : fHeight scaleFactor : 1.]);
315 //Ooops, all screwed up!!!
316 if (!scaledPixmap.Get()) {
317 NSLog(@"QuartzImage: -readColorBits:, can not create scaled pixmap");
318 return buffer;//empty buffer.
319 }
320
321 [scaledPixmap.Get() copy : self area : X11::Rectangle(0, 0, fWidth, fHeight)
322 withMask : nil clipOrigin : X11::Point() toPoint : X11::Point()];
323 }
324
325 unsigned char *dstPixel = buffer;
326
327 //fImageData has 4 bytes per pixel.
328 //TODO: possible overflows everywhere :(
329 const unsigned char *line = fScaleFactor == 1 ? &fData[0] + area.fY * fWidth * 4
330 : &scaledPixmap.Get()->fData[0] + area.fY * fWidth * 4;
331
332 const unsigned char *srcPixel = line + area.fX * 4;
333
334 for (unsigned i = 0; i < area.fHeight; ++i) {
335 for (unsigned j = 0; j < area.fWidth; ++j, srcPixel += 4, dstPixel += 4) {
336 dstPixel[0] = srcPixel[0];
337 dstPixel[1] = srcPixel[1];
338 dstPixel[2] = srcPixel[2];
339 dstPixel[3] = srcPixel[3];
340 }
341
342 line += fWidth * 4;
343 srcPixel = line + area.fX * 4;
344 }
345
346 return buffer;
347}
348
349//______________________________________________________________________________
350- (unsigned char *) fData
351{
352 return &fData[0];
353}
354
355//______________________________________________________________________________
356- (void) putPixel : (const unsigned char *) rgb X : (unsigned) x Y : (unsigned) y
357{
358 //Primitive version of XPutPixel.
359 assert(rgb != 0 && "putPixel:X:Y:, rgb parameter is null");
360 assert(x < fWidth && "putPixel:X:Y:, x parameter is >= self.fWidth");
361 assert(y < fHeight && "putPixel:X:Y:, y parameter is >= self.fHeight");
362
363 unsigned char * const data = &fData[0];
364 if (fScaleFactor > 1) {
365 //Ooops, and what should I do now???
366 const unsigned scaledW = fWidth * fScaleFactor;
367 unsigned char *dst = data + unsigned(y * fScaleFactor * scaledW * 4) + unsigned(x * fScaleFactor * 4);
368
369 for (unsigned i = 0; i < 2; ++i, dst += 4) {
370 dst[0] = rgb[0];
371 dst[1] = rgb[1];
372 dst[2] = rgb[2];
373 dst[3] = 255;
374 }
375
376 dst -= 8;
377 dst += scaledW * 4;
378
379 for (unsigned i = 0; i < 2; ++i, dst += 4) {
380 dst[0] = rgb[0];
381 dst[1] = rgb[1];
382 dst[2] = rgb[2];
383 dst[3] = 255;
384 }
385 } else {
386 unsigned char *dst = data + y * fWidth * 4 + x * 4;
387
388 dst[0] = rgb[0];
389 dst[1] = rgb[1];
390 dst[2] = rgb[2];
391 dst[3] = 255;
392 }
393}
394
395//______________________________________________________________________________
396- (void) addPixel : (const unsigned char *) rgb
397{
398 //Primitive version of XAddPixel.
399 assert(rgb != 0 && "addPixel:, rgb parameter is null");
400
401 for (unsigned i = 0; i < fHeight; ++i) {
402 for (unsigned j = 0; j < fWidth; ++j) {
403 fData[i * fWidth * 4 + j * 4] = rgb[0];
404 fData[i * fWidth * 4 + j * 4 + 1] = rgb[1];
405 fData[i * fWidth * 4 + j * 4 + 2] = rgb[2];
406 fData[i * fWidth * 4 + j * 4 + 3] = rgb[3];
407 }
408 }
409}
410
411@end
412
413@implementation QuartzImage
414
415@synthesize fIsStippleMask;
416@synthesize fID;
417
418//______________________________________________________________________________
419- (id) initWithW : (unsigned) width H : (unsigned) height data : (unsigned char *) data
420{
421 assert(width != 0 && "initWithW:H:data:, width parameter is 0");
422 assert(height != 0 && "initWithW:H:data:, height parameter is 0");
423 assert(data != 0 && "initWithW:H:data:, data parameter is null");
424
425 if (self = [super init]) {
426 Util::NSScopeGuard<QuartzImage> selfGuard(self);
427
428 //This w * h * 4 is ONLY for TGCocoa::CreatePixmapFromData.
429 //If needed something else, I'll make this code more generic.
430 try {
431 fImageData.resize(width * height * 4);
432 } catch (const std::bad_alloc &) {
433 NSLog(@"QuartzImage: -initWithW:H:data:, memory allocation failed");
434 return nil;
435 }
436
437 std::copy(data, data + width * height * 4, &fImageData[0]);
438
439 fIsStippleMask = NO;
440 const Util::CFScopeGuard<CGDataProviderRef>
441 provider(CGDataProviderCreateWithData(nullptr, &fImageData[0], width * height * 4, nullptr));
442 if (!provider.Get()) {
443 NSLog(@"QuartzImage: -initWithW:H:data: CGDataProviderCreateWithData failed");
444 return nil;
445 }
446
447 //RGB - this is only for TGCocoa::CreatePixmapFromData.
448 const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());
449 if (!colorSpace.Get()) {
450 NSLog(@"QuartzImage: -initWithW:H:data: CGColorSpaceCreateDeviceRGB failed");
451 return nil;
452 }
453
454 //8 bits per component, 32 bits per pixel, 4 bytes per pixel, kCGImageAlphaLast:
455 //all values hardcoded for TGCocoa::CreatePixmapFromData.
456 fImage.Reset(CGImageCreate(width, height, 8, 32, width * 4, colorSpace.Get(),
457 kCGImageAlphaLast, provider.Get(), 0, false,
458 kCGRenderingIntentDefault));
459
460 if (!fImage.Get()) {
461 NSLog(@"QuartzImage: -initWithW:H:data: CGImageCreate failed");
462 return nil;
463 }
464
465 fWidth = width;
466 fHeight = height;
467
468 selfGuard.Release();
469 }
470
471 return self;
472}
473
474//______________________________________________________________________________
475- (id) initMaskWithW : (unsigned) width H : (unsigned) height bitmapMask : (unsigned char *) mask
476{
477 assert(width != 0 && "initMaskWithW:H:bitmapMask:, width parameter is zero");
478 assert(height != 0 && "initMaskWithW:H:bitmapMask:, height parameter is zero");
479 assert(mask != 0 && "initMaskWithW:H:bitmapMask:, mask parameter is null");
480
481 if (self = [super init]) {
482 Util::NSScopeGuard<QuartzImage> selfGuard(self);
483
484 try {
485 fImageData.resize(width * height);
486 } catch (const std::bad_alloc &) {
487 NSLog(@"QuartzImage: -initMaskWithW:H:bitmapMask:, memory allocation failed");
488 return nil;
489 }
490
491 std::copy(mask, mask + width * height, &fImageData[0]);
492
493 fIsStippleMask = YES;
494 const Util::CFScopeGuard<CGDataProviderRef> provider(CGDataProviderCreateWithData(nullptr, &fImageData[0],
495 width * height, nullptr));
496 if (!provider.Get()) {
497 NSLog(@"QuartzImage: -initMaskWithW:H:bitmapMask: CGDataProviderCreateWithData failed");
498 return nil;
499 }
500
501 //0 -> decode, false -> shouldInterpolate.
502 fImage.Reset(CGImageMaskCreate(width, height, 8, 8, width, provider.Get(), 0, false));
503 if (!fImage.Get()) {
504 NSLog(@"QuartzImage: -initMaskWithW:H:bitmapMask:, CGImageMaskCreate failed");
505 return nil;
506 }
507
508 fWidth = width;
509 fHeight = height;
510
511 selfGuard.Release();
512 }
513
514 return self;
515}
516
517//______________________________________________________________________________
518- (id) initMaskWithW : (unsigned) width H : (unsigned) height
519{
520 //Two-step initialization.
521
522 assert(width != 0 && "initMaskWithW:H:, width parameter is zero");
523 assert(height != 0 && "initMaskWithW:H:, height parameter is zero");
524
525 if (self = [super init]) {
526 Util::NSScopeGuard<QuartzImage> selfGuard(self);
527
528 try {
529 fImageData.resize(width * height);
530 } catch (const std::bad_alloc &) {
531 NSLog(@"QuartzImage: -initMaskWithW:H:, memory allocation failed");
532 return nil;
533 }
534
535 fIsStippleMask = YES;
536 const Util::CFScopeGuard<CGDataProviderRef> provider(CGDataProviderCreateWithData(nullptr, &fImageData[0],
537 width * height, nullptr));
538 if (!provider.Get()) {
539 NSLog(@"QuartzImage: -initMaskWithW:H: CGDataProviderCreateWithData failed");
540 return nil;
541 }
542
543 //0 -> decode, false -> shouldInterpolate.
544 fImage.Reset(CGImageMaskCreate(width, height, 8, 8, width, provider.Get(), 0, false));
545 if (!fImage.Get()) {
546 NSLog(@"QuartzImage: -initMaskWithW:H:, CGImageMaskCreate failed");
547 return nil;
548 }
549
550 selfGuard.Release();
551
552 fWidth = width;
553 fHeight = height;
554 }
555
556 return self;
557}
558
559//______________________________________________________________________________
560- (id) initFromPixmap : (QuartzPixmap *) pixmap
561{
562 //Two-step initialization.
563 assert(pixmap != nil && "initFromPixmap:, pixmap parameter is nil");
564 assert(pixmap.fWidth != 0 && "initFromPixmap:, pixmap width is zero");
565 assert(pixmap.fHeight != 0 && "initFromPixmap:, pixmap height is zero");
566
567 return [self initWithW : pixmap.fWidth H : pixmap.fHeight data : pixmap.fData];
568}
569
570//______________________________________________________________________________
571- (id) initFromImage : (QuartzImage *) image
572{
573 assert(image != nil && "initFromImage:, image parameter is nil");
574 assert(image.fWidth != 0 && "initFromImage:, image width is 0");
575 assert(image.fHeight != 0 && "initFromImage:, image height is 0");
576 assert(image.fIsStippleMask == NO && "initFromImage:, image is a stipple mask, not implemented");
577
578 return [self initWithW : image.fWidth H : image.fHeight data : &image->fImageData[0]];
579}
580
581//______________________________________________________________________________
582- (id) initFromImageFlipped : (QuartzImage *) image
583{
584 assert(image != nil && "initFromImageFlipped:, image parameter is nil");
585 assert(image.fWidth != 0 && "initFromImageFlipped:, image width is 0");
586 assert(image.fHeight != 0 && "initFromImageFlipped:, image height is 0");
587
588 const unsigned bpp = image.fIsStippleMask ? 1 : 4;
589
590 if (self = [super init]) {
591 const unsigned width = image.fWidth;
592 const unsigned height = image.fHeight;
593
594 Util::NSScopeGuard<QuartzImage> selfGuard(self);
595
596 try {
597 fImageData.resize(width * height * bpp);
598 } catch (const std::bad_alloc &) {
599 NSLog(@"QuartzImage: -initFromImageFlipped:, memory allocation failed");
600 return nil;
601 }
602
603 const unsigned lineSize = bpp * width;
604 const unsigned char * const src = &image->fImageData[0];
605 unsigned char * const dst = &fImageData[0];
606 for (unsigned i = 0; i < height; ++i) {
607 const unsigned char *sourceLine = src + lineSize * (height - 1 - i);
608 unsigned char *dstLine = dst + i * lineSize;
609 std::copy(sourceLine, sourceLine + lineSize, dstLine);
610 }
611
612 if (bpp == 1) {
613 fIsStippleMask = YES;
614 const Util::CFScopeGuard<CGDataProviderRef> provider(CGDataProviderCreateWithData(nullptr, &fImageData[0],
615 width * height, nullptr));
616 if (!provider.Get()) {
617 NSLog(@"QuartzImage: -initFromImageFlipped:, CGDataProviderCreateWithData failed");
618 return nil;
619 }
620
621 //0 -> decode, false -> shouldInterpolate.
622 fImage.Reset(CGImageMaskCreate(width, height, 8, 8, width, provider.Get(), 0, false));
623 if (!fImage.Get()) {
624 NSLog(@"QuartzImage: -initFromImageFlipped:, CGImageMaskCreate failed");
625 return nil;
626 }
627 } else {
628 fIsStippleMask = NO;
629 const Util::CFScopeGuard<CGDataProviderRef> provider(CGDataProviderCreateWithData(nullptr, &fImageData[0],
630 width * height * 4, nullptr));
631 if (!provider.Get()) {
632 NSLog(@"QuartzImage: -initFromImageFlipped:, CGDataProviderCreateWithData failed");
633 return nil;
634 }
635
636 const Util::CFScopeGuard<CGColorSpaceRef> colorSpace(CGColorSpaceCreateDeviceRGB());
637 if (!colorSpace.Get()) {
638 NSLog(@"QuartzImage: -initFromImageFlipped:, CGColorSpaceCreateDeviceRGB failed");
639 return nil;
640 }
641
642 //8 bits per component, 32 bits per pixel, 4 bytes per pixel, kCGImageAlphaLast:
643 //all values hardcoded for TGCocoa::CreatePixmapFromData.
644 fImage.Reset(CGImageCreate(width, height, 8, 32, width * 4, colorSpace.Get(), kCGImageAlphaLast,
645 provider.Get(), 0, false, kCGRenderingIntentDefault));
646 if (!fImage.Get()) {
647 NSLog(@"QuartzImage: -initFromImageFlipped:, CGImageCreate failed");
648 return nil;
649 }
650 }
651
652 fWidth = width;
653 fHeight = height;
654
655 selfGuard.Release();
656 }
657
658 return self;
659}
660
661//______________________________________________________________________________
662- (BOOL) isRectInside : (X11::Rectangle) area
663{
664 if (area.fX < 0 || (unsigned)area.fX >= fWidth)
665 return NO;
666 if (area.fY < 0 || (unsigned)area.fY >= fHeight)
667 return NO;
668 if (area.fWidth > fWidth || !area.fWidth)
669 return NO;
670 if (area.fHeight > fHeight || !area.fHeight)
671 return NO;
672
673 return YES;
674}
675
676//______________________________________________________________________________
677- (unsigned char *) readColorBits : (X11::Rectangle) area
678{
679 assert([self isRectInside : area] == YES && "readColorBits: bad area parameter");
680 //Image, bitmap - they all must be converted to ARGB (bitmap) or BGRA (image) (for libAfterImage).
681 //Raw pointer - we pass the ownership.
682 unsigned char *buffer = 0;
683
684 try {
685 buffer = new unsigned char[area.fWidth * area.fHeight * 4]();
686 } catch (const std::bad_alloc &) {
687 NSLog(@"QuartzImage: -readColorBits:, memory allocation failed");
688 return 0;
689 }
690
691 unsigned char *dstPixel = buffer;
692 if (CGImageIsMask(fImage.Get())) {
693 //fImageData has 1 byte per pixel.
694 const unsigned char *line = &fImageData[0] + area.fY * fWidth;
695 const unsigned char *srcPixel = line + area.fX;
696
697 for (unsigned i = 0; i < area.fHeight; ++i) {
698 for (unsigned j = 0; j < area.fWidth; ++j, ++srcPixel, dstPixel += 4) {
699 if (!srcPixel[0])
700 dstPixel[0] = 255;//can be 1 or anything different from 0.
701 }
702
703 line += fWidth;
704 srcPixel = line + area.fX;
705 }
706
707 } else {
708 //fImageData has 4 bytes per pixel.
709 const unsigned char *line = &fImageData[0] + area.fY * fWidth * 4;
710 const unsigned char *srcPixel = line + area.fX * 4;
711
712 for (unsigned i = 0; i < area.fHeight; ++i) {
713 for (unsigned j = 0; j < area.fWidth; ++j, srcPixel += 4, dstPixel += 4) {
714 dstPixel[0] = srcPixel[2];
715 dstPixel[1] = srcPixel[1];
716 dstPixel[2] = srcPixel[0];
717 dstPixel[3] = srcPixel[3];
718 }
719
720 line += fWidth * 4;
721 srcPixel = line + area.fX * 4;
722 }
723
724 return buffer;
725 }
726
727 return buffer;
728}
729
730//______________________________________________________________________________
731- (BOOL) fIsPixmap
732{
733 return YES;
734}
735
736//______________________________________________________________________________
737- (BOOL) fIsOpenGLWidget
738{
739 return NO;
740}
741
742//______________________________________________________________________________
743- (CGFloat) fScaleFactor
744{
745 // TODO: this is to be understood yet ...
746 return 1.;
747}
748
749//______________________________________________________________________________
750- (unsigned) fWidth
751{
752 return fWidth;
753}
754
755//______________________________________________________________________________
756- (unsigned) fHeight
757{
758 return fHeight;
759}
760
761//______________________________________________________________________________
762- (CGImageRef) fImage
763{
764 return fImage.Get();
765}
766
767@end
768
769namespace ROOT {
770namespace MacOSX {
771namespace X11 {
772
773//______________________________________________________________________________
774CGImageRef CreateSubImage(QuartzImage *image, const Rectangle &area)
775{
776 assert(image != nil && "CreateSubImage, image parameter is nil");
777
778 const CGRect subImageRect = CGRectMake(area.fX, area.fY, area.fHeight, area.fWidth);
779 return CGImageCreateWithImageInRect(image.fImage, subImageRect);
780}
781
782namespace {
783
784//Now, close your eyes and open them at the end of this block. :)
785//Sure, this can be done easy, but I hate to convert between negative signed integers and
786//unsigned integers and the other way, so I have this implementation (integers will be always
787//positive and they obviously fit into unsigned integers).
788
789typedef std::pair<int, unsigned> range_type;
790
791//______________________________________________________________________________
792bool FindOverlapSameSigns(const range_type &left, const range_type &right, range_type &intersection)
793{
794 //"Same" means both xs are non-negative, or both are negative.
795 //left.x <= right.x.
796 const unsigned dX(right.first - left.first);//diff fits into the positive range of int.
797 //No intersection.
798 if (dX >= left.second)
799 return false;
800 //Find an intersection.
801 intersection.first = right.first;
802 intersection.second = std::min(right.second, left.second - dX);//left.second is always > dX.
803
804 return true;
805}
806
807//______________________________________________________________________________
808bool FindOverlapDifferentSigns(const range_type &left, const range_type &right, range_type &intersection)
809{
810 //x2 - x1 can overflow.
811 //Left.x is negative, right.x is non-negative (0 included).
812 const unsigned signedMinAbs(std::numeric_limits<unsigned>::max() / 2 + 1);
813
814 if (left.first == std::numeric_limits<int>::min()) {//hehehe
815 if (left.second <= signedMinAbs)
816 return false;
817
818 if (left.second - signedMinAbs <= unsigned(right.first))
819 return false;
820
821 intersection.first = right.first;
822 intersection.second = std::min(right.second, left.second - signedMinAbs - unsigned(right.first));
823 } else {
824 const unsigned leftXAbs(-left.first);//-left.first can't overflow.
825 if (leftXAbs >= left.second)
826 return false;
827
828 if (left.second - leftXAbs <= unsigned(right.first))
829 return false;
830
831 intersection.first = right.first;
832 intersection.second = std::min(right.second, left.second - leftXAbs - unsigned(right.first));
833 }
834
835 return true;
836}
837
838//______________________________________________________________________________
839bool FindOverlap(const range_type &range1, const range_type &range2, range_type &intersection)
840{
841 range_type left;
842 range_type right;
843
844 if (range1.first < range2.first) {
845 left = range1;
846 right = range2;
847 } else {
848 left = range2;
849 right = range1;
850 }
851
852 if (left.first < 0)
853 return right.first < 0 ? FindOverlapSameSigns(left, right, intersection) :
854 FindOverlapDifferentSigns(left, right, intersection);
855
856 return FindOverlapSameSigns(left, right, intersection);
857}
858
859}
860
861//______________________________________________________________________________
862bool AdjustCropArea(const Rectangle &srcRect, Rectangle &cropArea)
863{
864 //Find rects intersection.
865 range_type xIntersection;
866 if (!FindOverlap(range_type(srcRect.fX, srcRect.fWidth),
867 range_type(cropArea.fX, cropArea.fWidth), xIntersection))
868 return false;
869
870 range_type yIntersection;
871 if (!FindOverlap(range_type(srcRect.fY, srcRect.fHeight),
872 range_type(cropArea.fY, cropArea.fHeight), yIntersection))
873 return false;
874
875 cropArea.fX = xIntersection.first;
876 cropArea.fWidth = xIntersection.second;
877
878 cropArea.fY = yIntersection.first;
879 cropArea.fHeight = yIntersection.second;
880
881 return true;
882}
883
884//______________________________________________________________________________
885bool AdjustCropArea(QuartzImage *srcImage, Rectangle &cropArea)
886{
887 assert(srcImage != nil && "AdjustCropArea, srcImage parameter is nil");
888 assert(srcImage.fImage != nil && "AdjustCropArea, srcImage.fImage is nil");
889
890 return AdjustCropArea(X11::Rectangle(0, 0, srcImage.fWidth, srcImage.fHeight), cropArea);
891}
892
893//______________________________________________________________________________
894bool AdjustCropArea(QuartzImage *srcImage, NSRect &cropArea)
895{
896 assert(srcImage != nil && "AdjustCropArea, srcImage parameter is nil");
897 assert(srcImage.fImage != 0 && "AdjustCropArea, srcImage.fImage is null");
898
899 const Rectangle srcRect(0, 0, srcImage.fWidth, srcImage.fHeight);
900 Rectangle dstRect(int(cropArea.origin.x), int(cropArea.origin.y),
901 unsigned(cropArea.size.width), unsigned(cropArea.size.height));
902
903 if (AdjustCropArea(srcRect, dstRect)) {
904 cropArea.origin.x = dstRect.fX;
905 cropArea.origin.y = dstRect.fY;
906 cropArea.size.width = dstRect.fWidth;
907 cropArea.size.height = dstRect.fHeight;
908
909 return true;
910 }
911
912 return false;
913}
914
915//______________________________________________________________________________
916bool AdjustCropArea(QuartzPixmap *srcPixmap, X11::Rectangle &cropArea)
917{
918 assert(srcPixmap != nil && "AdjustCropArea, srcPixmap parameter is nil");
919
920 return AdjustCropArea(X11::Rectangle(0, 0, srcPixmap.fWidth, srcPixmap.fHeight), cropArea);
921}
922
923//______________________________________________________________________________
924bool TestBitmapBit(const unsigned char *bitmap, unsigned w, unsigned i, unsigned j)
925{
926 //Test if a bit (i,j) is set in a bitmap (w, h).
927
928 //Code in ROOT's GUI suggests, that byte is octet.
929 assert(bitmap != 0 && "TestBitmapBit, bitmap parameter is null");
930 assert(w != 0 && "TestBitmapBit, w parameter is 0");
931 assert(i < w && "TestBitmapBit, i parameter is >= w");
932
933 const unsigned bytesPerLine = (w + 7) / 8;
934 const unsigned char *line = bitmap + j * bytesPerLine;
935 const unsigned char byteValue = line[i / 8];
936
937 return byteValue & (1 << (i % 8));
938}
939
940//______________________________________________________________________________
941void FillPixmapBuffer(const unsigned char *bitmap, unsigned width, unsigned height,
942 ULong_t foregroundPixel, ULong_t backgroundPixel, unsigned depth,
943 unsigned char *imageData)
944{
945 assert(bitmap != 0 && "FillPixmapBuffer, bitmap parameter is null");
946 assert(width != 0 && "FillPixmapBuffer, width parameter is 0");
947 assert(height != 0 && "FillPixmapBuffer, height parameter is 0");
948 assert(imageData != 0 && "FillPixmapBuffer, imageData parameter is null");
949
950 if (depth > 1) {
951 unsigned char foregroundColor[4] = {};
952 PixelToRGB(foregroundPixel, foregroundColor);
953 unsigned char backgroundColor[4] = {};
954 PixelToRGB(backgroundPixel, backgroundColor);
955
956 for (unsigned j = 0; j < height; ++j) {
957 const unsigned line = j * width * 4;
958 for (unsigned i = 0; i < width; ++i) {
959 const unsigned pixel = line + i * 4;
960
961 if (TestBitmapBit(bitmap, width, i, j)) {
962 //Foreground color.
963 imageData[pixel] = foregroundColor[0];
964 imageData[pixel + 1] = foregroundColor[1];
965 imageData[pixel + 2] = foregroundColor[2];
966 } else {
967 imageData[pixel] = backgroundColor[0];
968 imageData[pixel + 1] = backgroundColor[1];
969 imageData[pixel + 2] = backgroundColor[2];
970 }
971
972 imageData[pixel + 3] = 255;
973 }
974 }
975 } else {
976 for (unsigned j = 0; j < height; ++j) {
977 const unsigned line = j * width;
978 for (unsigned i = 0; i < width; ++i) {
979 const unsigned pixel = line + i;
980 if (TestBitmapBit(bitmap, width, i, j))
981 imageData[pixel] = 0;
982 else
983 imageData[pixel] = 255;//mask out pixel.
984 }
985 }
986 }
987}
988
989}//X11
990}//MacOSX
991}//ROOT
static Int_t init()
include TDocParser_001 C image html pict1_TDocParser_001 png width
Definition: TDocParser.cxx:121
XFontStruct * id
Definition: TGX11.cxx:108
typedef void((*Func_t)())
DerivedType * Get() const
Definition: CocoaUtils.h:136
void Reset(NSObject *object)
Definition: CocoaUtils.h:141
TLine * line
ROOT::MacOSX::Util::CFScopeGuard< CGImageRef > fImage
Definition: QuartzPixmap.h:96
unsigned fWidth
Definition: QuartzPixmap.h:93
unsigned fHeight
Definition: QuartzPixmap.h:94
BOOL fIsOpenGLWidget()
unsigned fHeight
Definition: QuartzPixmap.h:38
std::vector< unsigned char > fData
Definition: QuartzPixmap.h:40
ROOT::MacOSX::Util::CFScopeGuard< CGContextRef > fContext
Definition: QuartzPixmap.h:41
CGImageRef createImageFromPixmap()
unsigned fWidth
Definition: QuartzPixmap.h:37
CGFloat fScaleFactor
Definition: QuartzPixmap.h:43
unsigned fID
Definition: QuartzPixmap.h:34
Double_t y[n]
Definition: legend1.C:17
#define H(x, y, z)
bool TestBitmapBit(const unsigned char *bitmap, unsigned w, unsigned i, unsigned j)
void FillPixmapBuffer(const unsigned char *bitmap, unsigned width, unsigned height, ULong_t foregroundPixel, ULong_t backgroundPixel, unsigned depth, unsigned char *imageData)
CGImageRef CreateSubImage(QuartzImage *image, const Rectangle &area)
int LocalYROOTToCocoa(NSView< X11Window > *parentView, CGFloat yROOT)
bool AdjustCropArea(const Rectangle &srcRect, Rectangle &cropArea)
null_t< F > null()
auto * a
Definition: textangle.C:12