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