mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-29 08:31:35 -08:00
Use bignums when Emacs converts to and from system types like off_t for file sizes whose values can exceed fixnum range. Formerly, Emacs sometimes generted floats and sometimes ad-hoc conses of integers. Emacs still accepts floats and conses for these system types, in case some stray Lisp code is generating them, though this usage is obsolescent. * doc/lispref/files.texi (File Attributes): * doc/lispref/hash.texi (Defining Hash): * doc/lispref/nonascii.texi (Character Sets): * doc/lispref/os.texi (User Identification): * doc/lispref/processes.texi (System Processes): * etc/NEWS: Document changes. * src/bignum.c (mpz_set_uintmax, make_biguint) (mpz_set_uintmax_slow, bignum_to_intmax, bignum_to_uintmax): New functions. (mpz_set_intmax_slow): Implement via mpz_limbs_write, to avoid the need for an extra pass through a negative number. * src/charset.c (Fencode_char): * src/composite.h (LGLYPH_SET_CODE): * src/dired.c (file_attributes): * src/dosfns.c, src/w32.c (list_system_processes) (system_process_attributes): * src/editfns.c (init_editfns, Fuser_uid, Fuser_real_uid) (Fgroup_gid, Fgroup_real_gid, Femacs_pid): * src/emacs-module.c (check_vec_index): * src/fns.c (Fsafe_length): * src/process.c (record_deleted_pid, Fprocess_id): * src/sysdep.c (list_system_processes, system_process_attributes): * src/xselect.c (x_own_selection, selection_data_to_lisp_data): * src/xterm.c (set_wm_state): * src/inotify.c (inotifyevent_to_event, add_watch) (inotify_callback): If an integer is out of fixnum range, use a bignum instead of converting it to a float or a cons of integers. * src/coding.c (Fdefine_coding_system_internal): * src/frame.c (frame_windows_min_size) (x_set_frame_parameters): * src/fringe.c (Fdefine_fringe_bitmap): * src/nsterm.m (mouseDown:): * src/syntax.c (find_defun_start): * src/w32fns.c (x_set_undecorated, w32_createwindow) (w32_wnd_proc, Fx_create_frame, Fx_show_tip) (w32_console_toggle_lock_key): * src/w32inevt.c (key_event): * src/w32proc.c (Fw32_get_locale_info): Do not mishandle floats by treating their addresses as their values. * src/data.c (store_symval_forwarding): * src/gnutls.c (Fgnutls_error_fatalp, Fgnutls_error_string): * src/keyboard.c (command_loop_1, make_lispy_event): * src/lread.c (read_filtered_event, read1) (substitute_object_recurse): * src/window.c (Fcoordinates_in_window_p, Fwindow_at) (window_resize_apply, Fset_window_vscroll): * src/xdisp.c (handle_single_display_spec, try_scrolling) (redisplay_window, calc_pixel_width_or_height) (calc_line_height_property, on_hot_spot_p): * src/xfaces.c (check_lface_attrs): * src/xselect.c (x_get_local_selection, cons_to_x_long) (lisp_data_to_selection_data, clean_local_selection_data) (x_check_property_data, x_fill_property_data): (x_send_client_event): Do not reject bignums. * src/data.c (INTBIG_TO_LISP, intbig_to_lisp) (uintbig_to_lisp): Remove. All uses removed. * src/data.c (cons_to_unsigned, cons_to_signed): * src/dbusbind.c (xd_signature, xd_extract_signed) (xd_extract_unsigned): * src/dispnew.c (sit_for): * src/dosfns.c, src/w32.c (system_process_attributes): * src/editfns.c (Fuser_full_name): * src/fileio.c (file_offset): * src/fileio.c (write_region): * src/font.c (font_unparse_xlfd, font_open_for_lface, Fopen_font): * src/frame.c (x_set_screen_gamma): * src/frame.h (NUMVAL, FRAME_PIXEL_X_FROM_CANON_X) (FRAME_PIXEL_Y_FROM_CANON_Y): * src/image.c (parse_image_spec, x_edge_detection) (compute_image_size): * src/json.c (json_to_lisp): * src/lcms.c (PARSE_LAB_LIST_FIELD, Flcms_cie_de2000) (PARSE_XYZ_LIST_FIELD, PARSE_JCH_LIST_FIELD) (PARSE_JAB_LIST_FIELD, PARSE_VIEW_CONDITION_FLOAT) (Flcms_temp_to_white_point): * src/nsimage.m (ns_load_image, setSizeFromSpec): * src/process.c (Fsignal_process, handle_child_signal): * src/sysdep.c (system_process_attributes): * src/xdisp.c (calc_line_height_property): Handle bignums. * src/data.c (Fnumber_to_string): Use proper predicate name in signal if the argument is not a number. * src/lisp.h (make_uint): New function. (INT_TO_INTEGER): New macro. (FIXED_OR_FLOATP, CHECK_FIXNUM_OR_FLOAT) (CHECK_FIXNUM_OR_FLOAT_COERCE_MARKER, INTEGER_TO_CONS) (make_fixnum_or_float): Remove; no longer used. * src/nsfns.m, src/w32fns.c, src/xfns.c (Fx_create_frame): Reject floating-point min-width or min-height. * src/process.c (handle_child_signal): Do not worry about floating-point pids, as they are no longer generated.
625 lines
16 KiB
Objective-C
625 lines
16 KiB
Objective-C
/* Image support for the NeXT/Open/GNUstep and macOS window system.
|
|
Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2018 Free Software
|
|
Foundation, Inc.
|
|
|
|
This file is part of GNU Emacs.
|
|
|
|
GNU Emacs is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
GNU Emacs is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/*
|
|
Originally by Carl Edman
|
|
Updated by Christian Limpach (chris@nice.ch)
|
|
OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
|
|
macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
|
|
GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
|
|
*/
|
|
|
|
/* This should be the first include, as it may set up #defines affecting
|
|
interpretation of even the system includes. */
|
|
#include <config.h>
|
|
|
|
#include "lisp.h"
|
|
#include "dispextern.h"
|
|
#include "nsterm.h"
|
|
#include "frame.h"
|
|
#include "coding.h"
|
|
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
C interface. This allows easy calling from C files. We could just
|
|
compile everything as Objective-C, but that might mean slower
|
|
compilation and possible difficulties on some platforms.
|
|
|
|
========================================================================== */
|
|
|
|
void *
|
|
ns_image_from_XBM (char *bits, int width, int height,
|
|
unsigned long fg, unsigned long bg)
|
|
{
|
|
NSTRACE ("ns_image_from_XBM");
|
|
return [[EmacsImage alloc] initFromXBM: (unsigned char *) bits
|
|
width: width height: height
|
|
fg: fg bg: bg];
|
|
}
|
|
|
|
void *
|
|
ns_image_for_XPM (int width, int height, int depth)
|
|
{
|
|
NSTRACE ("ns_image_for_XPM");
|
|
return [[EmacsImage alloc] initForXPMWithDepth: depth
|
|
width: width height: height];
|
|
}
|
|
|
|
void *
|
|
ns_image_from_file (Lisp_Object file)
|
|
{
|
|
NSTRACE ("ns_image_from_file");
|
|
return [EmacsImage allocInitFromFile: file];
|
|
}
|
|
|
|
bool
|
|
ns_load_image (struct frame *f, struct image *img,
|
|
Lisp_Object spec_file, Lisp_Object spec_data)
|
|
{
|
|
EmacsImage *eImg = nil;
|
|
NSSize size;
|
|
Lisp_Object lisp_index, lisp_rotation;
|
|
unsigned int index;
|
|
double rotation;
|
|
|
|
NSTRACE ("ns_load_image");
|
|
|
|
eassert (valid_image_p (img->spec));
|
|
|
|
lisp_index = Fplist_get (XCDR (img->spec), QCindex);
|
|
index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0;
|
|
|
|
lisp_rotation = Fplist_get (XCDR (img->spec), QCrotation);
|
|
rotation = NUMBERP (lisp_rotation) ? XFLOATINT (lisp_rotation) : 0;
|
|
|
|
if (STRINGP (spec_file))
|
|
{
|
|
eImg = [EmacsImage allocInitFromFile: spec_file];
|
|
}
|
|
else if (STRINGP (spec_data))
|
|
{
|
|
NSData *data;
|
|
|
|
data = [NSData dataWithBytes: SSDATA (spec_data)
|
|
length: SBYTES (spec_data)];
|
|
eImg = [[EmacsImage alloc] initWithData: data];
|
|
[eImg setPixmapData];
|
|
}
|
|
|
|
if (eImg == nil)
|
|
{
|
|
add_to_log ("Unable to load image %s", img->spec);
|
|
return 0;
|
|
}
|
|
|
|
if (![eImg setFrame: index])
|
|
{
|
|
add_to_log ("Unable to set index %d for image %s",
|
|
make_fixnum (index), img->spec);
|
|
return 0;
|
|
}
|
|
|
|
img->lisp_data = [eImg getMetadata];
|
|
|
|
if (rotation != 0)
|
|
{
|
|
EmacsImage *temp = [eImg rotate:rotation];
|
|
[eImg release];
|
|
eImg = temp;
|
|
}
|
|
|
|
[eImg setSizeFromSpec:XCDR (img->spec)];
|
|
|
|
size = [eImg size];
|
|
img->width = size.width;
|
|
img->height = size.height;
|
|
|
|
/* 4) set img->pixmap = emacsimage */
|
|
img->pixmap = eImg;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
ns_image_width (void *img)
|
|
{
|
|
return [(id)img size].width;
|
|
}
|
|
|
|
int
|
|
ns_image_height (void *img)
|
|
{
|
|
return [(id)img size].height;
|
|
}
|
|
|
|
unsigned long
|
|
ns_get_pixel (void *img, int x, int y)
|
|
{
|
|
return [(EmacsImage *)img getPixelAtX: x Y: y];
|
|
}
|
|
|
|
void
|
|
ns_put_pixel (void *img, int x, int y, unsigned long argb)
|
|
{
|
|
unsigned char alpha = (argb >> 24) & 0xFF;
|
|
if (alpha == 0)
|
|
alpha = 0xFF;
|
|
[(EmacsImage *)img setPixelAtX: x Y: y toRed: (argb >> 16) & 0xFF
|
|
green: (argb >> 8) & 0xFF blue: (argb & 0xFF) alpha: alpha];
|
|
}
|
|
|
|
void
|
|
ns_set_alpha (void *img, int x, int y, unsigned char a)
|
|
{
|
|
[(EmacsImage *)img setAlphaAtX: x Y: y to: a];
|
|
}
|
|
|
|
|
|
/* ==========================================================================
|
|
|
|
Class supporting bitmaps and images of various sorts.
|
|
|
|
========================================================================== */
|
|
|
|
@implementation EmacsImage
|
|
|
|
+ (instancetype)allocInitFromFile: (Lisp_Object)file
|
|
{
|
|
NSImageRep *imgRep;
|
|
Lisp_Object found;
|
|
EmacsImage *image;
|
|
|
|
/* Search bitmap-file-path for the file, if appropriate. */
|
|
found = x_find_image_file (file);
|
|
if (!STRINGP (found))
|
|
return nil;
|
|
found = ENCODE_FILE (found);
|
|
|
|
image = [[EmacsImage alloc] initByReferencingFile:
|
|
[NSString stringWithUTF8String: SSDATA (found)]];
|
|
|
|
image->bmRep = nil;
|
|
#ifdef NS_IMPL_COCOA
|
|
imgRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
|
|
#else
|
|
imgRep = [image bestRepresentationForDevice: nil];
|
|
#endif
|
|
if (imgRep == nil)
|
|
{
|
|
[image release];
|
|
return nil;
|
|
}
|
|
|
|
[image setSize: NSMakeSize([imgRep pixelsWide], [imgRep pixelsHigh])];
|
|
|
|
[image setName: [NSString stringWithUTF8String: SSDATA (file)]];
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
- (void)dealloc
|
|
{
|
|
[stippleMask release];
|
|
[bmRep release];
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
/* Create image from monochrome bitmap. If both FG and BG are 0
|
|
(black), set the background to white and make it transparent. */
|
|
- (instancetype)initFromXBM: (unsigned char *)bits width: (int)w height: (int)h
|
|
fg: (unsigned long)fg bg: (unsigned long)bg
|
|
{
|
|
unsigned char *planes[5];
|
|
unsigned char bg_alpha = 0xff;
|
|
|
|
[self initWithSize: NSMakeSize (w, h)];
|
|
|
|
bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
|
|
pixelsWide: w pixelsHigh: h
|
|
bitsPerSample: 8 samplesPerPixel: 4
|
|
hasAlpha: YES isPlanar: YES
|
|
colorSpaceName: NSCalibratedRGBColorSpace
|
|
bytesPerRow: w bitsPerPixel: 0];
|
|
|
|
[bmRep getBitmapDataPlanes: planes];
|
|
|
|
if (fg == 0 && bg == 0)
|
|
{
|
|
bg = 0xffffff;
|
|
bg_alpha = 0;
|
|
}
|
|
|
|
{
|
|
/* Pull bits out to set the (bytewise) alpha mask. */
|
|
int i, j, k;
|
|
unsigned char *s = bits;
|
|
unsigned char *rr = planes[0];
|
|
unsigned char *gg = planes[1];
|
|
unsigned char *bb = planes[2];
|
|
unsigned char *alpha = planes[3];
|
|
unsigned char fgr = (fg >> 16) & 0xff;
|
|
unsigned char fgg = (fg >> 8) & 0xff;
|
|
unsigned char fgb = fg & 0xff;
|
|
unsigned char bgr = (bg >> 16) & 0xff;
|
|
unsigned char bgg = (bg >> 8) & 0xff;
|
|
unsigned char bgb = bg & 0xff;
|
|
unsigned char c;
|
|
|
|
int idx = 0;
|
|
for (j = 0; j < h; ++j)
|
|
for (i = 0; i < w; )
|
|
{
|
|
c = *s++;
|
|
for (k = 0; i < w && k < 8; ++k, ++i)
|
|
{
|
|
if (c & 0x80)
|
|
{
|
|
*rr++ = fgr;
|
|
*gg++ = fgg;
|
|
*bb++ = fgb;
|
|
*alpha++ = 0xff;
|
|
}
|
|
else
|
|
{
|
|
*rr++ = bgr;
|
|
*gg++ = bgg;
|
|
*bb++ = bgb;
|
|
*alpha++ = bg_alpha;
|
|
}
|
|
idx++;
|
|
c <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
xbm_fg = fg;
|
|
[self addRepresentation: bmRep];
|
|
return self;
|
|
}
|
|
|
|
/* Set color for a bitmap image. */
|
|
- (instancetype)setXBMColor: (NSColor *)color
|
|
{
|
|
NSSize s = [self size];
|
|
unsigned char *planes[5];
|
|
EmacsCGFloat r, g, b, a;
|
|
NSColor *rgbColor;
|
|
|
|
if (bmRep == nil || color == nil)
|
|
return self;
|
|
|
|
if ([color colorSpaceName] != NSCalibratedRGBColorSpace)
|
|
rgbColor = [color colorUsingColorSpaceName: NSCalibratedRGBColorSpace];
|
|
else
|
|
rgbColor = color;
|
|
|
|
[rgbColor getRed: &r green: &g blue: &b alpha: &a];
|
|
|
|
[bmRep getBitmapDataPlanes: planes];
|
|
|
|
{
|
|
int i, len = s.width*s.height;
|
|
int rr = r * 0xff, gg = g * 0xff, bb = b * 0xff;
|
|
unsigned char fgr = (xbm_fg >> 16) & 0xff;
|
|
unsigned char fgg = (xbm_fg >> 8) & 0xff;
|
|
unsigned char fgb = xbm_fg & 0xff;
|
|
|
|
for (i = 0; i < len; ++i)
|
|
if (planes[0][i] == fgr && planes[1][i] == fgg && planes[2][i] == fgb)
|
|
{
|
|
planes[0][i] = rr;
|
|
planes[1][i] = gg;
|
|
planes[2][i] = bb;
|
|
}
|
|
xbm_fg = ((rr << 16) & 0xff0000) + ((gg << 8) & 0xff00) + (bb & 0xff);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
- (instancetype)initForXPMWithDepth: (int)depth width: (int)width height: (int)height
|
|
{
|
|
NSSize s = {width, height};
|
|
int i;
|
|
|
|
[self initWithSize: s];
|
|
|
|
bmRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
|
|
pixelsWide: width pixelsHigh: height
|
|
/* keep things simple for now */
|
|
bitsPerSample: 8 samplesPerPixel: 4 /*RGB+A*/
|
|
hasAlpha: YES isPlanar: YES
|
|
colorSpaceName: NSCalibratedRGBColorSpace
|
|
bytesPerRow: width bitsPerPixel: 0];
|
|
|
|
[bmRep getBitmapDataPlanes: pixmapData];
|
|
for (i =0; i<4; i++)
|
|
memset (pixmapData[i], 0, width*height);
|
|
[self addRepresentation: bmRep];
|
|
return self;
|
|
}
|
|
|
|
|
|
/* Attempt to pull out pixmap data from a BitmapImageRep; returns NO if fails. */
|
|
- (void) setPixmapData
|
|
{
|
|
NSEnumerator *reps;
|
|
NSImageRep *rep;
|
|
|
|
reps = [[self representations] objectEnumerator];
|
|
while ((rep = (NSImageRep *) [reps nextObject]))
|
|
{
|
|
if ([rep respondsToSelector: @selector (getBitmapDataPlanes:)])
|
|
{
|
|
NSBitmapImageRep *bmr = (NSBitmapImageRep *) rep;
|
|
|
|
if ([bmr numberOfPlanes] >= 3)
|
|
[bmr getBitmapDataPlanes: pixmapData];
|
|
|
|
[self setSize: NSMakeSize([bmr pixelsWide], [bmr pixelsHigh])];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Note: this and next work only for image created with initForXPMWithDepth,
|
|
initFromSkipXBM, or where setPixmapData was called successfully. */
|
|
/* return ARGB */
|
|
- (unsigned long) getPixelAtX: (int)x Y: (int)y
|
|
{
|
|
if (bmRep == nil)
|
|
return 0;
|
|
|
|
/* This method is faster but won't work for bitmaps. */
|
|
if (pixmapData[0] != NULL)
|
|
{
|
|
int loc = x + y * [self size].width;
|
|
return (pixmapData[3][loc] << 24) /* alpha */
|
|
| (pixmapData[0][loc] << 16) | (pixmapData[1][loc] << 8)
|
|
| (pixmapData[2][loc]);
|
|
}
|
|
else
|
|
{
|
|
NSColor *color = [bmRep colorAtX: x y: y];
|
|
EmacsCGFloat r, g, b, a;
|
|
[color getRed: &r green: &g blue: &b alpha: &a];
|
|
return ((int)(a * 255.0) << 24)
|
|
| ((int)(r * 255.0) << 16) | ((int)(g * 255.0) << 8)
|
|
| ((int)(b * 255.0));
|
|
|
|
}
|
|
}
|
|
|
|
- (void) setPixelAtX: (int)x Y: (int)y toRed: (unsigned char)r
|
|
green: (unsigned char)g blue: (unsigned char)b
|
|
alpha:(unsigned char)a
|
|
{
|
|
if (bmRep == nil)
|
|
return;
|
|
|
|
if (pixmapData[0] != NULL)
|
|
{
|
|
int loc = x + y * [self size].width;
|
|
pixmapData[0][loc] = r;
|
|
pixmapData[1][loc] = g;
|
|
pixmapData[2][loc] = b;
|
|
pixmapData[3][loc] = a;
|
|
}
|
|
else
|
|
{
|
|
[bmRep setColor:
|
|
[NSColor colorWithCalibratedRed: (r/255.0) green: (g/255.0)
|
|
blue: (b/255.0) alpha: (a/255.0)]
|
|
atX: x y: y];
|
|
}
|
|
}
|
|
|
|
- (void) setAlphaAtX: (int) x Y: (int) y to: (unsigned char) a
|
|
{
|
|
if (bmRep == nil)
|
|
return;
|
|
|
|
if (pixmapData[0] != NULL)
|
|
{
|
|
int loc = x + y * [self size].width;
|
|
|
|
pixmapData[3][loc] = a;
|
|
}
|
|
else
|
|
{
|
|
NSColor *color = [bmRep colorAtX: x y: y];
|
|
color = [color colorWithAlphaComponent: (a / 255.0)];
|
|
[bmRep setColor: color atX: x y: y];
|
|
}
|
|
}
|
|
|
|
/* Returns a pattern color, which is cached here. */
|
|
- (NSColor *)stippleMask
|
|
{
|
|
if (stippleMask == nil)
|
|
stippleMask = [[NSColor colorWithPatternImage: self] retain];
|
|
return stippleMask;
|
|
}
|
|
|
|
/* Find the first NSBitmapImageRep which has multiple frames. */
|
|
- (NSBitmapImageRep *)getAnimatedBitmapImageRep
|
|
{
|
|
for (NSImageRep * r in [self representations])
|
|
{
|
|
if ([r isKindOfClass:[NSBitmapImageRep class]])
|
|
{
|
|
NSBitmapImageRep * bm = (NSBitmapImageRep *)r;
|
|
if ([[bm valueForProperty:NSImageFrameCount] intValue] > 0)
|
|
return bm;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
/* If the image has multiple frames, get a count of them and the
|
|
animation delay, if available. */
|
|
- (Lisp_Object)getMetadata
|
|
{
|
|
Lisp_Object metadata = Qnil;
|
|
|
|
NSBitmapImageRep * bm = [self getAnimatedBitmapImageRep];
|
|
|
|
if (bm != nil)
|
|
{
|
|
int frames = [[bm valueForProperty:NSImageFrameCount] intValue];
|
|
float delay = [[bm valueForProperty:NSImageCurrentFrameDuration]
|
|
floatValue];
|
|
|
|
if (frames > 1)
|
|
metadata = Fcons (Qcount, Fcons (make_fixnum (frames), metadata));
|
|
if (delay > 0)
|
|
metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata));
|
|
}
|
|
return metadata;
|
|
}
|
|
|
|
/* Attempt to set the animation frame to be displayed. */
|
|
- (BOOL)setFrame: (unsigned int) index
|
|
{
|
|
NSBitmapImageRep * bm = [self getAnimatedBitmapImageRep];
|
|
|
|
if (bm != nil)
|
|
{
|
|
int frames = [[bm valueForProperty:NSImageFrameCount] intValue];
|
|
|
|
/* If index is invalid, give up. */
|
|
if (index < 0 || index > frames)
|
|
return NO;
|
|
|
|
[bm setProperty: NSImageCurrentFrame
|
|
withValue: [NSNumber numberWithUnsignedInt:index]];
|
|
}
|
|
|
|
/* Setting the frame has succeeded, or the image doesn't have
|
|
multiple frames. */
|
|
return YES;
|
|
}
|
|
|
|
- (void)setSizeFromSpec: (Lisp_Object) spec
|
|
{
|
|
NSSize size = [self size];
|
|
Lisp_Object value;
|
|
double scale = 1, aspect = size.width / size.height;
|
|
double width = -1, height = -1, max_width = -1, max_height = -1;
|
|
|
|
value = Fplist_get (spec, QCscale);
|
|
if (NUMBERP (value))
|
|
scale = XFLOATINT (value) ;
|
|
|
|
value = Fplist_get (spec, QCmax_width);
|
|
if (NUMBERP (value))
|
|
max_width = XFLOATINT (value);
|
|
|
|
value = Fplist_get (spec, QCmax_height);
|
|
if (NUMBERP (value))
|
|
max_height = XFLOATINT (value);
|
|
|
|
value = Fplist_get (spec, QCwidth);
|
|
if (NUMBERP (value))
|
|
{
|
|
width = XFLOATINT (value) * scale;
|
|
/* :width overrides :max-width. */
|
|
max_width = -1;
|
|
}
|
|
|
|
value = Fplist_get (spec, QCheight);
|
|
if (NUMBERP (value))
|
|
{
|
|
height = XFLOATINT (value) * scale;
|
|
/* :height overrides :max-height. */
|
|
max_height = -1;
|
|
}
|
|
|
|
if (width <= 0 && height <= 0)
|
|
{
|
|
width = size.width * scale;
|
|
height = size.height * scale;
|
|
}
|
|
else if (width > 0 && height <= 0)
|
|
height = width / aspect;
|
|
else if (height > 0 && width <= 0)
|
|
width = height * aspect;
|
|
|
|
if (max_width > 0 && width > max_width)
|
|
{
|
|
width = max_width;
|
|
height = max_width / aspect;
|
|
}
|
|
|
|
if (max_height > 0 && height > max_height)
|
|
{
|
|
height = max_height;
|
|
width = max_height * aspect;
|
|
}
|
|
|
|
[self setSize:NSMakeSize(width, height)];
|
|
}
|
|
|
|
- (instancetype)rotate: (double)rotation
|
|
{
|
|
EmacsImage *new_image;
|
|
NSPoint new_origin;
|
|
NSSize new_size, size = [self size];
|
|
NSRect rect = { NSZeroPoint, [self size] };
|
|
|
|
/* Create a bezier path of the outline of the image and do the
|
|
* rotation on it. */
|
|
NSBezierPath *bounds_path = [NSBezierPath bezierPathWithRect:rect];
|
|
NSAffineTransform *transform = [NSAffineTransform transform];
|
|
[transform rotateByDegrees: rotation * -1];
|
|
[bounds_path transformUsingAffineTransform:transform];
|
|
|
|
/* Now we can find out how large the rotated image needs to be. */
|
|
new_size = [bounds_path bounds].size;
|
|
new_image = [[EmacsImage alloc] initWithSize:new_size];
|
|
|
|
new_origin = NSMakePoint((new_size.width - size.width)/2,
|
|
(new_size.height - size.height)/2);
|
|
|
|
[new_image lockFocus];
|
|
|
|
/* Create the final transform. */
|
|
transform = [NSAffineTransform transform];
|
|
[transform translateXBy:new_size.width/2 yBy:new_size.height/2];
|
|
[transform rotateByDegrees: rotation * -1];
|
|
[transform translateXBy:-new_size.width/2 yBy:-new_size.height/2];
|
|
|
|
[transform concat];
|
|
[self drawAtPoint:new_origin fromRect:NSZeroRect
|
|
operation:NSCompositingOperationCopy fraction:1];
|
|
|
|
[new_image unlockFocus];
|
|
|
|
return new_image;
|
|
}
|
|
|
|
@end
|