• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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