1 /*
2 
3 Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
4                 ("Apple") in consideration of your agreement to the following terms, and your
5                 use, installation, modification or redistribution of this Apple software
6                 constitutes acceptance of these terms.  If you do not agree with these terms,
7                 please do not use, install, modify or redistribute this Apple software.
8 
9                 In consideration of your agreement to abide by the following terms, and subject
10                 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
11                 copyrights in this original Apple software (the "Apple Software"), to use,
12                 reproduce, modify and redistribute the Apple Software, with or without
13                 modifications, in source and/or binary forms; provided that if you redistribute
14                 the Apple Software in its entirety and without modifications, you must retain
15                 this notice and the following text and disclaimers in all such redistributions of
16                 the Apple Software.  Neither the name, trademarks, service marks or logos of
17                 Apple Computer, Inc. may be used to endorse or promote products derived from the
18                 Apple Software without specific prior written permission from Apple.  Except as
19                 expressly stated in this notice, no other rights or licenses, express or implied,
20                 are granted by Apple herein, including but not limited to any patent rights that
21                 may be infringed by your derivative works or by other works in which the Apple
22                 Software may be incorporated.
23 
24                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
25                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28                 COMBINATION WITH YOUR PRODUCTS.
29 
30                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 
38 */
39 
40 // Note that this is modified Apple source code, and not in its entireity (some classes are, some aren't),
41 // but we might as well leave it in here, even though it doesn't sound like we have to.
42 // -bob
43 
44 #include <mach/mach.h>
45 #include <Carbon/Carbon.h>
46 // Celestia's DPRINTF is different than Carbon's
47 #undef DPRINTF
48 #include <celutil/debug.h>
49 
50 class CGFrame : public CGRect
51 {
52 public:
53 
CGFrame(float x0,float y0,float w,float h)54 	CGFrame(float x0, float y0, float w, float h)
55 	{
56 		*this = CGRectMake(x0,y0,w,h);
57 	}
58 
59 	explicit CGFrame(float w = 0, float h = 0)
60 	{
61 		*this = CGRectMake(0,0,w,h);
62 	}
63 
CGFrame(const Rect & rect)64 	CGFrame(const Rect& rect)
65 	{
66 		origin.x = rect.left, size.width = rect.right - rect.left;
67 		origin.y = rect.top, size.height = rect.bottom - rect.top;
68 	}
69 
CGFrame(const CGRect & copy)70 	CGFrame(const CGRect& copy)
71 	{
72 		origin = copy.origin;
73 		size = copy.size;
74 	}
75 
CGFrame(const CGSize & size)76 	CGFrame(const CGSize& size)
77 	{
78 		origin.x = origin.y = 0;
79 		this->size = size;
80 	}
81 
CGFrame(float x,float y,const CGSize & size)82 	CGFrame(float x, float y, const CGSize& size)
83 	{
84 		*this = CGRectMake(x, y, size.width, size.height);
85 	}
86 
CGFrame(const CGPoint & pos,const CGSize & size)87 	CGFrame(const CGPoint& pos, const CGSize& size)
88 	{
89 		*this = CGRectMake(pos.x, pos.y, size.width, size.height);
90 	}
91 
Offset(float dx,float dy)92 	void Offset(float dx, float dy)
93 	{
94 		origin.x += dx, origin.y += dy;
95 	}
96 
Inset(float dx,float dy)97 	void Inset(float dx, float dy)
98 	{
99 		origin.x += dx, origin.y += dy;
100 		size.width -= dx*2, size.height -= dy*2;
101 	}
102 };
103 
104 class MemoryBuffer
105 {
106   size_t size;
107   int ref_count;
108 
MemoryBuffer(char * const data,size_t size)109   MemoryBuffer(char* const data, size_t size) : size(size), ref_count(1), data(data)
110   {
111   }
112 
~MemoryBuffer()113   ~MemoryBuffer()
114   {
115     vm_deallocate((vm_map_t) mach_task_self(), (vm_address_t) data, size);
116   }
117 
118 public:
119 
120   friend class _MemoryBuffer; // to shut the compiler up
121 
122   char* const data;
123 
Retain()124   MemoryBuffer* Retain()
125   {
126     ++ref_count;
127     return this;
128   }
129 
Release()130   void Release()
131   {
132     if (--ref_count == 0)
133       delete this;
134   }
135 
Create(size_t size)136   static MemoryBuffer* Create(size_t size)
137   {
138     char* data;
139     kern_return_t err = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*) &data, size, TRUE);
140 
141     return (err == KERN_SUCCESS) ? new MemoryBuffer(data, size) : NULL;
142   }
143 };
144 
145 class Datafile
146 {
147     int ref_count;
148     FILE* file;
149 
150 public:
151     MemoryBuffer* data_buffer;
152     unsigned long data_size;
153 
Datafile()154     Datafile() : ref_count(1), file(NULL), data_buffer(NULL), data_size(0)
155     {
156     }
157 
~Datafile()158     ~Datafile()
159     {
160         Reset();
161     }
162 
Retain()163     Datafile* Retain()
164     {
165         ++ref_count;
166         return this;
167     }
168 
Release()169     void Release()
170     {
171         if (--ref_count==0)
172             delete this;
173     }
174 
Open(const char * path)175     int Open(const char* path)
176     {
177         file = fopen(path,"r");
178         if (!file) {
179             DPRINTF(0,"Datafile::Open() - Couldn't open %s\n", path);
180             Reset();
181             return 0;
182         }
183         fseek(file, 0, SEEK_END);
184         data_size = ftell(file);
185         data_buffer = MemoryBuffer::Create(data_size);
186         if (!data_buffer) {
187             DPRINTF(0,"Datafile::Open() - Couldn't allocate MemoryBuffer of size %d\n", data_size);
188             Reset();
189             return 0;
190         }
191         //DPRINTF(0,"Datafile::Open() - Successfully opened '%s' %d bytes\n", path, data_size);
192         return 1;
193     }
194 
Read()195     int Read()
196     {
197         if ((file == NULL) || (data_buffer == NULL) || (data_size == 0)) {
198             DPRINTF(0,"Datafile::Read() - No file open, file of zero size, or no valid MemoryBuffer\n");
199             Reset();
200             return 0;
201         }
202 
203         fseek(file, 0, SEEK_SET);
204         if (fread((void*)data_buffer->data, 1, data_size, file) != data_size) {
205             DPRINTF(0,"Datafile::Read() - Didn't read to finish?");
206             Reset();
207             return 0;
208         }
209 
210         //DPRINTF(0,"Datafile::Read() - Successfully read all %d bytes into buffer\n",data_size);
211         return 1;
212     }
213 
Close()214     void Close()
215     {
216         if (file) {
217             fclose(file);
218             file = NULL;
219         }
220     }
221 
Reset()222     void Reset()
223     {
224         Close();
225         if (data_buffer) {
226             data_buffer->Release();
227             data_buffer = NULL;
228         }
229     }
230 };
231 
232 class CGBuffer
233 {
234 	Datafile file;
235 	CGImageRef image_ref;
236 	CGContextRef context_ref;
237 
238 	int ref_count;
239 
Init()240 	void Init()
241 	{
242 		ref_count = 1;
243 		buffer = NULL;
244 		image_ref = NULL;
245 		context_ref = NULL;
246 		image_finished = false;
247 	}
248 
CreateCGContext()249 	bool CreateCGContext()
250 	{
251 		if (context_ref)
252 		{
253 			CGContextRelease(context_ref);
254 			context_ref = NULL;
255 		}
256 
257 		if (buffer)
258 		{
259 			buffer->Release();
260 			buffer = NULL;
261 		}
262 
263 		size_t buffer_rowbytes = (size_t)(image_size.width * ((image_depth == 8) ? 1 : 4)); //CGImageGetBytesPerRow(image_ref);
264 
265 		buffer = MemoryBuffer::Create(buffer_rowbytes * (size_t)image_size.height);
266 
267 		if (!buffer)
268 			return false;
269 
270 		CGColorSpaceRef colorspace_ref = (image_depth == 8) ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
271 
272 		if (!colorspace_ref)
273 			return false;
274 
275 		CGImageAlphaInfo alpha_info = (image_depth == 8) ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast; //kCGImageAlphaLast; //RGBA format
276 
277 		context_ref = CGBitmapContextCreate(buffer->data, (size_t)image_size.width, (size_t)image_size.height, 8, buffer_rowbytes, colorspace_ref, alpha_info);
278 
279 		if (context_ref)
280 		{
281 			CGContextSetFillColorSpace(context_ref, colorspace_ref);
282 			CGContextSetStrokeColorSpace(context_ref, colorspace_ref);
283                         // move down, and flip vertically
284                         // to turn postscript style coordinates to "screen style"
285 			CGContextTranslateCTM(context_ref, 0.0, image_size.height);
286 			CGContextScaleCTM(context_ref, 1.0, -1.0);
287 		}
288 
289 		CGColorSpaceRelease(colorspace_ref);
290 		colorspace_ref = NULL;
291 
292 		return !!context_ref;
293 	}
294 
RenderCGImage(const CGRect & dst_rect)295 	void RenderCGImage(const CGRect& dst_rect)
296 	{
297 		if (!context_ref || !image_ref)
298 			return;
299 
300 		CGContextDrawImage(context_ref, dst_rect, image_ref);
301 	}
302 
303 public:
304 
305 	MemoryBuffer* buffer;
306 	CGSize image_size;
307 	size_t image_depth;
308 	bool image_finished;
309 
CGBuffer(const char * path)310 	CGBuffer(const char* path)
311 	{
312 		Init();
313 		Open(path);
314 	}
315 
~CGBuffer()316         ~CGBuffer()
317 	{
318 		Reset();
319 	}
320 
Retain()321 	CGBuffer* Retain()
322 	{
323 		++ref_count;
324 		return this;
325 	}
326 
Release()327 	void Release()
328 	{
329 		if (--ref_count == 0)
330 			delete this;
331 	}
332 
Open(const char * path)333 	bool Open(const char* path)
334 	{
335 		file.Reset();
336 		return file.Open(path);
337 	}
338 
LoadJPEG()339 	bool LoadJPEG()
340 	{
341 		if (!file.Read())
342 			return false;
343 
344 		file.Close();
345 
346 		CGDataProviderRef src_provider_ref = CGDataProviderCreateWithData(this, file.data_buffer->data, file.data_size, NULL);
347 
348 		if (!src_provider_ref)
349 			return false;
350 
351 		image_ref = CGImageCreateWithJPEGDataProvider(src_provider_ref, NULL, true, kCGRenderingIntentDefault);
352 
353 		CGDataProviderRelease(src_provider_ref);
354 		src_provider_ref = NULL;
355 
356 		if (!image_ref)
357 			return false;
358 
359 		image_size = CGSizeMake(CGImageGetWidth(image_ref), CGImageGetHeight(image_ref));
360 		image_depth = CGImageGetBitsPerPixel(image_ref);
361 
362 		return !!image_ref;
363 	}
364 
Render()365 	bool Render()
366 	{
367 		if (!image_ref)
368 			return false;
369 
370 		if (!CreateCGContext())
371 			return false;
372 
373 		RenderCGImage(CGFrame(image_size));
374 
375 		CGContextRelease(context_ref);
376 		context_ref = NULL;
377 
378 		CGImageRelease(image_ref);
379 		image_ref = NULL;
380 
381 		file.Reset();
382 
383 		return true;
384 	}
385 
Reset()386 	void Reset()
387 	{
388 		if (buffer)
389 		{
390 			buffer->Release();
391 			buffer = NULL;
392 		}
393 
394 		if (image_ref)
395 		{
396 			CGImageRelease(image_ref);
397 			image_ref = NULL;
398 		}
399 
400 		if (context_ref)
401 		{
402 			CGContextRelease(context_ref);
403 			context_ref = NULL;
404 		}
405 	}
406 };