mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-30 13:20:38 +00:00
Fixed many drawing issues (many ones being related to the flipping).
See bug report #27782 In particular, fixed -[NSImage drawXXX] and -[NSImage composite/dissolveXXX] methods to work exactly as Cocoa when the Cairo backend is used. Added a new draw operator (in addition to composite) to the backend. Cairo is the only backend that implements it for now. Eliminated as many flipping checks as possible. Warning: Untested with the winlib backend. You must update, recompile and install both Back and Gui. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@30523 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
67adeb4133
commit
0eee79f7b5
9 changed files with 274 additions and 28 deletions
41
ChangeLog
41
ChangeLog
|
@ -1,3 +1,44 @@
|
|||
2010-06-01 Quentin Mathe <quentin.mathe@gmail.com>
|
||||
|
||||
Fixed many drawing issues (many ones being related to the flipping).
|
||||
In particular, fixed -[NSImage drawXXX] methods to work exactly as
|
||||
Cocoa and improve the drawing performance in some cases (the last two
|
||||
points only holds with Cairo backend).
|
||||
Eliminated as many flipping checks as possible.
|
||||
Warning: Untested with the winlib backend.
|
||||
* Headers/AppKit/NSGraphicsContext.h:
|
||||
* Source/NSGraphicsContext.m:
|
||||
(-supportsDrawGState, -GSdraw:toPoint:fromRect:operation:fraction:):
|
||||
Added (see also the Backend Changelog).
|
||||
* Source/NSImage.m
|
||||
(-drawInRect:fromRect:operation:fraction:): Moved the previous
|
||||
implementation to -guiDrawInRect:fromRect:operation:fraction: and
|
||||
rewritten as a switch that checks -supportsDrawState.
|
||||
(-guiDrawInRect:fromRect:operation:fraction:): New method identical to
|
||||
the old -drawInRect:fromRect:operation:fraction:.
|
||||
(-nativeDrawInRect:fromRect:operation:fraction:): Added.
|
||||
New method that leverages the backend as much possible and implement
|
||||
semantic that exactly matches Cocoa.
|
||||
(-drawRepresentation:inRect:): Removed flipping
|
||||
check.
|
||||
* Source/NSImageRep.m (-drawInPoint:, -drawInRect:): Removed the
|
||||
flipping checks.
|
||||
* Source/NSImageCell.m (-drawInteriorWithFrame:inView:): Fixed
|
||||
-drawInRect:fromRect:operation:fraction use as Apple doc suggests it
|
||||
rather than using a negative height trick which doesn't work anymore
|
||||
(at least with Cairo) and has never worked on Mac OS X.
|
||||
* Source/NSView.m (-scrollRect:by:): Ajusted to do the copy bits on
|
||||
the window gstate rather than the view gstate.
|
||||
Required now that Cairo NSCopyBits/compositeGState won't compensate the
|
||||
flipping when copyOnSelf is YES. Moreover -scrollRect:by: was broken
|
||||
previously when the view wass not flipped (at least for Cairo).
|
||||
Finally NSCopyBits() behaves in another way on Mac OS X when the view
|
||||
gstate is used instead of the window gstate. By using the window gstate,
|
||||
we can more easily change how NSCopyBits() handles the view gstate to
|
||||
match Cocoa.
|
||||
* Source/NSClipView.m (-setBoundsOrigin:): Turned off dubious code that
|
||||
should probably be removed.
|
||||
|
||||
2010-06-01 Wolfgang Lux <wolfgang.lux@gmail.com>
|
||||
|
||||
* Source/NSDocumentController.m (-_setupOpenPanel): Disable
|
||||
|
|
|
@ -410,6 +410,12 @@ APPKIT_EXPORT NSGraphicsContext *GSCurrentContext(void);
|
|||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta;
|
||||
- (BOOL) supportsDrawGState;
|
||||
- (void) GSdraw: (int)gstateNum
|
||||
toPoint: (NSPoint)aPoint
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta;
|
||||
- (void) GSDrawImage: (NSRect)rect : (void *)imageref;
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
|
|
@ -118,6 +118,7 @@ PACKAGE_SCOPE
|
|||
int _gstate;
|
||||
void *_nextKeyView;
|
||||
void *_previousKeyView;
|
||||
NSRect *_dirtyRects;
|
||||
|
||||
@public
|
||||
/*
|
||||
|
|
|
@ -372,11 +372,11 @@ static inline NSRect integralRect (NSRect rect, NSView *view)
|
|||
}
|
||||
|
||||
/* ?? TODO: Understand the following code - and add explanatory comment */
|
||||
if ([NSView focusView] == _documentView)
|
||||
/*if ([NSView focusView] == _documentView)
|
||||
{
|
||||
PStranslate (NSMinX (originalBounds) - aPoint.x,
|
||||
NSMinY (originalBounds) - aPoint.y);
|
||||
}
|
||||
}*/
|
||||
|
||||
[_super_view reflectScrolledClipView: self];
|
||||
}
|
||||
|
|
|
@ -1540,6 +1540,44 @@ NSGraphicsContext *GSCurrentContext(void)
|
|||
[self subclassResponsibility: _cmd];
|
||||
}
|
||||
|
||||
/** <override-dummy />
|
||||
Returns whether the backend supports a GSDraw operator.
|
||||
|
||||
By default, returns NO.<br />
|
||||
When a GSContext backend subclass overrides this method to return YES, the
|
||||
backend must also implement -drawGState:fromRect:toPoint:op:fraction: in its
|
||||
GSState subclass.
|
||||
|
||||
When YES is returned, -[NSImage drawXXX] methods that involves rotation,
|
||||
scaling etc. will delegate as much as possible the image drawing to the backend,
|
||||
rather than trying to emulate the resulting image in Gui by using intermediate
|
||||
images to rotate and scale the content, and then composite the result with
|
||||
-GScomposite:toPoint:fromRect:operation:fraction:.
|
||||
|
||||
Backends which doesn't implement -compositeGState:fromRect:toPoint:op:fraction:
|
||||
can draw rotated or scaled images, but the semantic won't exactly match the
|
||||
NSImage documentation in non-trivial cases. */
|
||||
- (BOOL) supportsDrawGState
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
/** <override-dummy />
|
||||
Draws a gstate in a way that fully respects the destination transform,
|
||||
unlike the GSComposite operator which ignores the rotation and the scaling
|
||||
effect on the content.
|
||||
|
||||
Note: For the GScomposite operator, the scaling and rotation affects the
|
||||
destination point but not the content. */
|
||||
- (void) GSdraw: (int)gstateNum
|
||||
toPoint: (NSPoint)aPoint
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)delta
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
}
|
||||
|
||||
/** Generic method to draw an image into a rect. The image is defined
|
||||
by imageref, an opaque structure. Support for this method hasn't
|
||||
been implemented yet, so it should not be used anywhere. */
|
||||
|
|
182
Source/NSImage.m
182
Source/NSImage.m
|
@ -797,7 +797,6 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
return _color;
|
||||
}
|
||||
|
||||
|
||||
// Using the Image
|
||||
- (void) compositeToPoint: (NSPoint)aPoint
|
||||
operation: (NSCompositingOperation)op
|
||||
|
@ -813,8 +812,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
fromRect: (NSRect)aRect
|
||||
operation: (NSCompositingOperation)op
|
||||
{
|
||||
[self compositeToPoint: aPoint
|
||||
fromRect: aRect
|
||||
[self compositeToPoint: aPoint
|
||||
fromRect: aRect
|
||||
operation: op
|
||||
fraction: 1.0];
|
||||
}
|
||||
|
@ -850,27 +849,27 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
|
||||
if (cache != nil)
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
NSRect rect = [cache rect];
|
||||
|
||||
NSDebugLLog(@"NSImage", @"composite rect %@ in %@",
|
||||
NSStringFromRect(rect), NSStringFromRect(srcRect));
|
||||
|
||||
// Move the drawing rectangle to the origin of the image rep
|
||||
// and intersect the two rects.
|
||||
srcRect.origin.x += rect.origin.x;
|
||||
srcRect.origin.y += rect.origin.y;
|
||||
rect = NSIntersectionRect(srcRect, rect);
|
||||
|
||||
[GSCurrentContext() GScomposite: [[cache window] gState]
|
||||
toPoint: aPoint
|
||||
fromRect: rect
|
||||
operation: op
|
||||
fraction: delta];
|
||||
[ctxt GScomposite: [[cache window] gState]
|
||||
toPoint: aPoint
|
||||
fromRect: rect
|
||||
operation: op
|
||||
fraction: delta];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRect rect;
|
||||
|
||||
rect = NSMakeRect(aPoint.x, aPoint.y, _size.width, _size.height);
|
||||
NSRect rect = NSMakeRect(aPoint.x, aPoint.y, _size.width, _size.height);
|
||||
[self drawRepresentation: rep inRect: rect];
|
||||
}
|
||||
}
|
||||
|
@ -922,9 +921,6 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
{
|
||||
NSRect fillrect = aRect;
|
||||
|
||||
if ([[NSView focusView] isFlipped])
|
||||
fillrect.origin.y -= _size.height;
|
||||
|
||||
[_color set];
|
||||
NSRectFill(fillrect);
|
||||
|
||||
|
@ -957,10 +953,145 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
fraction: delta];
|
||||
}
|
||||
|
||||
- (void) drawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
operation: (NSCompositingOperation)op
|
||||
fraction: (float)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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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: [self bestRepresentationForDevice: nil]
|
||||
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;
|
||||
/* The size of the cache window that will hold the scaled image */
|
||||
NSSize cacheSize = NSMakeSize(imgSize.width * widthScaleFactor,
|
||||
imgSize.height * heightScaleFactor);
|
||||
NSRect srcRectInCache = NSMakeRect(srcRect.origin.x * widthScaleFactor,
|
||||
srcRect.origin.y * heightScaleFactor,
|
||||
srcRect.size.width * widthScaleFactor,
|
||||
srcRect.size.height * heightScaleFactor);
|
||||
|
||||
cache = [[NSCachedImageRep alloc]
|
||||
initWithSize: cacheSize
|
||||
depth: [[NSScreen mainScreen] depth]
|
||||
separate: YES
|
||||
alpha: YES];
|
||||
|
||||
[[[cache window] contentView] lockFocus];
|
||||
cacheCtxt = GSCurrentContext();
|
||||
|
||||
/* Clear the cache window surface */
|
||||
DPScompositerect(cacheCtxt, 0, 0, cacheSize.width, 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 */
|
||||
[[self bestRepresentationForDevice: nil]
|
||||
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, cacheSize.width, cacheSize.height,
|
||||
NSCompositeDestinationIn);
|
||||
}
|
||||
|
||||
[[[cache window] contentView] unlockFocus];
|
||||
|
||||
//NSLog(@"Draw in %@ from %@ from cache rect %@", NSStringFromRect(dstRect),
|
||||
// NSStringFromRect(srcRect), NSStringFromRect(srcRectInCache));
|
||||
|
||||
[ctxt GSdraw: gState
|
||||
toPoint: dstRect.origin
|
||||
fromRect: srcRectInCache
|
||||
operation: op
|
||||
fraction: delta];
|
||||
|
||||
[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;
|
||||
|
@ -1143,6 +1274,21 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
|||
}
|
||||
}
|
||||
|
||||
- (void) drawInRect: (NSRect)dstRect
|
||||
fromRect: (NSRect)srcRect
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) addRepresentation: (NSImageRep *)imageRep
|
||||
{
|
||||
GSRepData *repd;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include <Foundation/NSDebug.h>
|
||||
#include "AppKit/NSAffineTransform.h"
|
||||
#include "AppKit/NSCell.h"
|
||||
#include "AppKit/NSGraphics.h"
|
||||
#include "AppKit/NSImageCell.h"
|
||||
|
@ -197,6 +198,7 @@ scaleProportionally(NSSize imageSize, NSRect canvasRect)
|
|||
NSPoint position;
|
||||
BOOL is_flipped = [controlView isFlipped];
|
||||
NSSize imageSize, realImageSize;
|
||||
NSAffineTransform *xform = nil;
|
||||
|
||||
NSDebugLLog(@"NSImageCell", @"NSImageCell drawInteriorWithFrame called");
|
||||
|
||||
|
@ -273,10 +275,13 @@ scaleProportionally(NSSize imageSize, NSRect canvasRect)
|
|||
}
|
||||
|
||||
// account for flipped views
|
||||
if (is_flipped)
|
||||
if (is_flipped && controlView != nil)
|
||||
{
|
||||
position.y += imageSize.height;
|
||||
imageSize.height = -imageSize.height;
|
||||
xform = [NSAffineTransform transform];
|
||||
[xform translateXBy: 0 yBy: [controlView bounds].size.height];
|
||||
[xform scaleXBy: 1 yBy: -1];
|
||||
[xform concat];
|
||||
position.y = [controlView bounds].size.height - position.y - imageSize.height;
|
||||
}
|
||||
|
||||
// draw!
|
||||
|
@ -286,6 +291,12 @@ scaleProportionally(NSSize imageSize, NSRect canvasRect)
|
|||
realImageSize.height)
|
||||
operation: NSCompositeSourceOver
|
||||
fraction: 1.0];
|
||||
|
||||
if (is_flipped && controlView != nil)
|
||||
{
|
||||
[xform invert];
|
||||
[xform concat];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSSize) cellSize
|
||||
|
|
|
@ -453,8 +453,6 @@ implement, so we can't do that. */
|
|||
ctxt = GSCurrentContext();
|
||||
if (aPoint.x != 0 || aPoint.y != 0)
|
||||
{
|
||||
if ([[ctxt focusView] isFlipped])
|
||||
aPoint.y -= _size.height;
|
||||
ctm = GSCurrentCTM(ctxt);
|
||||
DPStranslate(ctxt, aPoint.x, aPoint.y);
|
||||
reset = 1;
|
||||
|
@ -480,8 +478,6 @@ implement, so we can't do that. */
|
|||
ctxt = GSCurrentContext();
|
||||
scale = NSMakeSize(NSWidth(aRect) / _size.width,
|
||||
NSHeight(aRect) / _size.height);
|
||||
if ([[ctxt focusView] isFlipped])
|
||||
aRect.origin.y -= NSHeight(aRect);
|
||||
ctm = GSCurrentCTM(ctxt);
|
||||
DPStranslate(ctxt, NSMinX(aRect), NSMinY(aRect));
|
||||
DPSscale(ctxt, scale.width, scale.height);
|
||||
|
|
|
@ -3002,9 +3002,16 @@ in the main thread.
|
|||
destPoint = aRect.origin;
|
||||
destPoint.x += delta.width;
|
||||
destPoint.y += delta.height;
|
||||
if ([self isFlipped])
|
||||
{
|
||||
destPoint.y += aRect.size.height;
|
||||
}
|
||||
|
||||
//NSLog(@"destPoint %@ in %@", NSStringFromPoint(destPoint), NSStringFromRect(_bounds));
|
||||
|
||||
[self lockFocus];
|
||||
NSCopyBits(0, aRect, destPoint);
|
||||
//NSCopyBits(0, aRect, destPoint);
|
||||
NSCopyBits([[self window] gState], [self convertRect: aRect toView: nil], destPoint);
|
||||
[self unlockFocus];
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue