diff options
Diffstat (limited to 'libs/cocos2d/CCRenderTexture.m')
-rwxr-xr-x | libs/cocos2d/CCRenderTexture.m | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/libs/cocos2d/CCRenderTexture.m b/libs/cocos2d/CCRenderTexture.m new file mode 100755 index 0000000..4a4768e --- /dev/null +++ b/libs/cocos2d/CCRenderTexture.m | |||
@@ -0,0 +1,340 @@ | |||
1 | /* | ||
2 | * cocos2d for iPhone: http://www.cocos2d-iphone.org | ||
3 | * | ||
4 | * Copyright (c) 2009 Jason Booth | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
7 | * of this software and associated documentation files (the "Software"), to deal | ||
8 | * in the Software without restriction, including without limitation the rights | ||
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
10 | * copies of the Software, and to permit persons to whom the Software is | ||
11 | * furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice shall be included in | ||
14 | * all copies or substantial portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | * THE SOFTWARE. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #import <Availability.h> | ||
27 | #import "CCRenderTexture.h" | ||
28 | #import "CCDirector.h" | ||
29 | #import "ccMacros.h" | ||
30 | #import "Support/ccUtils.h" | ||
31 | #import "Support/CCFileUtils.h" | ||
32 | |||
33 | @implementation CCRenderTexture | ||
34 | |||
35 | @synthesize sprite=sprite_; | ||
36 | |||
37 | // issue #994 | ||
38 | +(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format | ||
39 | { | ||
40 | return [[[self alloc] initWithWidth:w height:h pixelFormat:format] autorelease]; | ||
41 | } | ||
42 | |||
43 | +(id)renderTextureWithWidth:(int)w height:(int)h | ||
44 | { | ||
45 | return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888] autorelease]; | ||
46 | } | ||
47 | |||
48 | -(id)initWithWidth:(int)w height:(int)h | ||
49 | { | ||
50 | return [self initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888]; | ||
51 | } | ||
52 | |||
53 | -(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format | ||
54 | { | ||
55 | if ((self = [super init])) | ||
56 | { | ||
57 | NSAssert(format != kCCTexture2DPixelFormat_A8,@"only RGB and RGBA formats are valid for a render texture"); | ||
58 | |||
59 | w *= CC_CONTENT_SCALE_FACTOR(); | ||
60 | h *= CC_CONTENT_SCALE_FACTOR(); | ||
61 | |||
62 | glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); | ||
63 | |||
64 | // textures must be power of two | ||
65 | NSUInteger powW = ccNextPOT(w); | ||
66 | NSUInteger powH = ccNextPOT(h); | ||
67 | |||
68 | void *data = malloc((int)(powW * powH * 4)); | ||
69 | memset(data, 0, (int)(powW * powH * 4)); | ||
70 | pixelFormat_=format; | ||
71 | |||
72 | texture_ = [[CCTexture2D alloc] initWithData:data pixelFormat:pixelFormat_ pixelsWide:powW pixelsHigh:powH contentSize:CGSizeMake(w, h)]; | ||
73 | free( data ); | ||
74 | |||
75 | // generate FBO | ||
76 | ccglGenFramebuffers(1, &fbo_); | ||
77 | ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_); | ||
78 | |||
79 | // associate texture with FBO | ||
80 | ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0); | ||
81 | |||
82 | // check if it worked (probably worth doing :) ) | ||
83 | GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER); | ||
84 | if (status != CC_GL_FRAMEBUFFER_COMPLETE) | ||
85 | { | ||
86 | [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"]; | ||
87 | } | ||
88 | [texture_ setAliasTexParameters]; | ||
89 | |||
90 | sprite_ = [CCSprite spriteWithTexture:texture_]; | ||
91 | |||
92 | [texture_ release]; | ||
93 | [sprite_ setScaleY:-1]; | ||
94 | [self addChild:sprite_]; | ||
95 | |||
96 | // issue #937 | ||
97 | [sprite_ setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}]; | ||
98 | |||
99 | ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); | ||
100 | } | ||
101 | return self; | ||
102 | } | ||
103 | |||
104 | -(void)dealloc | ||
105 | { | ||
106 | // [self removeAllChildrenWithCleanup:YES]; | ||
107 | ccglDeleteFramebuffers(1, &fbo_); | ||
108 | [super dealloc]; | ||
109 | } | ||
110 | |||
111 | -(void)begin | ||
112 | { | ||
113 | // Save the current matrix | ||
114 | glPushMatrix(); | ||
115 | |||
116 | CGSize texSize = [texture_ contentSizeInPixels]; | ||
117 | |||
118 | |||
119 | // Calculate the adjustment ratios based on the old and new projections | ||
120 | CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; | ||
121 | float widthRatio = size.width / texSize.width; | ||
122 | float heightRatio = size.height / texSize.height; | ||
123 | |||
124 | |||
125 | // Adjust the orthographic propjection and viewport | ||
126 | ccglOrtho((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1); | ||
127 | glViewport(0, 0, texSize.width, texSize.height); | ||
128 | |||
129 | |||
130 | glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); | ||
131 | ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);//Will direct drawing to the frame buffer created above | ||
132 | |||
133 | // Issue #1145 | ||
134 | // There is no need to enable the default GL states here | ||
135 | // but since CCRenderTexture is mostly used outside the "render" loop | ||
136 | // these states needs to be enabled. | ||
137 | // Since this bug was discovered in API-freeze (very close of 1.0 release) | ||
138 | // This bug won't be fixed to prevent incompatibilities with code. | ||
139 | // | ||
140 | // If you understand the above mentioned message, then you can comment the following line | ||
141 | // and enable the gl states manually, in case you need them. | ||
142 | CC_ENABLE_DEFAULT_GL_STATES(); | ||
143 | } | ||
144 | |||
145 | -(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a | ||
146 | { | ||
147 | [self begin]; | ||
148 | |||
149 | // save clear color | ||
150 | GLfloat clearColor[4]; | ||
151 | glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); | ||
152 | |||
153 | glClearColor(r, g, b, a); | ||
154 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
155 | |||
156 | // restore clear color | ||
157 | glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); | ||
158 | } | ||
159 | |||
160 | -(void)end | ||
161 | { | ||
162 | ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); | ||
163 | // Restore the original matrix and viewport | ||
164 | glPopMatrix(); | ||
165 | CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; | ||
166 | glViewport(0, 0, size.width, size.height); | ||
167 | } | ||
168 | |||
169 | -(void)clear:(float)r g:(float)g b:(float)b a:(float)a | ||
170 | { | ||
171 | [self beginWithClear:r g:g b:b a:a]; | ||
172 | [self end]; | ||
173 | } | ||
174 | |||
175 | #pragma mark RenderTexture - Save Image | ||
176 | |||
177 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
178 | -(BOOL)saveBuffer:(NSString*)name | ||
179 | { | ||
180 | return [self saveBuffer:name format:kCCImageFormatJPG]; | ||
181 | } | ||
182 | |||
183 | -(BOOL)saveBuffer:(NSString*)fileName format:(int)format | ||
184 | { | ||
185 | NSString *fullPath = [CCFileUtils fullPathFromRelativePath:fileName]; | ||
186 | |||
187 | NSData *data = [self getUIImageAsDataFromBuffer:format]; | ||
188 | |||
189 | return [data writeToFile:fullPath atomically:YES]; | ||
190 | } | ||
191 | |||
192 | /* get buffer as UIImage */ | ||
193 | -(UIImage *)getUIImageFromBuffer | ||
194 | { | ||
195 | NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); | ||
196 | |||
197 | CGSize s = [texture_ contentSizeInPixels]; | ||
198 | int tx = s.width; | ||
199 | int ty = s.height; | ||
200 | |||
201 | int bitsPerComponent = 8; | ||
202 | int bitsPerPixel = 32; | ||
203 | int bytesPerPixel = (bitsPerComponent * 4)/8; | ||
204 | int bytesPerRow = bytesPerPixel * tx; | ||
205 | NSInteger myDataLength = bytesPerRow * ty; | ||
206 | |||
207 | NSMutableData *buffer = [[NSMutableData alloc] initWithCapacity:myDataLength]; | ||
208 | NSMutableData *pixels = [[NSMutableData alloc] initWithCapacity:myDataLength]; | ||
209 | |||
210 | if( ! (buffer && pixels) ) { | ||
211 | CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); | ||
212 | [buffer release]; | ||
213 | [pixels release]; | ||
214 | return nil; | ||
215 | } | ||
216 | |||
217 | [self begin]; | ||
218 | glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, [buffer mutableBytes]); | ||
219 | [self end]; | ||
220 | |||
221 | // make data provider with data. | ||
222 | |||
223 | CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault; | ||
224 | CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [buffer mutableBytes], myDataLength, NULL); | ||
225 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); | ||
226 | CGImageRef iref = CGImageCreate(tx, ty, | ||
227 | bitsPerComponent, bitsPerPixel, bytesPerRow, | ||
228 | colorSpaceRef, bitmapInfo, provider, | ||
229 | NULL, false, | ||
230 | kCGRenderingIntentDefault); | ||
231 | |||
232 | CGContextRef context = CGBitmapContextCreate([pixels mutableBytes], tx, | ||
233 | ty, CGImageGetBitsPerComponent(iref), | ||
234 | CGImageGetBytesPerRow(iref), CGImageGetColorSpace(iref), | ||
235 | bitmapInfo); | ||
236 | CGContextTranslateCTM(context, 0.0f, ty); | ||
237 | CGContextScaleCTM(context, 1.0f, -1.0f); | ||
238 | CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, tx, ty), iref); | ||
239 | CGImageRef outputRef = CGBitmapContextCreateImage(context); | ||
240 | UIImage* image = [[UIImage alloc] initWithCGImage:outputRef]; | ||
241 | |||
242 | CGImageRelease(iref); | ||
243 | CGContextRelease(context); | ||
244 | CGColorSpaceRelease(colorSpaceRef); | ||
245 | CGDataProviderRelease(provider); | ||
246 | CGImageRelease(outputRef); | ||
247 | |||
248 | [pixels release]; | ||
249 | [buffer release]; | ||
250 | |||
251 | return [image autorelease]; | ||
252 | } | ||
253 | |||
254 | -(NSData*)getUIImageAsDataFromBuffer:(int) format | ||
255 | { | ||
256 | NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); | ||
257 | |||
258 | CGSize s = [texture_ contentSizeInPixels]; | ||
259 | int tx = s.width; | ||
260 | int ty = s.height; | ||
261 | |||
262 | int bitsPerComponent=8; | ||
263 | int bitsPerPixel=32; | ||
264 | |||
265 | int bytesPerRow = (bitsPerPixel/8) * tx; | ||
266 | NSInteger myDataLength = bytesPerRow * ty; | ||
267 | |||
268 | GLubyte *buffer = malloc(sizeof(GLubyte)*myDataLength); | ||
269 | GLubyte *pixels = malloc(sizeof(GLubyte)*myDataLength); | ||
270 | |||
271 | if( ! (buffer && pixels) ) { | ||
272 | CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); | ||
273 | free(buffer); | ||
274 | free(pixels); | ||
275 | return nil; | ||
276 | } | ||
277 | |||
278 | [self begin]; | ||
279 | glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer); | ||
280 | [self end]; | ||
281 | |||
282 | int x,y; | ||
283 | |||
284 | for(y = 0; y <ty; y++) { | ||
285 | for(x = 0; x <tx * 4; x++) { | ||
286 | pixels[((ty - 1 - y) * tx * 4 + x)] = buffer[(y * 4 * tx + x)]; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | NSData* data; | ||
291 | |||
292 | if (format == kCCImageFormatRawData) | ||
293 | { | ||
294 | free(buffer); | ||
295 | //data frees buffer when it is deallocated | ||
296 | data = [NSData dataWithBytesNoCopy:pixels length:myDataLength]; | ||
297 | |||
298 | } else { | ||
299 | |||
300 | /* | ||
301 | CGImageCreate(size_t width, size_t height, | ||
302 | size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow, | ||
303 | CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider, | ||
304 | const CGFloat decode[], bool shouldInterpolate, | ||
305 | CGColorRenderingIntent intent) | ||
306 | */ | ||
307 | // make data provider with data. | ||
308 | CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault; | ||
309 | CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, myDataLength, NULL); | ||
310 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); | ||
311 | CGImageRef iref = CGImageCreate(tx, ty, | ||
312 | bitsPerComponent, bitsPerPixel, bytesPerRow, | ||
313 | colorSpaceRef, bitmapInfo, provider, | ||
314 | NULL, false, | ||
315 | kCGRenderingIntentDefault); | ||
316 | |||
317 | UIImage* image = [[UIImage alloc] initWithCGImage:iref]; | ||
318 | |||
319 | CGImageRelease(iref); | ||
320 | CGColorSpaceRelease(colorSpaceRef); | ||
321 | CGDataProviderRelease(provider); | ||
322 | |||
323 | |||
324 | |||
325 | if (format == kCCImageFormatPNG) | ||
326 | data = UIImagePNGRepresentation(image); | ||
327 | else | ||
328 | data = UIImageJPEGRepresentation(image, 1.0f); | ||
329 | |||
330 | [image release]; | ||
331 | |||
332 | free(pixels); | ||
333 | free(buffer); | ||
334 | } | ||
335 | |||
336 | return data; | ||
337 | } | ||
338 | |||
339 | #endif // __IPHONE_OS_VERSION_MAX_ALLOWED | ||
340 | @end | ||