merging cockos wdl
[wdl/wdl-ol.git] / WDL / swell / swell-gdi.mm
blob0bb5ad007bf14fe4118dbae21c0f3216ab7f6815
2 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Losers (who use OS X))
3    Copyright (C) 2006-2007, Cockos, Inc.
5     This software is provided 'as-is', without any express or implied
6     warranty.  In no event will the authors be held liable for any damages
7     arising from the use of this software.
9     Permission is granted to anyone to use this software for any purpose,
10     including commercial applications, and to alter it and redistribute it
11     freely, subject to the following restrictions:
13     1. The origin of this software must not be misrepresented; you must not
14        claim that you wrote the original software. If you use this software
15        in a product, an acknowledgment in the product documentation would be
16        appreciated but is not required.
17     2. Altered source versions must be plainly marked as such, and must not be
18        misrepresented as being the original software.
19     3. This notice may not be removed or altered from any source distribution.
20   
22     This file provides basic win32 GDI-->Quartz translation. It uses features that require OS X 10.4+
26 #ifndef SWELL_PROVIDED_BY_APP
28 #import <Carbon/Carbon.h>
29 #import <Cocoa/Cocoa.h>
30 #import <CoreFoundation/CFDictionary.h>
31 #import <objc/objc-runtime.h>
32 #include "swell.h"
33 #include "swell-internal.h"
35 #include "../mutex.h"
36 #include "../assocarray.h"
37 #include "../wdlcstring.h"
39 #ifdef __SSE__
40 #include <xmmintrin.h>
41 #endif
43 #ifdef SWELL_SUPPORT_OPENGL_BLIT
44 #include <OpenGL/gl.h>
45 #endif
47 int SWELL_GetOSXVersion()
49   static SInt32 v;
50   if (!v)
51   {
52     v=0x1040;
53     Gestalt(gestaltSystemVersion,&v);
54   }
55   return v;
58 #ifdef __AVX__
59 #include <immintrin.h>
60 #endif
62 #ifndef SWELL_NO_CORETEXT
63 static bool IsCoreTextSupported()
65 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
66   return SWELL_GetOSXVersion() >= 0x1050 && CTFontCreateWithName && CTLineDraw && CTFramesetterCreateWithAttributedString && CTFramesetterCreateFrame && 
67          CTFrameGetLines && CTLineGetTypographicBounds && CTLineCreateWithAttributedString && CTFontCopyPostScriptName
68          ;
69 #else
70   // targetting 10.5+, CT is always valid
71   return true;
72 #endif
75 static CTFontRef GetCoreTextDefaultFont()
77   static CTFontRef deffr;
78   static bool ok;
79   if (!ok)
80   {
81     ok=true;
82     if (IsCoreTextSupported())
83     {
84       deffr=(CTFontRef) [[NSFont labelFontOfSize:10.0] retain]; 
85     }
86   }
87   return deffr;
89 #endif // !SWELL_NO_CORETEXT
90   
92 static NSString *CStringToNSString(const char *str)
94   if (!str) str="";
95   NSString *ret;
96   
97   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8);
98   if (ret) return ret;
99   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII);
100   return ret;
103 CGColorSpaceRef __GetDisplayColorSpace()
105   static CGColorSpaceRef cs;
106   if (!cs)
107   {
108     // use monitor profile for 10.7+
109     if (SWELL_GetOSXVersion() >= 0x1070)
110     {
111       CMProfileRef systemMonitorProfile = NULL;
112       CMError getProfileErr = CMGetSystemProfile(&systemMonitorProfile);
113       if(noErr == getProfileErr)
114       {
115         cs = CGColorSpaceCreateWithPlatformColorSpace(systemMonitorProfile);
116         CMCloseProfile(systemMonitorProfile);
117       }
118     }
119   }
120   if (!cs) 
121     cs = CGColorSpaceCreateDeviceRGB();
122   return cs;
125 static CGColorRef CreateColor(int col, float alpha=1.0f)
127   CGFloat cols[4]={GetRValue(col)/255.0f,GetGValue(col)/255.0f,GetBValue(col)/255.0f,alpha};
128   CGColorRef color=CGColorCreate(__GetDisplayColorSpace(),cols);
129   return color;
133 #include "swell-gdi-internalpool.h"
135 int SWELL_IsRetinaHWND(HWND hwnd)
137   if (!hwnd || SWELL_GetOSXVersion() < 0x1070) return 0;
139   NSWindow *w=NULL;
140   if ([(id)hwnd isKindOfClass:[NSView class]]) w = [(NSView *)hwnd window];
141   else if ([(id)hwnd isKindOfClass:[NSWindow class]]) w = (NSWindow *)hwnd;
143   if (w)
144   {
145     NSRect r=NSMakeRect(0,0,1,1);
146     NSRect (*tmp)(id receiver, SEL operation, NSRect) = (NSRect (*)(id, SEL, NSRect))objc_msgSend_stret;
147     NSRect str = tmp(w,sel_getUid("convertRectToBacking:"),r);
149     if (str.size.width > 1.9) return 1;
150   }
151   return 0;
154 int SWELL_IsRetinaDC(HDC hdc)
156   HDC__ *src=(HDC__*)hdc;
157   if (!src || !HDC_VALID(src) || !src->ctx) return 0;
158   return CGContextConvertSizeToDeviceSpace((CGContextRef)src->ctx, CGSizeMake(1,1)).width > 1.9 ? 1 : 0;
162 HDC SWELL_CreateGfxContext(void *c)
164   HDC__ *ctx=SWELL_GDP_CTX_NEW();
165   NSGraphicsContext *nsc = (NSGraphicsContext *)c;
166 //  if (![nsc isFlipped])
167 //    nsc = [NSGraphicsContext graphicsContextWithGraphicsPort:[nsc graphicsPort] flipped:YES];
169   ctx->ctx=(CGContextRef)[nsc graphicsPort];
170 //  CGAffineTransform f={1,0,0,-1,0,0};
171   //CGContextSetTextMatrix(ctx->ctx,f);
172   //SetTextColor(ctx,0);
173   
174  // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
175   return ctx;
178 #define ALIGN_EXTRA 63
179 static void *ALIGN_FBUF(void *inbuf)
181   const UINT_PTR extra = ALIGN_EXTRA;
182   return (void *) (((UINT_PTR)inbuf+extra)&~extra); 
185 HDC SWELL_CreateMemContext(HDC hdc, int w, int h)
187   void *buf=calloc(w*4*h+ALIGN_EXTRA,1);
188   if (!buf) return 0;
189   CGContextRef c=CGBitmapContextCreate(ALIGN_FBUF(buf),w,h,8,w*4,__GetDisplayColorSpace(), kCGImageAlphaNoneSkipFirst);
190   if (!c)
191   {
192     free(buf);
193     return 0;
194   }
197   CGContextTranslateCTM(c,0.0,h);
198   CGContextScaleCTM(c,1.0,-1.0);
201   HDC__ *ctx=SWELL_GDP_CTX_NEW();
202   ctx->ctx=(CGContextRef)c;
203   ctx->ownedData=buf;
204   // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
205   
206   SetTextColor(ctx,0);
207   return ctx;
210 void SWELL_DeleteGfxContext(HDC ctx)
212   HDC__ *ct=(HDC__ *)ctx;
213   if (HDC_VALID(ct))
214   {   
215     if (ct->ownedData)
216     {
217       CGContextRelease(ct->ctx);
218       free(ct->ownedData);
219     }
220     if (ct->curtextcol) CFRelease(ct->curtextcol); 
221     SWELL_GDP_CTX_DELETE(ct);
222   }
224 HPEN CreatePen(int attr, int wid, int col)
226   return CreatePenAlpha(attr,wid,col,1.0f);
229 HBRUSH CreateSolidBrush(int col)
231   return CreateSolidBrushAlpha(col,1.0f);
236 HPEN CreatePenAlpha(int attr, int wid, int col, float alpha)
238   HGDIOBJ__ *pen=GDP_OBJECT_NEW();
239   pen->type=TYPE_PEN;
240   pen->wid=wid<0?0:wid;
241   pen->color=CreateColor(col,alpha);
242   return pen;
244 HBRUSH  CreateSolidBrushAlpha(int col, float alpha)
246   HGDIOBJ__ *brush=GDP_OBJECT_NEW();
247   brush->type=TYPE_BRUSH;
248   brush->color=CreateColor(col,alpha);
249   brush->wid=0; 
250   return brush;
254 HFONT CreateFontIndirect(LOGFONT *lf)
256   return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, 
257                     lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, 
258                     lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName);
261 static HGDIOBJ__ global_objs[2];
263 void DeleteObject(HGDIOBJ pen)
265   HGDIOBJ__ *p=(HGDIOBJ__ *)pen;
266   if (p >= global_objs && p < global_objs + sizeof(global_objs)/sizeof(global_objs[0])) return;
268   if (HGDIOBJ_VALID(p))
269   {
270     if (--p->additional_refcnt < 0)
271     {
272       if (p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP)
273       {
274         if (p->type == TYPE_PEN || p->type == TYPE_BRUSH)
275           if (p->wid<0) return;
276         if (p->color) CGColorRelease(p->color);
278         if (p->ct_FontRef) CFRelease(p->ct_FontRef);
280 #ifdef SWELL_ATSUI_TEXT_SUPPORT
281         if (p->atsui_font_style) ATSUDisposeStyle(p->atsui_font_style);
282 #endif
284         if (p->wid && p->bitmapptr) [p->bitmapptr release]; 
285         GDP_OBJECT_DELETE(p);
286       }
287       // JF> don't free unknown objects, this shouldn't ever happen anyway: else free(p);
288     }
289   }
293 HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen)
295   HDC__ *c=(HDC__ *)ctx;
296   HGDIOBJ__ *p=(HGDIOBJ__*) pen;
297   HGDIOBJ__ **mod=0;
298   if (!HDC_VALID(c)) return 0;
299   
300   if (p == (HGDIOBJ__*)TYPE_PEN) mod=&c->curpen;
301   else if (p == (HGDIOBJ__*)TYPE_BRUSH) mod=&c->curbrush;
302   else if (p == (HGDIOBJ__*)TYPE_FONT) mod=&c->curfont;
304   if (mod) // clearing a particular thing
305   {
306     HGDIOBJ__ *np=*mod;
307     *mod=0;
308     return HGDIOBJ_VALID(np,(int)(INT_PTR)p)?np:p;
309   }
311   if (!HGDIOBJ_VALID(p)) return 0;
312   
313   if (p->type == TYPE_PEN) mod=&c->curpen;
314   else if (p->type == TYPE_BRUSH) mod=&c->curbrush;
315   else if (p->type == TYPE_FONT) mod=&c->curfont;
316   
317   if (!mod) return 0;
318   
319   HGDIOBJ__ *op=*mod;
320   if (!HGDIOBJ_VALID(op,p->type)) op=(HGDIOBJ__*)(INT_PTR)p->type;
321   if (op != p) *mod=p;  
322   return op;
327 void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br)
329   HDC__ *c=(HDC__ *)ctx;
330   HGDIOBJ__ *b=(HGDIOBJ__*) br;
331   if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH) || b == (HGDIOBJ__*)TYPE_BRUSH || b->type != TYPE_BRUSH) return;
333   if (b->wid<0) return;
334   
335   CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
336   CGContextSetFillColorWithColor(c->ctx,b->color);
337   CGContextFillRect(c->ctx,rect);       
341 void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd)
343         xrnd/=3;
344         yrnd/=3;
345         POINT pts[10]={ // todo: curves between edges
346                 {x,y+yrnd},
347                 {x+xrnd,y},
348                 {x2-xrnd,y},
349                 {x2,y+yrnd},
350                 {x2,y2-yrnd},
351                 {x2-xrnd,y2},
352                 {x+xrnd,y2},
353                 {x,y2-yrnd},            
354     {x,y+yrnd},
355                 {x+xrnd,y},
357         
358         WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0]));
361 void Ellipse(HDC ctx, int l, int t, int r, int b)
363   HDC__ *c=(HDC__ *)ctx;
364   if (!HDC_VALID(c)) return;
365   
366   CGRect rect=CGRectMake(l,t,r-l,b-t);
367   
368   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >=0)
369   {
370     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
371     CGContextFillEllipseInRect(c->ctx,rect);    
372   }
373   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
374   {
375     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
376     CGContextStrokeEllipseInRect(c->ctx, rect); //, (float)max(1,c->curpen->wid));
377   }
380 void Rectangle(HDC ctx, int l, int t, int r, int b)
382   HDC__ *c=(HDC__ *)ctx;
383   if (!HDC_VALID(c)) return;
384   
385   CGRect rect=CGRectMake(l,t,r-l,b-t);
386   
387   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
388   {
389     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
390     CGContextFillRect(c->ctx,rect);     
391   }
392   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
393   {
394     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
395     CGContextStrokeRectWithWidth(c->ctx, rect, (float)max(1,c->curpen->wid));
396   }
400 HGDIOBJ GetStockObject(int wh)
402   switch (wh)
403   {
404     case NULL_BRUSH:
405     {
406       HGDIOBJ__ *p = &global_objs[0];
407       p->type=TYPE_BRUSH;
408       p->wid=-1;
409       return p;
410     }
411     case NULL_PEN:
412     {
413       HGDIOBJ__ *p = &global_objs[1];
414       p->type=TYPE_PEN;
415       p->wid=-1;
416       return p;
417     }
418   }
419   return 0;
422 void Polygon(HDC ctx, POINT *pts, int npts)
424   HDC__ *c=(HDC__ *)ctx;
425   if (!HDC_VALID(c)) return;
426   if (((!HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)||c->curbrush->wid<0) && (!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0)) || npts<2) return;
428   CGContextBeginPath(c->ctx);
429   CGContextMoveToPoint(c->ctx,(float)pts[0].x,(float)pts[0].y);
430   int x;
431   for (x = 1; x < npts; x ++)
432   {
433     CGContextAddLineToPoint(c->ctx,(float)pts[x].x,(float)pts[x].y);
434   }
435   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
436   {
437     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
438   }
439   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0)
440   {
441     CGContextSetLineWidth(c->ctx,(float)max(c->curpen->wid,1));
442     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);  
443   }
444   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);
447 void MoveToEx(HDC ctx, int x, int y, POINT *op)
449   HDC__ *c=(HDC__ *)ctx;
450   if (!HDC_VALID(c)) return;
451   if (op) 
452   { 
453     op->x = (int) (c->lastpos_x);
454     op->y = (int) (c->lastpos_y);
455   }
456   c->lastpos_x=(float)x;
457   c->lastpos_y=(float)y;
460 void PolyBezierTo(HDC ctx, POINT *pts, int np)
462   HDC__ *c=(HDC__ *)ctx;
463   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return;
464   
465   CGContextSetLineWidth(c->ctx,(float)max(c->curpen->wid,1));
466   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
467         
468   CGContextBeginPath(c->ctx);
469   CGContextMoveToPoint(c->ctx,c->lastpos_x,c->lastpos_y);
470   int x; 
471   float xp,yp;
472   for (x = 0; x < np-2; x += 3)
473   {
474     CGContextAddCurveToPoint(c->ctx,
475       (float)pts[x].x,(float)pts[x].y,
476       (float)pts[x+1].x,(float)pts[x+1].y,
477       xp=(float)pts[x+2].x,yp=(float)pts[x+2].y);    
478   }
479   c->lastpos_x=(float)xp;
480   c->lastpos_y=(float)yp;
481   CGContextStrokePath(c->ctx);
485 void SWELL_LineTo(HDC ctx, int x, int y)
487   HDC__ *c=(HDC__ *)ctx;
488   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return;
490   float w = (float)max(c->curpen->wid,1);
491   CGContextSetLineWidth(c->ctx,w);
492   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
493         
494   CGContextBeginPath(c->ctx);
495   CGContextMoveToPoint(c->ctx,c->lastpos_x + w * 0.5,c->lastpos_y + w*0.5);
496   float fx=(float)x,fy=(float)y;
497   
498   CGContextAddLineToPoint(c->ctx,fx+w*0.5,fy+w*0.5);
499   c->lastpos_x=fx;
500   c->lastpos_y=fy;
501   CGContextStrokePath(c->ctx);
504 void PolyPolyline(HDC ctx, POINT *pts, DWORD *cnts, int nseg)
506   HDC__ *c=(HDC__ *)ctx;
507   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return;
509   float w = (float)max(c->curpen->wid,1);
510   CGContextSetLineWidth(c->ctx,w);
511   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
512         
513   CGContextBeginPath(c->ctx);
514   
515   while (nseg-->0)
516   {
517     DWORD cnt=*cnts++;
518     if (!cnt) continue;
519     if (!--cnt) { pts++; continue; }
520     
521     CGContextMoveToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
522     pts++;
523     
524     while (cnt--)
525     {
526       CGContextAddLineToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
527       pts++;
528     }
529   }
530   CGContextStrokePath(c->ctx);
532 void *SWELL_GetCtxGC(HDC ctx)
534   HDC__ *ct=(HDC__ *)ctx;
535   if (!HDC_VALID(ct)) return 0;
536   return ct->ctx;
540 void SWELL_SetPixel(HDC ctx, int x, int y, int c)
542   HDC__ *ct=(HDC__ *)ctx;
543   if (!HDC_VALID(ct)) return;
544   CGContextBeginPath(ct->ctx);
545   CGContextMoveToPoint(ct->ctx,(float)x-0.5,(float)y-0.5);
546   CGContextAddLineToPoint(ct->ctx,(float)x+0.5,(float)y+0.5);
547   CGContextSetLineWidth(ct->ctx,(float)1.0);
548   CGContextSetRGBStrokeColor(ct->ctx,GetRValue(c)/255.0,GetGValue(c)/255.0,GetBValue(c)/255.0,1.0);
549   CGContextStrokePath(ct->ctx); 
553 static WDL_Mutex s_fontnamecache_mutex;
555 #ifdef SWELL_CLEANUP_ON_UNLOAD
556 static void releaseString(NSString *s) { [s release]; }
557 #endif
558 static WDL_StringKeyedArray<NSString *> s_fontnamecache(true,
559 #ifdef SWELL_CLEANUP_ON_UNLOAD
560       releaseString
561 #else
562       NULL
563 #endif
564       );
566 static NSString *SWELL_GetCachedFontName(const char *nm)
568   NSString *ret = NULL;
569   if (nm && *nm)
570   {
571     s_fontnamecache_mutex.Enter();
572     ret = s_fontnamecache.Get(nm);
573     s_fontnamecache_mutex.Leave();
574     if (!ret)
575     {
576       ret = CStringToNSString(nm);
577       if (ret)
578       {
579 #ifndef SWELL_NO_CORETEXT
580         // only do postscript name lookups on 10.9+
581         if (floor(NSFoundationVersionNumber) > 945.00) // NSFoundationVersionNumber10_8
582         {
583           NSFont *font = [NSFont fontWithName:ret size:10];
584           NSString *nr = font ? (NSString *)CTFontCopyPostScriptName((CTFontRef)font) : NULL;
585           if (nr) 
586           {
587             [ret release];
588             ret = nr;
589           }
590         }
591 #endif
593         s_fontnamecache_mutex.Enter();
594         s_fontnamecache.Insert(nm,ret);
595         s_fontnamecache_mutex.Leave();
596       }
597     }
598   }
599   return ret ? ret : @"";
602 HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, 
603                  char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, 
604                  char lfQuality, char lfPitchAndFamily, const char *lfFaceName)
606   HGDIOBJ__ *font=GDP_OBJECT_NEW();
607   font->type=TYPE_FONT;
608   float fontwid=lfHeight;
609   
610   if (!fontwid) fontwid=lfWidth;
611   if (fontwid<0)fontwid=-fontwid;
612   
613   if (fontwid < 2 || fontwid > 8192) fontwid=10;
614   
615   font->font_rotation = lfOrientation/10.0;
617 #ifndef SWELL_NO_CORETEXT
618   if (IsCoreTextSupported())
619   {
620     char buf[1024];
621     lstrcpyn_safe(buf,lfFaceName,900);
622     if (lfWeight >= FW_BOLD) strcat(buf," Bold");
623     if (lfItalic) strcat(buf," Italic");
625     font->ct_FontRef = (void*)CTFontCreateWithName((CFStringRef)SWELL_GetCachedFontName(buf),fontwid,NULL);
626     if (!font->ct_FontRef) font->ct_FontRef = (void*)[[NSFont labelFontOfSize:fontwid] retain]; 
628     font->font_quality = (!lfQuality || lfQuality == ANTIALIASED_QUALITY || lfQuality == NONANTIALIASED_QUALITY ? lfQuality : 0);
630     // 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
631     return font;
632   }
633 #endif
634   
635 #ifdef SWELL_ATSUI_TEXT_SUPPORT
636   ATSUFontID fontid=kATSUInvalidFontID;
637   if (lfFaceName && lfFaceName[0])
638   {
639     ATSUFindFontFromName(lfFaceName,strlen(lfFaceName),kFontFullName /* kFontFamilyName? */ ,(FontPlatformCode)kFontNoPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontid);
640     //    if (fontid==kATSUInvalidFontID) printf("looked up %s and got %d\n",lfFaceName,fontid);
641   }
642   
643   if (ATSUCreateStyle(&font->atsui_font_style) == noErr && font->atsui_font_style)
644   {    
645     Fixed fsize=Long2Fix(fontwid);
646     
647     Boolean isBold=lfWeight >= FW_BOLD;
648     Boolean isItal=!!lfItalic;
649     Boolean isUnder=!!lfUnderline;
650     
651     ATSStyleRenderingOptions render;
652     if (!lfQuality)
653       render = kATSStyleNoOptions;
654     else if (lfQuality == ANTIALIASED_QUALITY)
655       render = kATSStyleApplyAntiAliasing;
656     else if (lfQuality == NONANTIALIASED_QUALITY)
657       render = kATSStyleNoAntiAliasing;
658     else
659       render = kATSStyleNoOptions;
661     ATSUAttributeTag        theTags[] = { kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,kATSUSizeTag,kATSUFontTag, kATSUStyleRenderingOptionsTag };
662     ByteCount               theSizes[] = { sizeof(Boolean),sizeof(Boolean),sizeof(Boolean), sizeof(Fixed),sizeof(ATSUFontID), sizeof(ATSStyleRenderingOptions)  };
663     ATSUAttributeValuePtr   theValues[] =  {&isBold, &isItal, &isUnder,  &fsize, &fontid, &render } ;
664     
665     int attrcnt=sizeof(theTags)/sizeof(theTags[0]);
666     if (fontid == kATSUInvalidFontID) attrcnt--;    
667     
668     if (ATSUSetAttributes (font->atsui_font_style,                       
669                        attrcnt,                       
670                        theTags,                      
671                        theSizes,                      
672                        theValues)!=noErr)
673     {
674       ATSUDisposeStyle(font->atsui_font_style);
675       font->atsui_font_style=0;
676     }
677   }
678   else
679     font->atsui_font_style=0;
680   
681 #endif
682   
683   
684   return font;
687 int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName)
689   HDC__ *ct=(HDC__*)ctx;
690   if (!HDC_VALID(ct) || !nCount || !lpFaceName) return 0;
691   
692 #ifndef SWELL_NO_CORETEXT
693   CTFontRef fr=NULL;
694   if (HGDIOBJ_VALID(ct->curfont,TYPE_FONT)) fr=(CTFontRef)ct->curfont->ct_FontRef;
695   if (!fr)  fr=GetCoreTextDefaultFont();
696   
697   if (fr)
698   {
699     CFStringRef name=CTFontCopyDisplayName(fr);
700     const char* p=[(NSString*)name UTF8String];
701     if (p)
702     {
703       lstrcpyn(lpFaceName, p, nCount);
704       return strlen(lpFaceName);
705     }
706   }
707 #endif
708   
709   return 0;
712 BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm)
714   HDC__ *ct=(HDC__ *)ctx;
715   if (tm) // give some sane defaults
716   {
717     tm->tmInternalLeading=3;
718     tm->tmAscent=12;
719     tm->tmDescent=4;
720     tm->tmHeight=16;
721     tm->tmAveCharWidth = 10;
722   }
723   if (!HDC_VALID(ct)||!tm) return 0;
725   bool curfont_valid=HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
727 #ifdef SWELL_ATSUI_TEXT_SUPPORT
728   if (curfont_valid && ct->curfont->atsui_font_style)
729   {
730     ATSUTextMeasurement ascent=Long2Fix(10);
731     ATSUTextMeasurement descent=Long2Fix(3);
732     ATSUTextMeasurement sz=Long2Fix(0);
733     ATSUTextMeasurement width =Long2Fix(12);
734     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUAscentTag, sizeof(ATSUTextMeasurement), &ascent,NULL);
735     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUDescentTag, sizeof(ATSUTextMeasurement), &descent,NULL);
736     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUSizeTag, sizeof(ATSUTextMeasurement), &sz,NULL);
737     ATSUGetAttribute(ct->curfont->atsui_font_style, kATSULineWidthTag, sizeof(ATSUTextMeasurement),&width,NULL);
738     
739     float asc=Fix2X(ascent);
740     float desc=Fix2X(descent);
741     float size = Fix2X(sz);
742     
743     if (size < (asc+desc)*0.2) size=asc+desc;
744             
745     tm->tmAscent = (int)ceil(asc);
746     tm->tmDescent = (int)ceil(desc);
747     tm->tmInternalLeading=(int)ceil(asc+desc-size);
748     if (tm->tmInternalLeading<0)tm->tmInternalLeading=0;
749     tm->tmHeight=(int) ceil(asc+desc);
750     tm->tmAveCharWidth = (int) (ceil(asc+desc)*0.65); // (int)ceil(Fix2X(width));
751     
752     return 1;
753   }
754 #endif
756 #ifndef SWELL_NO_CORETEXT
757   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
758   if (!fr)  fr=GetCoreTextDefaultFont();
760   if (fr)
761   {
762     tm->tmInternalLeading = CTFontGetLeading(fr);
763     tm->tmAscent = CTFontGetAscent(fr);
764     tm->tmDescent = CTFontGetDescent(fr);
765     tm->tmHeight = (tm->tmInternalLeading + tm->tmAscent + tm->tmDescent);
766     tm->tmAveCharWidth = tm->tmHeight*2/3; // todo
768     if (tm->tmHeight)  tm->tmHeight++;
769     
770     return 1;
771   }
772 #endif
774   
775   return 1;
780 #ifdef SWELL_ATSUI_TEXT_SUPPORT
782 static int DrawTextATSUI(HDC ctx, CFStringRef strin, RECT *r, int align, bool *err)
784   HDC__ *ct=(HDC__ *)ctx;
785   HGDIOBJ__ *font=ct->curfont; // caller must specify a valid font
786   
787   UniChar strbuf[4096];
788   int strbuf_len;
790   {
791     strbuf[0]=0;
792     CFRange r = {0,CFStringGetLength(strin)};
793     if (r.length > 4095) r.length=4095;
794     strbuf_len=CFStringGetBytes(strin,r,kCFStringEncodingUTF16,' ',false,(UInt8*)strbuf,sizeof(strbuf)-2,NULL);
795     if (strbuf_len<0)strbuf_len=0;
796     else if (strbuf_len>4095) strbuf_len=4095;
797     strbuf[strbuf_len]=0;
798   }
799   
800   {
801     ATSUAttributeTag        theTags[] = { kATSUColorTag,   };
802     ByteCount               theSizes[] = { sizeof(RGBColor),  };
804     RGBColor tcolor;
805     ATSUAttributeValuePtr   theValues[] =  {&tcolor,  } ;
806     
807     tcolor.red = GetRValue(ct->cur_text_color_int)*256;
808     tcolor.green = GetGValue(ct->cur_text_color_int)*256;
809     tcolor.blue = GetBValue(ct->cur_text_color_int)*256;
810     
811     // error check this? we can live with the wrong color maybe?
812     ATSUSetAttributes(font->atsui_font_style,  sizeof(theTags)/sizeof(theTags[0]), theTags, theSizes, theValues);
813   }
814   
815   UniCharCount runLengths[1]={kATSUToTextEnd};
816   ATSUTextLayout layout; 
817   if (ATSUCreateTextLayoutWithTextPtr(strbuf, kATSUFromTextBeginning, kATSUToTextEnd, strbuf_len, 1, runLengths, &font->atsui_font_style, &layout)!=noErr)
818   {
819     *err=true;
820     return 0;
821   }
822   
823   {
824     Fixed frot = X2Fix(font->font_rotation);
825     
826     ATSULineTruncation tv = (align & DT_END_ELLIPSIS) ? kATSUTruncateEnd : kATSUTruncateNone;
827     ATSUAttributeTag        theTags[] = { kATSUCGContextTag, kATSULineTruncationTag, kATSULineRotationTag };
828     ByteCount               theSizes[] = { sizeof (CGContextRef), sizeof(ATSULineTruncation), sizeof(Fixed)};
829     ATSUAttributeValuePtr   theValues[] =  { &ct->ctx, &tv, &frot } ;
830     
831     
832     if (ATSUSetLayoutControls (layout,
833                            
834                            sizeof(theTags)/sizeof(theTags[0]),
835                            
836                            theTags,
837                            
838                            theSizes,
839                            
840                            theValues)!=noErr)
841     {
842       *err=true;
843       ATSUDisposeTextLayout(layout);   
844       return 0;
845     }
846   }
847   
848   
849   ATSUTextMeasurement leftFixed, rightFixed, ascentFixed, descentFixed;
851   if (ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, kATSUToTextEnd, &leftFixed, &rightFixed, &ascentFixed, &descentFixed)!=noErr) 
852   {
853     *err=true;
854     ATSUDisposeTextLayout(layout);   
855     return 0;
856   }
858   int w=Fix2Long(rightFixed);
859   int descent=Fix2Long(descentFixed);
860   int h=descent + Fix2Long(ascentFixed);
861   if (align&DT_CALCRECT)
862   {
863     ATSUDisposeTextLayout(layout);   
864     r->right=r->left+w;
865     r->bottom=r->top+h;
866     return h;  
867   }
868   CGContextSaveGState(ct->ctx);    
870   if (!(align & DT_NOCLIP))
871     CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
873   int l=r->left, t=r->top;
874     
875   if (fabs(font->font_rotation)<45.0)
876   {
877     if (align & DT_RIGHT) l = r->right-w;
878     else if (align & DT_CENTER) l = (r->right+r->left)/2 - w/2;      
879   }
880   else l+=Fix2Long(ascentFixed); // 90 degree special case (we should generalize this to be correct throughout the rotation range, but oh well)
881     
882   if (align & DT_BOTTOM) t = r->bottom-h;
883   else if (align & DT_VCENTER) t = (r->bottom+r->top)/2 - h/2;
884     
885   CGContextTranslateCTM(ct->ctx,0,t);
886   CGContextScaleCTM(ct->ctx,1,-1);
887   CGContextTranslateCTM(ct->ctx,0,-t-h);
888     
889   if (ct->curbkmode == OPAQUE)
890   {      
891     CGRect bgr = CGRectMake(l, t, w, h);
892     CGColorRef bgc = CreateColor(ct->curbkcol);
893     CGContextSetFillColorWithColor(ct->ctx, bgc);
894     CGContextFillRect(ct->ctx, bgr);
895     CGColorRelease(bgc);        
896   }
898   if (ATSUDrawText(layout,kATSUFromTextBeginning,kATSUToTextEnd,Long2Fix(l),Long2Fix(t+descent))!=noErr)
899     *err=true;
900   
901   CGContextRestoreGState(ct->ctx);    
902   
903   ATSUDisposeTextLayout(layout);   
904   
905   return h;
908 #endif
910 int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align)
912   HDC__ *ct=(HDC__ *)ctx;
913   if (!HDC_VALID(ct)) return 0;
914   
915   bool has_ml=false;
916   char tmp[4096];
917   const char *p=buf;
918   char *op=tmp;
919   while (*p && (op-tmp)<sizeof(tmp)-1 && (buflen<0 || (p-buf)<buflen))
920   {
921     if (*p == '&' && !(align&DT_NOPREFIX)) p++; 
923     if (*p == '\r')  p++; 
924     else if (*p == '\n' && (align&DT_SINGLELINE)) { *op++ = ' '; p++; }
925     else 
926     {
927       if (*p == '\n') has_ml=true;
928       *op++=*p++;
929     }
930   }
931   *op=0;
932   
933   if (!tmp[0]) return 0; // dont draw empty strings
934   
935   NSString *str=CStringToNSString(tmp);
936   
937   if (!str) return 0;
938   
939   bool curfont_valid = HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
940 #ifdef SWELL_ATSUI_TEXT_SUPPORT
941   if (curfont_valid && ct->curfont->atsui_font_style)
942   {
943     bool err=false;
944     int ret =  DrawTextATSUI(ctx,(CFStringRef)str,r,align,&err);
945     [str release];
946     
947     if (!err) return ret;
948     return 0;
949   }
950 #endif  
951   
952 #ifndef SWELL_NO_CORETEXT
953   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
954   if (!fr)  fr=GetCoreTextDefaultFont();
955   if (fr)
956   {
957     // Initialize string, font, and context
958     CFStringRef keys[] = { kCTFontAttributeName,kCTForegroundColorAttributeName };
959     CFTypeRef values[] = { fr,ct->curtextcol };
960     
961     int nk= sizeof(keys) / sizeof(keys[0]);
962     if (!values[1]) nk--;
963     
964     CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, nk,
965                                 &kCFTypeDictionaryKeyCallBacks,
966                                 &kCFTypeDictionaryValueCallBacks);
967        
968     CFAttributedStringRef attrString =
969           CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)str, attributes);
970     CFRelease(attributes);
971     [str release];
974     CTFrameRef frame = NULL;
975     CFArrayRef lines = NULL;
976     CTLineRef line = NULL;
977     CGFloat asc=0;
978     int line_w=0,line_h=0;
979     if (has_ml)
980     {
981       CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
982       if (framesetter)
983       {
984         CGMutablePathRef path=CGPathCreateMutable();
985         CGPathAddRect(path,NULL,CGRectMake(0,0,100000,100000));
986         frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0),path,NULL);
987         CFRelease(framesetter);
988         CFRelease(path);
989       }
990       if (frame)
991       {
992         lines = CTFrameGetLines(frame);
993         int n=CFArrayGetCount(lines);
994         int x;
995         for(x=0;x<n;x++)
996         {
997           CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
998           if (l)
999           {
1000             CGFloat asc=0,desc=0,lead=0;
1001             int w = (int) floor(CTLineGetTypographicBounds(l,&asc,&desc,&lead)+0.5);
1002             int h =(int) floor(asc+desc+lead+1.5);
1003             line_h+=h;
1004             if (line_w < w) line_w=w;
1005           }
1006         }
1007       }
1008     }
1009     else
1010     {
1011       line = CTLineCreateWithAttributedString(attrString);
1012        
1013       if (line)
1014       {
1015         CGFloat desc=0,lead=0;
1016         line_w = (int) floor(CTLineGetTypographicBounds(line,&asc,&desc,&lead)+0.5);
1017         line_h =(int) floor(asc+desc+lead+1.5);
1018       }
1019     }
1020     if (line_h) line_h++;
1022     CFRelease(attrString);
1023     
1024     if (align & DT_CALCRECT)
1025     {
1026       r->right = r->left+line_w;
1027       r->bottom = r->top+line_h;
1028       if (line) CFRelease(line);
1029       if (frame) CFRelease(frame);
1030       return line_h;
1031     }
1033     float xo=r->left,yo=r->top;
1034     if (align & DT_RIGHT) xo += (r->right-r->left) - line_w;
1035     else if (align & DT_CENTER) xo += (r->right-r->left)/2 - line_w/2;
1037     if (align & DT_BOTTOM) yo += (r->bottom-r->top) - line_h;
1038     else if (align & DT_VCENTER) yo += (r->bottom-r->top)/2 - line_h/2;
1040     
1041     CGContextSaveGState(ct->ctx);
1043     CGAffineTransform f={1,0,0,-1,0,0};
1044     CGContextSetTextMatrix(ct->ctx, f);
1046     if (!(align & DT_NOCLIP))
1047     {
1048       CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));          
1049     }
1050     
1051     CGColorRef bgc = NULL;
1052     if (ct->curbkmode == OPAQUE)
1053     {      
1054       bgc = CreateColor(ct->curbkcol);
1055     }
1057     if (ct->curfont->font_quality)
1058     {
1059       CGContextSetShouldAntialias(ct->ctx, ct->curfont->font_quality == ANTIALIASED_QUALITY);
1060     }
1062     if (line) 
1063     {
1064       if (bgc)
1065       {
1066         CGContextSetFillColorWithColor(ct->ctx, bgc);
1067         CGContextFillRect(ct->ctx, CGRectMake(xo,yo,line_w,line_h));
1068       }
1069       CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1070       CTLineDraw(line,ct->ctx);
1072     }
1073     if (lines)
1074     {
1075       int n=CFArrayGetCount(lines);
1076       int x;
1077       for(x=0;x<n;x++)
1078       {
1079         CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
1080         if (l)
1081         {
1082           CGFloat asc=0,desc=0,lead=0;
1083           float lw=CTLineGetTypographicBounds(l,&asc,&desc,&lead);
1085           if (bgc)
1086           {
1087             CGContextSetFillColorWithColor(ct->ctx, bgc);
1088             CGContextFillRect(ct->ctx, CGRectMake(xo,yo,lw,asc+desc+lead));
1089           }
1090           CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1091           CTLineDraw(l,ct->ctx);
1092           
1093           yo += floor(asc+desc+lead+0.5);          
1094         }
1095       }
1096       
1097     }
1098     
1099     CGContextRestoreGState(ct->ctx);
1100     if (bgc) CGColorRelease(bgc);
1101     if (line) CFRelease(line);
1102     if (frame) CFRelease(frame);
1103     
1104     return line_h;
1105   }
1106 #endif
1107   
1108   
1109   [str release];
1110   return 0;  
1112     
1113   
1118 void SetBkColor(HDC ctx, int col)
1120   HDC__ *ct=(HDC__ *)ctx;
1121   if (!HDC_VALID(ct)) return;
1122   ct->curbkcol=col;
1125 void SetBkMode(HDC ctx, int col)
1127   HDC__ *ct=(HDC__ *)ctx;
1128   if (!HDC_VALID(ct)) return;
1129   ct->curbkmode=col;
1132 int GetTextColor(HDC ctx)
1134   HDC__ *ct=(HDC__ *)ctx;
1135   if (!HDC_VALID(ct)) return -1;
1136   return ct->cur_text_color_int;
1139 void SetTextColor(HDC ctx, int col)
1141   HDC__ *ct=(HDC__ *)ctx;
1142   if (!HDC_VALID(ct)) return;
1143   ct->cur_text_color_int = col;
1144   
1145   if (ct->curtextcol) CFRelease(ct->curtextcol);
1147   ct->curtextcol = CreateColor(col);
1151 HICON CreateIconIndirect(ICONINFO* iconinfo)
1153   if (!iconinfo || !iconinfo->fIcon) return 0;  
1154   HGDIOBJ__* i=iconinfo->hbmColor;
1155   if (!HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return 0;
1156   NSImage* img=i->bitmapptr;
1157   if (!img) return 0;
1158     
1159   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1160   icon->type=TYPE_BITMAP;
1161   icon->wid=1;
1162   [img retain];
1163   icon->bitmapptr=img;
1164   return icon;   
1167 HICON LoadNamedImage(const char *name, bool alphaFromMask)
1169   NSImage *img=0;
1170   NSString *str=CStringToNSString(name); 
1171   if (strstr(name,"/"))
1172   {
1173     img=[[NSImage alloc] initWithContentsOfFile:str];
1174   }
1175   if (!img) 
1176   {
1177     img=[NSImage imageNamed:str];
1178     if (img) [img retain];
1179   }
1180   [str release];
1181   if (!img) 
1182   {
1183     return 0;
1184   }
1185     
1186   [img setFlipped:YES];
1187   if (alphaFromMask)
1188   {
1189     const NSSize sz=[img size];
1190     const int w = (int)sz.width, h=(int)sz.height;
1191     HDC hdc;
1192     if (w>0 && h>0 && NULL != (hdc=SWELL_CreateMemContext(NULL,w,h)))
1193     {
1194       [NSGraphicsContext saveGraphicsState];
1195       NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:((struct HDC__*)hdc)->ctx flipped:NO];
1196       [NSGraphicsContext setCurrentContext:gc];
1197       [img drawInRect:NSMakeRect(0,0,w,h) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
1198       [NSGraphicsContext restoreGraphicsState];
1200       NSImage *newImage=[[NSImage alloc] initWithData:[img TIFFRepresentation]];
1201       [newImage setFlipped:YES];
1203       const int *fb = (const int *)SWELL_GetCtxFrameBuffer(hdc);
1204       int y,rcnt=0;
1205       [newImage lockFocus];
1206       CGContextRef myContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
1207       for (y=0; y < h; y ++)
1208       {
1209         int x;
1210         for (x = 0; x < w; x++)
1211         {
1212 #ifdef __ppc__
1213           if ((*fb++ & 0xffffff) == 0xff00ff)
1214 #else
1215           if ((*fb++ & 0xffffff00) == 0xff00ff00)
1216 #endif
1217           {
1218             CGContextClearRect(myContext,CGRectMake(x,y,1,1));
1219             rcnt++;
1220           }
1221         }
1222       }
1223       [newImage unlockFocus];
1225       SWELL_DeleteGfxContext(hdc);
1227       if (rcnt)
1228       {
1229         [img release];
1230         img=newImage;    
1231       }
1232       else
1233         [newImage release];
1234     }
1235   }
1236   
1237   HGDIOBJ__ *i=GDP_OBJECT_NEW();
1238   i->type=TYPE_BITMAP;
1239   i->wid=1;
1240   i->bitmapptr = img;
1241   return i;
1244 void DrawImageInRect(HDC ctx, HICON img, const RECT *r)
1246   HGDIOBJ__ *i = (HGDIOBJ__ *)img;
1247   HDC__ *ct=(HDC__*)ctx;
1248   if (!HDC_VALID(ct) || !HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return;
1249   //CGContextDrawImage(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top),(CGImage*)i->bitmapptr);
1250   // probably a better way since this ignores the ctx
1251   [NSGraphicsContext saveGraphicsState];
1252   NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:ct->ctx flipped:NO];
1253   [NSGraphicsContext setCurrentContext:gc];
1254   NSImage *nsi=i->bitmapptr;
1255   NSRect rr=NSMakeRect(r->left,r->top,r->right-r->left,r->bottom-r->top);
1256   [nsi setFlipped:YES];
1257   [nsi drawInRect:rr fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1258   [nsi setFlipped:NO]; // todo: restore old flippedness?
1259   [NSGraphicsContext restoreGraphicsState];
1260 //  [gc release];
1264 BOOL GetObject(HICON icon, int bmsz, void *_bm)
1266   memset(_bm,0,bmsz);
1267   if (bmsz != sizeof(BITMAP)) return false;
1268   BITMAP *bm=(BITMAP *)_bm;
1269   HGDIOBJ__ *i = (HGDIOBJ__ *)icon;
1270   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return false;
1271   NSImage *img = i->bitmapptr;
1272   if (!img) return false;
1273   bm->bmWidth = (int) ([img size].width+0.5);
1274   bm->bmHeight = (int) ([img size].height+0.5);
1275   return true;
1279 void *GetNSImageFromHICON(HICON ico)
1281   HGDIOBJ__ *i = (HGDIOBJ__ *)ico;
1282   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return 0;
1283   return i->bitmapptr;
1286 #if 0
1287 static int ColorFromNSColor(NSColor *color, int valifnul)
1289   if (!color) return valifnul;
1290   float r,g,b;
1291   NSColor *color2=[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1292   if (!color2) 
1293   {
1294     NSLog(@"error converting colorspace from: %@\n",[color colorSpaceName]);
1295     return valifnul;
1296   }
1297   
1298   [color2 getRed:&r green:&g blue:&b alpha:NULL];
1299   return RGB((int)(r*255.0),(int)(g*255.0),(int)(b*255.0));
1301 #else
1302 #define ColorFromNSColor(a,b) (b)
1303 #endif
1304 int GetSysColor(int idx)
1306  // NSColors that seem to be valid: textBackgroundColor, selectedTextBackgroundColor, textColor, selectedTextColor
1307   
1308   switch (idx)
1309   {
1310     case COLOR_WINDOW: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1311     case COLOR_3DFACE: 
1312     case COLOR_BTNFACE: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1313     case COLOR_SCROLLBAR: return ColorFromNSColor([NSColor controlColor],RGB(32,32,32));
1314     case COLOR_3DSHADOW: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96));
1315     case COLOR_3DHILIGHT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(224,224,224));
1316     case COLOR_BTNTEXT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(0,0,0));
1317     case COLOR_3DDKSHADOW: return (ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96))>>1)&0x7f7f7f;
1318     case COLOR_INFOBK: return RGB(255,240,200);
1319     case COLOR_INFOTEXT: return RGB(0,0,0);
1320       
1321   }
1322   return 0;
1326 void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode)
1328   StretchBlt(hdcOut,x,y,w,h,hdcIn,xin,yin,w,h,mode);
1331 #ifndef __ppc__
1332 static void SWELL_fastDoubleUpImage(unsigned int *op, const unsigned int *ip, int w, int h, int sw, int newspan)
1334   int y = h;
1335   while (y-->0)
1336   {
1337     const unsigned int *rd = ip;
1338     unsigned int *wr = op;
1339     int remaining = w;
1341 #if 0 // def __AVX__
1342     // this isn't really any faster than SSE anyway
1343     if (remaining >= 8)
1344     {
1345       if (!((INT_PTR)rd & 31))
1346       {
1347         int x = remaining/8;
1348         while (x-->0)
1349         {
1350           const __m256 m =  _mm256_load_ps((const float *)rd);
1351           rd+=8;
1352           
1353           const __m256 p1 = _mm256_permutevar_ps(_mm256_permute2f128_ps(m,m,0),_mm256_set_epi32(3,3,2,2,1,1,0,0));
1354           const __m256 p2 = _mm256_permutevar_ps(_mm256_permute2f128_ps(m,m,1|(1<<4)),_mm256_set_epi32(3,3,2,2,1,1,0,0));
1355           
1356           unsigned int *wr2 = wr+newspan;
1357           _mm256_store_ps((float*)wr,p1);
1358           _mm256_store_ps((float*)wr2,p1);
1359         
1360           _mm256_store_ps((float*)wr + 8,p2);
1361           _mm256_store_ps((float*)wr2 + 8,p2);
1362           
1363           wr += 16;
1364         }
1365         remaining &= 7;
1366         
1367       }
1368     }
1369 #endif
1370     
1371 #ifdef __SSE__
1372     if (remaining >= 4)
1373     {
1374       // with SSE is about 2x faster than without
1375       if (((INT_PTR)rd & 7))
1376       {
1377         // input isn't 8 byte aligned, must use unaligned reads
1378         int x = remaining/4;
1379         while (x-->0)
1380         {
1381           __m128 m =  _mm_loadu_ps((const float *)rd);
1382           __m128 p1 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(1,1,0,0));
1383           __m128 p2 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(3,3,2,2));
1384           
1385           unsigned int *wr2 = wr+newspan;
1386           rd+=4;
1387           
1388           _mm_store_ps((float*)wr,p1);
1389           _mm_store_ps((float*)wr2,p1);
1390           
1391           _mm_store_ps((float*)wr + 4,p2);
1392           _mm_store_ps((float*)wr2 + 4,p2);
1393           
1394           wr += 8;
1395         }
1396       }
1397       else
1398       {
1399         // if rd is 8 byte aligned, we can do SSE without unaligned reads
1400         
1401         // but if it is not 16 byte aligned, we need to preprocess a pair of pixels
1402         // (advancing rd by 8 bytes, and wr by 16)
1403       
1404         if ((INT_PTR)rd & 15)
1405         {
1406           unsigned int *nwr = wr+newspan;
1407           wr[0] = wr[1] = nwr[0] = nwr[1] = rd[0];
1408           wr[2] = wr[3] = nwr[2] = nwr[3] = rd[1];
1409           wr+=4;
1410           rd+=2;
1411           remaining-=2;
1412         }
1413         
1414         int x = remaining/4;
1415         while (x-->0)
1416         {
1417           __m128 m =  _mm_load_ps((const float *)rd);
1418           __m128 p1 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(1,1,0,0));
1419           __m128 p2 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(3,3,2,2));
1421           unsigned int *wr2 = wr+newspan;
1422           rd+=4;
1423           
1424           _mm_store_ps((float*)wr,p1);
1425           _mm_store_ps((float*)wr2,p1);
1426           
1427           _mm_store_ps((float*)wr + 4,p2);
1428           _mm_store_ps((float*)wr2 + 4,p2);
1430           wr += 8;
1431         }
1432       }
1433       remaining &= 3;
1434     }
1435 #endif //__SSE__
1437     int x = remaining/2;
1438     while (x-->0)
1439     {
1440       unsigned int *nwr = wr+newspan;
1441       wr[0] = wr[1] = nwr[0] = nwr[1] = rd[0];
1442       wr[2] = wr[3] = nwr[2] = nwr[3] = rd[1];
1443       rd+=2;
1444       wr+=4;
1445     }
1446     if (remaining&1)
1447     {
1448       wr[0] = wr[1] = wr[newspan] = wr[newspan+1] = *rd;
1449     }
1450     ip += sw;
1451     op += newspan*2;
1452   }
1454 #endif
1456 void StretchBlt(HDC hdcOut, int x, int y, int destw, int desth, HDC hdcIn, int xin, int yin, int w, int h, int mode)
1458   if (!hdcOut || !hdcIn||w<1||h<1) return;
1459   HDC__ *src=(HDC__*)hdcIn;
1460   HDC__ *dest=(HDC__*)hdcOut;
1461   if (!HDC_VALID(src) || !HDC_VALID(dest) || !src->ownedData || !src->ctx || !dest->ctx) return;
1463   if (w<1||h<1) return;
1464   
1465   int sw=  CGBitmapContextGetWidth(src->ctx);
1466   int sh= CGBitmapContextGetHeight(src->ctx);
1467   
1468   int preclip_w=w;
1469   int preclip_h=h;
1470   
1471   if (xin<0) 
1472   { 
1473     x-=(xin*destw)/w;
1474     w+=xin; 
1475     xin=0; 
1476   }
1477   if (yin<0) 
1478   { 
1479     y-=(yin*desth)/h;
1480     h+=yin; 
1481     yin=0; 
1482   }
1483   if (xin+w > sw) w=sw-xin;
1484   if (yin+h > sh) h=sh-yin;
1485   
1486   if (w<1||h<1) return;
1488   if (destw==preclip_w) destw=w; // no scaling, keep width the same
1489   else if (w != preclip_w) destw = (w*destw)/preclip_w;
1490   
1491   if (desth == preclip_h) desth=h;
1492   else if (h != preclip_h) desth = (h*desth)/preclip_h;
1493   
1494   const bool use_alphachannel = mode == SRCCOPY_USEALPHACHAN;
1495   
1496   CGContextRef output = (CGContextRef)dest->ctx;
1497   CGRect outputr = CGRectMake(x,-desth-y,destw,desth);
1498   
1499   unsigned char *p = (unsigned char *)ALIGN_FBUF(src->ownedData);
1500   p += (xin + sw*yin)*4;
1502   
1503 #ifdef SWELL_SUPPORT_OPENGL_BLIT
1504   if (dest->GLgfxctx)
1505   {
1506     NSOpenGLContext *glCtx = (NSOpenGLContext*) dest->GLgfxctx;
1507     NSOpenGLContext *cCtx = [NSOpenGLContext currentContext];
1508     if (glCtx != cCtx)
1509     {
1510       [glCtx makeCurrentContext];
1511     }
1512     
1513     glDisable(GL_TEXTURE_2D);
1514     glEnable(GL_TEXTURE_RECTANGLE_EXT);
1515     
1516     GLuint texid=0;
1517     glGenTextures(1, &texid);
1518     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texid);
1519     glPixelStorei(GL_UNPACK_ROW_LENGTH, sw);
1520     glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER,  GL_LINEAR);
1521     glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_RGBA8,w,h,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8, p);
1522     
1523     glClear(GL_COLOR_BUFFER_BIT);
1524     glViewport(x,[[glCtx view] bounds].size.height-h-y,w,h);
1525     glBegin(GL_QUADS);
1526     
1527     glTexCoord2f(0.0f, 0.0f);
1528     glVertex2f(-1,1);
1529     
1530     glTexCoord2f(0.0f, h);
1531     glVertex2f(-1,-1);
1532     
1533     glTexCoord2f(w,h);
1534     glVertex2f(1,-1);
1535     
1536     glTexCoord2f(w, 0.0f);
1537     glVertex2f(1,1);
1538     glEnd();
1539     
1540     glDeleteTextures(1,&texid);
1541     glFlush();
1543     if (glCtx != cCtx) [cCtx makeCurrentContext];
1544     return;
1545   }
1546 #endif
1547   
1548   
1549   // this is only faster when using the native color profile, it seems
1550   WDL_HeapBuf *retina_hb = NULL;
1551   unsigned char *retina_buf = NULL;
1552 #ifndef __ppc__
1553   if (destw == w && desth == h && 
1554       CGContextConvertSizeToDeviceSpace(output, CGSizeMake(1,1)).width > 1.9)
1555   {
1556     const int newspan = (w*2+3)&~3;
1557     retina_hb = SWELL_GDP_GetTmpBuf();
1558     if (retina_hb && retina_hb->ResizeOK(sizeof(unsigned int) * newspan*h*2 + 32,false))
1559     {
1560       retina_buf = (unsigned char *)retina_hb->Get();
1561       const UINT_PTR align = (UINT_PTR)retina_buf & 31;
1562       if (align) retina_buf += 32-align;
1564       SWELL_fastDoubleUpImage((unsigned int *)retina_buf, 
1565                         (const unsigned int *)p,w,h,sw,newspan);
1567       sw = newspan;
1568       w *= 2;
1569       h *= 2;
1570     }
1571   }
1572 #endif
1574   
1575   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,retina_buf ? retina_buf : p,4*sw*h,NULL);
1576   CGImageRef img = CGImageCreate(w,h,8,32,4*sw,__GetDisplayColorSpace(),
1577       use_alphachannel?kCGImageAlphaFirst:kCGImageAlphaNoneSkipFirst,
1578       provider,NULL,NO,kCGRenderingIntentDefault);
1579   CGDataProviderRelease(provider);
1580   
1581   if (img)
1582   {
1583     CGContextSaveGState(output);
1584     CGContextScaleCTM(output,1.0,-1.0);
1585   
1586     CGContextSetInterpolationQuality(output,kCGInterpolationNone);
1587     CGContextDrawImage(output,outputr,img);
1588     CGContextRestoreGState(output);
1589   
1590     CGImageRelease(img);
1591   }
1592   SWELL_GDP_DisposeTmpBuf(retina_hb);
1595 void SWELL_PushClipRegion(HDC ctx)
1597   HDC__ *ct=(HDC__ *)ctx;
1598   if (HDC_VALID(ct) && ct->ctx) CGContextSaveGState(ct->ctx);
1601 void SWELL_SetClipRegion(HDC ctx, const RECT *r)
1603   HDC__ *ct=(HDC__ *)ctx;
1604   if (HDC_VALID(ct) && ct->ctx) CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
1608 void SWELL_PopClipRegion(HDC ctx)
1610   HDC__ *ct=(HDC__ *)ctx;
1611   if (HDC_VALID(ct) && ct->ctx) CGContextRestoreGState(ct->ctx);
1614 void *SWELL_GetCtxFrameBuffer(HDC ctx)
1616   HDC__ *ct=(HDC__ *)ctx;
1617   if (HDC_VALID(ct)) return ALIGN_FBUF(ct->ownedData);
1618   return 0;
1622 HDC GetDC(HWND h)
1624   if (h && [(id)h isKindOfClass:[NSWindow class]])
1625   {
1626     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1627     {
1628       PAINTSTRUCT ps={0,}; 
1629       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1630       if (ps.hdc) 
1631       {
1632         if ((ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1633         return ps.hdc;
1634       }
1635     }
1636     h=(HWND)[(id)h contentView];
1637   }
1638   
1639   if (h && [(id)h isKindOfClass:[NSView class]])
1640   {
1641     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1642     {
1643       PAINTSTRUCT ps={0,}; 
1644       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1645       if (HDC_VALID((HDC__*)ps.hdc)) 
1646       {
1647         if (((HDC__*)ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1648         return ps.hdc;
1649       }
1650     }
1651     
1652     if ([(NSView*)h lockFocusIfCanDraw])
1653     {
1654       HDC ret= SWELL_CreateGfxContext([NSGraphicsContext currentContext]);
1655       if (ret)
1656       {
1657          if ((ret)->ctx) CGContextSaveGState((ret)->ctx);
1658       }
1659       return ret;
1660     }
1661   }
1662   return 0;
1665 HDC GetWindowDC(HWND h)
1667   HDC ret=GetDC(h);
1668   if (ret)
1669   {
1670     NSView *v=NULL;
1671     if ([(id)h isKindOfClass:[NSWindow class]]) v=[(id)h contentView];
1672     else if ([(id)h isKindOfClass:[NSView class]]) v=(NSView *)h;
1673     
1674     if (v)
1675     {
1676       NSRect b=[v bounds];
1677       float xsc=b.origin.x;
1678       float ysc=b.origin.y;
1679       if ((xsc || ysc) && (ret)->ctx) CGContextTranslateCTM((ret)->ctx,xsc,ysc);
1680     }
1681   }
1682   return ret;
1685 void ReleaseDC(HWND h, HDC hdc)
1687   if (hdc)
1688   {
1689     if ((hdc)->ctx) CGContextRestoreGState((hdc)->ctx);
1690   }
1691   if (h && [(id)h isKindOfClass:[NSWindow class]])
1692   {
1693     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1694     {
1695       PAINTSTRUCT ps={0,}; 
1696       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1697       if (ps.hdc && ps.hdc==hdc) return;
1698     }
1699     h=(HWND)[(id)h contentView];
1700   }
1701   bool isView=h && [(id)h isKindOfClass:[NSView class]];
1702   if (isView)
1703   {
1704     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1705     {
1706       PAINTSTRUCT ps={0,}; 
1707       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1708       if (ps.hdc && ps.hdc==hdc) return;
1709     }
1710   }    
1711     
1712   if (hdc) SWELL_DeleteGfxContext(hdc);
1713   if (isView && hdc)
1714   {
1715     [(NSView *)h unlockFocus];
1716 //    if ([(NSView *)h window]) [[(NSView *)h window] flushWindow];
1717   }
1720 void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level)
1722   CGContextRef ctx=(CGContextRef)SWELL_GetCtxGC(hdc);
1723   if (ctx)
1724   {
1725   // level 0 for now = this
1726     HIThemeSetFill(kThemeBrushDialogBackgroundActive,NULL,ctx,kHIThemeOrientationNormal);
1727     CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
1728     CGContextFillRect(ctx,rect);                 
1729   }
1732 HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a)
1734   if (HGDIOBJ_VALID(a))
1735   {
1736     a->additional_refcnt++;
1737     return a;
1738   }
1739   return NULL;
1743 HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits)
1745   int spp = bitsperpixel/8;
1746   Boolean hasa = (bitsperpixel == 32);
1747   Boolean hasp = (numplanes > 1); // won't actually work yet for planar data
1748   NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:0 pixelsWide:width pixelsHigh:height 
1749                                                                bitsPerSample:8 samplesPerPixel:spp
1750                                                                hasAlpha:hasa isPlanar:hasp
1751                                                                colorSpaceName:NSDeviceRGBColorSpace
1752                                                                 bitmapFormat:NSAlphaFirstBitmapFormat 
1753                                                                  bytesPerRow:0 bitsPerPixel:0];    
1754   if (!rep) return 0;
1755   unsigned char* p = [rep bitmapData];
1756   int pspan = [rep bytesPerRow]; // might not be the same as width
1757   
1758   int y;
1759   for (y=0;y<height;y ++)
1760   {
1761     memcpy(p,bits,width*4);
1762     p+=pspan;
1763     bits += width*4;
1764   }
1766   NSImage* img = [[NSImage alloc] init];
1767   [img addRepresentation:rep]; 
1768   [rep release];
1769   
1770   HGDIOBJ__* obj = GDP_OBJECT_NEW();
1771   obj->type = TYPE_BITMAP;
1772   obj->wid = 1; // need free
1773   obj->bitmapptr = img;
1774   return obj;
1778 HIMAGELIST ImageList_CreateEx()
1780   return (HIMAGELIST)new WDL_PtrList<HGDIOBJ__>;
1783 BOOL ImageList_Remove(HIMAGELIST list, int idx)
1785   WDL_PtrList<HGDIOBJ__>* imglist=(WDL_PtrList<HGDIOBJ__>*)list;
1786   if (imglist && idx < imglist->GetSize())
1787   {
1788     if (idx < 0) 
1789     {
1790       int x,n=imglist->GetSize();
1791       for (x=0;x<n;x++)
1792       {
1793         HGDIOBJ__ *a = imglist->Get(x);
1794         if (a) DeleteObject(a);
1795       }
1796       imglist->Empty();
1797     }
1798     else 
1799     {
1800       HGDIOBJ__ *a = imglist->Get(idx);
1801       imglist->Set(idx, NULL); 
1802       if (a) DeleteObject(a);
1803     }
1804     return TRUE;
1805   }
1806   
1807   return FALSE;
1810 void ImageList_Destroy(HIMAGELIST list)
1812   if (!list) return;
1813   ImageList_Remove(list, -1);
1814   delete (WDL_PtrList<HGDIOBJ__>*)list;
1817 int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image)
1819   if (!image || !list) return -1;
1820   WDL_PtrList<HGDIOBJ__> *l=(WDL_PtrList<HGDIOBJ__> *)list;
1822   HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image;
1823   if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1;
1825   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1826   icon->type=TYPE_BITMAP;
1827   icon->wid=1;
1828   icon->bitmapptr = imgsrc->bitmapptr; // no need to duplicate it, can just retain a copy
1829   [icon->bitmapptr retain];
1830   image = (HICON) icon;
1832   if (offset<0||offset>=l->GetSize()) 
1833   {
1834     l->Add(image); 
1835     offset=l->GetSize()-1;
1836   }
1837   else
1838   {
1839     HICON old=l->Get(offset); 
1840     l->Set(offset,image);
1841     if (old) DeleteObject(old);
1842   }
1843   return offset;
1848 #endif