Commit | Line | Data |
---|---|---|
edfda783 | 1 | /* Image support for the NeXT/Open/GNUstep and MacOSX window system. |
acaf905b | 2 | Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2012 |
32d235f8 | 3 | Free Software Foundation, Inc. |
edfda783 AR |
4 | |
5 | This file is part of GNU Emacs. | |
6 | ||
32d235f8 | 7 | GNU Emacs is free software: you can redistribute it and/or modify |
edfda783 | 8 | it under the terms of the GNU General Public License as published by |
32d235f8 GM |
9 | the Free Software Foundation, either version 3 of the License, or |
10 | (at your option) any later version. | |
edfda783 AR |
11 | |
12 | GNU Emacs is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
32d235f8 | 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ |
edfda783 | 19 | |
32d235f8 | 20 | /* |
edfda783 AR |
21 | Originally by Carl Edman |
22 | Updated by Christian Limpach (chris@nice.ch) | |
23 | OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com) | |
24 | MacOSX/Aqua port by Christophe de Dinechin (descubes@earthlink.net) | |
25 | GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) | |
edfda783 AR |
26 | */ |
27 | ||
5a06864f AR |
28 | /* This should be the first include, as it may set up #defines affecting |
29 | interpretation of even the system includes. */ | |
08a494a3 | 30 | #include <config.h> |
5a06864f | 31 | |
edfda783 AR |
32 | #include "lisp.h" |
33 | #include "dispextern.h" | |
34 | #include "nsterm.h" | |
35 | #include "frame.h" | |
36 | ||
37 | extern Lisp_Object QCfile, QCdata; | |
38 | ||
39 | /* call tracing */ | |
40 | #if 0 | |
41 | int image_trace_num = 0; | |
42 | #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \ | |
43 | __FILE__, __LINE__, ++image_trace_num) | |
44 | #else | |
45 | #define NSTRACE(x) | |
46 | #endif | |
47 | ||
48 | ||
49 | /* ========================================================================== | |
50 | ||
51 | C interface. This allows easy calling from C files. We could just | |
52 | compile everything as Objective-C, but that might mean slower | |
53 | compilation and possible difficulties on some platforms.. | |
54 | ||
55 | ========================================================================== */ | |
56 | ||
57 | void * | |
58 | ns_image_from_XBM (unsigned char *bits, int width, int height) | |
59 | { | |
60 | NSTRACE (ns_image_from_XBM); | |
61 | return [[EmacsImage alloc] initFromXBM: bits | |
62 | width: width height: height | |
63 | flip: YES]; | |
64 | } | |
65 | ||
66 | void * | |
67 | ns_image_for_XPM (int width, int height, int depth) | |
68 | { | |
69 | NSTRACE (ns_image_for_XPM); | |
70 | return [[EmacsImage alloc] initForXPMWithDepth: depth | |
71 | width: width height: height]; | |
72 | } | |
73 | ||
74 | void * | |
75 | ns_image_from_file (Lisp_Object file) | |
76 | { | |
77 | NSTRACE (ns_image_from_bitmap_file); | |
78 | return [EmacsImage allocInitFromFile: file]; | |
79 | } | |
80 | ||
578098f3 | 81 | bool |
edfda783 AR |
82 | ns_load_image (struct frame *f, struct image *img, |
83 | Lisp_Object spec_file, Lisp_Object spec_data) | |
84 | { | |
7574650a | 85 | EmacsImage *eImg = nil; |
edfda783 AR |
86 | NSSize size; |
87 | ||
3d608a86 J |
88 | NSTRACE (ns_load_image); |
89 | ||
7574650a | 90 | if (STRINGP (spec_file)) |
edfda783 AR |
91 | { |
92 | eImg = [EmacsImage allocInitFromFile: spec_file]; | |
93 | } | |
7574650a | 94 | else if (STRINGP (spec_data)) |
edfda783 | 95 | { |
7574650a AS |
96 | NSData *data; |
97 | ||
0dc8cf50 | 98 | data = [NSData dataWithBytes: SSDATA (spec_data) |
7574650a | 99 | length: SBYTES (spec_data)]; |
edfda783 AR |
100 | eImg = [[EmacsImage alloc] initWithData: data]; |
101 | [eImg setPixmapData]; | |
102 | } | |
103 | ||
104 | if (eImg == nil) | |
105 | { | |
106 | add_to_log ("Unable to load image %s", img->spec, Qnil); | |
107 | return 0; | |
108 | } | |
109 | ||
110 | size = [eImg size]; | |
111 | img->width = size.width; | |
112 | img->height = size.height; | |
113 | ||
114 | /* 4) set img->pixmap = emacsimage */ | |
115 | img->pixmap = eImg; | |
116 | return 1; | |
117 | } | |
118 | ||
119 | ||
120 | int | |
121 | ns_image_width (void *img) | |
122 | { | |
123 | return [(id)img size].width; | |
124 | } | |
125 | ||
126 | int | |
127 | ns_image_height (void *img) | |
128 | { | |
129 | return [(id)img size].height; | |
130 | } | |
131 | ||
132 | unsigned long | |
133 | ns_get_pixel (void *img, int x, int y) | |
134 | { | |
135 | return [(EmacsImage *)img getPixelAtX: x Y: y]; | |
136 | } | |
137 | ||
138 | void | |
139 | ns_put_pixel (void *img, int x, int y, unsigned long argb) | |
140 | { | |
141 | unsigned char alpha = (argb >> 24) & 0xFF; | |
142 | if (alpha == 0) | |
143 | alpha = 0xFF; | |
144 | [(EmacsImage *)img setPixelAtX: x Y: y toRed: (argb >> 16) & 0xFF | |
145 | green: (argb >> 8) & 0xFF blue: (argb & 0xFF) alpha: alpha]; | |
146 | } | |
147 | ||
148 | void | |
149 | ns_set_alpha (void *img, int x, int y, unsigned char a) | |
150 | { | |
151 | [(EmacsImage *)img setAlphaAtX: x Y: y to: a]; | |
152 | } | |
153 | ||
154 | ||
155 | /* ========================================================================== | |
156 | ||
157 | Class supporting bitmaps and images of various sorts. | |
158 | ||
159 | ========================================================================== */ | |
160 | ||
161 | @implementation EmacsImage | |
162 | ||
163 | static EmacsImage *ImageList = nil; | |
164 | ||
165 | + allocInitFromFile: (Lisp_Object)file | |
166 | { | |
167 | EmacsImage *image = ImageList; | |
4c7077c3 | 168 | NSImageRep *imgRep; |
edfda783 AR |
169 | Lisp_Object found; |
170 | ||
171 | /* look for an existing image of the same name */ | |
172 | while (image != nil && | |
0dc8cf50 | 173 | [[image name] compare: [NSString stringWithUTF8String: SSDATA (file)]] |
edfda783 AR |
174 | != NSOrderedSame) |
175 | image = [image imageListNext]; | |
176 | ||
177 | if (image != nil) | |
178 | { | |
179 | [image reference]; | |
180 | return image; | |
181 | } | |
182 | ||
183 | /* Search bitmap-file-path for the file, if appropriate. */ | |
184 | found = x_find_image_file (file); | |
185 | if (!STRINGP (found)) | |
186 | return nil; | |
187 | ||
188 | image = [[EmacsImage alloc] initByReferencingFile: | |
0dc8cf50 | 189 | [NSString stringWithUTF8String: SSDATA (found)]]; |
edfda783 | 190 | |
4393663b JD |
191 | #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 |
192 | imgRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; | |
193 | #else | |
4c7077c3 | 194 | imgRep = [image bestRepresentationForDevice: nil]; |
4393663b | 195 | #endif |
4c7077c3 | 196 | if (imgRep == nil) |
edfda783 AR |
197 | { |
198 | [image release]; | |
199 | return nil; | |
200 | } | |
201 | ||
4c7077c3 AR |
202 | /* The next two lines cause the DPI of the image to be ignored. |
203 | This seems to be the behavior users expect. */ | |
204 | [image setScalesWhenResized: YES]; | |
205 | [image setSize: NSMakeSize([imgRep pixelsWide], [imgRep pixelsHigh])]; | |
206 | ||
0dc8cf50 | 207 | [image setName: [NSString stringWithUTF8String: SSDATA (file)]]; |
edfda783 AR |
208 | [image reference]; |
209 | ImageList = [image imageListSetNext: ImageList]; | |
210 | ||
211 | return image; | |
212 | } | |
213 | ||
214 | ||
215 | - reference | |
216 | { | |
217 | refCount++; | |
218 | return self; | |
219 | } | |
220 | ||
221 | ||
222 | - imageListSetNext: (id)arg | |
223 | { | |
224 | imageListNext = arg; | |
225 | return self; | |
226 | } | |
227 | ||
228 | ||
229 | - imageListNext | |
230 | { | |
231 | return imageListNext; | |
232 | } | |
233 | ||
234 | ||
235 | - (void)dealloc | |
236 | { | |
237 | id list = ImageList; | |
238 | ||
239 | if (refCount > 1) | |
240 | { | |
241 | refCount--; | |
242 | return; | |
243 | } | |
244 | ||
245 | [stippleMask release]; | |
246 | ||
247 | if (list == self) | |
248 | ImageList = imageListNext; | |
249 | else | |
250 | { | |
251 | while (list != nil && [list imageListNext] != self) | |
252 | list = [list imageListNext]; | |
253 | [list imageListSetNext: imageListNext]; | |
254 | } | |
255 | ||
256 | [super dealloc]; | |
257 | } | |
258 | ||
259 | ||
260 | - initFromXBM: (unsigned char *)bits width: (int)w height: (int)h | |
261 | flip: (BOOL)flip | |
262 | { | |
263 | return [self initFromSkipXBM: bits width: w height: h flip: flip length: 0]; | |
264 | } | |
265 | ||
266 | ||
267 | - initFromSkipXBM: (unsigned char *)bits width: (int)w height: (int)h | |
268 | flip: (BOOL)flip length: (int)length; | |
269 | { | |
270 | int bpr = (w + 7) / 8; | |
271 | unsigned char *planes[5]; | |
272 | ||
273 | [self initWithSize: NSMakeSize (w, h)]; | |
274 | ||
275 | bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL | |
276 | pixelsWide: w pixelsHigh: h | |
277 | bitsPerSample: 8 samplesPerPixel: 4 | |
278 | hasAlpha: YES isPlanar: YES | |
279 | colorSpaceName: NSCalibratedRGBColorSpace | |
280 | bytesPerRow: w bitsPerPixel: 0]; | |
281 | ||
282 | [bmRep getBitmapDataPlanes: planes]; | |
283 | { | |
284 | /* pull bits out to set the (bytewise) alpha mask */ | |
285 | int i, j, k; | |
286 | unsigned char *s = bits; | |
287 | unsigned char *alpha = planes[3]; | |
288 | unsigned char swt[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, | |
289 | 3, 11, 7, 15}; | |
290 | unsigned char c, bitPat; | |
291 | ||
292 | for (j = 0; j < h; j++) | |
293 | for (i = 0; i < bpr; i++) | |
294 | { | |
295 | if (length) | |
296 | { | |
297 | unsigned char s1, s2; | |
298 | while (*s++ != 'x' && s < bits + length); | |
299 | if (s >= bits + length) | |
300 | { | |
301 | [bmRep release]; | |
302 | return nil; | |
303 | } | |
620f13b0 | 304 | #define hexchar(x) ('0' <= (x) && (x) <= '9' ? (x) - '0' : (x) - 'a' + 10) |
edfda783 AR |
305 | s1 = *s++; |
306 | s2 = *s++; | |
307 | c = hexchar (s1) * 0x10 + hexchar (s2); | |
308 | } | |
309 | else | |
310 | c = *s++; | |
311 | ||
312 | bitPat = flip ? swt[c >> 4] | (swt[c & 0xf] << 4) : c ^ 255; | |
313 | for (k =0; k<8; k++) | |
314 | { | |
315 | *alpha++ = (bitPat & 0x80) ? 0xff : 0; | |
316 | bitPat <<= 1; | |
317 | } | |
318 | } | |
319 | } | |
320 | ||
321 | [self addRepresentation: bmRep]; | |
322 | ||
72af86bd AS |
323 | memset (planes[0], 0, w*h); |
324 | memset (planes[1], 0, w*h); | |
325 | memset (planes[2], 0, w*h); | |
edfda783 AR |
326 | [self setXBMColor: [NSColor blackColor]]; |
327 | return self; | |
328 | } | |
329 | ||
330 | ||
331 | /* Set color for a bitmap image (see initFromSkipXBM). Note that the alpha | |
332 | is used as a mask, so we just memset the entire array. */ | |
333 | - setXBMColor: (NSColor *)color | |
334 | { | |
335 | NSSize s = [self size]; | |
edfda783 | 336 | unsigned char *planes[5]; |
31e88bd8 | 337 | CGFloat r, g, b, a; |
edfda783 AR |
338 | NSColor *rgbColor; |
339 | ||
340 | if (bmRep == nil || color == nil) | |
3fdebbf9 | 341 | return self; |
edfda783 AR |
342 | |
343 | if ([color colorSpaceName] != NSCalibratedRGBColorSpace) | |
344 | rgbColor = [color colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; | |
345 | else | |
346 | rgbColor = color; | |
347 | ||
348 | [rgbColor getRed: &r green: &g blue: &b alpha: &a]; | |
349 | ||
350 | [bmRep getBitmapDataPlanes: planes]; | |
351 | ||
352 | /* we used to just do this, but Cocoa seems to have a bug when rendering | |
353 | an alpha-masked image onto a dark background where it bloats the mask */ | |
354 | /* memset (planes[0..2], r, g, b*0xff, len); */ | |
355 | { | |
356 | int i, len = s.width*s.height; | |
357 | int rr = r * 0xff, gg = g * 0xff, bb = b * 0xff; | |
358 | for (i =0; i<len; i++) | |
359 | if (planes[3][i] != 0) | |
360 | { | |
361 | planes[0][i] = rr; | |
362 | planes[1][i] = gg; | |
363 | planes[2][i] = bb; | |
364 | } | |
365 | } | |
3fdebbf9 AR |
366 | |
367 | return self; | |
edfda783 AR |
368 | } |
369 | ||
370 | ||
371 | - initForXPMWithDepth: (int)depth width: (int)width height: (int)height | |
372 | { | |
373 | NSSize s = {width, height}; | |
374 | int i; | |
375 | ||
376 | [self initWithSize: s]; | |
377 | ||
378 | bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL | |
379 | pixelsWide: width pixelsHigh: height | |
380 | /* keep things simple for now */ | |
381 | bitsPerSample: 8 samplesPerPixel: 4 /*RGB+A*/ | |
382 | hasAlpha: YES isPlanar: YES | |
383 | colorSpaceName: NSCalibratedRGBColorSpace | |
384 | bytesPerRow: width bitsPerPixel: 0]; | |
385 | ||
386 | [bmRep getBitmapDataPlanes: pixmapData]; | |
387 | for (i =0; i<4; i++) | |
72af86bd | 388 | memset (pixmapData[i], 0, width*height); |
edfda783 AR |
389 | [self addRepresentation: bmRep]; |
390 | return self; | |
391 | } | |
392 | ||
393 | ||
394 | /* attempt to pull out pixmap data from a BitmapImageRep; returns NO if fails */ | |
395 | - (void) setPixmapData | |
396 | { | |
397 | NSEnumerator *reps; | |
398 | NSImageRep *rep; | |
399 | ||
400 | reps = [[self representations] objectEnumerator]; | |
0dc8cf50 | 401 | while ((rep = (NSImageRep *) [reps nextObject])) |
edfda783 AR |
402 | { |
403 | if ([rep respondsToSelector: @selector (getBitmapDataPlanes:)]) | |
404 | { | |
405 | bmRep = (NSBitmapImageRep *) rep; | |
406 | onTiger = [bmRep respondsToSelector: @selector (colorAtX:y:)]; | |
407 | ||
408 | if ([bmRep numberOfPlanes] >= 3) | |
409 | [bmRep getBitmapDataPlanes: pixmapData]; | |
e0d2e69a AR |
410 | |
411 | /* The next two lines cause the DPI of the image to be ignored. | |
412 | This seems to be the behavior users expect. */ | |
413 | [self setScalesWhenResized: YES]; | |
414 | [self setSize: NSMakeSize([bmRep pixelsWide], [bmRep pixelsHigh])]; | |
415 | ||
edfda783 AR |
416 | break; |
417 | } | |
418 | } | |
419 | } | |
420 | ||
421 | ||
422 | /* note; this and next work only for image created with initForXPMWithDepth, | |
423 | initFromSkipXBM, or where setPixmapData was called successfully */ | |
424 | /* return ARGB */ | |
425 | - (unsigned long) getPixelAtX: (int)x Y: (int)y | |
426 | { | |
427 | if (bmRep == nil) | |
428 | return 0; | |
429 | ||
430 | /* this method is faster but won't work for bitmaps */ | |
431 | if (pixmapData[0] != NULL) | |
432 | { | |
433 | int loc = x + y * [self size].width; | |
434 | return (pixmapData[3][loc] << 24) /* alpha */ | |
435 | | (pixmapData[0][loc] << 16) | (pixmapData[1][loc] << 8) | |
436 | | (pixmapData[2][loc]); | |
437 | } | |
438 | else if (onTiger) | |
439 | { | |
440 | NSColor *color = [bmRep colorAtX: x y: y]; | |
31e88bd8 | 441 | CGFloat r, g, b, a; |
edfda783 AR |
442 | [color getRed: &r green: &g blue: &b alpha: &a]; |
443 | return ((int)(a * 255.0) << 24) | |
444 | | ((int)(r * 255.0) << 16) | ((int)(g * 255.0) << 8) | |
445 | | ((int)(b * 255.0)); | |
446 | ||
447 | } | |
448 | return 0; | |
449 | } | |
450 | ||
451 | - (void) setPixelAtX: (int)x Y: (int)y toRed: (unsigned char)r | |
452 | green: (unsigned char)g blue: (unsigned char)b | |
453 | alpha:(unsigned char)a; | |
454 | { | |
455 | if (bmRep == nil) | |
456 | return; | |
457 | ||
458 | if (pixmapData[0] != NULL) | |
459 | { | |
460 | int loc = x + y * [self size].width; | |
461 | pixmapData[0][loc] = r; | |
462 | pixmapData[1][loc] = g; | |
463 | pixmapData[2][loc] = b; | |
464 | pixmapData[3][loc] = a; | |
465 | } | |
466 | else if (onTiger) | |
467 | { | |
468 | [bmRep setColor: | |
d3810c21 AR |
469 | [NSColor colorWithCalibratedRed: (r/255.0) green: (g/255.0) |
470 | blue: (b/255.0) alpha: (a/255.0)] | |
edfda783 AR |
471 | atX: x y: y]; |
472 | } | |
473 | } | |
474 | ||
475 | - (void) setAlphaAtX: (int) x Y: (int) y to: (unsigned char) a | |
476 | { | |
477 | if (bmRep == nil) | |
478 | return; | |
479 | ||
480 | if (pixmapData[0] != NULL) | |
481 | { | |
482 | int loc = x + y * [self size].width; | |
483 | ||
484 | pixmapData[3][loc] = a; | |
485 | } | |
486 | else if (onTiger) | |
487 | { | |
488 | NSColor *color = [bmRep colorAtX: x y: y]; | |
489 | color = [color colorWithAlphaComponent: (a / 255.0)]; | |
490 | [bmRep setColor: color atX: x y: y]; | |
491 | } | |
492 | } | |
493 | ||
494 | /* returns a pattern color, which is cached here */ | |
495 | - (NSColor *)stippleMask | |
496 | { | |
497 | if (stippleMask == nil) | |
498 | stippleMask = [[NSColor colorWithPatternImage: self] retain]; | |
499 | return stippleMask; | |
500 | } | |
501 | ||
502 | @end |