blob: 180158093c57ac88b35d32dd096d0d8e162045e6 [file] [log] [blame]
yangsu@google.comc5aeccd2011-07-17 14:42:08 +00001#import "SkUIView.h"
2#include <QuartzCore/QuartzCore.h>
3
4//#define SKWIND_CONFIG SkBitmap::kRGB_565_Config
5#define SKWIND_CONFIG SkBitmap::kARGB_8888_Config
6#define SKGL_CONFIG kEAGLColorFormatRGB565
7//#define SKGL_CONFIG kEAGLColorFormatRGBA8
8
9#define FORCE_REDRAW
10
11//#define USE_GL_1
12//#define USE_GL_2
13#if defined(USE_GL_1) || defined(USE_GL_2)
14#define USE_GL
15#endif
16
17#include "SkCanvas.h"
18#include "GrContext.h"
19#include "GrGLInterface.h"
20#include "SkGpuDevice.h"
21#include "SkCGUtils.h"
22
23SkiOSDeviceManager::SkiOSDeviceManager() {
24 fGrContext = NULL;
25 fGrRenderTarget = NULL;
26 usingGL = false;
27}
28SkiOSDeviceManager::~SkiOSDeviceManager() {
29 SkSafeUnref(fGrContext);
30 SkSafeUnref(fGrRenderTarget);
31}
32
33void SkiOSDeviceManager::init(SampleWindow* win) {
34 win->attachGL();
35 if (NULL == fGrContext) {
36#if defined(USE_GL_1)
37 fGrContext = GrContext::Create(kOpenGL_Fixed_GrEngine, NULL);
38#elsif defined(USE_GL_2)
39 fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
40#endif
41 }
42 fGrRenderTarget = SkGpuDevice::Current3DApiRenderTarget();
43 if (NULL == fGrContext) {
44 SkDebugf("Failed to setup 3D");
45 win->detachGL();
46 }
47}
48bool SkiOSDeviceManager::supportsDeviceType(SampleWindow::DeviceType dType) {
49 switch (dType) {
50 case SampleWindow::kRaster_DeviceType:
51 case SampleWindow::kPicture_DeviceType: // fallthru
52 return true;
53 case SampleWindow::kGPU_DeviceType:
54 return NULL != fGrContext;
55 default:
56 return false;
57 }
58}
59bool SkiOSDeviceManager::prepareCanvas(SampleWindow::DeviceType dType,
60 SkCanvas* canvas,
61 SampleWindow* win) {
62 if (SampleWindow::kGPU_DeviceType == dType) {
63 canvas->setDevice(new SkGpuDevice(fGrContext, fGrRenderTarget))->unref();
64 usingGL = true;
65 }
66 else {
67 //The clip needs to be applied with a device attached to the canvas
68 //canvas->setBitmapDevice(win->getBitmap());
69 usingGL = false;
70 }
71 return true;
72}
73
74void SkiOSDeviceManager::publishCanvas(SampleWindow::DeviceType dType,
75 SkCanvas* canvas,
76 SampleWindow* win) {
77 if (SampleWindow::kGPU_DeviceType == dType) {
78 fGrContext->flush();
79 }
80 else {
81 CGContextRef cg = UIGraphicsGetCurrentContext();
82 SkCGDrawBitmap(cg, win->getBitmap(), 0, 0);
83 }
84 win->presentGL();
85}
86////////////////////////////////////////////////////////////////////////////////
87@implementation SkUIView
88
89@synthesize fWind, fTitle, fTitleItem;
90
91#include "SkApplication.h"
92#include "SkEvent.h"
93#include "SkWindow.h"
94
95static float gScreenScale = 1;
96
97#define kREDRAW_UIVIEW_GL "sk_redraw_uiview_gl_iOS"
98
99static const float SCALE_FOR_ZOOM_LENS = 4.0;
100#define Y_OFFSET_FOR_ZOOM_LENS 200
101#define SIZE_FOR_ZOOM_LENS 250
102
103static const float MAX_ZOOM_SCALE = 4.0;
104static const float MIN_ZOOM_SCALE = 2.0 / MAX_ZOOM_SCALE;
105
106extern bool gDoTraceDraw;
107#define DO_TRACE_DRAW_MAX 100
108
109struct FPSState {
110 static const int FRAME_COUNT = 60;
111
112 CFTimeInterval fNow0, fNow1;
113 CFTimeInterval fTime0, fTime1, fTotalTime;
114 int fFrameCounter;
115 int fDrawCounter;
116 SkString str;
117 FPSState() {
118 fTime0 = fTime1 = fTotalTime = 0;
119 fFrameCounter = 0;
120 }
121
122 void startDraw() {
123 fNow0 = CACurrentMediaTime();
124
125 if (0 == fDrawCounter && false) {
126 gDoTraceDraw = true;
127 SkDebugf("\n");
128 }
129 }
130
131 void endDraw() {
132 fNow1 = CACurrentMediaTime();
133
134 if (0 == fDrawCounter) {
135 gDoTraceDraw = true;
136 }
137 if (DO_TRACE_DRAW_MAX == ++fDrawCounter) {
138 fDrawCounter = 0;
139 }
140 }
141
142 void flush(SkOSWindow* hwnd) {
143 CFTimeInterval now2 = CACurrentMediaTime();
144
145 fTime0 += fNow1 - fNow0;
146 fTime1 += now2 - fNow1;
147
148 if (++fFrameCounter == FRAME_COUNT) {
149 CFTimeInterval totalNow = CACurrentMediaTime();
150 fTotalTime = totalNow - fTotalTime;
151
152 //SkMSec ms0 = (int)(1000 * fTime0 / FRAME_COUNT);
153 //SkMSec msTotal = (int)(1000 * fTotalTime / FRAME_COUNT);
154 //str.printf(" ms: %d [%d], fps: %3.1f", msTotal, ms0,
155 // FRAME_COUNT / fTotalTime);
156 str.printf(" fps:%3.1f", FRAME_COUNT / fTotalTime);
157 hwnd->setTitle(NULL);
158 fTotalTime = totalNow;
159 fTime0 = fTime1 = 0;
160 fFrameCounter = 0;
161 }
162 }
163};
164
165static FPSState gFPS;
166
167#define FPS_StartDraw() gFPS.startDraw()
168#define FPS_EndDraw() gFPS.endDraw()
169#define FPS_Flush(wind) gFPS.flush(wind)
170
171
172///////////////////////////////////////////////////////////////////////////////
173#ifdef USE_GL
174+ (Class) layerClass {
175 return [CAEAGLLayer class];
176}
177#endif
178
179- (id)initWithMyDefaults {
180 fRedrawRequestPending = false;
181 fFPSState = new FPSState;
182#ifdef USE_GL
183 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
184 eaglLayer.opaque = TRUE;
185 eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
186 [NSNumber numberWithBool:NO],
187 kEAGLDrawablePropertyRetainedBacking,
188 SKGL_CONFIG,
189 kEAGLDrawablePropertyColorFormat,
190 nil];
191
192#ifdef USE_GL_1
193 fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
194#else
195 fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
196#endif
197
198 if (!fGL.fContext || ![EAGLContext setCurrentContext:fGL.fContext])
199 {
200 [self release];
201 return nil;
202 }
203
204 // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
205 glGenFramebuffers(1, &fGL.fFramebuffer);
206 glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
207
208 glGenRenderbuffers(1, &fGL.fRenderbuffer);
209 glGenRenderbuffers(1, &fGL.fStencilbuffer);
210
211 glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
212 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fGL.fRenderbuffer);
213
214 glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
215 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fGL.fStencilbuffer);
216#endif
217
218 fDevManager = new SkiOSDeviceManager;
219 fWind = new SampleWindow(self, NULL, NULL, fDevManager);
220 application_init();
221 fWind->resize(self.frame.size.width, self.frame.size.height, SKWIND_CONFIG);
222 fMatrix.reset();
223 fZoomAround = false;
224
225 return self;
226}
227
228- (id)initWithCoder:(NSCoder*)coder {
229 if ((self = [super initWithCoder:coder])) {
230 self = [self initWithMyDefaults];
231 }
232 return self;
233}
234
235- (id)initWithFrame:(CGRect)frame {
236 if (self = [super initWithFrame:frame]) {
237 self = [self initWithMyDefaults];
238 }
239 return self;
240}
241
242- (void)dealloc {
243 delete fWind;
244 delete fDevManager;
245 delete fFPSState;
246 application_term();
247 [fTitleItem release];
248 [super dealloc];
249}
250- (void)drawWithCanvas:(SkCanvas*)canvas {
251 fRedrawRequestPending = false;
252 fFPSState->startDraw();
253 fWind->draw(canvas);
254 fFPSState->endDraw();
255#ifdef FORCE_REDRAW
256 fWind->inval(NULL);
257#endif
258 fFPSState->flush(fWind);
259}
260
261///////////////////////////////////////////////////////////////////////////////
262
263- (void)layoutSubviews {
264 int W, H;
265 gScreenScale = [UIScreen mainScreen].scale;
266#ifdef USE_GL
267 CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer;
268 if ([self respondsToSelector:@selector(setContentScaleFactor:)]) {
269 self.contentScaleFactor = gScreenScale;
270 }
271
272 // Allocate color buffer backing based on the current layer size
273 glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
274 [fGL.fContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer];
275
276 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &fGL.fWidth);
277 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &fGL.fHeight);
278
279 glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
280 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fGL.fWidth, fGL.fHeight);
281
282 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
283 NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
284 }
285
286 W = fGL.fWidth;
287 H = fGL.fHeight;
288#else
289 CGRect rect = [self bounds];
290 W = (int)CGRectGetWidth(rect);
291 H = (int)CGRectGetHeight(rect);
292#endif
293
294 printf("---- layoutSubviews %d %d\n", W, H);
295 fWind->resize(W, H);
296 fWind->inval(NULL);
297}
298
299#ifdef USE_GL
300#include "SkDevice.h"
301
302- (void)drawInGL {
303 // This application only creates a single context which is already set current at this point.
304 // This call is redundant, but needed if dealing with multiple contexts.
305 [EAGLContext setCurrentContext:fGL.fContext];
306
307 // This application only creates a single default framebuffer which is already bound at this point.
308 // This call is redundant, but needed if dealing with multiple framebuffers.
309 glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
310
311 GLint scissorEnable;
312 glGetIntegerv(GL_SCISSOR_TEST, &scissorEnable);
313 glDisable(GL_SCISSOR_TEST);
314 glClearColor(0,0,0,0);
315 glClear(GL_COLOR_BUFFER_BIT);
316 if (scissorEnable) {
317 glEnable(GL_SCISSOR_TEST);
318 }
319 glViewport(0, 0, fWind->width(), fWind->height());
320
321
322 GrContext* ctx = fDevManager->getGrContext();
323 SkASSERT(NULL != ctx);
324
325 SkCanvas canvas;
326 canvas.setDevice(new SkGpuDevice(ctx, SkGpuDevice::Current3DApiRenderTarget()))->unref();
327
328 // if we're not "retained", then we have to always redraw everything.
329 // This call forces us to ignore the fDirtyRgn, and draw everywhere.
330 // If we are "retained", we can skip this call (as the raster case does)
331 fWind->forceInvalAll();
332
333 [self drawWithCanvas:&canvas];
334
335 // This application only creates a single color renderbuffer which is already bound at this point.
336 // This call is redundant, but needed if dealing with multiple renderbuffers.
337 glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
338 [fGL.fContext presentRenderbuffer:GL_RENDERBUFFER];
339
340#if GR_COLLECT_STATS
341// static int frame = 0;
342// if (!(frame % 100)) {
343// ctx->printStats();
344// }
345// ctx->resetStats();
346// ++frame;
347#endif
348}
349
350#else // raster case
351
352- (void)drawRect:(CGRect)rect {
353 SkCanvas canvas;
354 canvas.setBitmapDevice(fWind->getBitmap());
355 [self drawWithCanvas:&canvas];
356}
357#endif
358
359//Gesture Handlers
360- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
361 for (UITouch *touch in touches) {
362 CGPoint loc = [touch locationInView:self];
363 fWind->handleClick(loc.x, loc.y, SkView::Click::kDown_State, touch);
364 }
365}
366
367- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
368 for (UITouch *touch in touches) {
369 CGPoint loc = [touch locationInView:self];
370 fWind->handleClick(loc.x, loc.y, SkView::Click::kMoved_State, touch);
371 }
372}
373
374- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
375 for (UITouch *touch in touches) {
376 CGPoint loc = [touch locationInView:self];
377 fWind->handleClick(loc.x, loc.y, SkView::Click::kUp_State, touch);
378 }
379}
380
381- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
382 for (UITouch *touch in touches) {
383 CGPoint loc = [touch locationInView:self];
384 fWind->handleClick(loc.x, loc.y, SkView::Click::kUp_State, touch);
385 }
386}
387
388///////////////////////////////////////////////////////////////////////////////
389
390- (void)setSkTitle:(const char *)title {
391 NSString* text = [NSString stringWithUTF8String:title];
392 if ([text length] > 0)
393 self.fTitle = text;
394
395 if (fTitleItem && fTitle) {
396 fTitleItem.title = [NSString stringWithFormat:@"%@%@", fTitle,
397 [NSString stringWithUTF8String:fFPSState->str.c_str()]];
398 }
399}
400
401- (BOOL)onHandleEvent:(const SkEvent&)evt {
402#ifdef USE_GL
403 if (evt.isType(kREDRAW_UIVIEW_GL)) {
404 [self drawInGL];
405 return true;
406 }
407#endif
408 return false;
409}
410
411- (void)postInvalWithRect:(const SkIRect*)r {
412#ifdef USE_GL
413
414#if 1
415 if (!fRedrawRequestPending) {
416 fRedrawRequestPending = true;
417 /*
418 performSelectorOnMainThread seems to starve updating other views
419 (e.g. our FPS view in the titlebar), so we use the afterDelay
420 version
421 */
422 if (0) {
423 [self performSelectorOnMainThread:@selector(drawInGL) withObject:nil waitUntilDone:NO];
424 } else {
425 [self performSelector:@selector(drawInGL) withObject:nil afterDelay:0];
426 }
427 }
428#else
429 if (!fRedrawRequestPending) {
430 SkEvent* evt = new SkEvent(kREDRAW_UIVIEW_GL);
431 evt->post(fWind->getSinkID());
432 fRedrawRequestPending = true;
433 }
434#endif
435
436#else
437 if (r) {
438 [self setNeedsDisplayInRect:CGRectMake(r->fLeft, r->fTop,
439 r->width(), r->height())];
440 } else {
441 [self setNeedsDisplay];
442 }
443#endif
444}
445
446@end