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