1// 2// SnapshotTaker.mm 3// LDView 4// 5// Created by Travis Cobbs on 10/14/07. 6// Copyright 2008 Travis Cobbs. All rights reserved. 7// 8 9#import "SnapshotTaker.h" 10#include <LDLib/LDrawModelViewer.h> 11#include <TRE/TREGLExtensions.h> 12#include <TCFoundation/TCAlert.h> 13#import "SnapshotAlertHandler.h" 14#import "AutoDeleter.h" 15#include <algorithm> 16 17#define PB_WIDTH 1024 18#define PB_HEIGHT 1024 19 20@implementation SnapshotTaker 21 22- (id)init 23{ 24 return [self initWithModelViewer:nil sharedContext:nil]; 25} 26 27- (BOOL)choosePixelFormat:(CGLPixelFormatObj *)pPixelFormat remote:(bool)remote 28{ 29START_IGNORE_DEPRECATION 30 int attrs[] = 31 { 32 kCGLPFAPBuffer, 33 kCGLPFAColorSize, 16, 34 kCGLPFAAlphaSize, 4, 35 kCGLPFADepthSize, 16, 36 kCGLPFAStencilSize, 8, 37 kCGLPFAMaximumPolicy, 38 kCGLPFAAccelerated, 39 kCGLPFANoRecovery, 40 0, // Spot for kCGLPFARemotePBuffer if tryRemote is set 41 0 42 }; 43END_IGNORE_DEPRECATION 44 GLint num; 45 46 if (remote) 47 { 48#pragma clang diagnostic push 49#pragma clang diagnostic ignored "-Wdeprecated-declarations" 50 attrs[sizeof(attrs) / sizeof(attrs[0]) - 2] = kCGLPFARemotePBuffer; 51#pragma clang diagnostic pop 52 } 53 if (CGLChoosePixelFormat((CGLPixelFormatAttribute *)attrs, pPixelFormat, &num) == kCGLNoError) 54 { 55 return YES; 56 } 57 return NO; 58} 59 60- (id)initWithModelViewer:(LDrawModelViewer *)theModelViewer sharedContext:(NSOpenGLContext *)theSharedContext 61{ 62 self = [super init]; 63 if (self) 64 { 65 sharedContext = theSharedContext; 66 modelViewer = theModelViewer; 67 snapshotAlertHandler = new SnapshotAlertHandler(self); 68 if (modelViewer) 69 { 70 ldSnapshotTaker = new LDSnapshotTaker(modelViewer); 71 if (TREGLExtensions::haveFramebufferObjectExtension()) 72 { 73 ldSnapshotTaker->setUseFBO(true); 74 } 75 } 76 } 77 return self; 78} 79 80- (void)contextCreatedWithPixelFormat:(CGLPixelFormatObj)pixelFormat 81{ 82 GLint virtualScreen; 83 84 CGLDestroyPixelFormat(pixelFormat); 85 CGLSetCurrentContext(context); 86 CGLGetVirtualScreen(context, &virtualScreen); 87START_IGNORE_DEPRECATION 88 CGLSetPBuffer(context, pbuffer, 0, 0, virtualScreen); 89END_IGNORE_DEPRECATION 90} 91 92- (BOOL)useFBO 93{ 94 return ldSnapshotTaker != NULL && ldSnapshotTaker->getUseFBO(); 95} 96 97- (void)setupContext 98{ 99 if ([self useFBO]) 100 { 101 return; 102 } 103START_IGNORE_DEPRECATION 104 CGLError result = CGLCreatePBuffer(PB_WIDTH, PB_HEIGHT, GL_TEXTURE_2D, GL_RGB, 0, &pbuffer); 105END_IGNORE_DEPRECATION 106 if (result == kCGLNoError) 107 { 108 CGLPixelFormatObj pixelFormat; 109 110 if ([self choosePixelFormat:&pixelFormat remote:sharedContext == nil]) 111 { 112 if (CGLCreateContext(pixelFormat, (CGLContextObj)[sharedContext CGLContextObj], &context) == kCGLNoError) 113 { 114 [self contextCreatedWithPixelFormat:pixelFormat]; 115 return; 116 } 117 CGLDestroyPixelFormat(pixelFormat); 118 } 119 if (sharedContext == nil) 120 { 121 NSLog(@"Error creating remote OpenGL context; trying non-remote context.\n"); 122 } 123 if ([self choosePixelFormat:&pixelFormat remote:false]) 124 { 125 if (CGLCreateContext(pixelFormat, (CGLContextObj)[sharedContext CGLContextObj], &context) == kCGLNoError) 126 { 127 [self contextCreatedWithPixelFormat:pixelFormat]; 128 return; 129 } 130 CGLDestroyPixelFormat(pixelFormat); 131 } 132 if (sharedContext == nil) 133 { 134 NSLog(@"Error creating OpenGL context for snapshot.\n"); 135 } 136 else 137 { 138 NSRunAlertPanel(@"Error", @"Error creating OpenGL context for snapshot.", @"OK", nil, nil); 139 } 140 } 141} 142 143- (void)dealloc 144{ 145 TCObject::release(snapshotAlertHandler); 146 TCObject::release(ldSnapshotTaker); 147 if (context) 148 { 149 CGLDestroyContext(context); 150 } 151 if (pbuffer) 152 { 153START_IGNORE_DEPRECATION 154 CGLDestroyPBuffer(pbuffer); 155END_IGNORE_DEPRECATION 156 } 157 [super dealloc]; 158} 159 160- (void)setImageType:(LDSnapshotTaker::ImageType)value 161{ 162 ldSnapshotTaker->setImageType(value); 163} 164 165- (void)setTrySaveAlpha:(bool)value 166{ 167 ldSnapshotTaker->setTrySaveAlpha(value); 168} 169 170- (void)setAutoCrop:(bool)value 171{ 172 ldSnapshotTaker->setAutoCrop(value); 173} 174 175- (void)saveFileSetup 176{ 177 if (![self useFBO]) 178 { 179 CGLSetCurrentContext(context); 180 glViewport(0, 0, PB_WIDTH, PB_HEIGHT); 181 if (modelViewer) 182 { 183 modelViewer->perspectiveView(); 184 } 185 glViewport(0, 0, PB_WIDTH, PB_HEIGHT); 186 glDepthFunc(GL_LEQUAL); 187 glEnable(GL_DEPTH_TEST); 188 glDrawBuffer(GL_FRONT); 189 glReadBuffer(GL_FRONT); 190 } 191 if (modelViewer) 192 { 193 if (modelViewer->getMainTREModel() == NULL && !modelViewer->getNeedsReload()) 194 { 195 modelViewer->loadModel(true); 196 } 197 } 198} 199 200- (LDSnapshotTaker *)ldSnapshotTaker 201{ 202 return ldSnapshotTaker; 203} 204 205- (void)snapshotCallback:(TCAlert *)alert; 206{ 207 if ([self useFBO]) 208 { 209 return; 210 } 211 if (strcmp(alert->getMessage(), "PreSave") == 0) 212 { 213 if (!context) 214 { 215 [self setupContext]; 216 } 217 [self saveFileSetup]; 218 } 219 else if (strcmp(alert->getMessage(), "PreFbo") == 0) 220 { 221 CGLPixelFormatObj pixelFormat; 222 if ([self choosePixelFormat:&pixelFormat remote:NO]) 223 { 224 if (CGLCreateContext(pixelFormat, NULL, &context) == kCGLNoError) 225 { 226 CGLSetCurrentContext(context); 227 TREGLExtensions::setup(); 228 ldSnapshotTaker = (LDSnapshotTaker*)alert->getSender()->retain(); 229 ldSnapshotTaker->setUseFBO(true); 230 } 231 CGLDestroyPixelFormat(pixelFormat); 232 } 233 } 234} 235 236- (bool)saveFile 237{ 238 if (ldSnapshotTaker) 239 { 240 return ldSnapshotTaker->saveImage(); 241 } 242 else 243 { 244 bool tried; 245 LDSnapshotTaker::doCommandLine(true, true, &tried); 246 return tried; 247 } 248} 249 250- (NSImage *)imageWithWidth:(int)width height:(int)height zoomToFit:(bool)zoomToFit 251{ 252 int actualWidth = width; 253 int actualHeight = height; 254 255 ldSnapshotTaker->setTrySaveAlpha(false); 256 TCByte *imageData = ldSnapshotTaker->grabImage(actualWidth, actualHeight, zoomToFit, NULL, NULL); 257 if (imageData) 258 { 259 int rowSize = TCImage::roundUp(actualWidth * 3, 4); 260 TCByte *imageDataArray[5] = { imageData, NULL, NULL, NULL, NULL }; 261 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:imageDataArray pixelsWide:actualWidth pixelsHigh:actualHeight bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:rowSize bitsPerPixel:24]; 262 NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize((float)actualWidth, (float)actualHeight)]; 263 int y; 264 TCByte *tmpRow = new TCByte[rowSize]; 265 266 // The good news is that drawing this image to the printer doesn't 267 // copy the image data. The bad news is that means that we can't free 268 // the image data until we get back up to the main autorelease pool, 269 // so create an AutoDeleter class that will free the memory when it 270 // gets released during processing of the main autorelease pool. 271 [AutoDeleter autoDeleterWithBytePointer:imageData]; 272 // Flip the image 273 for (y = 0; y < actualHeight / 2; y++) 274 { 275 TCByte *botRow = &imageData[y * rowSize]; 276 TCByte *topRow = &imageData[(actualHeight - y - 1) * rowSize]; 277 278 memcpy(tmpRow, botRow, rowSize); 279 memcpy(botRow, topRow, rowSize); 280 memcpy(topRow, tmpRow, rowSize); 281 } 282 delete[] tmpRow; 283 [image addRepresentation:imageRep]; 284 [imageRep release]; 285 return [image autorelease]; 286 } 287 return nil; 288} 289 290- (bool)saveFile:(NSString *)filename width:(int)width height:(int)height zoomToFit:(bool)zoomToFit 291{ 292 [self saveFileSetup]; 293 return ldSnapshotTaker->saveImage([filename UTF8String], width, height, zoomToFit); 294} 295 296@end 297