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/back/trunk@30523 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Quentin Mathe 2010-06-01 11:04:36 +00:00
parent 1d0aa595c9
commit b53fe57e7c
5 changed files with 242 additions and 95 deletions

View file

@ -1,3 +1,26 @@
2010-06-01 Quentin Mathe <quentin.mathe@gmail.com>
Fixed composite operator to behave correctly and added a new draw
operator to get -[NSImage drawXXX] methods work exactly as Cocoa and
improve the drawing performance in some cases.
Eliminated all flipping checks in the backend to ensure the flipping
remains an high-level AppKit concept.
* Source/gsc/GSContext.m:
(-GSdraw:toPoint:fromRect:operation:fraction:): Added.
This method calls -drawGState:fromRect:toPoint:op:fraction:.
* Headers/gsc/GSGState.h (-drawGState:fromRect:toPoint:op:): Added
as an informal protocol which can be implemented by subclasses
* Source/cairo/CairoContext.m (-supportsDrawGState): Added overriden
implementation that enables -drawGState:fromRect:toPoint:op:fraction:.
* Source/cairo/CairoGState.m:
(-drawOrientationMarkersIn:): Added.
(-DPSimage::::::): Removed flipping check.
(-compositeGState:fromRect:toPoint:op:fraction:): Fixed to precisely
implement the PostScript behavior which is to ignore rotation and
scaling effect for the content but not for the destination point.
Also documented in details since this code is complex.
(-drawGState:fromRect:toPoint:op:fraction:): Added.
2010-05-25 Riccardo Mottola <rmottola@users.sf.net>
* Source/x11/XGDragView.m

View file

