update plists MACOSX_DEPLOYMENT_TARGET = 10.7.0
[wdl/wdl-ol.git] / WDL / swell / swell-gdi.mm
blob90ad60cce881597bb9efb746785ff2260fd88919
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2    Copyright (C) 2006 and later, Cockos, Inc.
4     This software is provided 'as-is', without any express or implied
5     warranty.  In no event will the authors be held liable for any damages
6     arising from the use of this software.
8     Permission is granted to anyone to use this software for any purpose,
9     including commercial applications, and to alter it and redistribute it
10     freely, subject to the following restrictions:
12     1. The origin of this software must not be misrepresented; you must not
13        claim that you wrote the original software. If you use this software
14        in a product, an acknowledgment in the product documentation would be
15        appreciated but is not required.
16     2. Altered source versions must be plainly marked as such, and must not be
17        misrepresented as being the original software.
18     3. This notice may not be removed or altered from any source distribution.
19   
21     This file provides basic win32 GDI-->Quartz translation. It uses features that require OS X 10.4+
25 #ifndef SWELL_PROVIDED_BY_APP
27 #import <Carbon/Carbon.h>
28 #import <Cocoa/Cocoa.h>
29 #import <CoreFoundation/CFDictionary.h>
30 #import <objc/objc-runtime.h>
31 #include "swell.h"
32 #include "swell-internal.h"
34 #include "../mutex.h"
35 #include "../assocarray.h"
36 #include "../wdlcstring.h"
38 #ifdef __SSE__
39 #include <xmmintrin.h>
40 #endif
42 #ifdef SWELL_SUPPORT_OPENGL_BLIT
43 #include <OpenGL/gl.h>
44 #endif
46 // reimplement here so that swell-gdi isn't dependent on swell-misc, and vice-versa
47 static int SWELL_GDI_GetOSXVersion()
49   static SInt32 v;
50   if (!v)
51   {
52     if (NSAppKitVersionNumber >= 1266.0) 
53     {
54       v=0x10a0; // 10.10+ Gestalt(gsv) return 0x109x, so we bump this to 0x10a0
55     }
56     else 
57     {
58       SInt32 a = 0x1040;
59       Gestalt(gestaltSystemVersion,&a);
60       v=a;
61     }
62   }
63   return v;
66 #ifdef __AVX__
67 #include <immintrin.h>
68 #endif
70 #ifndef MAC_OS_X_VERSION_10_6
71 // 10.5 SDK doesn't include CGContextSetAllowsFontSmoothing() in header (but apparently does in libs)
72 CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef c, bool) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER;
73 #endif
75 #ifndef SWELL_NO_CORETEXT
76 static bool IsCoreTextSupported()
78 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
79   return SWELL_GDI_GetOSXVersion() >= 0x1050 && CTFontCreateWithName && CTLineDraw && CTFramesetterCreateWithAttributedString && CTFramesetterCreateFrame && 
80          CTFrameGetLines && CTLineGetTypographicBounds && CTLineCreateWithAttributedString && CTFontCopyPostScriptName
81          ;
82 #else
83   // targetting 10.5+, CT is always valid
84   return true;
85 #endif
88 static CTFontRef GetCoreTextDefaultFont()
90   static CTFontRef deffr;
91   static bool ok;
92   if (!ok)
93   {
94     ok=true;
95     if (IsCoreTextSupported())
96     {
97       deffr=(CTFontRef) [[NSFont labelFontOfSize:10.0] retain]; 
98     }
99   }
100   return deffr;
102 #endif // !SWELL_NO_CORETEXT
103   
105 static NSString *CStringToNSString(const char *str)
107   if (!str) str="";
108   NSString *ret;
109   
110   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8);
111   if (ret) return ret;
112   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII);
113   return ret;
115 CGColorSpaceRef __GetBitmapColorSpace()
117   static CGColorSpaceRef cs;
118   if (!cs) cs = CGColorSpaceCreateDeviceRGB();
119   return cs;
122 CGColorSpaceRef __GetDisplayColorSpace()
124   static CGColorSpaceRef cs;
125   if (!cs)
126   {
127     // use monitor profile for 10.7+
128     if (SWELL_GDI_GetOSXVersion() >= 0x1070)
129     {
131 #ifdef MAC_OS_X_VERSION_10_11
132       // OSX 10.11 SDK removes CMGetSystemProfile
133       // this may be preferable on older SDKs as well, need to test (though CGDisplayCopyColorSpace is only available on 10.5+)
134       cs = CGDisplayCopyColorSpace(CGMainDisplayID());
135 #else
136       CMProfileRef systemMonitorProfile = NULL;
137       CMError getProfileErr = CMGetSystemProfile(&systemMonitorProfile);
138       if(noErr == getProfileErr)
139       {
140         cs = CGColorSpaceCreateWithPlatformColorSpace(systemMonitorProfile);
141         CMCloseProfile(systemMonitorProfile);
142       }
143 #endif
144     }
145   }
146   if (!cs) 
147     cs = CGColorSpaceCreateDeviceRGB();
148   return cs;
151 static CGColorRef CreateColor(int col, float alpha=1.0f)
153   CGFloat cols[4]={GetRValue(col)/255.0f,GetGValue(col)/255.0f,GetBValue(col)/255.0f,alpha};
154   CGColorRef color=CGColorCreate(__GetBitmapColorSpace(),cols);
155   return color;
159 #include "swell-gdi-internalpool.h"
161 int SWELL_IsRetinaHWND(HWND hwnd)
163   if (!hwnd || SWELL_GDI_GetOSXVersion() < 0x1070) return 0;
165   NSWindow *w=NULL;
166   if ([(id)hwnd isKindOfClass:[NSView class]]) w = [(NSView *)hwnd window];
167   else if ([(id)hwnd isKindOfClass:[NSWindow class]]) w = (NSWindow *)hwnd;
169   if (w)
170   {
171     NSRect r=NSMakeRect(0,0,1,1);
172     NSRect (*tmp)(id receiver, SEL operation, NSRect) = (NSRect (*)(id, SEL, NSRect))objc_msgSend_stret;
173     NSRect str = tmp(w,sel_getUid("convertRectToBacking:"),r);
175     if (str.size.width > 1.9) return 1;
176   }
177   return 0;
180 int SWELL_IsRetinaDC(HDC hdc)
182   HDC__ *src=(HDC__*)hdc;
183   if (!src || !HDC_VALID(src) || !src->ctx) return 0;
184   return CGContextConvertSizeToDeviceSpace((CGContextRef)src->ctx, CGSizeMake(1,1)).width > 1.9 ? 1 : 0;
188 HDC SWELL_CreateGfxContext(void *c)
190   HDC__ *ctx=SWELL_GDP_CTX_NEW();
191   NSGraphicsContext *nsc = (NSGraphicsContext *)c;
192 //  if (![nsc isFlipped])
193 //    nsc = [NSGraphicsContext graphicsContextWithGraphicsPort:[nsc graphicsPort] flipped:YES];
195   ctx->ctx=(CGContextRef)[nsc graphicsPort];
196 //  CGAffineTransform f={1,0,0,-1,0,0};
197   //CGContextSetTextMatrix(ctx->ctx,f);
198   //SetTextColor(ctx,0);
199   
200  // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
201   return ctx;
204 #define ALIGN_EXTRA 63
205 static void *ALIGN_FBUF(void *inbuf)
207   const UINT_PTR extra = ALIGN_EXTRA;
208   return (void *) (((UINT_PTR)inbuf+extra)&~extra); 
211 HDC SWELL_CreateMemContext(HDC hdc, int w, int h)
213   void *buf=calloc(w*4*h+ALIGN_EXTRA,1);
214   if (!buf) return 0;
215   CGContextRef c=CGBitmapContextCreate(ALIGN_FBUF(buf),w,h,8,w*4, __GetBitmapColorSpace(), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host);
216   if (!c)
217   {
218     free(buf);
219     return 0;
220   }
223   CGContextTranslateCTM(c,0.0,h);
224   CGContextScaleCTM(c,1.0,-1.0);
225   CGContextSetAllowsFontSmoothing(c,0); // we may wish to enable this for some contexts eventually, but this is to match previous behavior
227   HDC__ *ctx=SWELL_GDP_CTX_NEW();
228   ctx->ctx=(CGContextRef)c;
229   ctx->ownedData=buf;
230   // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
231   
232   SetTextColor(ctx,0);
233   return ctx;
236 void SWELL_DeleteGfxContext(HDC ctx)
238   HDC__ *ct=(HDC__ *)ctx;
239   if (HDC_VALID(ct))
240   {   
241     if (ct->ownedData)
242     {
243       CGContextRelease(ct->ctx);
244       free(ct->ownedData);
245     }
246     if (ct->curtextcol) CFRelease(ct->curtextcol); 
247     SWELL_GDP_CTX_DELETE(ct);
248   }
250 HPEN CreatePen(int attr, int wid, int col)
252   return CreatePenAlpha(attr,wid,col,1.0f);
255 HBRUSH CreateSolidBrush(int col)
257   return CreateSolidBrushAlpha(col,1.0f);
262 HPEN CreatePenAlpha(int attr, int wid, int col, float alpha)
264   HGDIOBJ__ *pen=GDP_OBJECT_NEW();
265   pen->type=TYPE_PEN;
266   pen->wid=wid<0?0:wid;
267   pen->color=CreateColor(col,alpha);
268   return pen;
270 HBRUSH  CreateSolidBrushAlpha(int col, float alpha)
272   HGDIOBJ__ *brush=GDP_OBJECT_NEW();
273   brush->type=TYPE_BRUSH;
274   brush->color=CreateColor(col,alpha);
275   brush->wid=0; 
276   return brush;
280 HFONT CreateFontIndirect(LOGFONT *lf)
282   return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, 
283                     lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, 
284                     lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName);
287 static HGDIOBJ__ global_objs[2];
289 void DeleteObject(HGDIOBJ pen)
291   HGDIOBJ__ *p=(HGDIOBJ__ *)pen;
292   if (p >= global_objs && p < global_objs + sizeof(global_objs)/sizeof(global_objs[0])) return;
294   if (HGDIOBJ_VALID(p))
295   {
296     if (--p->additional_refcnt < 0)
297     {
298       if (p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP)
299       {
300         if (p->type == TYPE_PEN || p->type == TYPE_BRUSH)
301           if (p->wid<0) return;
302         if (p->color) CGColorRelease(p->color);
304         if (p->ct_FontRef) CFRelease(p->ct_FontRef);
306 #ifdef SWELL_ATSUI_TEXT_SUPPORT
307         if (p->atsui_font_style) ATSUDisposeStyle(p->atsui_font_style);
308 #endif
310         if (p->wid && p->bitmapptr) [p->bitmapptr release]; 
311         GDP_OBJECT_DELETE(p);
312       }
313       // JF> don't free unknown objects, this shouldn't ever happen anyway: else free(p);
314     }
315   }
319 HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen)
321   HDC__ *c=(HDC__ *)ctx;
322   HGDIOBJ__ *p=(HGDIOBJ__*) pen;
323   HGDIOBJ__ **mod=0;
324   if (!HDC_VALID(c)) return 0;
325   
326   if (p == (HGDIOBJ__*)TYPE_PEN) mod=&c->curpen;
327   else if (p == (HGDIOBJ__*)TYPE_BRUSH) mod=&c->curbrush;
328   else if (p == (HGDIOBJ__*)TYPE_FONT) mod=&c->curfont;
330   if (mod) // clearing a particular thing
331   {
332     HGDIOBJ__ *np=*mod;
333     *mod=0;
334     return HGDIOBJ_VALID(np,(int)(INT_PTR)p)?np:p;
335   }
337   if (!HGDIOBJ_VALID(p)) return 0;
338   
339   if (p->type == TYPE_PEN) mod=&c->curpen;
340   else if (p->type == TYPE_BRUSH) mod=&c->curbrush;
341   else if (p->type == TYPE_FONT) mod=&c->curfont;
342   
343   if (!mod) return 0;
344   
345   HGDIOBJ__ *op=*mod;
346   if (!HGDIOBJ_VALID(op,p->type)) op=(HGDIOBJ__*)(INT_PTR)p->type;
347   if (op != p) *mod=p;  
348   return op;
353 void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br)
355   HDC__ *c=(HDC__ *)ctx;
356   HGDIOBJ__ *b=(HGDIOBJ__*) br;
357   if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH) || b == (HGDIOBJ__*)TYPE_BRUSH || b->type != TYPE_BRUSH) return;
359   if (b->wid<0) return;
360   
361   CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
362   CGContextSetFillColorWithColor(c->ctx,b->color);
363   CGContextFillRect(c->ctx,rect);       
367 void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd)
369         xrnd/=3;
370         yrnd/=3;
371         POINT pts[10]={ // todo: curves between edges
372                 {x,y+yrnd},
373                 {x+xrnd,y},
374                 {x2-xrnd,y},
375                 {x2,y+yrnd},
376                 {x2,y2-yrnd},
377                 {x2-xrnd,y2},
378                 {x+xrnd,y2},
379                 {x,y2-yrnd},            
380     {x,y+yrnd},
381                 {x+xrnd,y},
383         
384         WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0]));
387 void Ellipse(HDC ctx, int l, int t, int r, int b)
389   HDC__ *c=(HDC__ *)ctx;
390   if (!HDC_VALID(c)) return;
391   
392   CGRect rect=CGRectMake(l,t,r-l,b-t);
393   
394   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >=0)
395   {
396     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
397     CGContextFillEllipseInRect(c->ctx,rect);    
398   }
399   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
400   {
401     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
402     CGContextStrokeEllipseInRect(c->ctx, rect); //, (float)wdl_max(1,c->curpen->wid));
403   }
406 void Rectangle(HDC ctx, int l, int t, int r, int b)
408   HDC__ *c=(HDC__ *)ctx;
409   if (!HDC_VALID(c)) return;
410   
411   CGRect rect=CGRectMake(l,t,r-l,b-t);
412   
413   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
414   {
415     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
416     CGContextFillRect(c->ctx,rect);     
417   }
418   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
419   {
420     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
421     CGContextStrokeRectWithWidth(c->ctx, rect, (float)wdl_max(1,c->curpen->wid));
422   }
426 HGDIOBJ GetStockObject(int wh)
428   switch (wh)
429   {
430     case NULL_BRUSH:
431     {
432       HGDIOBJ__ *p = &global_objs[0];
433       p->type=TYPE_BRUSH;
434       p->wid=-1;
435       return p;
436     }
437     case NULL_PEN:
438     {
439       HGDIOBJ__ *p = &global_objs[1];
440       p->type=TYPE_PEN;
441       p->wid=-1;
442       return p;
443     }
444   }
445   return 0;
448 void Polygon(HDC ctx, POINT *pts, int npts)
450   HDC__ *c=(HDC__ *)ctx;
451   if (!HDC_VALID(c)) return;
452   if (((!HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)||c->curbrush->wid<0) && (!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0)) || npts<2) return;
454   CGContextBeginPath(c->ctx);
455   CGContextMoveToPoint(c->ctx,(float)pts[0].x,(float)pts[0].y);
456   int x;
457   for (x = 1; x < npts; x ++)
458   {
459     CGContextAddLineToPoint(c->ctx,(float)pts[x].x,(float)pts[x].y);
460   }
461   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
462   {
463     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
464   }
465   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0)
466   {
467     CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1));
468     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);  
469   }
470   CGContextDrawPath(c->ctx,HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 && HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid>=0 ?  kCGPathFillStroke : HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 ? kCGPathStroke : kCGPathFill);
473 void MoveToEx(HDC ctx, int x, int y, POINT *op)
475   HDC__ *c=(HDC__ *)ctx;
476   if (!HDC_VALID(c)) return;
477   if (op) 
478   { 
479     op->x = (int) (c->lastpos_x);
480     op->y = (int) (c->lastpos_y);
481   }
482   c->lastpos_x=(float)x;
483   c->lastpos_y=(float)y;
486 void PolyBezierTo(HDC ctx, POINT *pts, int np)
488   HDC__ *c=(HDC__ *)ctx;
489   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return;
490   
491   CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1));
492   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
493         
494   CGContextBeginPath(c->ctx);
495   CGContextMoveToPoint(c->ctx,c->lastpos_x,c->lastpos_y);
496   int x; 
497   float xp,yp;
498   for (x = 0; x < np-2; x += 3)
499   {
500     CGContextAddCurveToPoint(c->ctx,
501       (float)pts[x].x,(float)pts[x].y,
502       (float)pts[x+1].x,(float)pts[x+1].y,
503       xp=(float)pts[x+2].x,yp=(float)pts[x+2].y);    
504   }
505   c->lastpos_x=(float)xp;
506   c->lastpos_y=(float)yp;
507   CGContextStrokePath(c->ctx);
511 void SWELL_LineTo(HDC ctx, int x, int y)
513   HDC__ *c=(HDC__ *)ctx;
514   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return;
516   float w = (float)wdl_max(c->curpen->wid,1);
517   CGContextSetLineWidth(c->ctx,w);
518   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
519         
520   CGContextBeginPath(c->ctx);
521   CGContextMoveToPoint(c->ctx,c->lastpos_x + w * 0.5,c->lastpos_y + w*0.5);
522   float fx=(float)x,fy=(float)y;
523   
524   CGContextAddLineToPoint(c->ctx,fx+w*0.5,fy+w*0.5);
525   c->lastpos_x=fx;
526   c->lastpos_y=fy;
527   CGContextStrokePath(c->ctx);
530 void PolyPolyline(HDC ctx, POINT *pts, DWORD *cnts, int nseg)
532   HDC__ *c=(HDC__ *)ctx;
533   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return;
535   float w = (float)wdl_max(c->curpen->wid,1);
536   CGContextSetLineWidth(c->ctx,w);
537   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
538         
539   CGContextBeginPath(c->ctx);
540   
541   while (nseg-->0)
542   {
543     DWORD cnt=*cnts++;
544     if (!cnt) continue;
545     if (!--cnt) { pts++; continue; }
546     
547     CGContextMoveToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
548     pts++;
549     
550     while (cnt--)
551     {
552       CGContextAddLineToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
553       pts++;
554     }
555   }
556   CGContextStrokePath(c->ctx);
558 void *SWELL_GetCtxGC(HDC ctx)
560   HDC__ *ct=(HDC__ *)ctx;
561   if (!HDC_VALID(ct)) return 0;
562   return ct->ctx;
566 void SWELL_SetPixel(HDC ctx, int x, int y, int c)
568   HDC__ *ct=(HDC__ *)ctx;
569   if (!HDC_VALID(ct)) return;
570   CGContextBeginPath(ct->ctx);
571   CGContextMoveToPoint(ct->ctx,(float)x-0.5,(float)y-0.5);
572   CGContextAddLineToPoint(ct->ctx,(float)x+0.5,(float)y+0.5);
573   CGContextSetLineWidth(ct->ctx,(float)1.0);
574   CGContextSetRGBStrokeColor(ct->ctx,GetRValue(c)/255.0,GetGValue(c)/255.0,GetBValue(c)/255.0,1.0);
575   CGContextStrokePath(ct->ctx); 
579 static WDL_Mutex s_fontnamecache_mutex;
581 #ifdef SWELL_CLEANUP_ON_UNLOAD
582 static void releaseString(NSString *s) { [s release]; }
583 #endif
584 static WDL_StringKeyedArray<NSString *> s_fontnamecache(true,
585 #ifdef SWELL_CLEANUP_ON_UNLOAD
586       releaseString
587 #else
588       NULL
589 #endif
590       );
592 static NSString *SWELL_GetCachedFontName(const char *nm)
594   NSString *ret = NULL;
595   if (nm && *nm)
596   {
597     s_fontnamecache_mutex.Enter();
598     ret = s_fontnamecache.Get(nm);
599     s_fontnamecache_mutex.Leave();
600     if (!ret)
601     {
602       ret = CStringToNSString(nm);
603       if (ret)
604       {
605 #ifndef SWELL_NO_CORETEXT
606         // only do postscript name lookups on 10.9+
607         if (floor(NSFoundationVersionNumber) > 945.00) // NSFoundationVersionNumber10_8
608         {
609           NSFont *font = [NSFont fontWithName:ret size:10];
610           NSString *nr = font ? (NSString *)CTFontCopyPostScriptName((CTFontRef)font) : NULL;
611           if (nr) 
612           {
613             [ret release];
614             ret = nr;
615           }
616         }
617 #endif
619         s_fontnamecache_mutex.Enter();
620         s_fontnamecache.Insert(nm,ret);
621         s_fontnamecache_mutex.Leave();
622       }
623     }
624   }
625   return ret ? ret : @"";
628 HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, 
629                  char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, 
630                  char lfQuality, char lfPitchAndFamily, const char *lfFaceName)
632   HGDIOBJ__ *font=GDP_OBJECT_NEW();
633   font->type=TYPE_FONT;
634   float fontwid=lfHeight;
635   
636   if (!fontwid) fontwid=lfWidth;
637   if (fontwid<0)fontwid=-fontwid;
638   
639   if (fontwid < 2 || fontwid > 8192) fontwid=10;
640   
641   font->font_rotation = lfOrientation/10.0;
643 #ifndef SWELL_NO_CORETEXT
644   if (IsCoreTextSupported())
645   {
646     char buf[1024];
647     lstrcpyn_safe(buf,lfFaceName,900);
648     if (lfWeight >= FW_BOLD) strcat(buf," Bold");
649     if (lfItalic) strcat(buf," Italic");
651     font->ct_FontRef = (void*)CTFontCreateWithName((CFStringRef)SWELL_GetCachedFontName(buf),fontwid,NULL);
652     if (!font->ct_FontRef) font->ct_FontRef = (void*)[[NSFont labelFontOfSize:fontwid] retain]; 
654     font->font_quality = (!lfQuality || lfQuality == ANTIALIASED_QUALITY || lfQuality == NONANTIALIASED_QUALITY ? lfQuality : 0);
656     // might want to make this conditional (i.e. only return font if created successfully), but I think we'd rather fallback to a system font than use ATSUI
657     return font;
658   }
659 #endif
660   
661 #ifdef SWELL_ATSUI_TEXT_SUPPORT
662   ATSUFontID fontid=kATSUInvalidFontID;
663   if (lfFaceName && lfFaceName[0])
664   {
665     ATSUFindFontFromName(lfFaceName,strlen(lfFaceName),kFontFullName /* kFontFamilyName? */ ,(FontPlatformCode)kFontNoPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontid);
666     //    if (fontid==kATSUInvalidFontID) printf("looked up %s and got %d\n",lfFaceName,fontid);
667   }
668   
669   if (ATSUCreateStyle(&font->atsui_font_style) == noErr && font->atsui_font_style)
670   {    
671     Fixed fsize=Long2Fix(fontwid);
672     
673     Boolean isBold=lfWeight >= FW_BOLD;
674     Boolean isItal=!!lfItalic;
675     Boolean isUnder=!!lfUnderline;
676     
677     ATSStyleRenderingOptions render;
678     if (!lfQuality)
679       render = kATSStyleNoOptions;
680     else if (lfQuality == ANTIALIASED_QUALITY)
681       render = kATSStyleApplyAntiAliasing;
682     else if (lfQuality == NONANTIALIASED_QUALITY)
683       render = kATSStyleNoAntiAliasing;
684     else
685       render = kATSStyleNoOptions;
687     ATSUAttributeTag        theTags[] = { kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,kATSUSizeTag,kATSUFontTag, kATSUStyleRenderingOptionsTag };
688     ByteCount               theSizes[] = { sizeof(Boolean),sizeof(Boolean),sizeof(Boolean), sizeof(Fixed),sizeof(ATSUFontID), sizeof(ATSStyleRenderingOptions)  };
689     ATSUAttributeValuePtr   theValues[] =  {&isBold, &isItal, &isUnder,  &fsize, &fontid, &render } ;
690     
691     int attrcnt=sizeof(theTags)/sizeof(theTags[0]);
692     if (fontid == kATSUInvalidFontID) attrcnt--;    
693     
694     if (ATSUSetAttributes (font->atsui_font_style,                       
695                        attrcnt,                       
696                        theTags,                      
697                        theSizes,                      
698                        theValues)!=noErr)
699     {
700       ATSUDisposeStyle(font->atsui_font_style);
701       font->atsui_font_style=0;
702     }
703   }
704   else
705     font->atsui_font_style=0;
706   
707 #endif
708   
709   
710   return font;
713 int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName)
715   HDC__ *ct=(HDC__*)ctx;
716   if (!HDC_VALID(ct) || !nCount || !lpFaceName) return 0;
717   
718 #ifndef SWELL_NO_CORETEXT
719   CTFontRef fr=NULL;
720   if (HGDIOBJ_VALID(ct->curfont,TYPE_FONT)) fr=(CTFontRef)ct->curfont->ct_FontRef;
721   if (!fr)  fr=GetCoreTextDefaultFont();
722   
723   if (fr)
724   {
725     CFStringRef name=CTFontCopyDisplayName(fr);
726     const char* p=[(NSString*)name UTF8String];
727     if (p)
728     {
729       lstrcpyn_safe(lpFaceName, p, nCount);
730       return (int)strlen(lpFaceName);
731     }
732   }
733 #endif
734   
735   return 0;
738 BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm)
740   HDC__ *ct=(HDC__ *)ctx;
741   if (tm) // give some sane defaults
742   {
743     tm->tmInternalLeading=3;
744     tm->tmAscent=12;
745     tm->tmDescent=4;
746     tm->tmHeight=16;
747     tm->tmAveCharWidth = 10;
748   }
749   if (!HDC_VALID(ct)||!tm) return 0;
751   bool curfont_valid=HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
753 #ifdef SWELL_ATSUI_TEXT_SUPPORT
754   if (curfont_valid && ct->curfont->atsui_font_style)
755   {
756     ATSUTextMeasurement ascent=Long2Fix(10);
757     ATSUTextMeasurement descent=Long2Fix(3);
758     ATSUTextMeasurement sz=Long2Fix(0);
759     ATSUTextMeasurement width =Long2Fix(12);
760     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUAscentTag, sizeof(ATSUTextMeasurement), &ascent,NULL);
761     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUDescentTag, sizeof(ATSUTextMeasurement), &descent,NULL);
762     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUSizeTag, sizeof(ATSUTextMeasurement), &sz,NULL);
763     ATSUGetAttribute(ct->curfont->atsui_font_style, kATSULineWidthTag, sizeof(ATSUTextMeasurement),&width,NULL);
764     
765     float asc=Fix2X(ascent);
766     float desc=Fix2X(descent);
767     float size = Fix2X(sz);
768     
769     if (size < (asc+desc)*0.2) size=asc+desc;
770             
771     tm->tmAscent = (int)ceil(asc);
772     tm->tmDescent = (int)ceil(desc);
773     tm->tmInternalLeading=(int)ceil(asc+desc-size);
774     if (tm->tmInternalLeading<0)tm->tmInternalLeading=0;
775     tm->tmHeight=(int) ceil(asc+desc);
776     tm->tmAveCharWidth = (int) (ceil(asc+desc)*0.65); // (int)ceil(Fix2X(width));
777     
778     return 1;
779   }
780 #endif
782 #ifndef SWELL_NO_CORETEXT
783   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
784   if (!fr)  fr=GetCoreTextDefaultFont();
786   if (fr)
787   {
788     tm->tmInternalLeading = CTFontGetLeading(fr);
789     tm->tmAscent = CTFontGetAscent(fr);
790     tm->tmDescent = CTFontGetDescent(fr);
791     tm->tmHeight = (tm->tmInternalLeading + tm->tmAscent + tm->tmDescent);
792     tm->tmAveCharWidth = tm->tmHeight*2/3; // todo
794     if (tm->tmHeight)  tm->tmHeight++;
795     
796     return 1;
797   }
798 #endif
800   
801   return 1;
806 #ifdef SWELL_ATSUI_TEXT_SUPPORT
808 static int DrawTextATSUI(HDC ctx, CFStringRef strin, RECT *r, int align, bool *err)
810   HDC__ *ct=(HDC__ *)ctx;
811   HGDIOBJ__ *font=ct->curfont; // caller must specify a valid font
812   
813   UniChar strbuf[4096];
814   int strbuf_len;
816   {
817     strbuf[0]=0;
818     CFRange r = {0,CFStringGetLength(strin)};
819     if (r.length > 4095) r.length=4095;
820     strbuf_len=CFStringGetBytes(strin,r,kCFStringEncodingUTF16,' ',false,(UInt8*)strbuf,sizeof(strbuf)-2,NULL);
821     if (strbuf_len<0)strbuf_len=0;
822     else if (strbuf_len>4095) strbuf_len=4095;
823     strbuf[strbuf_len]=0;
824   }
825   
826   {
827     ATSUAttributeTag        theTags[] = { kATSUColorTag,   };
828     ByteCount               theSizes[] = { sizeof(RGBColor),  };
830     RGBColor tcolor;
831     ATSUAttributeValuePtr   theValues[] =  {&tcolor,  } ;
832     
833     tcolor.red = GetRValue(ct->cur_text_color_int)*256;
834     tcolor.green = GetGValue(ct->cur_text_color_int)*256;
835     tcolor.blue = GetBValue(ct->cur_text_color_int)*256;
836     
837     // error check this? we can live with the wrong color maybe?
838     ATSUSetAttributes(font->atsui_font_style,  sizeof(theTags)/sizeof(theTags[0]), theTags, theSizes, theValues);
839   }
840   
841   UniCharCount runLengths[1]={kATSUToTextEnd};
842   ATSUTextLayout layout; 
843   if (ATSUCreateTextLayoutWithTextPtr(strbuf, kATSUFromTextBeginning, kATSUToTextEnd, strbuf_len, 1, runLengths, &font->atsui_font_style, &layout)!=noErr)
844   {
845     *err=true;
846     return 0;
847   }
848   
849   {
850     Fixed frot = X2Fix(font->font_rotation);
851     
852     ATSULineTruncation tv = (align & DT_END_ELLIPSIS) ? kATSUTruncateEnd : kATSUTruncateNone;
853     ATSUAttributeTag        theTags[] = { kATSUCGContextTag, kATSULineTruncationTag, kATSULineRotationTag };
854     ByteCount               theSizes[] = { sizeof (CGContextRef), sizeof(ATSULineTruncation), sizeof(Fixed)};
855     ATSUAttributeValuePtr   theValues[] =  { &ct->ctx, &tv, &frot } ;
856     
857     
858     if (ATSUSetLayoutControls (layout,
859                            
860                            sizeof(theTags)/sizeof(theTags[0]),
861                            
862                            theTags,
863                            
864                            theSizes,
865                            
866                            theValues)!=noErr)
867     {
868       *err=true;
869       ATSUDisposeTextLayout(layout);   
870       return 0;
871     }
872   }
873   
874   
875   ATSUTextMeasurement leftFixed, rightFixed, ascentFixed, descentFixed;
877   if (ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, kATSUToTextEnd, &leftFixed, &rightFixed, &ascentFixed, &descentFixed)!=noErr) 
878   {
879     *err=true;
880     ATSUDisposeTextLayout(layout);   
881     return 0;
882   }
884   int w=Fix2Long(rightFixed);
885   int descent=Fix2Long(descentFixed);
886   int h=descent + Fix2Long(ascentFixed);
887   if (align&DT_CALCRECT)
888   {
889     ATSUDisposeTextLayout(layout);   
890     r->right=r->left+w;
891     r->bottom=r->top+h;
892     return h;  
893   }
894   CGContextSaveGState(ct->ctx);    
896   if (!(align & DT_NOCLIP))
897     CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
899   int l=r->left, t=r->top;
900     
901   if (fabs(font->font_rotation)<45.0)
902   {
903     if (align & DT_RIGHT) l = r->right-w;
904     else if (align & DT_CENTER) l = (r->right+r->left)/2 - w/2;      
905   }
906   else l+=Fix2Long(ascentFixed); // 90 degree special case (we should generalize this to be correct throughout the rotation range, but oh well)
907     
908   if (align & DT_BOTTOM) t = r->bottom-h;
909   else if (align & DT_VCENTER) t = (r->bottom+r->top)/2 - h/2;
910     
911   CGContextTranslateCTM(ct->ctx,0,t);
912   CGContextScaleCTM(ct->ctx,1,-1);
913   CGContextTranslateCTM(ct->ctx,0,-t-h);
914     
915   if (ct->curbkmode == OPAQUE)
916   {      
917     CGRect bgr = CGRectMake(l, t, w, h);
918     CGColorRef bgc = CreateColor(ct->curbkcol);
919     CGContextSetFillColorWithColor(ct->ctx, bgc);
920     CGContextFillRect(ct->ctx, bgr);
921     CGColorRelease(bgc);        
922   }
924   if (ATSUDrawText(layout,kATSUFromTextBeginning,kATSUToTextEnd,Long2Fix(l),Long2Fix(t+descent))!=noErr)
925     *err=true;
926   
927   CGContextRestoreGState(ct->ctx);    
928   
929   ATSUDisposeTextLayout(layout);   
930   
931   return h;
934 #endif
936 int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align)
938   HDC__ *ct=(HDC__ *)ctx;
939   if (!HDC_VALID(ct)) return 0;
940   
941   bool has_ml=false;
942   char tmp[4096];
943   const char *p=buf;
944   char *op=tmp;
945   while (*p && (op-tmp)<sizeof(tmp)-1 && (buflen<0 || (p-buf)<buflen))
946   {
947     if (*p == '&' && !(align&DT_NOPREFIX)) p++; 
949     if (*p == '\r')  p++; 
950     else if (*p == '\n' && (align&DT_SINGLELINE)) { *op++ = ' '; p++; }
951     else 
952     {
953       if (*p == '\n') has_ml=true;
954       *op++=*p++;
955     }
956   }
957   *op=0;
958   
959   if (!tmp[0]) return 0; // dont draw empty strings
960   
961   NSString *str=CStringToNSString(tmp);
962   
963   if (!str) return 0;
964   
965   bool curfont_valid = HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
966 #ifdef SWELL_ATSUI_TEXT_SUPPORT
967   if (curfont_valid && ct->curfont->atsui_font_style)
968   {
969     bool err=false;
970     int ret =  DrawTextATSUI(ctx,(CFStringRef)str,r,align,&err);
971     [str release];
972     
973     if (!err) return ret;
974     return 0;
975   }
976 #endif  
977   
978 #ifndef SWELL_NO_CORETEXT
979   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
980   if (!fr)  fr=GetCoreTextDefaultFont();
981   if (fr)
982   {
983     // Initialize string, font, and context
984     CFStringRef keys[] = { kCTFontAttributeName,kCTForegroundColorAttributeName };
985     CFTypeRef values[] = { fr,ct->curtextcol };
986     
987     int nk= sizeof(keys) / sizeof(keys[0]);
988     if (!values[1]) nk--;
989     
990     CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, nk,
991                                 &kCFTypeDictionaryKeyCallBacks,
992                                 &kCFTypeDictionaryValueCallBacks);
993        
994     CFAttributedStringRef attrString =
995           CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)str, attributes);
996     CFRelease(attributes);
997     [str release];
1000     CTFrameRef frame = NULL;
1001     CFArrayRef lines = NULL;
1002     CTLineRef line = NULL;
1003     CGFloat asc=0;
1004     int line_w=0,line_h=0;
1005     if (has_ml)
1006     {
1007       CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
1008       if (framesetter)
1009       {
1010         CGMutablePathRef path=CGPathCreateMutable();
1011         CGPathAddRect(path,NULL,CGRectMake(0,0,100000,100000));
1012         frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0),path,NULL);
1013         CFRelease(framesetter);
1014         CFRelease(path);
1015       }
1016       if (frame)
1017       {
1018         lines = CTFrameGetLines(frame);
1019         const int n = (int)CFArrayGetCount(lines);
1020         for (int x=0;x<n;x++)
1021         {
1022           CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
1023           if (l)
1024           {
1025             CGFloat desc=0,lead=0;
1026             int w = (int) floor(CTLineGetTypographicBounds(l,&asc,&desc,&lead)+0.5);
1027             int h =(int) floor(asc+desc+lead+1.5);
1028             line_h+=h;
1029             if (line_w < w) line_w=w;
1030           }
1031         }
1032       }
1033     }
1034     else
1035     {
1036       line = CTLineCreateWithAttributedString(attrString);
1037        
1038       if (line)
1039       {
1040         CGFloat desc=0,lead=0;
1041         line_w = (int) floor(CTLineGetTypographicBounds(line,&asc,&desc,&lead)+0.5);
1042         line_h =(int) floor(asc+desc+lead+1.5);
1043       }
1044     }
1045     if (line_h) line_h++;
1047     CFRelease(attrString);
1048     
1049     if (align & DT_CALCRECT)
1050     {
1051       r->right = r->left+line_w;
1052       r->bottom = r->top+line_h;
1053       if (line) CFRelease(line);
1054       if (frame) CFRelease(frame);
1055       return line_h;
1056     }
1058     float xo=r->left,yo=r->top;
1059     if (align & DT_RIGHT) xo += (r->right-r->left) - line_w;
1060     else if (align & DT_CENTER) xo += (r->right-r->left)/2 - line_w/2;
1062     if (align & DT_BOTTOM) yo += (r->bottom-r->top) - line_h;
1063     else if (align & DT_VCENTER) yo += (r->bottom-r->top)/2 - line_h/2;
1065     
1066     CGContextSaveGState(ct->ctx);
1068     CGAffineTransform f={1,0,0,-1,0,0};
1069     CGContextSetTextMatrix(ct->ctx, f);
1071     if (!(align & DT_NOCLIP))
1072     {
1073       CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));          
1074     }
1075     
1076     CGColorRef bgc = NULL;
1077     if (ct->curbkmode == OPAQUE)
1078     {      
1079       bgc = CreateColor(ct->curbkcol);
1080     }
1082     if (ct->curfont->font_quality)
1083     {
1084       CGContextSetShouldAntialias(ct->ctx, ct->curfont->font_quality == ANTIALIASED_QUALITY);
1085     }
1087     if (line) 
1088     {
1089       if (bgc)
1090       {
1091         CGContextSetFillColorWithColor(ct->ctx, bgc);
1092         CGContextFillRect(ct->ctx, CGRectMake(xo,yo,line_w,line_h));
1093       }
1094       CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1095       CTLineDraw(line,ct->ctx);
1097     }
1098     if (lines)
1099     {
1100       const int n = (int)CFArrayGetCount(lines);
1101       for (int x=0;x<n;x++)
1102       {
1103         CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
1104         if (l)
1105         {
1106           CGFloat desc=0.0,lead=0.0;
1107           asc=0.0;
1108           float lw=CTLineGetTypographicBounds(l,&asc,&desc,&lead);
1110           if (bgc)
1111           {
1112             CGContextSetFillColorWithColor(ct->ctx, bgc);
1113             CGContextFillRect(ct->ctx, CGRectMake(xo,yo,lw,asc+desc+lead));
1114           }
1115           CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1116           CTLineDraw(l,ct->ctx);
1117           
1118           yo += floor(asc+desc+lead+0.5);          
1119         }
1120       }
1121     }
1122     
1123     CGContextRestoreGState(ct->ctx);
1124     if (bgc) CGColorRelease(bgc);
1125     if (line) CFRelease(line);
1126     if (frame) CFRelease(frame);
1127     
1128     return line_h;
1129   }
1130 #endif
1131   
1132   
1133   [str release];
1134   return 0;  
1138 int GetGlyphIndicesW(HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags)
1140   HDC__ *ct=(HDC__*)ctx;
1141   if (HDC_VALID(ct) && HGDIOBJ_VALID(ct->curfont, TYPE_FONT))
1142   {
1143 #ifndef SWELL_NO_CORETEXT
1144     CTFontRef f=(CTFontRef)ct->curfont->ct_FontRef;
1145     if (f && CTFontGetGlyphsForCharacters(f, (const UniChar*)buf, (CGGlyph*)indices, (CFIndex)len)) return len;
1146 #endif
1147   }
1148   
1149   int i;
1150   for (i=0; i < len; ++i) indices[i]=(flags == GGI_MARK_NONEXISTING_GLYPHS ? 0xFFFF : 0);
1151   return 0;
1157 void SetBkColor(HDC ctx, int col)
1159   HDC__ *ct=(HDC__ *)ctx;
1160   if (!HDC_VALID(ct)) return;
1161   ct->curbkcol=col;
1164 void SetBkMode(HDC ctx, int col)
1166   HDC__ *ct=(HDC__ *)ctx;
1167   if (!HDC_VALID(ct)) return;
1168   ct->curbkmode=col;
1171 int GetTextColor(HDC ctx)
1173   HDC__ *ct=(HDC__ *)ctx;
1174   if (!HDC_VALID(ct)) return -1;
1175   return ct->cur_text_color_int;
1178 void SetTextColor(HDC ctx, int col)
1180   HDC__ *ct=(HDC__ *)ctx;
1181   if (!HDC_VALID(ct)) return;
1182   ct->cur_text_color_int = col;
1183   
1184   if (ct->curtextcol) CFRelease(ct->curtextcol);
1186   ct->curtextcol = CreateColor(col);
1190 HICON CreateIconIndirect(ICONINFO* iconinfo)
1192   if (!iconinfo || !iconinfo->fIcon) return 0;  
1193   HGDIOBJ__* i=iconinfo->hbmColor;
1194   if (!HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return 0;
1195   NSImage* img=i->bitmapptr;
1196   if (!img) return 0;
1197     
1198   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1199   icon->type=TYPE_BITMAP;
1200   icon->wid=1;
1201   [img retain];
1202   icon->bitmapptr=img;
1203   return icon;   
1206 HICON LoadNamedImage(const char *name, bool alphaFromMask)
1208   NSImage *img=0;
1209   NSString *str=CStringToNSString(name); 
1210   if (strstr(name,"/"))
1211   {
1212     img=[[NSImage alloc] initWithContentsOfFile:str];
1213   }
1214   if (!img) 
1215   {
1216     img=[NSImage imageNamed:str];
1217     if (img) [img retain];
1218   }
1219   [str release];
1220   if (!img) 
1221   {
1222     return 0;
1223   }
1224     
1225   [img setFlipped:YES];
1226   if (alphaFromMask)
1227   {
1228     const NSSize sz=[img size];
1229     const int w = (int)sz.width, h=(int)sz.height;
1230     HDC hdc;
1231     if (w>0 && h>0 && NULL != (hdc=SWELL_CreateMemContext(NULL,w,h)))
1232     {
1233       [NSGraphicsContext saveGraphicsState];
1234       NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:((struct HDC__*)hdc)->ctx flipped:NO];
1235       [NSGraphicsContext setCurrentContext:gc];
1236       [img drawInRect:NSMakeRect(0,0,w,h) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
1237       [NSGraphicsContext restoreGraphicsState];
1239       // on yosemite, calling [img TIFFRepresentation] seems to change img somehow for some images, ouch.
1240       // in this case, we should always replace img with newImage (set rcnt=1), but in general
1241       // maybe we shoulnt use alphaFromMask anyhow
1242       NSImage *newImage=[[NSImage alloc] initWithData:[img TIFFRepresentation]];
1243       [newImage setFlipped:YES];
1245       const int *fb = (const int *)SWELL_GetCtxFrameBuffer(hdc);
1246       int y,rcnt=0;
1247       [newImage lockFocus];
1248       CGContextRef myContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
1249       for (y=0; y < h; y ++)
1250       {
1251         int x;
1252         for (x = 0; x < w; x++)
1253         {
1254           if ((*fb++ & 0xffffff) == 0xff00ff)
1255           {
1256             CGContextClearRect(myContext,CGRectMake(x,y,1,1));
1257             rcnt++;
1258           }
1259         }
1260       }
1261       [newImage unlockFocus];
1263       SWELL_DeleteGfxContext(hdc);
1265       if (rcnt)
1266       {
1267         [img release];
1268         img=newImage;    
1269       }
1270       else
1271         [newImage release];
1272     }
1273   }
1274   
1275   HGDIOBJ__ *i=GDP_OBJECT_NEW();
1276   i->type=TYPE_BITMAP;
1277   i->wid=1;
1278   i->bitmapptr = img;
1279   return i;
1282 void DrawImageInRect(HDC ctx, HICON img, const RECT *r)
1284   HGDIOBJ__ *i = (HGDIOBJ__ *)img;
1285   HDC__ *ct=(HDC__*)ctx;
1286   if (!HDC_VALID(ct) || !HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return;
1287   //CGContextDrawImage(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top),(CGImage*)i->bitmapptr);
1288   // probably a better way since this ignores the ctx
1289   [NSGraphicsContext saveGraphicsState];
1290   NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:ct->ctx flipped:NO];
1291   [NSGraphicsContext setCurrentContext:gc];
1292   NSImage *nsi=i->bitmapptr;
1293   NSRect rr=NSMakeRect(r->left,r->top,r->right-r->left,r->bottom-r->top);
1294   [nsi setFlipped:YES];
1295   [nsi drawInRect:rr fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1296   [nsi setFlipped:NO]; // todo: restore old flippedness?
1297   [NSGraphicsContext restoreGraphicsState];
1298 //  [gc release];
1302 BOOL GetObject(HICON icon, int bmsz, void *_bm)
1304   memset(_bm,0,bmsz);
1305   if (bmsz != sizeof(BITMAP)) return false;
1306   BITMAP *bm=(BITMAP *)_bm;
1307   HGDIOBJ__ *i = (HGDIOBJ__ *)icon;
1308   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return false;
1309   NSImage *img = i->bitmapptr;
1310   if (!img) return false;
1311   bm->bmWidth = (int) ([img size].width+0.5);
1312   bm->bmHeight = (int) ([img size].height+0.5);
1313   return true;
1317 void *GetNSImageFromHICON(HICON ico)
1319   HGDIOBJ__ *i = (HGDIOBJ__ *)ico;
1320   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return 0;
1321   return i->bitmapptr;
1324 #if 0
1325 static int ColorFromNSColor(NSColor *color, int valifnul)
1327   if (!color) return valifnul;
1328   float r,g,b;
1329   NSColor *color2=[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1330   if (!color2) 
1331   {
1332     NSLog(@"error converting colorspace from: %@\n",[color colorSpaceName]);
1333     return valifnul;
1334   }
1335   
1336   [color2 getRed:&r green:&g blue:&b alpha:NULL];
1337   return RGB((int)(r*255.0),(int)(g*255.0),(int)(b*255.0));
1339 #else
1340 #define ColorFromNSColor(a,b) (b)
1341 #endif
1342 int GetSysColor(int idx)
1344  // NSColors that seem to be valid: textBackgroundColor, selectedTextBackgroundColor, textColor, selectedTextColor
1345   
1346   switch (idx)
1347   {
1348     case COLOR_WINDOW: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1349     case COLOR_3DFACE: 
1350     case COLOR_BTNFACE: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1351     case COLOR_SCROLLBAR: return ColorFromNSColor([NSColor controlColor],RGB(32,32,32));
1352     case COLOR_3DSHADOW: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96));
1353     case COLOR_3DHILIGHT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(224,224,224));
1354     case COLOR_BTNTEXT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(0,0,0));
1355     case COLOR_3DDKSHADOW: return (ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96))>>1)&0x7f7f7f;
1356     case COLOR_INFOBK: return RGB(255,240,200);
1357     case COLOR_INFOTEXT: return RGB(0,0,0);
1358       
1359   }
1360   return 0;
1364 void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode)
1366   StretchBlt(hdcOut,x,y,w,h,hdcIn,xin,yin,w,h,mode);
1369 void StretchBlt(HDC hdcOut, int x, int y, int destw, int desth, HDC hdcIn, int xin, int yin, int w, int h, int mode)
1371   if (!hdcOut || !hdcIn||w<1||h<1) return;
1372   HDC__ *src=(HDC__*)hdcIn;
1373   HDC__ *dest=(HDC__*)hdcOut;
1374   if (!HDC_VALID(src) || !HDC_VALID(dest) || !src->ownedData || !src->ctx || !dest->ctx) return;
1376   if (w<1||h<1) return;
1377   
1378   const int sw = (int)CGBitmapContextGetWidth(src->ctx);
1379   const int sh = (int)CGBitmapContextGetHeight(src->ctx);
1380   
1381   const int preclip_w=w;
1382   const int preclip_h=h;
1383   
1384   if (xin<0) 
1385   { 
1386     x-=(xin*destw)/w;
1387     w+=xin; 
1388     xin=0; 
1389   }
1390   if (yin<0) 
1391   { 
1392     y-=(yin*desth)/h;
1393     h+=yin; 
1394     yin=0; 
1395   }
1396   if (xin+w > sw) w=sw-xin;
1397   if (yin+h > sh) h=sh-yin;
1398   
1399   if (w<1||h<1) return;
1401   if (destw==preclip_w) destw=w; // no scaling, keep width the same
1402   else if (w != preclip_w) destw = (w*destw)/preclip_w;
1403   
1404   if (desth == preclip_h) desth=h;
1405   else if (h != preclip_h) desth = (h*desth)/preclip_h;
1406   
1407   const bool use_alphachannel = mode == SRCCOPY_USEALPHACHAN;
1408   
1409   CGContextRef output = (CGContextRef)dest->ctx;
1410   CGRect outputr = CGRectMake(x,-desth-y,destw,desth);
1411   
1412   unsigned char *p = (unsigned char *)ALIGN_FBUF(src->ownedData);
1413   p += (xin + sw*yin)*4;
1415   
1416 #ifdef SWELL_SUPPORT_OPENGL_BLIT
1417   if (dest->GLgfxctx)
1418   {
1419     NSOpenGLContext *glCtx = (NSOpenGLContext*) dest->GLgfxctx;
1420     NSOpenGLContext *cCtx = [NSOpenGLContext currentContext];
1421     if (glCtx != cCtx)
1422     {
1423       [glCtx makeCurrentContext];
1424     }
1425     
1426     glDisable(GL_TEXTURE_2D);
1427     glEnable(GL_TEXTURE_RECTANGLE_EXT);
1428     
1429     GLuint texid=0;
1430     glGenTextures(1, &texid);
1431     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texid);
1432     glPixelStorei(GL_UNPACK_ROW_LENGTH, sw);
1433     glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER,  GL_LINEAR);
1434     glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_RGBA8,w,h,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV, p);
1435     
1436     glViewport(x,[[glCtx view] bounds].size.height-desth-y,destw,desth);
1437     glBegin(GL_QUADS);
1438     
1439     glTexCoord2f(0.0f, 0.0f);
1440     glVertex2f(-1,1);
1441     
1442     glTexCoord2f(0.0f, h);
1443     glVertex2f(-1,-1);
1444     
1445     glTexCoord2f(w,h);
1446     glVertex2f(1,-1);
1447     
1448     glTexCoord2f(w, 0.0f);
1449     glVertex2f(1,1);
1450     glEnd();
1451     
1452     glDeleteTextures(1,&texid);
1453     glFlush();
1455     if (glCtx != cCtx) [cCtx makeCurrentContext];
1456     return;
1457   }
1458 #endif
1459   
1460   
1461   
1462   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,p,4*sw*h,NULL);
1463   CGImageRef img = CGImageCreate(w,h,8,32,4*sw,__GetDisplayColorSpace(),
1464       (use_alphachannel?kCGImageAlphaFirst:kCGImageAlphaNoneSkipFirst)|kCGBitmapByteOrder32Host,
1465       provider,NULL,NO,kCGRenderingIntentDefault);
1466   CGDataProviderRelease(provider);
1467   
1468   if (img)
1469   {
1470     CGContextSaveGState(output);
1471     CGContextScaleCTM(output,1.0,-1.0);
1472   
1473     CGContextSetInterpolationQuality(output,kCGInterpolationNone);
1474     CGContextDrawImage(output,outputr,img);
1475     CGContextRestoreGState(output);
1476   
1477     CGImageRelease(img);
1478   }
1481 void SWELL_PushClipRegion(HDC ctx)
1483   HDC__ *ct=(HDC__ *)ctx;
1484   if (HDC_VALID(ct) && ct->ctx) CGContextSaveGState(ct->ctx);
1487 void SWELL_SetClipRegion(HDC ctx, const RECT *r)
1489   HDC__ *ct=(HDC__ *)ctx;
1490   if (HDC_VALID(ct) && ct->ctx) CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
1494 void SWELL_PopClipRegion(HDC ctx)
1496   HDC__ *ct=(HDC__ *)ctx;
1497   if (HDC_VALID(ct) && ct->ctx) CGContextRestoreGState(ct->ctx);
1500 void *SWELL_GetCtxFrameBuffer(HDC ctx)
1502   HDC__ *ct=(HDC__ *)ctx;
1503   if (HDC_VALID(ct)) return ALIGN_FBUF(ct->ownedData);
1504   return 0;
1508 HDC GetDC(HWND h)
1510   if (h && [(id)h isKindOfClass:[NSWindow class]])
1511   {
1512     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1513     {
1514       PAINTSTRUCT ps={0,}; 
1515       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1516       if (ps.hdc) 
1517       {
1518         if ((ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1519         return ps.hdc;
1520       }
1521     }
1522     h=(HWND)[(id)h contentView];
1523   }
1524   
1525   if (h && [(id)h isKindOfClass:[NSView class]])
1526   {
1527     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1528     {
1529       PAINTSTRUCT ps={0,}; 
1530       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1531       if (HDC_VALID((HDC__*)ps.hdc)) 
1532       {
1533         if (((HDC__*)ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1534         return ps.hdc;
1535       }
1536     }
1537     
1538     if ([(NSView*)h lockFocusIfCanDraw])
1539     {
1540       HDC ret= SWELL_CreateGfxContext([NSGraphicsContext currentContext]);
1541       if (ret)
1542       {
1543         if (ret->ctx) CGContextSaveGState(ret->ctx);
1544         if (!ret->GLgfxctx && [(id)h respondsToSelector:@selector(swellGetGLContext)])
1545         {
1546           NSOpenGLContext *glctx = (NSOpenGLContext*)[(id)h swellGetGLContext];
1547           ret->GLgfxctx = glctx;
1548           if (glctx) [glctx setView:(NSView *)h];
1549         }
1550       }
1551       return ret;
1552     }
1553   }
1554   return 0;
1557 HDC GetWindowDC(HWND h)
1559   HDC ret=GetDC(h);
1560   if (ret)
1561   {
1562     NSView *v=NULL;
1563     if ([(id)h isKindOfClass:[NSWindow class]]) v=[(id)h contentView];
1564     else if ([(id)h isKindOfClass:[NSView class]]) v=(NSView *)h;
1565     
1566     if (v)
1567     {
1568       NSRect b=[v bounds];
1569       float xsc=b.origin.x;
1570       float ysc=b.origin.y;
1571       if ((xsc || ysc) && (ret)->ctx) CGContextTranslateCTM((ret)->ctx,xsc,ysc);
1572     }
1573   }
1574   return ret;
1577 void ReleaseDC(HWND h, HDC hdc)
1579   if (hdc)
1580   {
1581     if ((hdc)->ctx) CGContextRestoreGState((hdc)->ctx);
1582   }
1583   if (h && [(id)h isKindOfClass:[NSWindow class]])
1584   {
1585     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1586     {
1587       PAINTSTRUCT ps={0,}; 
1588       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1589       if (ps.hdc && ps.hdc==hdc) return;
1590     }
1591     h=(HWND)[(id)h contentView];
1592   }
1593   bool isView=h && [(id)h isKindOfClass:[NSView class]];
1594   if (isView)
1595   {
1596     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1597     {
1598       PAINTSTRUCT ps={0,}; 
1599       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1600       if (ps.hdc && ps.hdc==hdc) return;
1601     }
1602   }    
1603   if (hdc && hdc->GLgfxctx)
1604   {
1605     if ([NSOpenGLContext currentContext] == hdc->GLgfxctx) [NSOpenGLContext clearCurrentContext]; 
1606     hdc->GLgfxctx = NULL;
1607   }
1608     
1609   if (hdc) SWELL_DeleteGfxContext(hdc);
1610   if (isView && hdc)
1611   {
1612     [(NSView *)h unlockFocus];
1613 //    if ([(NSView *)h window]) [[(NSView *)h window] flushWindow];
1614   }
1617 void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level)
1619   CGContextRef ctx=(CGContextRef)SWELL_GetCtxGC(hdc);
1620   if (ctx)
1621   {
1622   // level 0 for now = this
1623     HIThemeSetFill(kThemeBrushDialogBackgroundActive,NULL,ctx,kHIThemeOrientationNormal);
1624     CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
1625     CGContextFillRect(ctx,rect);                 
1626   }
1629 HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a)
1631   if (HGDIOBJ_VALID(a))
1632   {
1633     a->additional_refcnt++;
1634     return a;
1635   }
1636   return NULL;
1640 HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits)
1642   int spp = bitsperpixel/8;
1643   Boolean hasa = (bitsperpixel == 32);
1644   Boolean hasp = (numplanes > 1); // won't actually work yet for planar data
1645   NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:0 pixelsWide:width pixelsHigh:height 
1646                                                                bitsPerSample:8 samplesPerPixel:spp
1647                                                                hasAlpha:hasa isPlanar:hasp
1648                                                                colorSpaceName:NSDeviceRGBColorSpace
1649                                                                 bitmapFormat:NSAlphaFirstBitmapFormat 
1650                                                                  bytesPerRow:0 bitsPerPixel:0];    
1651   if (!rep) return 0;
1652   unsigned char* p = [rep bitmapData];
1653   const int pspan = (int)[rep bytesPerRow]; // might not be the same as width
1654   
1655   for (int y=0;y<height;y ++)
1656   {
1657 #ifdef __ppc__
1658     memcpy(p,bits,width*4);
1659 #else
1660     unsigned char *wr = p;
1661     const unsigned char *rd = bits;
1662     int x = width;
1663     // convert BGRA to ARGB
1664     while (x--)
1665     {
1666       wr[0] = rd[3];
1667       wr[1] = rd[2];
1668       wr[2] = rd[1];
1669       wr[3] = rd[0];
1670       wr+=4;
1671       rd+=4;
1672     }
1673 #endif
1674     p+=pspan;
1675     bits += width*4;
1676   }
1678   NSImage* img = [[NSImage alloc] init];
1679   [img addRepresentation:rep]; 
1680   [rep release];
1681   
1682   HGDIOBJ__* obj = GDP_OBJECT_NEW();
1683   obj->type = TYPE_BITMAP;
1684   obj->wid = 1; // need free
1685   obj->bitmapptr = img;
1686   return obj;
1690 HIMAGELIST ImageList_CreateEx()
1692   return (HIMAGELIST)new WDL_PtrList<HGDIOBJ__>;
1695 BOOL ImageList_Remove(HIMAGELIST list, int idx)
1697   WDL_PtrList<HGDIOBJ__>* imglist=(WDL_PtrList<HGDIOBJ__>*)list;
1698   if (imglist && idx < imglist->GetSize())
1699   {
1700     if (idx < 0) 
1701     {
1702       int x,n=imglist->GetSize();
1703       for (x=0;x<n;x++)
1704       {
1705         HGDIOBJ__ *a = imglist->Get(x);
1706         if (a) DeleteObject(a);
1707       }
1708       imglist->Empty();
1709     }
1710     else 
1711     {
1712       HGDIOBJ__ *a = imglist->Get(idx);
1713       imglist->Set(idx, NULL); 
1714       if (a) DeleteObject(a);
1715     }
1716     return TRUE;
1717   }
1718   
1719   return FALSE;
1722 void ImageList_Destroy(HIMAGELIST list)
1724   if (!list) return;
1725   ImageList_Remove(list, -1);
1726   delete (WDL_PtrList<HGDIOBJ__>*)list;
1729 int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image)
1731   if (!image || !list) return -1;
1732   WDL_PtrList<HGDIOBJ__> *l=(WDL_PtrList<HGDIOBJ__> *)list;
1734   HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image;
1735   if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1;
1737   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1738   icon->type=TYPE_BITMAP;
1739   icon->wid=1;
1740   icon->bitmapptr = imgsrc->bitmapptr; // no need to duplicate it, can just retain a copy
1741   [icon->bitmapptr retain];
1742   image = (HICON) icon;
1744   if (offset<0||offset>=l->GetSize()) 
1745   {
1746     l->Add(image); 
1747     offset=l->GetSize()-1;
1748   }
1749   else
1750   {
1751     HICON old=l->Get(offset); 
1752     l->Set(offset,image);
1753     if (old) DeleteObject(old);
1754   }
1755   return offset;
1758 int ImageList_Add(HIMAGELIST list, HBITMAP image, HBITMAP mask)
1760   if (!image || !list) return -1;
1761   WDL_PtrList<HGDIOBJ__> *l=(WDL_PtrList<HGDIOBJ__> *)list;
1762   
1763   HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image;
1764   if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1;
1765   
1766   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1767   icon->type=TYPE_BITMAP;
1768   icon->wid=1;
1769   NSImage *nsimg = [imgsrc->bitmapptr copy]; // caller still owns the image
1770   [nsimg setFlipped:YES];
1771   icon->bitmapptr = nsimg;
1772   image = (HICON) icon;
1773   
1774   l->Add(image);
1775   return l->GetSize();
1778 int AddFontResourceEx(LPCTSTR str, DWORD fl, void *pdv)
1780   if (SWELL_GDI_GetOSXVersion() < 0x1060)  return 0;
1781   static bool l;
1782   static bool (*_CTFontManagerRegisterFontsForURL)( CFURLRef fontURL, uint32_t scope, CFErrorRef *error );
1783   if (!l)
1784   {
1785     CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreText"));
1786     if (b)
1787     {
1788       *(void **)&_CTFontManagerRegisterFontsForURL = CFBundleGetFunctionPointerForName(b,CFSTR("CTFontManagerRegisterFontsForURL"));
1789     }
1791     l=true;
1792   }
1793   
1794   if (!_CTFontManagerRegisterFontsForURL) return 0;
1795   
1796   CFStringRef s=(CFStringRef)CStringToNSString(str); 
1798   CFURLRef r=CFURLCreateWithFileSystemPath(NULL,s,kCFURLPOSIXPathStyle,true);
1799   CFErrorRef err=NULL;
1800   const int v = _CTFontManagerRegisterFontsForURL(r,
1801       (fl & FR_PRIVATE) ? 1/*kCTFontManagerScopeProcess*/ : 2/*kCTFontManagerScopeUser*/,
1802       &err)?1:0;
1804   // release err? don't think so
1806   CFRelease(s);
1807   CFRelease(r);
1808   return v;
1812 #endif