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