@ -105,6 +105,16 @@ typedef enum {
@end
/** Informal protocol to which backends can conform to when they support drawing a
graphics state with arbitrary transforms on the current graphics context. */
@interface NSObject (GSDrawGState)
- (void) drawGState: (GSGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
op: (NSCompositingOperation)op
fraction: (float)delta;
@end
#include "GSGStateOps.h"
#endif /* _GSGState_h_INCLUDE */

View file

@ -96,6 +96,11 @@
return YES;
}
- (BOOL) supportsDrawGState
{
return YES;
}
- (void) flushGraphics
{
#if BUILD_SERVER == SERVER_x11

View file

@ -1038,6 +1038,17 @@ _set_op(cairo_t *ct, NSCompositingOperation op)
}
}
/* For debugging */
- (void) drawOrientationMarkersIn: (cairo_t *)ct
{
cairo_rectangle(_ct, 0, 0, 20, 10);
cairo_set_source_rgba(_ct, 0, 1, 0, 1);
cairo_fill(_ct);
cairo_rectangle(_ct, 0, 30, 20, 10);
cairo_set_source_rgba(_ct, 0, 0, 1, 1);
cairo_fill(_ct);
}
- (void) DPSimage: (NSAffineTransform *)matrix : (int)pixelsWide
: (int)pixelsHigh : (int)bitsPerSample
: (int)samplesPerPixel : (int)bitsPerPixel
@ -1197,45 +1208,26 @@ _set_op(cairo_t *ct, NSCompositingOperation op)
tstruct.tX, tstruct.tY);
cairo_transform(_ct, &local_matrix);
// Make up for flip done in GUI
if (viewIsFlipped)
{
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
cpattern = cairo_pattern_create_for_surface(surface);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -2*pixelsHigh);
cairo_pattern_set_matrix(cpattern, &local_matrix);
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
{
cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
}
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, 0, pixelsHigh, pixelsWide, pixelsHigh);
}
else
{
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
cpattern = cairo_pattern_create_for_surface(surface);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -pixelsHigh);
cairo_pattern_set_matrix(cpattern, &local_matrix);
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
{
cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
}
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, 0, 0, pixelsWide, pixelsHigh);
}
{
cairo_pattern_t *cpattern;
cairo_matrix_t source_matrix;
cpattern = cairo_pattern_create_for_surface(surface);
cairo_matrix_init_scale(&source_matrix, 1, -1);
cairo_matrix_translate(&source_matrix, 0, -pixelsHigh);
cairo_pattern_set_matrix(cpattern, &source_matrix);
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 6, 0))
{
cairo_pattern_set_extend(cpattern, CAIRO_EXTEND_PAD);
}
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, 0, 0, pixelsWide, pixelsHigh);
}
cairo_clip(_ct);
cairo_paint(_ct);
//[self drawOrientationMarkersIn: _ct];
cairo_surface_destroy(surface);
cairo_restore(_ct);
@ -1271,94 +1263,114 @@ _set_op(cairo_t *ct, NSCompositingOperation op)
}
- (void) compositeGState: (CairoGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
fromRect: (NSRect)srcRect
toPoint: (NSPoint)destPoint
op: (NSCompositingOperation)op
fraction: (float)delta
{
cairo_surface_t *src;
cairo_surface_t *src = cairo_get_target(source->_ct);
NSSize ssize = NSZeroSize;
BOOL copyOnSelf = (src == cairo_get_target(_ct));
/* The source rect in the source base coordinate space.
This rect is the minimum bounding rect of srcRect. */
NSRect srcRectInBase = NSZeroRect;
/* The destination point in the target base coordinate space */
NSPoint destPointInBase = NSZeroPoint;
/* The origin of srcRectInBase */
double minx, miny;
/* The composited content size */
double width, height;
/* The adjusted destination point in the target base coordinate space */
double x, y;
NSSize ssize;
/* Alternative source rect origin in the source current CTM */
NSPoint srcRectAltOrigin = NSMakePoint(srcRect.origin.x, srcRect.origin.y + srcRect.size.height);
/* Alternative source rect origin in the source base coordinate space */
NSPoint srcRectAltOriginInBase = [source->ctm transformPoint: srcRectAltOrigin];
/* The source rect origin in the source base coordinate space */
NSPoint srcRectOriginInBase = [source->ctm transformPoint: srcRect.origin];
BOOL originFlippedBetweenBaseAndSource = NO;
/* The delta between the origins of srcRect and srcRectInBase */
double dx, dy;
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
BOOL copyOnSelf = NO;
cairo_matrix_t source_matrix;
if (!_ct || !source->_ct)
{
return;
}
/*
* we check if we copy on ourself, if that's the case
* we'll use the groups trick...
*/
src = cairo_get_target(source->_ct);
if (src == cairo_get_target(_ct))
{
NSRect targetRect;
targetRect.origin = aPoint;
targetRect.size = aRect.size;
if (!NSIsEmptyRect(NSIntersectionRect(aRect, targetRect)))
{
copyOnSelf = YES;
}
}
//NSLog(@"Composite surface %p source size %@ target size %@", self->_surface, NSStringFromSize([self->_surface size]), NSStringFromSize([source->_surface size]));
cairo_save(_ct);
/* When the target and source are the same surface, we use the group tricks */
if (copyOnSelf) cairo_push_group(_ct);
cairo_new_path(_ct);
_set_op(_ct, op);
if (viewIsFlipped && !copyOnSelf)
_set_op(_ct, op);
//NSLog(@"Point %@", NSStringFromPoint(destPoint));
/* Scales and/or rotates the local destination point with the current AppKit CTM */
destPointInBase = [ctm transformPoint: destPoint];
//NSLog(@"Point in base %@", NSStringFromPoint(destPointInBase));
/* Scales and/or rotates the source rect and retrieves the minimum bounding
rectangle that encloses it and makes it our source area */
[source->ctm boundingRectFor: srcRect result: &srcRectInBase];
//NSLog(@"Bounding rect %@ from %@", NSStringFromRect(srcRectInBase), NSStringFromRect(srcRect));
/* Find whether the source rect origin in the base is the same than in the
source current CTM.
We need to know the origin in the base to compute how much the source
bounding rect origin is shifted relatively to the closest source rect corner.
We use this delta (dx, dy) to correctly composite from a rotated source. */
originFlippedBetweenBaseAndSource =
((srcRect.origin.y < srcRectAltOrigin.y && srcRectOriginInBase.y > srcRectAltOriginInBase.y)
|| (srcRect.origin.y > srcRectAltOrigin.y && srcRectOriginInBase.y < srcRectAltOriginInBase.y));
if (originFlippedBetweenBaseAndSource)
{
aPoint.y -= aRect.size.height;
srcRectOriginInBase = srcRectAltOriginInBase;
}
dx = srcRectOriginInBase.x - srcRectInBase.origin.x;
dy = srcRectOriginInBase.y - srcRectInBase.origin.y;
{
NSRect newRect;
newRect.origin = aPoint;
newRect.size = aRect.size;
[ctm boundingRectFor: newRect result: &newRect];
aPoint = newRect.origin;
}
[source->ctm boundingRectFor: aRect result: &aRect];
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
{
NSSize size = [source->_surface size];
// For cairo > 1.8 we seem to need this adjustment
aRect.origin.y -= 2*(source->offset.y - size.height);
}
x = floorf(aPoint.x);
y = floorf(aPoint.y + 0.5);
minx = NSMinX(aRect);
miny = NSMinY(aRect);
width = NSWidth(aRect);
height = NSHeight(aRect);
//NSLog(@"Point in base adjusted %@", NSStringFromPoint(NSMakePoint(destPointInBase.x - dx, destPointInBase.y - dy)));
if (source->_surface != nil)
{
ssize = [source->_surface size];
}
else
{
ssize = NSMakeSize(0, 0);
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
{
// For cairo > 1.8 we seem to need this adjustment
srcRectInBase.origin.y -= 2 * (source->offset.y - ssize.height);
}
x = floorf(destPointInBase.x);
y = floorf(destPointInBase.y + 0.5);
minx = NSMinX(srcRectInBase);
miny = NSMinY(srcRectInBase);
width = NSWidth(srcRectInBase);
height = NSHeight(srcRectInBase);
/* We respect the AppKit CTM effect on the origin 'aPoint' (see
-[ctm transformPoint:]), but we ignore the scaling and rotation effect on
the composited content and size. Which means we never rotate or scale the
content we composite.
We use a pattern as a trick to simulate a target CTM change, this way we
don't touch the source CTM even when both source and target are identical
(e.g. scrolling case).
We must use a pattern matrix that matches the AppKit base CTM set up in
-DPSinitgraphics to ensure no transform is applied to the source content,
translation adjustements related to destination point and source rect put
aside. */
cpattern = cairo_pattern_create_for_surface(src);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, -x + minx, - ssize.height - y + miny);
cairo_pattern_set_matrix(cpattern, &local_matrix);
cairo_matrix_init_scale(&source_matrix, 1, -1);
//cairo_matrix_translate(&source_matrix, 0, -[_surface size].height);
cairo_matrix_translate(&source_matrix, minx - x + dx, miny - y + dy - ssize.height);
cairo_pattern_set_matrix(cpattern, &source_matrix);
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, x, y, width, height);
@ -1382,6 +1394,82 @@ _set_op(cairo_t *ct, NSCompositingOperation op)
cairo_restore(_ct);
}
/** Unlike -compositeGState, -drawGSstate fully respects the AppKit CTM but
doesn't support to use the receiver cairo target as the source. */
- (void) drawGState: (CairoGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
op: (NSCompositingOperation)op
fraction: (float)delta
{
NSAffineTransformStruct tstruct = [ctm transformStruct];
cairo_surface_t *src = cairo_get_target(source->_ct);
double width, height;
double x, y;
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
cairo_matrix_t source_matrix;
if (!_ct || !source->_ct)
{
return;
}
cairo_save(_ct);
cairo_new_path(_ct);
_set_op(_ct, op);
if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 8, 0))
{
NSSize size = [source->_surface size];
// For cairo > 1.8 we seem to need this adjustment
aRect.origin.y -= 2*(source->offset.y - size.height);
}
x = floorf(aPoint.x);
y = floorf(aPoint.y + 0.5);
width = NSWidth(aRect);
height = NSHeight(aRect);
// NOTE: We don't keep the Cairo matrix in sync with the AppKit matrix (aka
// -[NSGraphicsContext GSCurrentCTM])
/* Prepare a Cairo matrix with the current AppKit CTM */
cairo_matrix_init(&local_matrix,
tstruct.m11, tstruct.m12,
tstruct.m21, tstruct.m22,
tstruct.tX, tstruct.tY);
/* Append the local point transformation */
cairo_matrix_translate(&local_matrix, x - aRect.origin.x, y - aRect.origin.y);
/* Concat to the Cairo matrix created in -DPSinitgraphics, which adjusts the
mismatch between the Cairo top left vs AppKit bottom left origin. */
cairo_transform(_ct, &local_matrix);
//[self drawOrientationMarkersIn: _ct];
cpattern = cairo_pattern_create_for_surface(src);
cairo_matrix_init_scale(&source_matrix, 1, -1);
cairo_matrix_translate(&source_matrix, 0, -[source->_surface size].height);
cairo_pattern_set_matrix(cpattern, &source_matrix);
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, aRect.origin.x, aRect.origin.y, width, height);
cairo_clip(_ct);
if (delta < 1.0)
{
cairo_paint_with_alpha(_ct, delta);
}
else
{
cairo_paint(_ct);
}
cairo_restore(_ct);
}
@end
@implementation CairoGState (PatternColor)

View file

@ -821,6 +821,27 @@ static NSMapTable *gtable;
fraction: delta];
}
- (void) GSdraw: (int)gstateNum
toPoint: (NSPoint)aPoint
fromRect: (NSRect)srcRect
operation: (NSCompositingOperation)op
fraction: (float)delta
{
GSGState *g = gstate;
if (gstateNum)
{
[self DPSexecuserobject: gstateNum];
ctxt_pop(g, opstack, GSGState);
}
[gstate drawGState: g
fromRect: srcRect
toPoint: aPoint
op: op
fraction: delta];
}
- (void) GSDrawImage: (NSRect) rect: (void *) imageref
{
NSBitmapImageRep *bitmap;