mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 15:11:37 +00:00
* Headers/AppKit/NSImageRep.h: Add
-drawInRect:fromRect:operation:fraction:respectFlipped:hints: method * Source/NSImage.m: * Source/NSImageRep.m: Refactor drawing code from NSImage to NSImageRep. This should cause no functionlity change, but it lets NSImageRep subclasses implement more efficient versions of -drawInRect:fromRect:... that don't involve the (expensive) drawing of the image in a temporary offscreen window and then drawing from there on to the destination surface. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@33764 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
e4979c9102
commit
99abec2ebf
4 changed files with 397 additions and 439 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2011-08-18 Eric Wasylishen <ewasylishen@gmail.com>
|
||||
|
||||
* Headers/AppKit/NSImageRep.h: Add
|
||||
-drawInRect:fromRect:operation:fraction:respectFlipped:hints: method
|
||||
* Source/NSImage.m:
|
||||
* Source/NSImageRep.m: Refactor drawing code from NSImage to NSImageRep.
|
||||
This should cause no functionlity change, but it lets NSImageRep
|
||||
subclasses implement more efficient versions of -drawInRect:fromRect:...
|
||||
method that don't involve the wasteful drawing the image in a temporary
|
||||
offscreen window and then drawing from there on to the destination surface.
|
||||
|
||||
2011-08-17 Eric Wasylishen <ewasylishen@gmail.com>
|
||||
|
||||
* Source/Functions.m (NSDrawNinePartImage): Pixel-align drawing rect using
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#import <Foundation/NSGeometry.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <AppKit/AppKitDefines.h>
|
||||
#import <AppKit/NSGraphicsContext.h>
|
||||
|
||||
@class NSString;
|
||||
@class NSArray;
|
||||
|
@ -147,6 +148,14 @@ enum {
|
|||
- (BOOL)draw;
|
||||
- (BOOL)drawAtPoint:(NSPoint)aPoint;
|
||||
- (BOOL)drawInRect:(NSRect)aRect;
|
||||
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST)
|
||||
- (BOOL) drawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
respectFlipped: (BOOL)respectFlipped
|
||||
hints: (NSDictionary*)hints;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Managing NSImageRep Subclasses
|
||||
|
|
500
Source/NSImage.m
500
Source/NSImage.m
|
@ -55,24 +55,6 @@
|
|||
#import "GNUstepGUI/GSDisplayServer.h"
|
||||
#import "GSThemePrivate.h"
|
||||
|
||||
|
||||
/* Helpers. Would be nicer to use the C99 fmin/fmax functions, but that
|
||||
isn't currently possible. */
|
||||
static double gs_min(double x, double y)
|
||||
{
|
||||
if (x > y)
|
||||
return y;
|
||||
else
|
||||
return x;
|
||||
}
|
||||
static double gs_max(double x, double y)
|
||||
{
|
||||
if (x < y)
|
||||
return y;
|
||||
else
|
||||
return x;
|
||||
}
|
||||
|
||||
BOOL NSImageForceCaching = NO; /* use on missmatch */
|
||||
|
||||
@implementation NSBundle (NSImageAdditions)
|
||||
|
@ -922,390 +904,9 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
[self drawInRect: NSMakeRect(point.x, point.y, srcRect.size.width, srcRect.size.height)
|
||||
fromRect: srcRect
|
||||
operation: op
|
||||
fraction: delta];
|
||||
}
|
||||
|
||||
/* New code path that delegates as much as possible to the backend and whose
|
||||
behavior precisely matches Cocoa. */
|
||||
- (void) nativeDrawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
NSSize imgSize = [self size];
|
||||
float widthScaleFactor;
|
||||
float heightScaleFactor;
|
||||
NSImageRep *rep;
|
||||
|
||||
if (NSEqualRects(srcRect, NSZeroRect))
|
||||
{
|
||||
srcRect.size = imgSize;
|
||||
/* For -drawAtPoint:fromRect:operation:fraction: used with a zero rect */
|
||||
if (NSEqualSizes(dstRect.size, NSZeroSize))
|
||||
{
|
||||
dstRect.size = imgSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a rep to use
|
||||
|
||||
rep = [self bestRepresentationForRect: dstRect
|
||||
context: nil
|
||||
hints: nil];
|
||||
if (rep == nil)
|
||||
return;
|
||||
|
||||
if (!dstRect.size.width || !dstRect.size.height
|
||||
|| !srcRect.size.width || !srcRect.size.height)
|
||||
return;
|
||||
|
||||
// Clip to image bounds
|
||||
if (srcRect.origin.x < 0)
|
||||
srcRect.origin.x = 0;
|
||||
if (srcRect.origin.y < 0)
|
||||
srcRect.origin.y = 0;
|
||||
if (NSMaxX(srcRect) > imgSize.width)
|
||||
srcRect.size.width = imgSize.width - srcRect.origin.x;
|
||||
if (NSMaxY(srcRect) > imgSize.height)
|
||||
srcRect.size.height = imgSize.height - srcRect.origin.y;
|
||||
|
||||
widthScaleFactor = dstRect.size.width / srcRect.size.width;
|
||||
heightScaleFactor = dstRect.size.height / srcRect.size.height;
|
||||
|
||||
if (![ctxt isDrawingToScreen])
|
||||
{
|
||||
/* We can't composite or dissolve if we aren't drawing to a screen,
|
||||
so we'll just draw the right part of the image in the right
|
||||
place. */
|
||||
NSPoint p;
|
||||
|
||||
p.x = dstRect.origin.x / widthScaleFactor - srcRect.origin.x;
|
||||
p.y = dstRect.origin.y / heightScaleFactor - srcRect.origin.y;
|
||||
|
||||
DPSgsave(ctxt);
|
||||
DPSrectclip(ctxt, dstRect.origin.x, dstRect.origin.y,
|
||||
dstRect.size.width, dstRect.size.height);
|
||||
DPSscale(ctxt, widthScaleFactor, heightScaleFactor);
|
||||
[self drawRepresentation: rep
|
||||
inRect: NSMakeRect(p.x, p.y, imgSize.width, imgSize.height)];
|
||||
DPSgrestore(ctxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* We cannot ask the backend to draw the image directly when the source rect
|
||||
doesn't cover the whole image.
|
||||
Cairo doesn't support to specify a source rect for a surface used as a
|
||||
source, see cairo_set_source_surface()).
|
||||
CoreGraphics is similarly limited, see CGContextDrawImage().
|
||||
For now, we always use a two step process:
|
||||
- draw the image data in a cache to apply the srcRect to inRect scaling
|
||||
- draw the cache into the destination context
|
||||
It might be worth to move the first step to the backend, so we don't have
|
||||
to create a cache window but just an intermediate surface.
|
||||
We create a cache every time but otherwise we are more efficient than the
|
||||
old code path since the cache size is limited to what we actually draw
|
||||
and doesn't involve drawing the whole image. */
|
||||
{
|
||||
/* An intermediate image used to scale the image to be drawn as needed */
|
||||
NSCachedImageRep *cache;
|
||||
/* The scaled image graphics state we used as the source from which we
|
||||
draw into the destination (the current graphics context)*/
|
||||
int gState;
|
||||
/* The context of the cache window */
|
||||
NSGraphicsContext *cacheCtxt;
|
||||
NSSize repSize = [rep size];
|
||||
/* The size of the cache window that will hold the scaled image */
|
||||
NSSize cacheSize;
|
||||
|
||||
CGFloat imgToCacheWidthScaleFactor;
|
||||
CGFloat imgToCacheHeightScaleFactor;;
|
||||
|
||||
NSRect srcRectInCache;
|
||||
NSAffineTransform *transform, *backup;
|
||||
|
||||
if (NSEqualSizes(repSize, NSZeroSize))
|
||||
{
|
||||
repSize = dstRect.size;
|
||||
}
|
||||
|
||||
if (([rep pixelsWide] == NSImageRepMatchesDevice &&
|
||||
[rep pixelsHigh] == NSImageRepMatchesDevice) &&
|
||||
(dstRect.size.width > repSize.width ||
|
||||
dstRect.size.height > repSize.height))
|
||||
{
|
||||
cacheSize = [[ctxt GSCurrentCTM] transformSize: dstRect.size];
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheSize = [[ctxt GSCurrentCTM] transformSize: repSize];
|
||||
}
|
||||
|
||||
if (cacheSize.width < 0)
|
||||
cacheSize.width *= -1;
|
||||
if (cacheSize.height < 0)
|
||||
cacheSize.height *= -1;
|
||||
|
||||
imgToCacheWidthScaleFactor = cacheSize.width / imgSize.width;
|
||||
imgToCacheHeightScaleFactor = cacheSize.height / imgSize.height;
|
||||
|
||||
srcRectInCache = NSMakeRect(srcRect.origin.x * imgToCacheWidthScaleFactor,
|
||||
srcRect.origin.y * imgToCacheHeightScaleFactor,
|
||||
srcRect.size.width * imgToCacheWidthScaleFactor,
|
||||
srcRect.size.height * imgToCacheHeightScaleFactor);
|
||||
|
||||
cache = [[NSCachedImageRep alloc]
|
||||
initWithSize: NSMakeSize(ceil(cacheSize.width), ceil(cacheSize.height))
|
||||
depth: [[NSScreen mainScreen] depth]
|
||||
separate: YES
|
||||
alpha: YES];
|
||||
|
||||
[[[cache window] contentView] lockFocus];
|
||||
cacheCtxt = GSCurrentContext();
|
||||
|
||||
/* Clear the cache window surface */
|
||||
DPScompositerect(cacheCtxt, 0, 0, ceil(cacheSize.width), ceil(cacheSize.height), NSCompositeClear);
|
||||
gState = [cacheCtxt GSDefineGState];
|
||||
|
||||
//NSLog(@"Draw in cache size %@", NSStringFromSize(cacheSize));
|
||||
|
||||
/* We must not use -drawRepresentation:inRect: because the image must drawn
|
||||
scaled even when -scalesWhenResized is NO */
|
||||
[rep
|
||||
drawInRect: NSMakeRect(0, 0, cacheSize.width, cacheSize.height)];
|
||||
/* If we're doing a dissolve, use a DestinationIn composite to lower
|
||||
the alpha of the pixels. */
|
||||
if (delta != 1.0)
|
||||
{
|
||||
DPSsetalpha(cacheCtxt, delta);
|
||||
DPScompositerect(cacheCtxt, 0, 0, ceil(cacheSize.width), ceil(cacheSize.height),
|
||||
NSCompositeDestinationIn);
|
||||
}
|
||||
|
||||
[[[cache window] contentView] unlockFocus];
|
||||
|
||||
//NSLog(@"Draw in %@ from %@ from cache rect %@", NSStringFromRect(dstRect),
|
||||
// NSStringFromRect(srcRect), NSStringFromRect(srcRectInCache));
|
||||
|
||||
backup = [ctxt GSCurrentCTM];
|
||||
|
||||
transform = [NSAffineTransform transform];
|
||||
[transform translateXBy: dstRect.origin.x yBy: dstRect.origin.y];
|
||||
[transform scaleXBy: dstRect.size.width / srcRectInCache.size.width
|
||||
yBy: dstRect.size.height / srcRectInCache.size.height];
|
||||
[transform concat];
|
||||
|
||||
[ctxt GSdraw: gState
|
||||
toPoint: NSMakePoint(0,0)
|
||||
fromRect: srcRectInCache
|
||||
operation: op
|
||||
fraction: delta];
|
||||
|
||||
[ctxt GSSetCTM: backup];
|
||||
|
||||
[ctxt GSUndefineGState: gState];
|
||||
DESTROY(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/* Old code path that can probably partially be merged with the new native implementation.
|
||||
Fallback for backends other than Cairo. */
|
||||
- (void) guiDrawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
NSAffineTransform *transform;
|
||||
NSSize s;
|
||||
NSImageRep *rep;
|
||||
|
||||
s = [self size];
|
||||
|
||||
if (NSEqualRects(srcRect, NSZeroRect))
|
||||
{
|
||||
srcRect.size = s;
|
||||
/* For -drawAtPoint:fromRect:operation:fraction: used with a zero rect */
|
||||
if (NSEqualSizes(dstRect.size, NSZeroSize))
|
||||
{
|
||||
dstRect.size = s;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a rep to use
|
||||
|
||||
rep = [self bestRepresentationForRect: dstRect
|
||||
context: nil
|
||||
hints: nil];
|
||||
|
||||
if (rep == nil)
|
||||
return;
|
||||
|
||||
if (!dstRect.size.width || !dstRect.size.height
|
||||
|| !srcRect.size.width || !srcRect.size.height)
|
||||
return;
|
||||
|
||||
// CLip to image bounds
|
||||
if (srcRect.origin.x < 0)
|
||||
srcRect.origin.x = 0;
|
||||
if (srcRect.origin.y < 0)
|
||||
srcRect.origin.y = 0;
|
||||
if (NSMaxX(srcRect) > s.width)
|
||||
srcRect.size.width = s.width - srcRect.origin.x;
|
||||
if (NSMaxY(srcRect) > s.height)
|
||||
srcRect.size.height = s.height - srcRect.origin.y;
|
||||
|
||||
if (![ctxt isDrawingToScreen])
|
||||
{
|
||||
/* We can't composite or dissolve if we aren't drawing to a screen,
|
||||
so we'll just draw the right part of the image in the right
|
||||
place. */
|
||||
NSPoint p;
|
||||
double fx, fy;
|
||||
|
||||
fx = dstRect.size.width / srcRect.size.width;
|
||||
fy = dstRect.size.height / srcRect.size.height;
|
||||
|
||||
p.x = dstRect.origin.x / fx - srcRect.origin.x;
|
||||
p.y = dstRect.origin.y / fy - srcRect.origin.y;
|
||||
|
||||
DPSgsave(ctxt);
|
||||
DPSrectclip(ctxt, dstRect.origin.x, dstRect.origin.y,
|
||||
dstRect.size.width, dstRect.size.height);
|
||||
DPSscale(ctxt, fx, fy);
|
||||
[self drawRepresentation: rep
|
||||
inRect: NSMakeRect(p.x, p.y, s.width, s.height)];
|
||||
DPSgrestore(ctxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Figure out what the effective transform from image space to
|
||||
'window space' is. */
|
||||
transform = [ctxt GSCurrentCTM];
|
||||
|
||||
[transform scaleXBy: dstRect.size.width / srcRect.size.width
|
||||
yBy: dstRect.size.height / srcRect.size.height];
|
||||
|
||||
/* We can't composite or dissolve directly from the image reps, so we
|
||||
create a temporary off-screen window large enough to hold the
|
||||
transformed image, draw the image rep there, and composite from there
|
||||
to the destination.
|
||||
|
||||
Optimization: Since we do the entire image at once, we might need a
|
||||
huge buffer. If this starts hurting too much, there are a couple of
|
||||
things we could do to:
|
||||
|
||||
1. Take srcRect into account and only process the parts of the image
|
||||
we really need.
|
||||
2. Take the clipping path into account. Desirable, especially if we're
|
||||
being drawn as lots of small strips in a scrollview. We don't have
|
||||
the clipping path here, though.
|
||||
3. Allocate a permanent but small buffer and process the image
|
||||
piecewise.
|
||||
|
||||
*/
|
||||
{
|
||||
NSCachedImageRep *cache;
|
||||
NSAffineTransformStruct ts;
|
||||
NSPoint p;
|
||||
double x0, y0, x1, y1, w, h;
|
||||
int gState;
|
||||
NSGraphicsContext *ctxt1;
|
||||
|
||||
/* Figure out how big we need to make the window that'll hold the
|
||||
transformed image. */
|
||||
p = [transform transformPoint: NSMakePoint(0, s.height)];
|
||||
x0 = x1 = p.x;
|
||||
y0 = y1 = p.y;
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(s.width, 0)];
|
||||
x0 = gs_min(x0, p.x);
|
||||
y0 = gs_min(y0, p.y);
|
||||
x1 = gs_max(x1, p.x);
|
||||
y1 = gs_max(y1, p.y);
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(s.width, s.height)];
|
||||
x0 = gs_min(x0, p.x);
|
||||
y0 = gs_min(y0, p.y);
|
||||
x1 = gs_max(x1, p.x);
|
||||
y1 = gs_max(y1, p.y);
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(0, 0)];
|
||||
x0 = gs_min(x0, p.x);
|
||||
y0 = gs_min(y0, p.y);
|
||||
x1 = gs_max(x1, p.x);
|
||||
y1 = gs_max(y1, p.y);
|
||||
|
||||
x0 = floor(x0);
|
||||
y0 = floor(y0);
|
||||
x1 = ceil(x1);
|
||||
y1 = ceil(y1);
|
||||
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
|
||||
/* This is where we want the origin of image space to be in our
|
||||
window. */
|
||||
p.x -= x0;
|
||||
p.y -= y0;
|
||||
|
||||
cache = [[NSCachedImageRep alloc]
|
||||
initWithSize: NSMakeSize(w, h)
|
||||
depth: [[NSScreen mainScreen] depth]
|
||||
separate: YES
|
||||
alpha: YES];
|
||||
|
||||
[[[cache window] contentView] lockFocus];
|
||||
// The context of the cache window
|
||||
ctxt1 = GSCurrentContext();
|
||||
DPScompositerect(ctxt1, 0, 0, w, h, NSCompositeClear);
|
||||
|
||||
/* Set up the effective transform. We also save a gState with this
|
||||
transform to make it easier to do the final composite. */
|
||||
ts = [transform transformStruct];
|
||||
ts.tX = p.x;
|
||||
ts.tY = p.y;
|
||||
[transform setTransformStruct: ts];
|
||||
[ctxt1 GSSetCTM: transform];
|
||||
gState = [ctxt1 GSDefineGState];
|
||||
|
||||
|
||||
/* We must not use -drawRepresentation:inRect: because the image must drawn
|
||||
scaled even when -scalesWhenResized is NO */
|
||||
|
||||
// FIXME: should the background color be filled here?
|
||||
// If I don't I get black backgrounds on images with xlib; maybe an xlib backend bug
|
||||
PSgsave();
|
||||
if (_color != nil)
|
||||
{
|
||||
[_color set];
|
||||
NSRectFill(NSMakeRect(0, 0, s.width, s.height));
|
||||
}
|
||||
[rep drawInRect: NSMakeRect(0, 0, s.width, s.height)];
|
||||
PSgrestore();
|
||||
|
||||
/* If we're doing a dissolve, use a DestinationIn composite to lower
|
||||
the alpha of the pixels. */
|
||||
if (delta != 1.0)
|
||||
{
|
||||
DPSsetalpha(ctxt1, delta);
|
||||
DPScompositerect(ctxt1, 0, 0, s.width, s.height,
|
||||
NSCompositeDestinationIn);
|
||||
}
|
||||
|
||||
[[[cache window] contentView] unlockFocus];
|
||||
|
||||
|
||||
DPScomposite(ctxt, srcRect.origin.x, srcRect.origin.y,
|
||||
srcRect.size.width, srcRect.size.height, gState,
|
||||
dstRect.origin.x, dstRect.origin.y, op);
|
||||
|
||||
[ctxt GSUndefineGState: gState];
|
||||
|
||||
DESTROY(cache);
|
||||
}
|
||||
fraction: delta
|
||||
respectFlipped: NO
|
||||
hints: nil];
|
||||
}
|
||||
|
||||
- (void) drawInRect: (NSRect)dstRect
|
||||
|
@ -1313,57 +914,78 @@ Fallback for backends other than Cairo. */
|
|||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
if ([GSCurrentContext() supportsDrawGState])
|
||||
{
|
||||
[self nativeDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self guiDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
|
||||
}
|
||||
[self drawInRect: dstRect
|
||||
fromRect: srcRect
|
||||
operation: op
|
||||
fraction: delta
|
||||
respectFlipped: NO
|
||||
hints: nil];
|
||||
}
|
||||
|
||||
- (void) drawInRect: (NSRect)dstRect
|
||||
/**
|
||||
* Base drawing method in NSImage; all other draw methods call this one
|
||||
*/
|
||||
- (void) drawInRect: (NSRect)dstRect // Negative width/height => Nothing draws.
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
respectFlipped: (BOOL)respectFlipped
|
||||
hints: (NSDictionary*)hints
|
||||
{
|
||||
NSAffineTransform *backup = nil;
|
||||
NSGraphicsContext *ctx = GSCurrentContext();
|
||||
BOOL compensateForFlip = (respectFlipped && [ctx isFlipped]);
|
||||
NSImageRep *rep;
|
||||
NSGraphicsContext *ctxt;
|
||||
NSSize imgSize, repSize;
|
||||
NSRect repSrcRect;
|
||||
|
||||
// FIXME: Hints are currently ignored
|
||||
ctxt = GSCurrentContext();
|
||||
imgSize = [self size];
|
||||
|
||||
if (compensateForFlip)
|
||||
// Handle abbreviated parameters
|
||||
|
||||
if (NSEqualRects(srcRect, NSZeroRect))
|
||||
{
|
||||
CGFloat height;
|
||||
NSAffineTransform *newXform;
|
||||
|
||||
height = dstRect.size.height != 0 ?
|
||||
dstRect.size.height : [self size].height;
|
||||
|
||||
backup = [ctx GSCurrentCTM];
|
||||
|
||||
newXform = [backup copy];
|
||||
[newXform translateXBy: dstRect.origin.x yBy: dstRect.origin.y + height];
|
||||
[newXform scaleXBy: 1 yBy: -1];
|
||||
[ctx GSSetCTM: newXform];
|
||||
[newXform release];
|
||||
|
||||
dstRect.origin = NSMakePoint(0, 0);
|
||||
srcRect.size = imgSize;
|
||||
}
|
||||
if (NSEqualSizes(dstRect.size, NSZeroSize)) // For -drawAtPoint:fromRect:operation:fraction:
|
||||
{
|
||||
dstRect.size = imgSize;
|
||||
}
|
||||
|
||||
[self drawInRect: dstRect
|
||||
fromRect: srcRect
|
||||
operation: op
|
||||
fraction: delta];
|
||||
if (imgSize.width <= 0 || imgSize.height <= 0)
|
||||
return;
|
||||
|
||||
if (compensateForFlip)
|
||||
{
|
||||
[ctx GSSetCTM: backup];
|
||||
}
|
||||
// Select a rep
|
||||
|
||||
rep = [self bestRepresentationForRect: dstRect
|
||||
context: ctxt
|
||||
hints: hints];
|
||||
if (rep == nil)
|
||||
return;
|
||||
|
||||
repSize = [rep size];
|
||||
|
||||
// Convert srcRect from image coordinate space to rep coordinate space
|
||||
{
|
||||
const CGFloat imgToRepWidthScaleFactor = repSize.width / imgSize.width;
|
||||
const CGFloat imgToRepHeightScaleFactor = repSize.height / imgSize.height;
|
||||
|
||||
repSrcRect = NSMakeRect(srcRect.origin.x * imgToRepWidthScaleFactor,
|
||||
srcRect.origin.y * imgToRepHeightScaleFactor,
|
||||
srcRect.size.width * imgToRepWidthScaleFactor,
|
||||
srcRect.size.height * imgToRepHeightScaleFactor);
|
||||
}
|
||||
|
||||
// FIXME: Insert caching code here which gets the cached
|
||||
// copy of rep and draws from that, if caching is enabled.
|
||||
|
||||
// FIXME: Draw background?
|
||||
|
||||
[rep drawInRect: dstRect
|
||||
fromRect: repSrcRect
|
||||
operation: op
|
||||
fraction: delta
|
||||
respectFlipped: respectFlipped
|
||||
hints: hints];
|
||||
}
|
||||
|
||||
- (void) addRepresentation: (NSImageRep *)imageRep
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#import <Foundation/NSAffineTransform.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
@ -36,14 +37,19 @@
|
|||
#import <Foundation/NSNotification.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
#import <Foundation/NSDebug.h>
|
||||
#import "AppKit/NSAffineTransform.h"
|
||||
#import "AppKit/NSImageRep.h"
|
||||
#import "AppKit/NSBitmapImageRep.h"
|
||||
#import "AppKit/NSCachedImageRep.h"
|
||||
#import "AppKit/NSEPSImageRep.h"
|
||||
#import "AppKit/NSPasteboard.h"
|
||||
#import "AppKit/NSGraphicsContext.h"
|
||||
#import "AppKit/NSView.h"
|
||||
#import "AppKit/NSColor.h"
|
||||
#import "AppKit/NSWindow.h"
|
||||
#import "AppKit/NSScreen.h"
|
||||
#import "AppKit/DPSOperators.h"
|
||||
#import "AppKit/PSOperators.h"
|
||||
#import "GNUstepGUI/GSGhostscriptImageRep.h"
|
||||
#import "GNUstepGUI/GSImageMagickImageRep.h"
|
||||
|
||||
|
@ -491,6 +497,316 @@ implement, so we can't do that. */
|
|||
return ok;
|
||||
}
|
||||
|
||||
/* New code path that delegates as much as possible to the backend and whose
|
||||
behavior precisely matches Cocoa. */
|
||||
- (void) nativeDrawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
/* An intermediate image used to scale the image to be drawn as needed */
|
||||
NSCachedImageRep *cache;
|
||||
/* The scaled image graphics state we used as the source from which we
|
||||
draw into the destination (the current graphics context)*/
|
||||
int gState;
|
||||
/* The context of the cache window */
|
||||
NSGraphicsContext *cacheCtxt;
|
||||
const NSSize repSize = [self size];
|
||||
/* The size of the cache window */
|
||||
NSSize cacheSize;
|
||||
|
||||
CGFloat repToCacheWidthScaleFactor;
|
||||
CGFloat repToCacheHeightScaleFactor;
|
||||
|
||||
NSRect srcRectInCache;
|
||||
NSAffineTransform *transform, *backup;
|
||||
|
||||
// FIXME: Revisit this calculation of cache size
|
||||
|
||||
if (([self pixelsWide] == NSImageRepMatchesDevice &&
|
||||
[self pixelsHigh] == NSImageRepMatchesDevice) &&
|
||||
(dstRect.size.width > repSize.width ||
|
||||
dstRect.size.height > repSize.height))
|
||||
{
|
||||
cacheSize = [[ctxt GSCurrentCTM] transformSize: dstRect.size];
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheSize = [[ctxt GSCurrentCTM] transformSize: repSize];
|
||||
}
|
||||
|
||||
if (cacheSize.width < 0)
|
||||
cacheSize.width *= -1;
|
||||
if (cacheSize.height < 0)
|
||||
cacheSize.height *= -1;
|
||||
|
||||
repToCacheWidthScaleFactor = cacheSize.width / repSize.width;
|
||||
repToCacheHeightScaleFactor = cacheSize.height / repSize.height;
|
||||
|
||||
srcRectInCache = NSMakeRect(srcRect.origin.x * repToCacheWidthScaleFactor,
|
||||
srcRect.origin.y * repToCacheHeightScaleFactor,
|
||||
srcRect.size.width * repToCacheWidthScaleFactor,
|
||||
srcRect.size.height * repToCacheHeightScaleFactor);
|
||||
|
||||
cache = [[NSCachedImageRep alloc]
|
||||
initWithSize: NSMakeSize(ceil(cacheSize.width), ceil(cacheSize.height))
|
||||
depth: [[NSScreen mainScreen] depth]
|
||||
separate: YES
|
||||
alpha: YES];
|
||||
|
||||
[[[cache window] contentView] lockFocus];
|
||||
cacheCtxt = GSCurrentContext();
|
||||
|
||||
/* Clear the cache window surface */
|
||||
DPScompositerect(cacheCtxt, 0, 0, ceil(cacheSize.width), ceil(cacheSize.height), NSCompositeClear);
|
||||
gState = [cacheCtxt GSDefineGState];
|
||||
|
||||
//NSLog(@"Draw in cache size %@", NSStringFromSize(cacheSize));
|
||||
|
||||
[self drawInRect: NSMakeRect(0, 0, cacheSize.width, cacheSize.height)];
|
||||
|
||||
[[[cache window] contentView] unlockFocus];
|
||||
|
||||
//NSLog(@"Draw in %@ from %@ from cache rect %@", NSStringFromRect(dstRect),
|
||||
// NSStringFromRect(srcRect), NSStringFromRect(srcRectInCache));
|
||||
|
||||
backup = [ctxt GSCurrentCTM];
|
||||
|
||||
transform = [NSAffineTransform transform];
|
||||
[transform translateXBy: dstRect.origin.x yBy: dstRect.origin.y];
|
||||
[transform scaleXBy: dstRect.size.width / srcRectInCache.size.width
|
||||
yBy: dstRect.size.height / srcRectInCache.size.height];
|
||||
[transform concat];
|
||||
|
||||
[ctxt GSdraw: gState
|
||||
toPoint: NSMakePoint(0,0)
|
||||
fromRect: srcRectInCache
|
||||
operation: op
|
||||
fraction: delta];
|
||||
|
||||
[ctxt GSSetCTM: backup];
|
||||
|
||||
[ctxt GSUndefineGState: gState];
|
||||
DESTROY(cache);
|
||||
}
|
||||
|
||||
/* Old code path that can probably partially be merged with the new native implementation.
|
||||
Fallback for backends other than Cairo. */
|
||||
- (void) guiDrawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
NSAffineTransform *transform;
|
||||
NSSize repSize;
|
||||
|
||||
repSize = [self size];
|
||||
|
||||
/* Figure out what the effective transform from rep space to
|
||||
'window space' is. */
|
||||
transform = [ctxt GSCurrentCTM];
|
||||
|
||||
[transform scaleXBy: dstRect.size.width / srcRect.size.width
|
||||
yBy: dstRect.size.height / srcRect.size.height];
|
||||
|
||||
/* We can't composite or dissolve directly from the image reps, so we
|
||||
create a temporary off-screen window large enough to hold the
|
||||
transformed image, draw the image rep there, and composite from there
|
||||
to the destination.
|
||||
|
||||
Optimization: Since we do the entire image at once, we might need a
|
||||
huge buffer. If this starts hurting too much, there are a couple of
|
||||
things we could do to:
|
||||
|
||||
|
||||
1. Take srcRect into account and only process the parts of the image
|
||||
we really need.
|
||||
2. Take the clipping path into account. Desirable, especially if we're
|
||||
being drawn as lots of small strips in a scrollview. We don't have
|
||||
the clipping path here, though.
|
||||
3. Allocate a permanent but small buffer and process the image
|
||||
piecewise.
|
||||
|
||||
*/
|
||||
{
|
||||
NSCachedImageRep *cache;
|
||||
NSAffineTransformStruct ts;
|
||||
NSPoint p;
|
||||
double x0, y0, x1, y1, w, h;
|
||||
int gState;
|
||||
NSGraphicsContext *ctxt1;
|
||||
|
||||
/* Figure out how big we need to make the window that'll hold the
|
||||
transformed image. */
|
||||
p = [transform transformPoint: NSMakePoint(0, repSize.height)];
|
||||
x0 = x1 = p.x;
|
||||
y0 = y1 = p.y;
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(repSize.width, 0)];
|
||||
x0 = MIN(x0, p.x);
|
||||
y0 = MIN(y0, p.y);
|
||||
x1 = MAX(x1, p.x);
|
||||
y1 = MAX(y1, p.y);
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(repSize.width, repSize.height)];
|
||||
x0 = MIN(x0, p.x);
|
||||
y0 = MIN(y0, p.y);
|
||||
x1 = MAX(x1, p.x);
|
||||
y1 = MAX(y1, p.y);
|
||||
|
||||
p = [transform transformPoint: NSMakePoint(0, 0)];
|
||||
x0 = MIN(x0, p.x);
|
||||
y0 = MIN(y0, p.y);
|
||||
x1 = MAX(x1, p.x);
|
||||
y1 = MAX(y1, p.y);
|
||||
|
||||
x0 = floor(x0);
|
||||
y0 = floor(y0);
|
||||
x1 = ceil(x1);
|
||||
y1 = ceil(y1);
|
||||
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
|
||||
/* This is where we want the origin of image space to be in our
|
||||
window. */
|
||||
p.x -= x0;
|
||||
p.y -= y0;
|
||||
|
||||
cache = [[NSCachedImageRep alloc]
|
||||
initWithSize: NSMakeSize(w, h)
|
||||
depth: [[NSScreen mainScreen] depth]
|
||||
separate: YES
|
||||
alpha: YES];
|
||||
|
||||
[[[cache window] contentView] lockFocus];
|
||||
// The context of the cache window
|
||||
ctxt1 = GSCurrentContext();
|
||||
DPScompositerect(ctxt1, 0, 0, w, h, NSCompositeClear);
|
||||
|
||||
/* Set up the effective transform. We also save a gState with this
|
||||
transform to make it easier to do the final composite. */
|
||||
ts = [transform transformStruct];
|
||||
ts.tX = p.x;
|
||||
ts.tY = p.y;
|
||||
[transform setTransformStruct: ts];
|
||||
[ctxt1 GSSetCTM: transform];
|
||||
gState = [ctxt1 GSDefineGState];
|
||||
|
||||
{
|
||||
// Hack for xlib. Without it, transparent parts of images are black
|
||||
[[NSColor clearColor] set];
|
||||
NSRectFill(NSMakeRect(0, 0, repSize.width, repSize.height));
|
||||
}
|
||||
|
||||
[self drawInRect: NSMakeRect(0, 0, repSize.width, repSize.height)];
|
||||
|
||||
/* If we're doing a dissolve, use a DestinationIn composite to lower
|
||||
the alpha of the pixels. */
|
||||
if (delta != 1.0)
|
||||
{
|
||||
DPSsetalpha(ctxt1, delta);
|
||||
DPScompositerect(ctxt1, 0, 0, repSize.width, repSize.height,
|
||||
NSCompositeDestinationIn);
|
||||
}
|
||||
|
||||
[[[cache window] contentView] unlockFocus];
|
||||
|
||||
DPScomposite(ctxt, srcRect.origin.x, srcRect.origin.y,
|
||||
srcRect.size.width, srcRect.size.height, gState,
|
||||
dstRect.origin.x, dstRect.origin.y, op);
|
||||
|
||||
[ctxt GSUndefineGState: gState];
|
||||
|
||||
DESTROY(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback implementation for subclasses which don't implement their
|
||||
* own direct drawing
|
||||
* TODO: explain how -draw, -drawInRect:, -drawAtPoint: clear their background
|
||||
*/
|
||||
- (BOOL) drawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
respectFlipped: (BOOL)respectFlipped
|
||||
hints: (NSDictionary*)hints
|
||||
{
|
||||
NSAffineTransform *backup = nil;
|
||||
NSGraphicsContext *ctx = GSCurrentContext();
|
||||
const BOOL compensateForFlip = (respectFlipped && [ctx isFlipped]);
|
||||
const NSSize repSize = [self size];
|
||||
|
||||
// Handle abbreviated parameters
|
||||
|
||||
if (NSEqualRects(srcRect, NSZeroRect))
|
||||
{
|
||||
srcRect.size = repSize;
|
||||
}
|
||||
if (NSEqualSizes(dstRect.size, NSZeroSize))
|
||||
{
|
||||
dstRect.size = repSize;
|
||||
}
|
||||
|
||||
if (dstRect.size.width <= 0 || dstRect.size.height <= 0
|
||||
|| srcRect.size.width <= 0 || srcRect.size.height <= 0)
|
||||
return NO;
|
||||
|
||||
// Clip to image bounds
|
||||
|
||||
if (srcRect.origin.x < 0)
|
||||
srcRect.origin.x = 0;
|
||||
if (srcRect.origin.y < 0)
|
||||
srcRect.origin.y = 0;
|
||||
if (NSMaxX(srcRect) > repSize.width)
|
||||
srcRect.size.width = repSize.width - srcRect.origin.x;
|
||||
if (NSMaxY(srcRect) > repSize.height)
|
||||
srcRect.size.height = repSize.height - srcRect.origin.y;
|
||||
|
||||
// FIXME: Hints are currently ignored
|
||||
|
||||
// Compensate for flip
|
||||
|
||||
if (compensateForFlip)
|
||||
{
|
||||
NSAffineTransform *newXform;
|
||||
|
||||
backup = [ctx GSCurrentCTM];
|
||||
|
||||
newXform = [backup copy];
|
||||
[newXform translateXBy: dstRect.origin.x yBy: dstRect.origin.y + dstRect.size.height];
|
||||
[newXform scaleXBy: 1 yBy: -1];
|
||||
[ctx GSSetCTM: newXform];
|
||||
[newXform release];
|
||||
|
||||
dstRect.origin = NSMakePoint(0, 0);
|
||||
}
|
||||
|
||||
// Draw
|
||||
|
||||
if ([ctx supportsDrawGState])
|
||||
{
|
||||
[self nativeDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self guiDrawInRect: dstRect fromRect: srcRect operation: op fraction: delta];
|
||||
}
|
||||
|
||||
// Undo flip compensation
|
||||
|
||||
if (compensateForFlip)
|
||||
{
|
||||
[ctx GSSetCTM: backup];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// NSCopying protocol
|
||||
- (id) copyWithZone: (NSZone *)zone
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue