This is Morpher.m in view mode; [Download] [Up]
// Morpher.m // // created by Martin Wennerberg on Sun 08-Sep-1996 // // when who modification #import "Morpher.h" #import "MorphLine.h" #import <AppKit/AppKit.h> #import "algebra.h" #import "NSBitmapImageRep_editing.h" #define SMALL_FLOAT 0.00001 #define CONTROL 0.001 #define RANGE 1.25 #define SCALE 1000 static struct RGBA mixRGBA(struct RGBA a, struct RGBA b, float d) { struct RGBA res; d = MIN(d, 1); d = MAX(d, 0); res.r = a.r + (b.r - a.r) * d; res.g = a.g + (b.g - a.g) * d; res.b = a.b + (b.b - a.b) * d; res.a = a.a + (b.a - a.a) * d; return res; } static inline NSPoint transform (NSPoint p, NSAffineTransformStruct m) { NSPoint res; res.x = p.x * m.m11 + p.y * m.m12 + m.tX; res.y = p.x * m.m21 + p.y * m.m22 + m.tY; return res; } static NSArray *copyObjectsInArray(NSArray *source) { NSMutableArray *dest = [NSMutableArray arrayWithCapacity:[source count]]; NSEnumerator *sourceEnum = [source objectEnumerator]; MorphLine *sourceObj; while ((sourceObj = [sourceEnum nextObject])) [dest addObject:[[sourceObj copy] autorelease]]; return dest; } @implementation Morpher - initWithFirstBitmap:(NSBitmapImageRep *)im0 lastBitmap:(NSBitmapImageRep *)im1 morphLines:(NSArray *) lines { self = [super init]; if (!self) return nil; firstBitmap = [im0 retain]; lastBitmap = [im1 retain]; // We currently only support 8 bit per color images if ([firstBitmap bitsPerPixel] / [firstBitmap samplesPerPixel] != 8) NSLog(@"Unsupported image format. Convert first image to 8 bit per color."); if ([lastBitmap bitsPerPixel] / [lastBitmap samplesPerPixel] != 8) NSLog(@"Unsupported image format. Convert last image to 8 bit per color."); morphLines = copyObjectsInArray(lines); return self; } - (NSAffineTransformStruct) transformFromDelta:(float)beforeDelta toDelta:(float)afterDelta control:(MorphLine *)line { NSAffineTransformStruct m; float a1, a2; float cosa1, cosa2, sina1, sina2; float w1, h1, w2, h2; NSPoint P1, P2; float s; /* scale */ memset (&m, 0, sizeof(m)); P1 = [line startPointAtDelta:beforeDelta]; P2 = [line startPointAtDelta:afterDelta]; w1 = [line endPointAtDelta:beforeDelta].x - P1.x; h1 = [line endPointAtDelta:beforeDelta].y - P1.y; w2 = [line endPointAtDelta:afterDelta].x - P2.x; h2 = [line endPointAtDelta:afterDelta].y - P2.y; s = sqrt(w2 * w2 + h2 * h2) / sqrt(w1 * w1 + h1 * h1); a1 = atan2(h1, w1); a2 = atan2(h2, w2); cosa1 = cos(a1); cosa2 = cos(a2); sina1 = sin(a1); sina2 = sin(a2); m.m11 = s * cosa2 * cosa1 + sina2 * sina1; m.m12 = s * cosa2 * sina1 - sina2 * cosa1; m.m21 = s * sina2 * cosa1 - cosa2 * sina1; m.m22 = s * sina2 * sina1 + cosa2 * cosa1; m.tX = m.m11 * (-P1.x) + m.m12 * (-P1.y) + P2.x; m.tY = m.m21 * (-P1.x) + m.m22 * (-P1.y) + P2.y; return m; } - (void)calcMorphBitmap:(NSBitmapImageRep *) bitmap atDelta:(float) delta { MorphLine *morphLine; NSAffineTransformStruct *trans0; // transformation from 0 to delta NSAffineTransformStruct *trans1; // transformation from 1 to delta NSPoint dp; // destination point NSPoint p0; // source point at 0 NSPoint p1; // source point at 1 struct RGBA RGBA0, RGBA1; // color at 0 and 1 struct RGBA rgba; // destination color (this is what we're trying to calculate) int lineCount; int i; float dissolveDelta; struct LineCoords *PdQd; struct LineCoords *destLineCoordinates; NSPoint D; /* Displacement */ float weight, weightsum; NSPoint DSUM0, DSUM1; /* Displacement sum for the to images */ float dissolveStartSum, dissolveEndSum; float dissolveStartDelta, dissolveEndDelta; progress = 0; lineCount = [morphLines count]; trans0 = (NSAffineTransformStruct *) alloca (lineCount * sizeof(NSAffineTransformStruct)); trans1 = (NSAffineTransformStruct *) alloca (lineCount * sizeof(NSAffineTransformStruct)); destLineCoordinates = (struct LineCoords *) alloca (lineCount * sizeof(struct LineCoords)); [self fillTransMatrixes:trans0 withDeltaFrom:delta to:0]; [self fillTransMatrixes:trans1 withDeltaFrom:delta to:1]; [self fillDestLineCoordinates:destLineCoordinates delta:delta]; // Loop through all pixels in the destination bitmap and set them to the morphed colors for (dp.y = 0; dp.y < 1; dp.y += 1.0 / [bitmap size].height) { progress = dp.y / [bitmap size].height; for (dp.x = 0; dp.x < 1; dp.x += 1.0 / [bitmap size].width) { /* Calc the SourcePoints p0 and p1 */ D = NSZeroPoint; weightsum = 0.0; DSUM0 = NSZeroPoint; DSUM1 = NSZeroPoint; dissolveStartSum = 0.0; dissolveEndSum = 0.0; for (i = 0; i < lineCount; ++i) { morphLine = [morphLines objectAtIndex:i]; PdQd = &destLineCoordinates[i]; /* calculate the source points to sample */ p0 = transform (dp, trans0[i]); p1 = transform (dp, trans1[i]); /* calculate the weight */ D = pt_sub (p0, dp); weight = 1.0 / line_dist2_to_point (PdQd->start, PdQd->end, dp); DSUM0 = pt_sum (DSUM0, pt_scale (D, weight, weight)); weightsum += weight; D = pt_sub (p1, dp); DSUM1 = pt_sum (DSUM1, pt_scale (D, weight, weight)); dissolveStartSum += morphLine->dissolveStartDelta * weight; dissolveEndSum += morphLine->dissolveEndDelta * weight; } /* average (by weight) the source points */ p0 = pt_sum(dp, pt_scale(DSUM0, 1.0 / weightsum, 1.0 / weightsum)); p1 = pt_sum(dp, pt_scale(DSUM1, 1.0 / weightsum, 1.0 / weightsum)); dissolveStartDelta = dissolveStartSum / weightsum; dissolveEndDelta = dissolveEndSum / weightsum; /* make sure 0 <= dissolveDelta <= 1 */ if (ABS(dissolveEndDelta - dissolveStartDelta) <= SMALL_FLOAT ) if (dissolveStartDelta == 0.0) dissolveDelta = 1.0; else if (delta <= dissolveStartDelta) dissolveDelta = 0.0; else dissolveDelta = 11.0; else if (delta <= dissolveStartDelta) dissolveDelta = 0.0; else { dissolveDelta = (delta - dissolveStartDelta) / (dissolveEndDelta - dissolveStartDelta); dissolveDelta = MIN (dissolveDelta, 1.0); dissolveDelta = MAX (dissolveDelta, 0.0); } RGBA0 = [firstBitmap RGBAAtPoint:pt_scale (p0, [firstBitmap size].width, [firstBitmap size].height)]; RGBA1 = [lastBitmap RGBAAtPoint:pt_scale ( p1, [lastBitmap size].width, [lastBitmap size].height)]; rgba = mixRGBA (RGBA0, RGBA1, dissolveDelta); [bitmap setRGBA:rgba atPoint:pt_scale ( dp, [bitmap size].width, [bitmap size].height)]; } } progress = 1.0; } - (void) fillTransMatrixes:(NSAffineTransformStruct *) matrixes withDeltaFrom:(float) beforeDelta to:(float) afterDelta { int i; MorphLine *line; for (i = 0; i < [morphLines count]; ++i) { line = [morphLines objectAtIndex:i]; matrixes[i] = [self transformFromDelta:beforeDelta toDelta:afterDelta control:line]; // matrixes[i] = [[line transformFromDelta:beforeDelta toDelta:afterDelta] transformStruct]; } } - (void) fillDestLineCoordinates:(struct LineCoords *)lines delta:(float)delta { int i; MorphLine *morphLine; for (i = 0; i < [morphLines count]; ++i) { morphLine = [morphLines objectAtIndex:i]; lines[i].start = [morphLine startPointAtDelta:delta]; lines[i].end = [morphLine endPointAtDelta:delta]; } } - (float) progress { return progress; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.