mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-31 01:30:47 +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
ea63bbc0ff
commit
f3a9150d7c
4 changed files with 397 additions and 439 deletions
|
@ -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…
Add table
Add a link
Reference in a new issue