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