1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#if !defined(__has_feature) || !__has_feature(objc_arc) 6#error "This file requires ARC support." 7#endif 8 9#import "remoting/ios/ui/cursor_texture.h" 10 11@implementation CursorTexture 12 13- (id)init { 14 self = [super init]; 15 if (self) { 16 _needCursorRedraw = NO; 17 _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(0, 0, 0, 0); 18 } 19 return self; 20} 21 22- (const webrtc::DesktopSize&)textureSize { 23 return _textureSize; 24} 25 26- (void)setTextureSize:(const webrtc::DesktopSize&)size { 27 if (!_textureSize.equals(size)) { 28 _textureSize.set(size.width(), size.height()); 29 _needInitialize = true; 30 } 31} 32 33- (const webrtc::MouseCursor&)cursor { 34 return *_cursor.get(); 35} 36 37- (void)setCursor:(webrtc::MouseCursor*)cursor { 38 _cursor.reset(cursor); 39 40 if (_cursor.get() != NULL && _cursor->image().data()) { 41 _needCursorRedraw = true; 42 } 43} 44 45- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty { 46 glGenTextures(1, &_textureId); 47 [Utility bindTextureForIOS:_textureId]; 48 49 // This is the Cursor layer, and is stamped on top of Desktop as a 50 // transparent image 51 effectProperty.target = GLKTextureTarget2D; 52 effectProperty.name = _textureId; 53 effectProperty.envMode = GLKTextureEnvModeDecal; 54 effectProperty.enabled = GL_TRUE; 55 56 [Utility logGLErrorCode:@"CursorTexture bindToTexture"]; 57 // Release context 58 glBindTexture(GL_TEXTURE_2D, 0); 59} 60 61- (BOOL)needDrawAtPosition:(const webrtc::DesktopVector&)position { 62 return (_cursor.get() != NULL && 63 (_needInitialize || _needCursorRedraw == YES || 64 _cursorDrawnToGL.left() != position.x() - _cursor->hotspot().x() || 65 _cursorDrawnToGL.top() != position.y() - _cursor->hotspot().y())); 66} 67 68- (void)drawWithMousePosition:(const webrtc::DesktopVector&)position { 69 if (_textureSize.height() == 0 && _textureSize.width() == 0) { 70 return; 71 } 72 73 [Utility bindTextureForIOS:_textureId]; 74 75 if (_needInitialize) { 76 glTexImage2D(GL_TEXTURE_2D, 77 0, 78 GL_RGBA, 79 _textureSize.width(), 80 _textureSize.height(), 81 0, 82 GL_RGBA, 83 GL_UNSIGNED_BYTE, 84 NULL); 85 86 [Utility logGLErrorCode:@"CursorTexture initializeTextureSurfaceWithSize"]; 87 _needInitialize = false; 88 } 89 // When the cursor needs to be redraw in a different spot then we must clear 90 // the previous area. 91 92 DCHECK([self needDrawAtPosition:position]); 93 94 if (_cursorDrawnToGL.width() > 0 && _cursorDrawnToGL.height() > 0) { 95 webrtc::BasicDesktopFrame transparentCursor(_cursorDrawnToGL.size()); 96 97 if (transparentCursor.data() != NULL) { 98 DCHECK(transparentCursor.kBytesPerPixel == 99 _cursor->image().kBytesPerPixel); 100 memset(transparentCursor.data(), 101 0, 102 transparentCursor.stride() * transparentCursor.size().height()); 103 104 [Utility drawSubRectToGLFromRectOfSize:_textureSize 105 subRect:_cursorDrawnToGL 106 data:transparentCursor.data()]; 107 108 // there is no longer any cursor drawn to screen 109 _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(0, 0, 0, 0); 110 } 111 } 112 113 if (_cursor.get() != NULL) { 114 115 CGRect screen = 116 CGRectMake(0.0, 0.0, _textureSize.width(), _textureSize.height()); 117 CGRect cursor = CGRectMake(position.x() - _cursor->hotspot().x(), 118 position.y() - _cursor->hotspot().y(), 119 _cursor->image().size().width(), 120 _cursor->image().size().height()); 121 122 if (CGRectContainsRect(screen, cursor)) { 123 _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(cursor.origin.x, 124 cursor.origin.y, 125 cursor.size.width, 126 cursor.size.height); 127 128 [Utility drawSubRectToGLFromRectOfSize:_textureSize 129 subRect:_cursorDrawnToGL 130 data:_cursor->image().data()]; 131 132 } else if (CGRectIntersectsRect(screen, cursor)) { 133 // Some of the cursor falls off screen, need to clip it 134 CGRect intersection = CGRectIntersection(screen, cursor); 135 _cursorDrawnToGL = 136 webrtc::DesktopRect::MakeXYWH(intersection.origin.x, 137 intersection.origin.y, 138 intersection.size.width, 139 intersection.size.height); 140 141 webrtc::BasicDesktopFrame partialCursor(_cursorDrawnToGL.size()); 142 143 if (partialCursor.data()) { 144 DCHECK(partialCursor.kBytesPerPixel == _cursor->image().kBytesPerPixel); 145 146 uint32_t src_stride = _cursor->image().stride(); 147 uint32_t dst_stride = partialCursor.stride(); 148 149 uint8_t* source = _cursor->image().data(); 150 source += abs((static_cast<int32_t>(cursor.origin.y) - 151 _cursorDrawnToGL.top())) * 152 src_stride; 153 source += abs((static_cast<int32_t>(cursor.origin.x) - 154 _cursorDrawnToGL.left())) * 155 _cursor->image().kBytesPerPixel; 156 uint8_t* dst = partialCursor.data(); 157 158 for (uint32_t y = 0; y < _cursorDrawnToGL.height(); y++) { 159 memcpy(dst, source, dst_stride); 160 source += src_stride; 161 dst += dst_stride; 162 } 163 164 [Utility drawSubRectToGLFromRectOfSize:_textureSize 165 subRect:_cursorDrawnToGL 166 data:partialCursor.data()]; 167 } 168 } 169 } 170 171 _needCursorRedraw = false; 172 [Utility logGLErrorCode:@"CursorTexture drawWithMousePosition"]; 173 // Release context 174 glBindTexture(GL_TEXTURE_2D, 0); 175} 176 177- (void)releaseTexture { 178 glDeleteTextures(1, &_textureId); 179} 180 181@end 182