Logo ROOT   6.12/07
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 
28 namespace X11 = ROOT::MacOSX::X11;
29 namespace Util = ROOT::MacOSX::Util;
30 namespace 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 //______________________________________________________________________________
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 
311  Util::NSScopeGuard<QuartzPixmap> scaledPixmap;
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 
769 namespace ROOT {
770 namespace MacOSX {
771 namespace X11 {
772 
773 //______________________________________________________________________________
774 CGImageRef 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 
782 namespace {
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 
789 typedef std::pair<int, unsigned> range_type;
790 
791 //______________________________________________________________________________
792 bool 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 //______________________________________________________________________________
808 bool 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 //______________________________________________________________________________
839 bool 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 //______________________________________________________________________________
862 bool 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 //______________________________________________________________________________
885 bool 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 //______________________________________________________________________________
894 bool 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 //______________________________________________________________________________
916 bool 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 //______________________________________________________________________________
924 bool 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 //______________________________________________________________________________
941 void 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
unsigned char * fData()
TLine * line
unsigned fID
Definition: QuartzPixmap.h:34
bool AdjustCropArea(const Rectangle &srcRect, Rectangle &cropArea)
#define H(x, y, z)
unsigned fHeight()
void FillPixmapBuffer(const unsigned char *bitmap, unsigned width, unsigned height, ULong_t foregroundPixel, ULong_t backgroundPixel, unsigned depth, unsigned char *imageData)
null_t< F > null()
bool TestBitmapBit(const unsigned char *bitmap, unsigned w, unsigned i, unsigned j)
CGImageRef CreateSubImage(QuartzImage *image, const Rectangle &area)
XFontStruct * id
Definition: TGX11.cxx:108
BOOL fIsOpenGLWidget()
auto * a
Definition: textangle.C:12
CGImageRef createImageFromPixmap()
ROOT::MacOSX::Util::CFScopeGuard< CGContextRef > fContext
Definition: QuartzPixmap.h:41
CGFloat fScaleFactor()
unsigned fWidth()
int LocalYROOTToCocoa(NSView< X11Window > *parentView, CGFloat yROOT)
static Int_t init()
Double_t y[n]
Definition: legend1.C:17
typedef void((*Func_t)())