1#include "CocoMM.h" 2 3#ifdef PLATFORM_COCOA 4 5#define LLOG(x) 6 7namespace Upp { 8 9CGImageRef createCGImage(const Image& img) 10{ 11 if(IsNull(img)) 12 return NULL; 13 CGDataProvider *dataProvider = CGDataProviderCreateWithData(NULL, ~img, img.GetLength() * sizeof(RGBA), NULL); 14 static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // TODO: This is probably wrong... 15 Upp::Size isz = img.GetSize(); 16 CGImageRef cg_img = CGImageCreate(isz.cx, isz.cy, 8, 32, isz.cx * sizeof(RGBA), 17 colorSpace, kCGImageAlphaPremultipliedFirst, 18 dataProvider, 0, false, kCGRenderingIntentDefault); 19 CGDataProviderRelease(dataProvider); 20 return cg_img; 21} 22 23struct ImageSysData { 24 Image img; 25 CGImageRef cgimg = NULL; 26 27 void Init(const Image& img); 28 ~ImageSysData(); 29}; 30 31// Note: Cocoa U++ has to ignore paintonly flag as the data of image are directly referenced by 32// data provider 33 34void ImageSysData::Init(const Image& m) 35{ 36 img = m; 37 cgimg = createCGImage(img); 38 // Do not call SysImageRealized(img) here 39} 40 41ImageSysData::~ImageSysData() 42{ 43 SysImageReleased(img); 44 // Do not call CGImageRelease(cgimg); here 45} 46 47struct ImageSysDataMaker : LRUCache<ImageSysData, int64>::Maker { 48 Image img; 49 50 virtual int64 Key() const { return img.GetSerialId(); } 51 virtual int Make(ImageSysData& object) const { object.Init(img); return img.GetLength(); } 52}; 53 54static LRUCache<ImageSysData, int64> cg_image_cache; 55 56static void sCleanImageCache(const Image& img) 57{ 58 static int Rsz; 59 Rsz += img.GetLength(); 60 if(Rsz > 200 * 200) { // we do not want to do this for each small image painted... 61 Rsz = 0; 62 cg_image_cache.Remove([](const ImageSysData& object) { 63 return object.img.GetRefCount() == 1; 64 }); 65 } 66} 67 68void SystemDraw::SysDrawImageOp(int x, int y, const Image& img, Color color) 69{ 70 GuiLock __; 71 72 if(img.GetLength() == 0) 73 return; 74 75 sCleanImageCache(img); 76 77 ImageSysDataMaker m; 78 LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); 79 m.img = IsNull(color) ? img : CachedSetColorKeepAlpha(img, color); // TODO: Can setcolor be optimized out? By masks e.g.? 80 ImageSysData& sd = cg_image_cache.Get(m); 81 if(sd.cgimg) { 82 Size isz = img.GetSize(); 83 CGContextSaveGState(cgHandle); 84 Point off = GetOffset(); 85 CGContextTranslateCTM(cgHandle, x + off.x, y + off.y); 86 CGContextScaleCTM(cgHandle, 1.0, -1.0); 87 CGContextDrawImage(cgHandle, CGRectMake(0, -isz.cy, isz.cx, isz.cy), sd.cgimg); 88 CGContextRestoreGState(cgHandle); 89 } 90 Size sz = Ctrl::GetPrimaryScreenArea().GetSize(); 91 cg_image_cache.Shrink(4 * sz.cx * sz.cy, 1000); // Cache must be after Paint because of PaintOnly! 92} 93 94// TODO: https://stackoverflow.com/questions/10733228/native-osx-lion-resize-cursor-for-custom-nswindow-or-nsview 95 96enum { 97 MM_Arrow = 1, 98 MM_Wait, 99 MM_IBeam, 100 MM_No, 101 MM_SizeAll, 102 MM_SizeHorz, 103 MM_SizeVert, 104 MM_SizeTopLeft, 105 MM_SizeTop, 106 MM_SizeTopRight, 107 MM_SizeLeft, 108 MM_SizeRight, 109 MM_SizeBottomLeft, 110 MM_SizeBottom, 111 MM_SizeBottomRight, 112 MM_Cross, 113 MM_Hand, 114}; 115 116 117#define FCURSOR_(x) { static Image h; ONCELOCK { h = CreateImage(Size(1, 1), Black); h.SetAuxData(x); } return h; } 118 119Image Image::Arrow() FCURSOR_(MM_Arrow) 120Image Image::Wait() FCURSOR_(MM_Wait) 121Image Image::IBeam() FCURSOR_(MM_IBeam) 122Image Image::No() FCURSOR_(MM_No) 123Image Image::SizeAll() FCURSOR_(MM_SizeAll) 124Image Image::SizeHorz() FCURSOR_(MM_SizeHorz) 125Image Image::SizeVert() FCURSOR_(MM_SizeVert) 126Image Image::SizeTopLeft() FCURSOR_(MM_SizeTopLeft) 127Image Image::SizeTop() FCURSOR_(MM_SizeTop) 128Image Image::SizeTopRight() FCURSOR_(MM_SizeTopRight) 129Image Image::SizeLeft() FCURSOR_(MM_SizeLeft) 130Image Image::SizeRight() FCURSOR_(MM_SizeRight) 131Image Image::SizeBottomLeft() FCURSOR_(MM_SizeBottomLeft) 132Image Image::SizeBottom() FCURSOR_(MM_SizeBottom) 133Image Image::SizeBottomRight() FCURSOR_(MM_SizeBottomRight) 134Image Image::Cross() FCURSOR_(MM_Cross) 135Image Image::Hand() FCURSOR_(MM_Hand) 136 137// TODO: Missing kinds (sizers mostly) 138// https://stackoverflow.com/questions/10733228/native-osx-lion-resize-cursor-for-custom-nswindow-or-nsview/21786835#21786835 139 140#pragma clang diagnostic push 141#pragma clang diagnostic ignored "-Wundeclared-selector" 142 143#define XCURSOR(id) \ 144 if([NSCursor respondsToSelector:@selector(id)]) return [NSCursor performSelector:@selector(id)]; 145 146NSCursor *GetNSCursor(int kind) 147{ 148 switch(kind) { 149 case MM_SizeAll: 150 XCURSOR(_moveCursor); 151 break; 152 case MM_SizeHorz: 153 XCURSOR(_windowResizeEastWestCursor); 154 break; 155 case MM_SizeVert: 156 XCURSOR(_windowResizeNorthSouthCursor); 157 break; 158 case MM_SizeTopLeft: 159 XCURSOR(_windowResizeNorthWestSouthEastCursor); 160 break; 161 case MM_SizeTop: 162 XCURSOR(_windowResizeNorthCursor); 163 break; 164 case MM_SizeTopRight: 165 XCURSOR(_windowResizeNorthEastSouthWestCursor); 166 break; 167 case MM_SizeLeft: 168 XCURSOR(_windowResizeWestCursor); 169 break; 170 case MM_SizeRight: 171 XCURSOR(_windowResizeEastCursor); 172 break; 173 case MM_SizeBottomLeft: 174 XCURSOR(_windowResizeNorthEastSouthWestCursor); 175 break; 176 case MM_SizeBottom: 177 XCURSOR(_windowResizeSouthCursor); 178 break; 179 case MM_SizeBottomRight: 180 XCURSOR(_windowResizeNorthWestSouthEastCursor); 181 break; 182 case MM_IBeam: return [NSCursor IBeamCursor]; 183 case MM_Cross: return [NSCursor crosshairCursor]; 184 case MM_Hand: return [NSCursor pointingHandCursor]; 185 case MM_No: return [NSCursor operationNotAllowedCursor]; 186 } 187 return [NSCursor arrowCursor]; 188} 189 190#pragma clang diagnostic pop 191 192NSImage *CreateNSImage(const Image& img, CGImageRef cgimg) 193{ 194 sCleanImageCache(img); 195 Size isz = img.GetSize(); 196 double scale = 1.0 / DPI(1); 197 NSSize size; 198 size.width = scale * isz.cx; 199 size.height = scale * isz.cy; 200 NSImage *nsimage = [[NSImage alloc] initWithCGImage:cgimg size:size]; 201 cg_image_cache.Shrink(4 * 1024 * 768, 1000); // Cache must be after Paint because of PaintOnly! 202 return nsimage; 203} 204 205struct NSImageSysData { 206 Image img; 207 NSImage *nsimage = NULL; 208 209 void Init(const Image& img); 210 ~NSImageSysData(); 211}; 212 213void NSImageSysData::Init(const Image& img) 214{ 215 ImageSysDataMaker m; 216 m.img = img; 217 nsimage = CreateNSImage(img, cg_image_cache.Get(m).cgimg); 218} 219 220NSImageSysData::~NSImageSysData() 221{ 222 if(nsimage) 223 [nsimage release]; 224} 225 226struct NSImageSysDataMaker : LRUCache<NSImageSysData, int64>::Maker { 227 Image img; 228 229 virtual int64 Key() const { return img.GetSerialId(); } 230 virtual int Make(NSImageSysData& object) const { object.Init(img); return img.GetLength(); } 231}; 232 233static LRUCache<NSImageSysData, int64> nsimage_cache; 234 235NSImage *GetNSImage(const Image& img) 236{ 237 NSImageSysDataMaker m; 238 m.img = img; 239 NSImage *nsimage = nsimage_cache.Get(m).nsimage; 240 nsimage_cache.Shrink(4 * 1024*768, 1000); 241 return nsimage; 242} 243 244void Ctrl::SetNSAppImage(const Image& img) 245{ 246 ImageSysDataMaker m; 247 LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); 248 m.img = img; 249 ImageSysData& sd = cg_image_cache.Get(m); 250 static CGImageRef cgimg; 251 static NSImage *nsimg; 252 if(sd.cgimg != cgimg) { 253 cgimg = sd.cgimg; 254 if(nsimg) 255 [nsimg release]; 256 nsimg = CreateNSImage(img, cgimg); 257 } 258 [NSApp setApplicationIconImage:nsimg]; 259} 260 261void Ctrl::SetMouseCursor(const Image& img) 262{ 263 if(GetDragAndDropSource()) 264 return; 265 int64 h = img.GetAuxData(); 266 if(h) { 267 [GetNSCursor(h) set]; 268 return; 269 } 270 ImageSysDataMaker m; 271 LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); 272 m.img = img; 273 ImageSysData& sd = cg_image_cache.Get(m); 274 static CGImageRef cgimg; 275 static NSCursor *cursor; 276 if(sd.cgimg != cgimg) { 277 cgimg = sd.cgimg; 278 if(cursor) 279 [cursor release]; 280 NSImage *nsimg = CreateNSImage(img, cgimg); 281 double scale = 1.0 / DPI(1); 282 Point p = img.GetHotSpot(); 283 NSPoint hot; 284 hot.x = scale * p.x; 285 hot.y = scale * p.y; 286 cursor = [[NSCursor alloc] initWithImage:nsimg hotSpot:hot]; 287 [nsimg release]; 288 } 289 [cursor set]; 290} 291 292void ImageDraw::Init(int cx, int cy) 293{ 294 ib.Create(cx, cy); 295 Fill(~ib, RGBAZero(), ib.GetLength()); 296 297 static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 298 299 SystemDraw::Init(CGBitmapContextCreateWithData(~ib, cx, cy, 8, cx * sizeof(RGBA), 300 colorSpace, kCGImageAlphaPremultipliedFirst, 301 NULL, NULL), NULL); 302 CGContextTranslateCTM(cgHandle, 0, cy); 303 if(IsUHDMode()) { 304 CGContextScaleCTM(cgHandle, 2, -2); 305 CGContextTranslateCTM(cgHandle, 0, -cy / 2.0); 306 } 307 else 308 CGContextScaleCTM(cgHandle, 1, -1); 309} 310 311ImageDraw::ImageDraw(Size sz) 312{ 313 Init(sz.cx, sz.cy); 314} 315 316ImageDraw::ImageDraw(int cx, int cy) 317{ 318 Init(cx, cy); 319} 320 321Draw& ImageDraw::Alpha() 322{ 323 if(!alpha) 324 alpha.Create(ib.GetSize()); 325 return *alpha; 326} 327 328ImageDraw::~ImageDraw() 329{ 330 CGContextRelease(cgHandle); 331 handle = NULL; // avoid releasing invalid handle in ~SystemDraw 332} 333 334Image ImageDraw::GetStraight() const 335{ 336 ImageBuffer ib2(ib.GetSize()); 337 memcpy_t(~ib2, ~ib, ib.GetLength()); 338 if(alpha) { 339 RGBA *e = ib2.End(); 340 RGBA *t = ib2; 341 const RGBA *s = alpha->ib; 342 while(t < e) { 343 t->a = s->r; 344 s++; 345 t++; 346 } 347 } 348 return Image(ib2); 349} 350 351ImageDraw::operator Image() const 352{ 353 if(alpha) 354 return Premultiply(GetStraight()); 355 ImageBuffer ib2(ib.GetSize()); 356 memcpy_t(~ib2, ~ib, ib.GetLength()); 357 return Image(ib2); 358} 359 360Image GetIconForFile(const char *path) 361{ 362 ImageDraw iw(DPI(16, 16)); 363 364 CGContextRef cg = (CGContextRef) iw.GetCGHandle(); 365 366 367 NSImage *image; 368 CFRef<CFStringRef> fexe = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); 369 image = [[NSWorkspace sharedWorkspace]iconForFile:(__bridge NSString *)~fexe]; 370/* 371 } 372 else { // not used any more 373 CFRef<CFStringRef> fext = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); 374 image = *ext == '*' ? [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] 375 : [[NSWorkspace sharedWorkspace]iconForFileType:(__bridge NSString *)~fext]; 376 } 377*/ 378 NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:cg flipped:YES]; 379 NSGraphicsContext* cgc = [NSGraphicsContext currentContext]; 380 [NSGraphicsContext setCurrentContext:gc]; 381 [image drawInRect:NSMakeRect(0, 0, DPI(16), DPI(16))]; 382 [NSGraphicsContext setCurrentContext:cgc]; 383 384 return iw; 385} 386 387}; 388 389#endif 390