1 // -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 // This file is part of the LibreOffice project.
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #import <UIKit/UIKit.h>
13 #import "MLOMainViewController.h"
14 #import "MLORenderManager_Impl.h"
15 #import "MLORenderBuffer.h"
16 #import "MLOScalingBuffer.h"
17 #import "MLOGestureLimiter.h"
18 #import "MLOGestureEngine_Impl.h"
19 #import "MLOSelectionViewController.h"
20 #import "NSObject+MLOUtils.h"
21 #import "MLOPostRenderManager.h"
23 #import <QuartzCore/QuartzCore.h>
25 #include <touch/touch.h>
27 static MLORenderManager * instance = nil;
30 HORIZONAL_BUFFER_SCALE=1.0f,
31 VERTICAL_BUFFER_SCALE=1.0f,
32 BUFFER_SCALE_BIAS =1.0f,
34 RENDERING_BIAS_RATIO = 1.0f/(BUFFER_SCALE_BIAS*HORIZONAL_BUFFER_SCALE *VERTICAL_BUFFER_SCALE),
36 MAX_RENDER_PER_SECOND_ON_DEFAULT_ZOOM =4 * RENDERING_BIAS_RATIO,
37 MAX_RENDER_PER_SECOND_ON_MAX_ZOOM_IN=3 * RENDERING_BIAS_RATIO;
39 static const NSTimeInterval
40 LO_PAN_RENDER_MAX_DURATION = 0.3f,
41 LO_PINCH_RENDER_MAX_DURATION = 0.5f,
42 RESET_TRANSFORM_ANIMATION_DURATION=0.1f,
43 LO_RENDER_BACKOFF_MIN = 1.0f / MAX_RENDER_PER_SECOND_ON_DEFAULT_ZOOM,
44 LO_RENDER_BACKOFF_MAX = 1.0f / MAX_RENDER_PER_SECOND_ON_MAX_ZOOM_IN,
45 LO_RENDER_BACK_OFF_MAX_DELTA = LO_RENDER_BACKOFF_MAX - LO_RENDER_BACKOFF_MIN;
49 static const NSInteger BUFFER_COUNT=2;
51 #define MLOGestureDirectionString(enum) [@[@"X",@"Y",@"Z"] objectAtIndex:enum]
53 @interface MLORenderManager ()
54 @property MLOGestureEngine * gestureEngine;
55 @property MLOPostRenderManager * post;
56 @property NSArray * buffers;
57 @property NSInteger activeBufferIndex,nextBufferIndex,frameIdCounter;
58 @property NSTimeInterval bufferTransfromResetDeadline,renderBlockReleaseTime;
59 @property CGFloat inRenderTiltX,inRenderTiltY, inRenderTiltScale;
62 @implementation MLORenderManager
64 +(MLORenderManager *) getInstance{
66 instance = [MLORenderManager new];
71 -(MLORenderBuffer *)getBufferAtIndex:(NSInteger) index{
72 return [_buffers objectAtIndex:index];
78 self.gestureEngine = nil;
79 [self createBufffers];
80 self.renderBlockReleaseTime = 0;
81 self.activeBufferIndex = 0;
82 self.nextBufferIndex = _activeBufferIndex + 1;
83 [self loRenderWillBegin];
84 self.view.backgroundColor = [UIColor whiteColor];
85 self.currentGesture = NO_GESTURE;
90 -(void) createBufffers{
91 NSMutableArray * array = [NSMutableArray new];
93 for (NSInteger i = 0 ; i < BUFFER_COUNT; i++) {
95 [array addObject:[[MLORenderBuffer alloc] initWithArrayIndex: i renderManager:self]];
98 self.buffers = [NSArray arrayWithArray:array];
100 for (NSInteger i = 0 ; i < BUFFER_COUNT; i++) {
102 MLORenderBuffer * buffer = [_buffers objectAtIndex:i];
104 NSInteger previousIndex = BUFFER_COUNT -1;
108 buffer.previous = [_buffers objectAtIndex:previousIndex];
109 [self.view addSubview:buffer];
113 -(void)showLibreOffice:(MLOGestureEngine *) gestureEngine{
114 self.gestureEngine = gestureEngine;
117 -(void)hideLibreOffice{
118 self.currentGesture = NO_GESTURE;
120 self.gestureEngine = nil;
123 -(void) panDeltaX:(CGFloat) deltaX deltaY:(CGFloat) deltaY{
125 self.currentGesture = PAN;
127 if(deltaX || deltaY){
128 [[self getActiveBuffer] moveDeltaX:deltaX deltaY:deltaY];
130 self.inRenderTiltX+=deltaX;
131 self.inRenderTiltY+=deltaY;
135 -(void) pinchDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY scale:(CGFloat)scale{
137 if(ENABLE_PINCH_RENDERING_VIA_IOS){
139 self.currentGesture = PINCH;
141 if(self.scaler ==nil){
142 self.scaler = [[MLOScalingBuffer alloc] initWithRenderManager:self];
145 [self.scaler scale:scale deltaX:deltaX deltaY:deltaY];
147 self.inRenderTiltScale*=scale;
148 self.inRenderTiltX = self.inRenderTiltX*scale +deltaX;
149 self.inRenderTiltY = self.inRenderTiltY*scale +deltaY;
155 self.currentGesture = NO_GESTURE;
156 NSLog(@"RenderManager: self.currentGesture = NO_GESTURE");
159 -(CGPoint) getShiftFromCanvasCenter{
161 CGPoint bufferCenter= [self currentBufferCenter];
162 CGPoint canvasCenter = _gestureEngine.mainViewController.canvas.center;
164 return CGPointMake(bufferCenter.x - canvasCenter.x,
165 bufferCenter.y - canvasCenter.y);
168 -(CGPoint) currentBufferCenter{
169 if(self.currentGesture ==PINCH){
170 return self.scaler.center;
172 return [self getActiveBuffer].center;
175 -(void)loRenderWillBegin{
176 self.inRenderTiltX = NO_MOVE_DELTA;
177 self.inRenderTiltY = NO_MOVE_DELTA;
178 self.inRenderTiltScale = NO_SCALE;
181 -(void)setWidth:(NSInteger) width height:(NSInteger) height{
183 self.view.frame = CGRectMake(0,0, width,height);
185 CGFloat bufferWidth = width*HORIZONAL_BUFFER_SCALE;
186 CGFloat bufferHeight = height*VERTICAL_BUFFER_SCALE;
187 self.bufferFrame = CGRectMake(0,0, bufferWidth,bufferHeight);
189 for (MLORenderBuffer * buffer in _buffers) {
190 buffer.frame = self.bufferFrame;
193 touch_lo_set_view_size(bufferWidth,bufferHeight);
195 -(MLORenderBuffer *) getActiveBuffer{
196 return[_buffers objectAtIndex:self.activeBufferIndex];
199 -(MLORenderBuffer *) getNextBuffer{
200 return[_buffers objectAtIndex:self.nextBufferIndex];
203 -(void) renderInContext:(CGContextRef) context{
204 // used for magnification
205 [[self getActiveBuffer].layer renderInContext:context];
209 self.nextBufferIndex = (self.nextBufferIndex +1)% BUFFER_COUNT;
210 self.activeBufferIndex = (self.activeBufferIndex +1)% BUFFER_COUNT;
213 -(void)swapPreviousBuffer:(MLORenderingUIView*) previous withNextBuffer:(MLORenderBuffer *) next{
215 NSTimeInterval bufferTransformResetDelay = [self getBufferTransformResetDelay];
216 NSTimeInterval bufferTransformResetDeadline =CACurrentMediaTime() + bufferTransformResetDelay;
218 self.bufferTransfromResetDeadline = bufferTransformResetDeadline;
220 if(self.scaler && self.scaler.didRender){
221 previous = self.scaler;
224 [self showBuffer:next];
232 -(NSTimeInterval) getBufferTransformResetDelay{
234 switch(self.currentGesture){
235 case PAN: return LO_PAN_RENDER_MAX_DURATION;
236 case PINCH: return LO_PINCH_RENDER_MAX_DURATION;
237 case NO_GESTURE: return 0;
241 -(void)showBuffer:(MLORenderBuffer *) buffer{
246 -(CGFloat) currentZoomRatio{
247 return [_gestureEngine.limiter zoom] / MAX_ZOOM;
250 -(void) renderWithRect:(CGRect) rect{
252 if(ENABLE_LO_DESKTOP){
254 switch(self.currentGesture){
257 NSTimeInterval now = CACurrentMediaTime();
259 NSTimeInterval delta = LO_RENDER_BACKOFF_MIN +
260 LO_RENDER_BACK_OFF_MAX_DELTA * [self currentZoomRatio];
262 NSTimeInterval releaseTime = now + delta;
264 NSTimeInterval currentReleaseTime = self.renderBlockReleaseTime;
266 NSInteger currentFrameId = self.frameIdCounter++;
268 if(now > currentReleaseTime){
270 [self pereodicRender:rect releaseTime:releaseTime];
274 [self performBlock:^{
276 if((self.renderBlockReleaseTime == currentReleaseTime)
277 && (currentFrameId==0)){
279 [self pereodicRender:rect releaseTime:releaseTime];
287 [[self getNextBuffer] setNeedsDisplayInRect:rect];
294 [[self getNextBuffer] setNeedsDisplayInRect:self.bufferFrame];
297 -(void)pereodicRender:(CGRect) rect releaseTime:(NSTimeInterval) releaseTime{
299 static NSTimeInterval lastRender = 0;
301 [[self getNextBuffer] setNeedsDisplayInRect:rect];
303 self.frameIdCounter = 0;
305 self.renderBlockReleaseTime =releaseTime;
307 NSTimeInterval now = CACurrentMediaTime();
308 NSLog(@"Render interval %f",now - lastRender);
317 // Functions called in the LO thread, which thus need to dispatch any
318 // CocoaTouch activity to happen on the GUI thread. Use
319 // dispatch_async() consistently.
322 void touch_ui_damaged(int minX, int minY, int width, int height)
324 CGRect rect = CGRectMake(minX, minY, width, height);
326 dispatch_async(dispatch_get_main_queue(), ^{
328 [[MLORenderManager getInstance] renderWithRect:rect];
331 // NSLog(@"lo_damaged: %dx%d@(%d,%d)", (int)rect.size.width, (int)rect.size.height, (int)rect.origin.x, (int)rect.origin.y);
336 // vim:set shiftwidth=4 softtabstop=4 expandtab: