1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/cocoa/throbber_view.h"
#include "base/logging.h"
const float kAnimationIntervalSeconds = 0.03; // 30ms, same as windows
@interface ThrobberView(PrivateMethods)
- (void)animate;
@end
// A very simple object that is the target for the animation timer so that
// the view isn't. We do this to avoid retain cycles as the timer
// retains its target.
@interface TimerTarget : NSObject {
@private
ThrobberView* throbber_; // Weak, owns us
}
- (id)initWithThrobber:(ThrobberView*)view;
@end
@implementation TimerTarget
- (id)initWithThrobber:(ThrobberView*)view {
if ((self = [super init])) {
throbber_ = view;
}
return self;
}
- (void)animate:(NSTimer*)timer {
[throbber_ animate];
}
@end
@implementation ThrobberView
- (id)initWithFrame:(NSRect)frame image:(NSImage*)image {
if ((self = [super initWithFrame:frame])) {
[self setImage:image];
}
return self;
}
- (void)dealloc {
[timer_ invalidate];
[super dealloc];
}
- (void)removeFromSuperview {
[timer_ invalidate];
timer_ = nil;
[super removeFromSuperview];
}
// Called when the TimerTarget gets tickled by our timer. Increment the frame
// counter and mark as needing display.
- (void)animate {
animationFrame_ = ++animationFrame_ % numFrames_;
[self setNeedsDisplay:YES];
}
// Overridden to draw the appropriate frame in the image strip.
- (void)drawRect:(NSRect)rect {
float imageDimension = [image_ extent].size.height;
float xOffset = animationFrame_ * imageDimension;
NSRect sourceImageRect =
NSMakeRect(xOffset, 0, imageDimension, imageDimension);
[image_ drawInRect:[self bounds]
fromRect:sourceImageRect
operation:NSCompositeSourceOver
fraction:1.0];
}
// Stores the internal representation of the image from |image|. We use
// CoreImage for speed (though this doesn't seem to help perf issues). We
// validate that the image is of the appropriate ratio. If the image has more
// than one frame, restarts the timer.
- (void)setImage:(NSImage*)image {
// Reset the animation counter so there's no chance we are off the end.
animationFrame_ = 0;
[timer_ invalidate];
timer_ = nil;
// Ensure that the height divides evenly into the width. Cache the
// number of frames in the animation for later.
NSSize imageSize = [image size];
DCHECK(imageSize.height && imageSize.width);
if (!imageSize.height)
return;
DCHECK((int)imageSize.width % (int)imageSize.height == 0);
numFrames_ = (int)imageSize.width / (int)imageSize.height;
DCHECK(numFrames_);
// First check if we have a bitmap image rep and use it, otherwise fall
// back to creating one.
NSBitmapImageRep* rep = [[image representations] objectAtIndex:0];
if (![rep isKindOfClass:[NSBitmapImageRep class]]) {
[image lockFocus];
NSRect imageRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
rep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:imageRect]
autorelease];
[image unlockFocus];
}
image_.reset([[CIImage alloc] initWithBitmapImageRep:rep]);
#if 0
// TODO(pinkerton): The invalidation of the view to trigger re-draw causes
// the entire title-bar to redraw (you can see it with QuartzDebug). For some
// reason, setting isOpaque on this view, or any of its parent views, doesn't
// help. As a result, enabling this timer causes new tab to take a very long
// time on a loaded machine, crushing our perf bot when it's under load. For
// now, I'm disabling the timer so we draw the first frame of the animation,
// but nothing more. There are a couple of ways we can fix this:
// 1) Try to figure out why the invalidate is invalidating the entire title bar
// 2) Find some way to draw only the pixels we want and nothing else, but I
// don't know how we'd do that.
if (numFrames_ > 1) {
// Start a timer for the animation frames.
target_.reset([[TimerTarget alloc] initWithThrobber:self]);
timer_ =
[NSTimer scheduledTimerWithTimeInterval:kAnimationIntervalSeconds
target:target_.get()
selector:@selector(animate:)
userInfo:nil
repeats:YES];
}
#endif
}
@end
|