mirror of
https://github.com/gnustep/libs-back.git
synced 2025-02-23 11:51:27 +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/back/trunk@30523 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
1d0aa595c9
commit
b53fe57e7c
5 changed files with 242 additions and 95 deletions
23
ChangeLog
23
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -96,6 +96,11 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) supportsDrawGState
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) flushGraphics
|
||||
{
|
||||
#if BUILD_SERVER == SERVER_x11
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue