1 /*****************************************************************************\
2      Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3                 This file is licensed under the Snes9x License.
4    For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6 
7 /***********************************************************************************
8   SNES9X for Mac OS (c) Copyright John Stiles
9 
10   Snes9x for Mac OS X
11 
12   (c) Copyright 2001 - 2011  zones
13   (c) Copyright 2002 - 2005  107
14   (c) Copyright 2002         PB1400c
15   (c) Copyright 2004         Alexander and Sander
16   (c) Copyright 2004 - 2005  Steven Seeger
17   (c) Copyright 2005         Ryan Vogt
18  ***********************************************************************************/
19 
20 
21 #include "snes9x.h"
22 #include "memmap.h"
23 #include "screenshot.h"
24 
25 #include <QuickTime/QuickTime.h>
26 
27 #include "mac-prefix.h"
28 #include "mac-file.h"
29 #include "mac-gworld.h"
30 #include "mac-os.h"
31 #include "mac-render.h"
32 #include "mac-screenshot.h"
33 
34 static Handle GetScreenAsRawHandle (int, int);
35 static void ExportCGImageToPNGFile (CGImageRef, const char *);
36 
37 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
38 typedef struct QDPict* QDPictRef;
39 extern "C" QDPictRef QDPictCreateWithProvider (CGDataProviderRef provider);
40 extern "C" void QDPictRelease (QDPictRef pictRef);
41 extern "C" OSStatus QDPictDrawToCGContext (CGContextRef ctx, CGRect rect, QDPictRef pictRef);
42 #endif
43 
GetScreenAsRawHandle(int destWidth,int destHeight)44 static Handle GetScreenAsRawHandle (int destWidth, int destHeight)
45 {
46 	CGContextRef	ctx;
47 	CGColorSpaceRef	color;
48 	CGImageRef		image;
49 	Handle			data = NULL;
50 
51 	image = CreateGameScreenCGImage();
52 	if (image)
53 	{
54 		data = NewHandleClear(destWidth * destHeight * 2);
55 		if (data)
56 		{
57 			HLock(data);
58 
59 			color = CGColorSpaceCreateDeviceRGB();
60 			if (color)
61 			{
62 				ctx = CGBitmapContextCreate(*data, destWidth, destHeight, 5, destWidth * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Big : 0));
63 				if (ctx)
64 				{
65 					CGContextDrawImage(ctx, CGRectMake(0.0f, 0.0f, (float) destWidth, (float) destHeight), image);
66 					CGContextRelease(ctx);
67 				}
68 
69 				CGColorSpaceRelease(color);
70 			}
71 
72 			HUnlock(data);
73 		}
74 
75 		CGImageRelease(image);
76 	}
77 
78 	return (data);
79 }
80 
WriteThumbnailToResourceFork(FSRef * ref,int destWidth,int destHeight)81 void WriteThumbnailToResourceFork (FSRef *ref, int destWidth, int destHeight)
82 {
83 	OSStatus		err;
84 	HFSUniStr255	fork;
85 	SInt16			resf;
86 
87 	err = FSGetResourceForkName(&fork);
88 	if (err == noErr)
89 	{
90 		err = FSCreateResourceFork(ref, fork.length, fork.unicode, 0);
91 		if ((err == noErr) || (err == errFSForkExists))
92 		{
93 			err = FSOpenResourceFile(ref, fork.length, fork.unicode, fsWrPerm, &resf);
94 			if (err == noErr)
95 			{
96 				Handle	pict;
97 
98 				pict = GetScreenAsRawHandle(destWidth, destHeight);
99 				if (pict)
100 				{
101 					AddResource(pict, 'Thum', 128, "\p");
102 					WriteResource(pict);
103 					ReleaseResource(pict);
104 				}
105 
106 				CloseResFile(resf);
107 			}
108 		}
109 	}
110 }
111 
ExportCGImageToPNGFile(CGImageRef image,const char * path)112 static void ExportCGImageToPNGFile (CGImageRef image, const char *path)
113 {
114 	OSStatus				err;
115 	GraphicsExportComponent	exporter;
116 	CFStringRef				str;
117 	CFURLRef				url;
118 	Handle					dataRef;
119 	OSType					dataRefType;
120 
121 	str = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
122 	if (str)
123 	{
124 		url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
125 		if (url)
126 		{
127 			err = QTNewDataReferenceFromCFURL(url, 0, &dataRef, &dataRefType);
128 			if (err == noErr)
129 			{
130 				err = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, &exporter);
131 				if (err == noErr)
132 				{
133 					err = GraphicsExportSetInputCGImage(exporter, image);
134 					if (err == noErr)
135 					{
136 						err = GraphicsExportSetOutputDataReference(exporter, dataRef, dataRefType);
137 						if (err == noErr)
138 							err = GraphicsExportDoExport(exporter, NULL);
139 					}
140 
141 					CloseComponent(exporter);
142 				}
143 
144 				DisposeHandle(dataRef);
145 			}
146 
147 			CFRelease(url);
148 		}
149 
150 		CFRelease(str);
151 	}
152 }
153 
CreateGameScreenCGImage(void)154 CGImageRef CreateGameScreenCGImage (void)
155 {
156 	CGDataProviderRef	prov;
157 	CGColorSpaceRef		color;
158 	CGImageRef			image = NULL;
159 	int					rowbytes;
160 
161 	rowbytes = IPPU.RenderedScreenWidth * 2;
162 
163 	prov = CGDataProviderCreateWithData(NULL, GFX.Screen, 512 * 2 * 478, NULL);
164 	if (prov)
165 	{
166 		color = CGColorSpaceCreateDeviceRGB();
167 		if (color)
168 		{
169 			image = CGImageCreate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, 5, 16, rowbytes, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0), prov, NULL, 1, kCGRenderingIntentDefault);
170 			CGColorSpaceRelease(color);
171 		}
172 
173 		CGDataProviderRelease(prov);
174 	}
175 
176 	return (image);
177 }
178 
CreateBlitScreenCGImage(int width,int height,int rowbytes,uint8 * buffer)179 CGImageRef CreateBlitScreenCGImage (int width, int height, int rowbytes, uint8 *buffer)
180 {
181 	CGDataProviderRef	prov;
182 	CGColorSpaceRef		color;
183 	CGImageRef			image = NULL;
184 
185 	prov = CGDataProviderCreateWithData(NULL, buffer, rowbytes * height, NULL);
186 	if (prov)
187 	{
188 		color = CGColorSpaceCreateDeviceRGB();
189 		if (color)
190 		{
191 			image = CGImageCreate(width, height, 5, 16, rowbytes, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0), prov, NULL, 1, kCGRenderingIntentDefault);
192 			CGColorSpaceRelease(color);
193 		}
194 
195 		CGDataProviderRelease(prov);
196 	}
197 
198 	return (image);
199 }
200 
DrawThumbnailResource(FSRef * ref,CGContextRef ctx,CGRect bounds)201 void DrawThumbnailResource (FSRef *ref, CGContextRef ctx, CGRect bounds)
202 {
203 	OSStatus			err;
204 	CGDataProviderRef	prov;
205 	CGColorSpaceRef		color;
206 	CGImageRef			image;
207 	QDPictRef			qdpr;
208 	Handle				pict;
209 	HFSUniStr255		fork;
210 	SInt16				resf;
211 	Size				size;
212 
213 	CGContextSaveGState(ctx);
214 
215 	CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
216 	CGContextFillRect(ctx, bounds);
217 
218 	err = FSGetResourceForkName(&fork);
219 	if (err == noErr)
220 	{
221 		err = FSOpenResourceFile(ref, fork.length, fork.unicode, fsRdPerm, &resf);
222 		if (err == noErr)
223 		{
224 			pict = Get1Resource('PICT', 128);
225 			if (pict)
226 			{
227 				HLock(pict);
228 
229 				size = GetHandleSize(pict);
230 				prov = CGDataProviderCreateWithData(NULL, (void *) *pict, size, NULL);
231 				if (prov)
232 				{
233 					qdpr = QDPictCreateWithProvider(prov);
234 					if (qdpr)
235 					{
236 						QDPictDrawToCGContext(ctx, bounds, qdpr);
237 						QDPictRelease(qdpr);
238 					}
239 
240 					CGDataProviderRelease(prov);
241 				}
242 
243 				HUnlock(pict);
244 				ReleaseResource(pict);
245 			}
246 			else
247 			{
248 				pict = Get1Resource('Thum', 128);
249 				if (pict)
250 				{
251 					HLock(pict);
252 
253 					size = GetHandleSize(pict);
254 					prov = CGDataProviderCreateWithData(NULL, (void *) *pict, size, NULL);
255 					if (prov)
256 					{
257 						color = CGColorSpaceCreateDeviceRGB();
258 						if (color)
259 						{
260 							image = CGImageCreate(128, 120, 5, 16, 256, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Big : 0), prov, NULL, 0, kCGRenderingIntentDefault);
261 							if (image)
262 							{
263 								CGContextDrawImage(ctx, bounds, image);
264 								CGImageRelease(image);
265 							}
266 
267 							CGColorSpaceRelease(color);
268 						}
269 
270 						CGDataProviderRelease(prov);
271 					}
272 
273 					HUnlock(pict);
274 					ReleaseResource(pict);
275 				}
276 			}
277 
278 			CloseResFile(resf);
279 		}
280 	}
281 
282 	CGContextRestoreGState(ctx);
283 }
284 
S9xDoScreenshot(int width,int height)285 bool8 S9xDoScreenshot (int width, int height)
286 {
287 	Settings.TakeScreenshot = false;
288 
289 	uint16	*data;
290 
291 	data = (uint16 *) malloc(512 * 478 * 2);
292 	if (data)
293 	{
294 		uint16	*sp, *dp;
295 
296 		if (width > 256 && height > 239)
297 		{
298 			for (int y = 0; y < height; y++)
299 			{
300 				sp = GFX.Screen + y * GFX.RealPPL;
301 				dp = data + y * 512;
302 
303 				for (int x = 0; x < width; x++)
304 					*dp++ = *sp++;
305 			}
306 		}
307 		else
308 		if (width > 256)
309 		{
310 			for (int y = 0; y < height; y++)
311 			{
312 				sp = GFX.Screen + y * GFX.RealPPL;
313 				dp = data + y * 2 * 512;
314 
315 				for (int x = 0; x < width; x++)
316 				{
317 					*dp = *(dp + 512) = *sp++;
318 					dp++;
319 				}
320 			}
321 		}
322 		else
323 		{
324 			for (int y = 0; y < height; y++)
325 			{
326 				sp = GFX.Screen + y * GFX.RealPPL;
327 				dp = data + y * 2 * 512;
328 
329 				for (int x = 0; x < width; x++)
330 				{
331 					*dp = *(dp + 1) = *(dp + 512) = *(dp + 512 + 1) = *sp++;
332 					dp += 2;
333 				}
334 			}
335 		}
336 
337 		CGImageRef	image;
338 
339 		image = CreateBlitScreenCGImage(512, (height > 239) ? height : (height * 2), 1024, (uint8 *) data);
340 		if (image)
341 		{
342 			ExportCGImageToPNGFile(image, S9xGetPNGFilename());
343 			CGImageRelease(image);
344 		}
345 
346 		free(data);
347 	}
348 
349 	return (true);
350 }
351