diff --git a/ChangeLog b/ChangeLog index a514a57..3688e12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,69 @@ +2013-09-23 Ivan Vucica + + * Source/opal/OpalContext.m: + Once again pretending we're not drawing on screen. This is a temporary + fix for -DPSimage:. + + -DPSgsave is now passed on to current gstate before GSContext is given + chance to replace it. + + Changes relating to rename of OpalSurface and OpalGState methods + -cgContext to -CGContext. + + * Source/opal/OpalGState.m: + Apparently mostly functional -compositeGState:. Includes disabled + modification of code from CairoGState. + + Apparently functional -drawGState. + + -DPSsetlinecap: stub (need to match linecap constants.) + -DPSsetmiterlimit:. + + Extended -copyWithZone: to assign a 'default' OpalGStateRef to the + newly copied gstate in case we currently have no context. This is + done by creating a context and copying whatever's in that context + onto the new OpalGState. + + Improved -DPSinitgraphics by setting the device offset that was + set here and calling CGContextSaveGState() as many times as it + was supposed to be called while CGContext did not exist. Noted + that we should, instead of recreating contexts, just reset the + internal GState of Opal. + + Added -GSSetCTM:. + + -flushGraphics no longer 'flushes' rect 0,0,1024,1024. Now it instead + queries surface for its size. + + In case surface exists but not the CGContext, -DPSgsave creates it. + Otherwise, it records that gsave should be run upon context + creation. + + Added -DPSsetlinewidth:. + + Changes relating to rename of OpalSurface and OpalGState methods + -cgContext to -CGContext. + + * Source/opal/OpalSurface.m: + Added reminder of how we should handle recreation of CGContexts. + + Disabled non-doublebuffered windows. (We always need a backing + CGBitmapContext so we can implement -compositeGState: and -drawGState:. + + Added accessors -x11CGContext and -backingCGContext. + + Fixed bug where sometimes we'd get incorrect expose values and would + try to incorrectly copy the backing image, stretching the resulting + on-screen image. + + Added -size accessor. + + * Headers/opal/OpalGState.h: + Some accessors. _CGContextSaveGStatesOnContextCreation ivar. + + * Headers/opal/OpalSurface.h: + New and renamed accessors. + 2013-09-19 Ivan Vucica * Source/opal/OpalContext.m: diff --git a/Headers/opal/OpalGState.h b/Headers/opal/OpalGState.h index b3dbefe..3f2ac91 100644 --- a/Headers/opal/OpalGState.h +++ b/Headers/opal/OpalGState.h @@ -57,6 +57,15 @@ have a different cairo_t with the same surface. **/ OPGStateRef _opGState; + + /** + Sometimes, -DPSgsave may get called before context has + been created. + + We need a counter for how many times CGContextSaveGState() + needs to be called in first -DPSinitgraphics that gets called. + **/ + int _CGContextSaveGStatesOnContextCreation; } - (void) DPSinitclip; @@ -84,10 +93,13 @@ - (void) GSSetSurface: (OpalSurface *)opalSurface : (int)x : (int)y; + +- (void) DPSgsave; +- (void) DPSgrestore; @end @interface OpalGState (Accessors) -- (CGContextRef) cgContext; +- (CGContextRef) CGContext; - (OPGStateRef) OPGState; - (void) setOPGState: (OPGStateRef) opGState; @end diff --git a/Headers/opal/OpalSurface.h b/Headers/opal/OpalSurface.h index 54b83d0..5e5d213 100644 --- a/Headers/opal/OpalSurface.h +++ b/Headers/opal/OpalSurface.h @@ -37,8 +37,12 @@ - (id) initWithDevice: (void *)device; - (struct _gswindow_device_t *) device; -- (CGContextRef) cgContext; +- (CGContextRef) CGContext; - (void) createCGContexts; +- (NSSize) size; + +- (CGContextRef) backingCGContext; +- (CGContextRef) x11CGContext; @end @interface OpalSurface (DebugExtensions) diff --git a/Source/opal/OpalContext.m b/Source/opal/OpalContext.m index 12d14ea..ffa52aa 100644 --- a/Source/opal/OpalContext.m +++ b/Source/opal/OpalContext.m @@ -65,6 +65,9 @@ - (BOOL) isDrawingToScreen { +#warning isDrawingToScreen returning NO to fix DPSimage + return NO; + // NOTE: This was returning NO because it was not looking at the // return value of GSCurrentSurface. Now it returns YES, which // seems to have broken image drawing (yellow rectangles are drawn instead) @@ -75,8 +78,8 @@ - (void) DPSgsave { - [super DPSgsave]; [OGSTATE DPSgsave]; + [super DPSgsave]; } - (void) DPSgrestore { @@ -90,7 +93,7 @@ - (void) DPSsetgstate: (int)gstateID { - OPGStateRef previousGState = OPContextCopyGState([OGSTATE cgContext]); + OPGStateRef previousGState = OPContextCopyGState([OGSTATE CGContext]); [OGSTATE setOPGState: previousGState]; [previousGState release]; // FIXME @@ -99,17 +102,15 @@ OPGStateRef newGState = [OGSTATE OPGState]; if (newGState) { - OPContextSetGState([OGSTATE cgContext], newGState); + OPContextSetGState([OGSTATE CGContext], newGState); [OGSTATE setOPGState: nil]; } } -/* -// FIXME: we should add this as soon as we implement -drawGState:... + - (BOOL) supportsDrawGState { return YES; } -*/ /** This handles 'expose' event notifications that arrive from @@ -127,7 +128,7 @@ { OpalSurface * surface; [OGSTATE GSCurrentSurface: &surface : NULL : NULL]; - return [surface cgContext]; + return [surface CGContext]; } #if BUILD_SERVER == SERVER_x11 diff --git a/Source/opal/OpalGState.m b/Source/opal/OpalGState.m index 2ac43d8..9e1804d 100644 --- a/Source/opal/OpalGState.m +++ b/Source/opal/OpalGState.m @@ -33,8 +33,19 @@ #import "opal/OpalFontInfo.h" #import "x11/XGServerWindow.h" -#define CGCTX [self cgContext] +#define CGCTX [self CGContext] +static inline NSString * _CGRectRepr(CGRect rect) +{ + return [NSString stringWithFormat: @"(%g,%g,%g,%g)", + rect.origin.x, rect.origin.y, + rect.size.width, rect.size.height]; +} +static inline CGRect _CGRectFromNSRect(NSRect nsrect) +{ + return CGRectMake(nsrect.origin.x, nsrect.origin.y, + nsrect.size.width, nsrect.size.height); +} @implementation OpalGState @@ -75,7 +86,7 @@ : (const unsigned char *const[5])data { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); - + NSDebugLLog(@"OpalGState", @" %s - %@ - cgctx %@", __PRETTY_FUNCTION__, _opalSurface, [self CGContext]); // This depends on CGAffineTransform and NSAffineTransformStruct having // the same in-memory layout. // Here's an elementary check if that is true. @@ -84,10 +95,18 @@ NSAffineTransformStruct nsAT = [matrix transformStruct]; CGAffineTransform cgAT = *(CGAffineTransform *)&nsAT; + NSDebugLLog(@"OpalGState", @"tf: %@ x %@", matrix, [self GSCurrentCTM]); CGContextSaveGState(CGCTX); -// CGContextSetRGBFillColor(CGCTX, 1, 0, 0, 1); + //OPContextSetIdentityCTM(CGCTX); CGContextConcatCTM(CGCTX, cgAT); -// CGContextFillRect(CGCTX, CGRectMake(0, 0, pixelsWide, pixelsHigh)); + //OPContextSetCairoDeviceOffset(CGCTX, 0, 0); + //CGContextMoveToPoint(CGCTX, 0, 0); +#if 0 + CGContextSetRGBFillColor(CGCTX, 1, 0, 0, 1); + CGContextFillRect(CGCTX, CGRectMake(-512, -512, 1024, 1024 /*pixelsWide, pixelsHigh*/ )); +#endif +// CGContextRestoreGState(CGCTX); +// return; // TODO: // We may want to normalize colorspace names between Opal and -gui, @@ -141,6 +160,8 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); false, /* shouldInterpolate? */ kCGRenderingIntentDefault ); CGContextDrawImage(CGCTX, CGRectMake(0, 0, pixelsWide, pixelsHigh), img); +//[_opalSurface _saveImage: img withPrefix:@"/tmp/opalback-dpsimage-" size: NSZeroSize]; + CGDataProviderRelease(dataProvider); CGImageRelease(img); CGContextRestoreGState(CGCTX); @@ -153,22 +174,257 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); fraction: (CGFloat)delta { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); -#if 0 - CGContextSaveGState(CGCTX); - CGContextSetRGBFillColor(CGCTX, 1, 1, 0, 1); - CGContextFillRect(CGCTX, CGRectMake(destPoint.x, destPoint.y, srcRect.size.width, srcRect.size.height)); - CGContextRestoreGState(CGCTX); -#else - CGRect srcCGRect = CGRectMake(srcRect.origin.x, srcRect.origin.y, - srcRect.size.width, srcRect.size.height); + CGContextRef destContexts[2] = { [_opalSurface backingCGContext], [_opalSurface x11CGContext] }; - // FIXME: this presumes that the backing cgContext of 'source' is + /* x11 context needs to have correct ctm applied */ + CGContextSaveGState([_opalSurface x11CGContext]); + OPContextSetIdentityCTM([_opalSurface x11CGContext]); + CGContextConcatCTM([_opalSurface x11CGContext], CGContextGetCTM([_opalSurface backingCGContext])); + + for (int i = 0; i < 1; i++) // not drawing into x11cgctx after all. + { + CGContextRef ctx = destContexts[i]; + + [self compositeGState: source + fromRect: srcRect + toPoint: destPoint + op: op + fraction: delta + destCGContext: ctx]; + } + + /* restore x11 context's previous state */ + CGContextRestoreGState([_opalSurface x11CGContext]); +} +- (void) drawGState: (OpalGState *)source + fromRect: (NSRect)srcRect + toPoint: (NSPoint)destPoint + op: (NSCompositingOperation)op + fraction: (CGFloat)delta +{ + NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); + CGContextRef destContexts[2] = { [_opalSurface backingCGContext], [_opalSurface x11CGContext] }; + + for (int i = 0; i < 2; i++) + { + CGContextRef ctx = destContexts[i]; + + [self drawGState: source + fromRect: srcRect + toPoint: destPoint + op: op + fraction: delta + destCGContext: ctx]; + } +} +- (void) compositeGState: (OpalGState *)source + fromRect: (NSRect)srcRect + toPoint: (NSPoint)destPoint + op: (NSCompositingOperation)op + fraction: (CGFloat)delta + destCGContext: (CGContextRef) destCGContext +{ + // NOTE: This method seems to need to paint to X11 context, too. + + NSDebugLLog(@"OpalGState", @"%p (%@): %s - from %@ of gstate %p (cgctx %p) to %@ of %p (cgctx %p)", self, [self class], __PRETTY_FUNCTION__, NSStringFromRect(srcRect), source, [source CGContext], NSStringFromPoint(destPoint), self, [self CGContext]); +#if 0 + CGContextSaveGState(destCGContext); + CGContextSetRGBFillColor(destCGContext, 1, 1, 0, 1); + CGContextFillRect(destCGContext, CGRectMake(destPoint.x, destPoint.y, srcRect.size.width, srcRect.size.height)); + CGContextRestoreGState(destCGContext); +#else +#if 1 + NSSize ssize = [source->_opalSurface size]; + srcRect = [[source GSCurrentCTM] rectInMatrixSpace: srcRect]; + destPoint = [[self GSCurrentCTM] pointInMatrixSpace: destPoint]; + + srcRect.origin.y = ssize.height-srcRect.origin.y-srcRect.size.height; + + CGRect srcCGRect = _CGRectFromNSRect(srcRect); + CGRect destCGRect = CGRectMake(destPoint.x, destPoint.y, + srcRect.size.width, srcRect.size.height); + NSLog(@"Source cgctx: %p, self: %p - from %@ to %@ with ctm %@", [source CGContext], self, _CGRectRepr(srcCGRect), _CGRectRepr(destCGRect), [self GSCurrentCTM]); + // FIXME: this presumes that the backing CGContext of 'source' is // an OpalSurface with a backing CGBitmapContext - CGImageRef backingImage = CGBitmapContextCreateImage([source cgContext]); - CGContextMoveToPoint(CGCTX, destPoint.x, destPoint.y); + CGImageRef backingImage = CGBitmapContextCreateImage([source CGContext]); + CGImageRef subImage = CGImageCreateWithImageInRect(backingImage, srcCGRect); + + CGContextSaveGState(destCGContext); + OPContextSetIdentityCTM(destCGContext); + OPContextSetCairoDeviceOffset(destCGContext, 0, 0); + // TODO: this ignores op // TODO: this ignores delta - CGContextDrawImage(CGCTX, srcCGRect, backingImage); + CGContextDrawImage(destCGContext, destCGRect, subImage); + + OPContextSetCairoDeviceOffset(CGCTX, -offset.x, + offset.y - [_opalSurface device]->buffer_height); + + CGContextRestoreGState(destCGContext); + + CGImageRelease(subImage); + CGImageRelease(backingImage); +#else + CGImageRef src; + NSSize ssize = NSZeroSize; + BOOL copyOnSelf; + /* 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; + /* Alternative source rect origin in the source current CTM */ + NSPoint srcRectAltOrigin; + /* Alternative source rect origin in the source base coordinate space */ + NSPoint srcRectAltOriginInBase; + /* The source rect origin in the source base coordinate space */ + NSPoint srcRectOriginInBase; + BOOL originFlippedBetweenBaseAndSource = NO; + /* The delta between the origins of srcRect and srcRectInBase */ + double dx, dy; + + if (![source CGContext] || !destCGContext) + return; + + src = CGBitmapContextCreateImage([source CGContext]); + copyOnSelf = ([source CGContext] == destCGContext); + srcRectAltOrigin = NSMakePoint(srcRect.origin.x, srcRect.origin.y + srcRect.size.height); + srcRectAltOriginInBase = [[source GSCurrentCTM] transformPoint: srcRectAltOrigin]; + srcRectOriginInBase = [[source GSCurrentCTM] transformPoint: srcRect.origin]; + + CGContextSaveGState(destCGContext); + + /* 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); + */ + + /* Scales and/or rotates the local destination point with the current AppKit CTM */ + destPointInBase = [ctm transformPoint: destPoint]; + + /* Scales and/or rotates the source rect and retrieves the minimum bounding + rectangle that encloses it and makes it our source area */ + [[source GSCurrentCTM] boundingRectFor: srcRect result: &srcRectInBase]; + + /* 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) + { + srcRectOriginInBase = srcRectAltOriginInBase; + } + dx = srcRectOriginInBase.x - srcRectInBase.origin.x; + dy = srcRectOriginInBase.y - srcRectInBase.origin.y; + + if (source->_opalSurface != nil) + { + ssize = [source->_opalSurface size]; + } + + /* + // ? + 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 = destPointInBase.x; + y = destPointInBase.y; + minx = NSMinX(srcRectInBase); + miny = NSMinY(srcRectInBase); + width = NSWidth(srcRectInBase); + height = NSHeight(srcRectInBase); + + + /* Comment from cairo backend: + -----8<---- */ + /* 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. */ + /* -----8<---- + We don't have patterns with their own matrices at our disposal, so the + relevant code is NOT here. Instead we directly use srcRectInBase + */ + + NSLog(@"dy: %d", (int)dy); + CGRect srcCGRect = CGRectMake(srcRect.origin.x, srcRect.origin.y, srcRect.size.width, srcRect.size.height); + srcCGRect = CGRectMake(minx, miny, width, height); + CGImageRef srcSubImage = CGImageCreateWithImageInRect(src, srcCGRect); + //OPContextSetIdentityCTM(destCGContext); + //CGContextScaleCTM(destCGContext, 1, -1); + //CGContextTranslateCTM(destCGContext, -(minx - x + dx), miny - y + dy - ssize.height); + //OPContextResetClip(destCGContext); + //CGContextAddRect(destCGContext, CGRectMake(x, y, width, height)); + //CGContextClip(destCGContext); + // TODO: this ignores op + // TODO: this ignores delta + // (delta == opacity, in cairo backend) + CGRect destCGRect = CGRectMake(x, y, width, height); + CGContextDrawImage(destCGContext, destCGRect, srcSubImage); + CGContextSetRGBFillColor(destCGContext, 0.6, 1.0, 0.2, 0.2); + CGContextFillRect(destCGContext, destCGRect); +// NSLog(@" --> compsoiting subimage %@", srcSubImage); +//[source->_opalSurface _saveImage: srcSubImage withPrefix:[NSString stringWithFormat: @"/tmp/opalback-compositing-subimage-%p-", [source CGContext]] size: srcCGRect.size ]; +//[source->_opalSurface _saveImage: src withPrefix:[NSString stringWithFormat: @"/tmp/opalback-compositing-image-%p-", [source CGContext]] size: NSZeroSize ]; + CGImageRelease(src); + + CGContextRestoreGState(destCGContext); +#endif +#endif +} + +/** Unlike -compositeGState, -drawGSstate fully respects the AppKit CTM but +doesn't support to use the receiver cairo target as the source. */ +/* This method is required if -[OpalContext supportsDrawGState] returns YES */ +- (void) drawGState: (OpalGState *)source + fromRect: (NSRect)srcRect + toPoint: (NSPoint)destPoint + op: (NSCompositingOperation)op + fraction: (CGFloat)delta + destCGContext: (CGContextRef)destCGContext +{ + // TODO: CairoGState has a lot more complex implementation. + // For now, we'll just call compositeGState and live + // with the fact that CTM is not respected. + + NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); +#if 0 + [self compositeGState: source fromRect: srcRect toPoint: destPoint op: op fraction: delta destCGContext: destCGContext]; +#else + CGRect srcCGRect = CGRectMake(srcRect.origin.x, srcRect.origin.y, + srcRect.size.width, srcRect.size.height); + CGRect destCGRect = CGRectMake(destPoint.x, destPoint.y, + srcRect.size.width, srcRect.size.height); + CGImageRef backingImage = CGBitmapContextCreateImage([source CGContext]); + CGImageRef subImage = CGImageCreateWithImageInRect(backingImage, srcCGRect); + // TODO: this ignores op + // TODO: this ignores delta + CGContextDrawImage(destCGContext, destCGRect, subImage); + CGImageRelease(subImage); CGImageRelease(backingImage); #endif } @@ -204,19 +460,20 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); - // TODO: stub + CGContextSetLineJoin(CGCTX, linejoin); } - (void) DPSsetlinecap: (int)linecap { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); - // TODO: stub + // TODO: ensure match of linecap constants between Opal and DPS + CGContextSetLineCap(CGCTX, linecap); } - (void) DPSsetmiterlimit: (CGFloat)miterlimit { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); - // TODO: stub + CGContextSetMiterLimit(CGCTX, miterlimit); } @end @@ -227,10 +484,25 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); - (id)copyWithZone: (NSZone *)zone { + NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); OpalGState * theCopy = (OpalGState *) [super copyWithZone: zone]; [_opalSurface retain]; - theCopy->_opGState = OPContextCopyGState(CGCTX); + if (CGCTX) + { + theCopy->_opGState = OPContextCopyGState(CGCTX); + } + else + { + // FIXME: perhaps Opal could provide an API for getting the default + // gstate? + CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGContextRef ctx = CGBitmapContextCreate(NULL, 1, 1, 8, 32, colorSpace, kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(colorSpace); + theCopy->_opGState = OPContextCopyGState(ctx); + CGContextRelease(ctx); + NSLog(@"Included default gstate %p", theCopy->_opGState); + } return theCopy; } @@ -258,7 +530,7 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); : (int)x : (int)y { - NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); + NSDebugLLog(@"OpalGState", @"%p (%@): %s - %@ %d %d", self, [self class], __PRETTY_FUNCTION__, opalSurface, x, y); if(_opalSurface != opalSurface) { @@ -287,7 +559,29 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); [super DPSinitgraphics]; + if (!_opalSurface) + { + NSLog(@"%s: called before GSSetSurface:::", __PRETTY_FUNCTION__); + return; + } + + // TODO: instead of recreating contexts, we should only reset + // the gstate portion of the contexts. Add OPContextResetGState() which + // recreates _ct and resets _ctadditions. See DPSinitgraphics in + // CairoGState. + [_opalSurface createCGContexts]; + + OPContextSetCairoDeviceOffset(CGCTX, -offset.x, + offset.y - [_opalSurface device]->buffer_height); + + while (_CGContextSaveGStatesOnContextCreation > 0) + { + NSLog(@"%d more times", _CGContextSaveGStatesOnContextCreation); + CGContextSaveGState(CGCTX); + _CGContextSaveGStatesOnContextCreation--; + } + /* if ([_opalSurface device]) { @@ -304,13 +598,13 @@ NSLog(@" : samplesperpixel = %d", samplesPerPixel); @implementation OpalGState (Accessors) -- (CGContextRef) cgContext +- (CGContextRef) CGContext { if (!_opalSurface) NSDebugMLLog(@"OpalGState", @"No OpalSurface"); - else if (![_opalSurface cgContext]) + else if (![_opalSurface CGContext]) NSDebugMLLog(@"OpalGState", @"No OpalSurface CGContext"); - return [_opalSurface cgContext]; + return [_opalSurface CGContext]; } - (OPGStateRef) OPGState @@ -473,7 +767,9 @@ static CGFloat theAlpha = 1.; // TODO: removeme - (NSAffineTransform *) GSCurrentCTM { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); - + + return ctm; + CGAffineTransform cgCTM = CGContextGetCTM(CGCTX); NSAffineTransform * affineTransform = [NSAffineTransform transform]; @@ -488,25 +784,54 @@ static CGFloat theAlpha = 1.; // TODO: removeme return affineTransform; } +- (void) GSSetCTM: (NSAffineTransform *)newCTM +{ + // This depends on CGAffineTransform and NSAffineTransformStruct having + // the same in-memory layout. + // Here's an elementary check if that is true. + // We should probably check this in -back's "configure" script. + assert(sizeof(CGAffineTransform) == sizeof(NSAffineTransformStruct)); + NSAffineTransformStruct nsAT = [newCTM transformStruct]; + CGAffineTransform cgAT = *(CGAffineTransform *)&nsAT; + + OPContextSetIdentityCTM(CGCTX); + CGContextConcatCTM(CGCTX, cgAT); + + [super GSSetCTM: newCTM]; +} - (void) flushGraphics { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); CGContextFlush(CGCTX); - [_opalSurface handleExpose:CGRectMake(0, 0, 1024, 1024)]; // FIXME + [_opalSurface handleExpose: [_opalSurface size]]; } - (void) DPSgsave { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); -#warning Opal bug: nil ctx should 'only' print a warning instead of crashing - if (CGCTX) - CGContextSaveGState(CGCTX); + if (!CGCTX) + { + if (_opalSurface) + { + [_opalSurface createCGContexts]; + } + else + { + NSLog(@"%s: called before CGContext was created; possible -gui bug?", __PRETTY_FUNCTION__); + _CGContextSaveGStatesOnContextCreation++; + } + } + + CGContextSaveGState(CGCTX); } - (void) DPSgrestore { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); -#warning Opal bug: nil ctx should 'only' print a warning instead of crashing - if (CGCTX) - CGContextRestoreGState(CGCTX); + if (!CGCTX) + { + NSLog(@"%s: called before CGContext was created; possible -gui bug?", __PRETTY_FUNCTION__); + _CGContextSaveGStatesOnContextCreation--; + } + CGContextRestoreGState(CGCTX); } - (void *) saveClip { @@ -626,6 +951,13 @@ static CGFloat theAlpha = 1.; // TODO: removeme *y = currentPoint.y; NSDebugLLog(@"OpalGState", @" %p (%@): %s (returning: %f %f)", self, [self class], __PRETTY_FUNCTION__, *x, *y); } + +- (void) DPSsetlinewidth: (CGFloat) width +{ + NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); + + CGContextSetLineWidth(CGCTX, width); +} @end // MARK: Non-required unimplemented methods @@ -643,10 +975,6 @@ static CGFloat theAlpha = 1.; // TODO: removeme empty NSWindow. */ -- (void) DPSsetlinewidth: (CGFloat) width -{ - NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); -} - (void) DPSsetgstate: (NSInteger) gst { NSDebugLLog(@"OpalGState", @"%p (%@): %s", self, [self class], __PRETTY_FUNCTION__); diff --git a/Source/opal/OpalSurface.m b/Source/opal/OpalSurface.m index 578c9ba..d0a0d23 100644 --- a/Source/opal/OpalSurface.m +++ b/Source/opal/OpalSurface.m @@ -114,18 +114,29 @@ static CGContextRef createCGBitmapContext (int pixelsWide, // FIXME: this method and class presumes we are being passed // a window device. + // FIXME: this method does not destroy existing contexts if + // needed, nor transfer Opal GState. + + if (_x11CGContext || _backingCGContext) + { + NSLog(@"FIXME: Replacement of OpalSurface %p's CGContexts (x11=%p,backing=%p) without transfer of gstate", self, _x11CGContext, _backingCGContext); + } Display * display = _gsWindowDevice->display; Window window = _gsWindowDevice->ident; _x11CGContext = OPX11ContextCreate(display, window); +#if 0 if (_gsWindowDevice->type == NSBackingStoreNonretained) { // Don't double-buffer: // use the window surface as the drawing destination. } else +#else +#warning All windows have to be doublebuffered +#endif { // Do double-buffer: // Create a similar surface to the window which supports alpha @@ -143,9 +154,15 @@ static CGContextRef createCGBitmapContext (int pixelsWide, #warning NOTE! Doublebuffering disabled. #endif } - - - + +#if 0 + CGContextSaveGState(_backingCGContext); + CGContextSetRGBFillColor(_backingCGContext, (rand() % 255) / 255., (rand() % 255) / 255., (rand() % 255) / 255., 1); + CGContextFillRect(_backingCGContext, CGRectMake(-512, -512, 1024, 1024 /*pixelsWide, pixelsHigh*/ )); + CGContextRestoreGState(_backingCGContext); +#endif + NSDebugLLog(@"OpalSurface", @"Created CGContexts: X11=%p, backing=%p", _x11CGContext, _backingCGContext); + } - (gswindow_device_t *) device @@ -153,10 +170,19 @@ static CGContextRef createCGBitmapContext (int pixelsWide, return _gsWindowDevice; } -- (CGContextRef) cgContext +- (CGContextRef) CGContext { return _backingCGContext ? _backingCGContext : _x11CGContext; } +- (CGContextRef) backingCGContext +{ + return _backingCGContext; +} +- (CGContextRef) x11CGContext +{ + return _x11CGContext; +} + - (void) handleExposeRect: (NSRect)rect { @@ -169,12 +195,13 @@ static CGContextRef createCGBitmapContext (int pixelsWide, #if 1 CGRect cgRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + + NSDebugLLog(@"OpalSurface", @"Exposing %@", NSStringFromRect(*(NSRect *)&cgRect)); + + cgRect = CGRectIntegral(cgRect); + cgRect = CGRectIntersection(cgRect, CGRectMake(0, 0, CGImageGetWidth(backingImage), CGImageGetHeight(backingImage))); CGRect subimageCGRect = cgRect; - //subimageCGRect.origin.y = CGImageGetHeight(backingImage) - cgRect.origin.y - cgRect.size.height; - - // TODO: opal might be able to provide a variant of DrawImage that does - // not require creating a subimage CGImageRef subImage = CGImageCreateWithImageInRect(backingImage, subimageCGRect); CGContextSaveGState(_x11CGContext); @@ -182,7 +209,8 @@ static CGContextRef createCGBitmapContext (int pixelsWide, OPContextSetIdentityCTM(_x11CGContext); cgRect.origin.y = [self device]->buffer_height - cgRect.origin.y - cgRect.size.height; - NSDebugLLog(@"OpalSurface", @"Painting from %@ to %@", NSStringFromRect(*(NSRect *)&subimageCGRect), NSStringFromRect(*(NSRect *)&cgRect)); + NSDebugLLog(@"OpalSurface", @" ... actually from %@ to %@", NSStringFromRect(*(NSRect *)&subimageCGRect), NSStringFromRect(*(NSRect *)&cgRect)); + CGContextDrawImage(_x11CGContext, cgRect, subImage); @@ -200,8 +228,11 @@ static CGContextRef createCGBitmapContext (int pixelsWide, CGContextDrawImage(_x11CGContext, CGRectMake(0, 0, [self device]->buffer_width, [self device]->buffer_height), backingImage); #endif + +#if 0 [self _saveImage: backingImage withPrefix:@"/tmp/opalback-backing-" size: CGSizeZero]; [self _saveImage: subImage withPrefix:@"/tmp/opalback-subimage-" size: subimageCGRect.size ]; +#endif CGImageRelease(backingImage); CGImageRelease(subImage); @@ -212,7 +243,7 @@ static CGContextRef createCGBitmapContext (int pixelsWide, - (void) _saveImage: (CGImageRef) img withPrefix: (NSString *) prefix size: (CGSize) size { -#if 0 +#if 1 #warning Saving debug images #if 1 @@ -247,19 +278,10 @@ static CGContextRef createCGBitmapContext (int pixelsWide, return YES; } -- (void) dummyDraw +- (NSSize) size { - - NSDebugLLog(@"OpalSurface", @"performing dummy draw"); - - CGContextSaveGState([self cgContext]); - - CGRect r = CGRectMake(0, 0, 1024, 1024); - CGContextSetRGBFillColor([self cgContext], 1, 0, 0, 1); - CGContextFillRect([self cgContext], r); - - CGContextRestoreGState([self cgContext]); - + return NSMakeSize(_gsWindowDevice->buffer_width, + _gsWindowDevice->buffer_height); } @end