root/OpenSceneGraph/trunk/src/osgPlugins/quicktime/QTImportExport.cpp @ 13041

Revision 13041, 15.4 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1/*
2 *  QTImportExport.cpp
3 *  cefix
4 *
5 *  Created by Stephan Huber on 07.02.08.
6 *  Copyright 2008 __MyCompanyName__. All rights reserved.
7 *
8 */
9#include <map>
10#include <sstream>
11#include "QTImportExport.h"
12#include "QTUtils.h"
13
14#include <osgDB/FileNameUtils>
15#include <osg/Image>
16
17
18/** small exception class bundling a error-message */
19class QTImportExportException : public std::exception {
20
21    public:
22        QTImportExportException(int err, const std::string& msg) : std::exception(), _err(err), _msg(msg) {}
23
24        virtual const char* what() { return _msg.c_str(); }
25        int getErrorCode() { return _err; }
26
27        virtual ~QTImportExportException() throw () {}
28
29    private:
30        int _err;
31        std::string _msg;
32};
33
34QuicktimeImportExport::QuicktimeImportExport()
35:    _error(0),
36    _lastError("")
37{
38}
39
40
41// ----------------------------------------------------------------------------------------------------------
42// flipImage
43// ----------------------------------------------------------------------------------------------------------
44
45void QuicktimeImportExport::flipImage(unsigned char* pixels, int bytesPerPixel, unsigned int width, unsigned height)
46{
47    // Flip the image
48    unsigned imageSize = width * height * bytesPerPixel;
49    char *tBuffer = new char [imageSize];
50    unsigned int rowBytes = width * bytesPerPixel;
51    unsigned int i,j;
52    for (i = 0, j = imageSize - rowBytes; i < imageSize; i += rowBytes, j -= rowBytes)
53        memcpy( &tBuffer[j], &pixels[i], (size_t)rowBytes );
54
55    memcpy(pixels, tBuffer, (size_t)imageSize);
56    delete[] tBuffer;
57}
58
59
60
61
62// ----------------------------------------------------------------------------------------------------------
63// prepareBufferForOSG
64// ----------------------------------------------------------------------------------------------------------
65
66unsigned char* QuicktimeImportExport::pepareBufferForOSG(unsigned char * buffer, int bytesPerPixel, unsigned int width, unsigned int height)
67{
68    unsigned char *pixels = new unsigned char [height * width * 4];
69    unsigned char *dstp = pixels;
70    unsigned char *srcp = buffer;
71    unsigned int i, j;
72
73    int roffset, goffset, boffset, aoffset;
74    aoffset = -1;
75    int sourceStep;
76
77    switch (bytesPerPixel) {
78        case 1:
79            sourceStep = 1;
80            roffset = goffset = boffset = 0;
81            break;
82        case 3:
83            sourceStep = 3;
84            roffset = 0;
85            goffset = 1;
86            boffset = 2;
87            break;
88        case 4:
89            sourceStep = 4;
90            aoffset = 1;
91            roffset = 2;
92            goffset = 3;
93            boffset = 0;
94            break;
95
96    }
97
98    for (i = 0; i < height; ++i )
99    {
100        for (j = 0; j < width; ++j )
101        {
102            dstp[0] = (aoffset < 0) ? 0 : srcp[aoffset];
103            dstp[1] = srcp[roffset];
104            dstp[2] = srcp[goffset];
105            dstp[3] = srcp[boffset];
106            srcp+=sourceStep;
107            dstp+=4;
108
109        }
110    }
111
112    flipImage(pixels, bytesPerPixel, width, height);
113    return pixels;
114
115}
116
117
118
119// ----------------------------------------------------------------------------------------------------------
120// prepareBufferForQuicktime
121// ----------------------------------------------------------------------------------------------------------
122
123unsigned char* QuicktimeImportExport::prepareBufferForQuicktime(unsigned char* buffer, GLenum pixelFormat, int bytesPerPixel, unsigned int width, unsigned int height)
124{
125    unsigned char *pixels = new unsigned char [height * width * 4];
126    unsigned char *dstp = pixels;
127    unsigned char *srcp = buffer;
128    unsigned int i, j;
129
130    int roffset, goffset, boffset, aoffset;
131    aoffset = -1;
132    int sourceStep;
133
134    switch (bytesPerPixel) {
135        case 1:
136            sourceStep = 1;
137            roffset = goffset = boffset = 0;
138            break;
139        case 3:
140            sourceStep = 3;
141            roffset = 0;
142            goffset = 1;
143            boffset = 2;
144            break;
145        case 4:
146            sourceStep = 4;
147            switch (pixelFormat) {
148                case GL_RGBA:
149                    aoffset = 3;
150                    roffset = 0;
151                    goffset = 1;
152                    boffset = 2;
153                    break;
154
155                case GL_BGRA:
156                    aoffset = 0;
157                    roffset = 1;
158                    goffset = 2;
159                    boffset = 3;
160                    break;
161            }
162    }
163
164
165    for (i = 0; i < height; ++i )
166    {
167        for (j = 0; j < width; ++j )
168        {
169            dstp[0] = (aoffset < 0) ? 0 : srcp[aoffset];
170            dstp[1] = srcp[roffset];
171            dstp[2] = srcp[goffset];
172            dstp[3] = srcp[boffset];
173            srcp+=sourceStep;
174            dstp+=4;
175
176        }
177    }
178
179    flipImage(pixels, 4, width, height);
180
181    return pixels;
182
183}
184
185// ----------------------------------------------------------------------------------------------------------
186// readFromStream
187// ----------------------------------------------------------------------------------------------------------
188
189osg::Image* QuicktimeImportExport::readFromStream(std::istream & inStream, const std::string& fileTypeHint, long sizeHint)
190{
191    char* content = NULL;
192    long length = 0;
193     if (sizeHint != 0)
194    {
195        length = sizeHint;
196        content = new char[length];
197        inStream.read (content,length);
198    }
199    else
200    {
201        int readBytes(0), newBytes(0);
202
203        char buffer[10240];
204
205        while (!inStream.eof()) {
206            inStream.read(buffer, 10240);
207            newBytes = inStream.gcount();
208            if (newBytes > 0) {
209                char* newcontent = new char[readBytes + newBytes];
210
211                if (readBytes > 0)
212                    memcpy(newcontent, content, readBytes);
213
214                memcpy(&newcontent[readBytes], &buffer, newBytes);
215                readBytes += newBytes;
216                if (content) delete[] content;
217                content = newcontent;
218            }
219        }
220        length = readBytes;
221    }
222
223    osg::Image* img = doImport(reinterpret_cast<unsigned char*>(content), length, fileTypeHint);
224
225    if (content) delete[] content;
226    return img;
227 }
228
229
230Handle getPtrDataRef(unsigned char *data, unsigned int size, const std::string &filename)
231{
232     // Load Data Reference
233     Handle dataRef;
234     Handle fileNameHandle;
235     PointerDataRefRecord ptrDataRefRec;
236     ComponentInstance dataRefHandler;
237     unsigned char pstr[255];
238
239     ptrDataRefRec.data = data;
240     ptrDataRefRec.dataLength = size;
241
242     /*err = */PtrToHand(&ptrDataRefRec, &dataRef, sizeof(PointerDataRefRecord));
243
244     // Open a Data Handler for the Data Reference
245     /*err = */OpenADataHandler(dataRef, PointerDataHandlerSubType, NULL,
246         (OSType)0, NULL, kDataHCanRead, &dataRefHandler);
247
248     // Convert From CString in filename to a PascalString in pstr
249     if (filename.length() > 255) {
250         //hmm...not good, pascal string limit is 255!
251         //do some error handling maybe?!
252         throw QTImportExportException(0, "filename length limit exceeded");
253     }
254     CopyCStringToPascal(filename.c_str(), pstr);
255
256    // Add filename extension
257     /*err = */PtrToHand(pstr, &fileNameHandle, filename.length() + 1);
258     /*err = */DataHSetDataRefExtension(dataRefHandler, fileNameHandle,
259         kDataRefExtensionFileName);
260     DisposeHandle(fileNameHandle);
261
262     // Release old handler which does not have the extensions
263     DisposeHandle(dataRef);
264
265     // Grab the SAFE_NEW version of the data ref from the data handler
266     /*err = */ DataHGetDataRef(dataRefHandler, &dataRef);
267
268     CloseComponent(dataRefHandler);
269
270     return dataRef;
271}
272
273
274osg::Image* QuicktimeImportExport::doImport(unsigned char* data, unsigned int sizeData, const std::string& fileTypeHint)
275{
276    GWorldPtr gworld = 0;
277    OSType pixelFormat;
278    int rowStride;
279    GraphicsImportComponent gicomp = 0;
280    Rect rectImage;
281    GDHandle origDevice = 0;
282    CGrafPtr origPort = 0;
283    ImageDescriptionHandle desc = 0;
284    int depth = 32;
285    unsigned int xsize, ysize;
286    unsigned char* imageData = 0;
287
288    // Data Handle for file data ( & load data from file )
289    Handle dataRef = getPtrDataRef(data, sizeData, fileTypeHint);
290
291    try {
292        OSErr err = noErr;
293
294        // GraphicsImporter - Get Importer for our filetype
295        GetGraphicsImporterForDataRef(dataRef, 'ptr ', &gicomp);
296
297        // GWorld - Get Texture Info
298        err = GraphicsImportGetNaturalBounds(gicomp, &rectImage);
299        if (err != noErr) {
300            throw QTImportExportException(err, "GraphicsImportGetNaturalBounds failed");
301
302        }
303        xsize = (unsigned int)(rectImage.right - rectImage.left);
304        ysize = (unsigned int)(rectImage.bottom - rectImage.top);
305
306        // ImageDescription - Get Image Description
307        err = GraphicsImportGetImageDescription(gicomp, &desc);
308        if (err != noErr) {
309            throw QTImportExportException(err, "GraphicsImportGetImageDescription failed");
310        }
311
312        // ImageDescription - Get Bit Depth
313        HLock(reinterpret_cast<char **>(desc));
314
315
316        // GWorld - Pixel Format stuff
317        pixelFormat = k32ARGBPixelFormat; // Make sure its forced...NOTE: i'm pretty sure this cannot be RGBA!
318
319        // GWorld - Row stride
320        rowStride = xsize * 4; // (width * depth_bpp / 8)
321
322        // GWorld - Allocate output buffer
323        imageData = new unsigned char[rowStride * ysize];
324
325        // GWorld - Actually Create IT!
326        QTNewGWorldFromPtr(&gworld, pixelFormat, &rectImage, 0, 0, 0, imageData, rowStride);
327        if (!gworld) {
328            throw QTImportExportException(-1, "QTNewGWorldFromPtr failed");
329        }
330
331        // Save old Graphics Device and Graphics Port to reset to later
332        GetGWorld (&origPort, &origDevice);
333
334        // GraphicsImporter - Set Destination GWorld (our buffer)
335        err = GraphicsImportSetGWorld(gicomp, gworld, 0);
336        if (err != noErr) {
337            throw QTImportExportException(err, "GraphicsImportSetGWorld failed");
338        }
339
340        // GraphicsImporter - Set Quality Level
341        err = GraphicsImportSetQuality(gicomp, codecLosslessQuality);
342        if (err != noErr) {
343            throw QTImportExportException(err, "GraphicsImportSetQuality failed");
344        }
345
346        // Lock pixels so that we can draw to our memory texture
347        if (!GetGWorldPixMap(gworld) || !LockPixels(GetGWorldPixMap(gworld))) {
348            throw QTImportExportException(0, "GetGWorldPixMap failed");
349        }
350
351
352        //*** Draw GWorld into our Memory Texture!
353        GraphicsImportDraw(gicomp);
354
355        // Clean up
356        UnlockPixels(GetGWorldPixMap(gworld));
357        SetGWorld(origPort, origDevice); // set graphics port to offscreen (we don't need it now)
358        DisposeGWorld(gworld);
359        CloseComponent(gicomp);
360        DisposeHandle(reinterpret_cast<char **>(desc));
361        DisposeHandle(dataRef);
362    }
363    catch (QTImportExportException& e)
364    {
365        setError(e.what());
366
367        if (gworld) {
368            UnlockPixels(GetGWorldPixMap(gworld));
369            SetGWorld(origPort, origDevice); // set graphics port to offscreen (we don't need it now)
370            DisposeGWorld(gworld);
371        }
372        if (gicomp)
373            CloseComponent(gicomp);
374        if (desc)
375            DisposeHandle(reinterpret_cast<char **>(desc));
376
377        if (imageData)
378            delete[] imageData;
379        if (dataRef)
380            DisposeHandle(dataRef);
381
382        return NULL;
383    }
384
385
386
387    unsigned int bytesPerPixel = depth / 8;
388    unsigned int glpixelFormat;
389    switch(bytesPerPixel) {
390        case 3 :
391            glpixelFormat = GL_RGB;
392            break;
393        case 4 :
394            glpixelFormat = GL_RGBA;
395            break;
396        default :
397            delete[] imageData;
398            setError("unknown pixelformat");
399            return NULL;
400            break;
401    }
402
403    unsigned char* swizzled = pepareBufferForOSG(imageData, bytesPerPixel, xsize, ysize);
404
405    delete[] imageData;
406
407    osg::Image* image = new osg::Image();
408    image->setFileName(fileTypeHint.c_str());
409    image->setImage(xsize,ysize,1,
410        bytesPerPixel,
411        glpixelFormat,
412        GL_UNSIGNED_BYTE,
413        swizzled,
414        osg::Image::USE_NEW_DELETE );
415
416
417    return image;
418}
419
420
421 void QuicktimeImportExport::writeToStream(std::ostream& outStream, osg::Image* image, const std::string& fileTypeHint)
422 {
423
424    std::string ext = osgDB::getFileExtension(fileTypeHint);
425    //Build map  of extension <-> osFileTypes
426    static std::map<std::string, OSType> extmap;
427    if (extmap.size() == 0) {
428        extmap["jpg"]  = kQTFileTypeJPEG;
429        extmap["jpeg"] = kQTFileTypeJPEG;
430        extmap["bmp"]  = kQTFileTypeBMP;
431        extmap["tif"]  = kQTFileTypeTIFF;
432        extmap["tiff"] = kQTFileTypeTIFF;
433        extmap["png"]  = kQTFileTypePNG;
434        extmap["gif"]  = kQTFileTypeGIF;
435        extmap["psd"]  = kQTFileTypePhotoShop;
436        extmap["sgi"]  = kQTFileTypeSGIImage;
437        extmap["rgb"]  = kQTFileTypeSGIImage;
438        extmap["rgba"] = kQTFileTypeSGIImage;
439    }
440
441
442    std::map<std::string, OSType>::iterator cur = extmap.find(ext);
443
444    // can not handle this type of file, perhaps a movie?
445    if (cur == extmap.end())
446        return;
447
448    unsigned int numBytes = image->computeNumComponents(image->getPixelFormat());
449    unsigned char* pixels = prepareBufferForQuicktime(
450        image->data(),
451        image->getPixelFormat(),
452        numBytes,
453        image->s(),
454        image->t()
455    );
456
457
458    OSType desiredType = cur->second;
459    GraphicsExportComponent geComp = NULL;
460    GWorldPtr gw = 0;
461    Handle dataHandle;
462    dataHandle = NewHandle(0);
463
464    try {
465        OSErr err = OpenADefaultComponent(GraphicsExporterComponentType, desiredType, &geComp);
466        Rect bounds = {0,0, image->t(), image->s()};
467
468        err = QTNewGWorldFromPtr(&gw, k32ARGBPixelFormat, &bounds, 0,0,0, pixels, image->s()*4);
469        if (err != noErr) {
470            throw QTImportExportException(err,  "could not create gworld for type " + ext);
471        }
472
473        err = GraphicsExportSetInputGWorld(geComp, gw);
474        if (err != noErr) {
475            throw QTImportExportException(err, "could not set input gworld for type " + ext);
476        }
477
478        err = GraphicsExportSetOutputHandle( geComp, dataHandle);
479        if (err != noErr) {
480            throw QTImportExportException(err, "could not set output file for type " + ext);
481        }
482
483        // Set the compression quality (needed for JPEG, not necessarily for other formats)
484        if (desiredType == kQTFileTypeJPEG) {
485            err = GraphicsExportSetCompressionQuality(geComp, codecLosslessQuality);
486            if (err != noErr) {
487                throw QTImportExportException(err, "could not set compression for type " + ext);
488            }
489        }
490
491        if(4 == numBytes)
492        {
493            err = GraphicsExportSetDepth( geComp, k32ARGBPixelFormat );    // depth
494        }
495        // else k24RGBPixelFormat???
496
497        // do the export
498        err = GraphicsExportDoExport(geComp, NULL);
499        if (err != noErr) {
500            throw QTImportExportException(err, "could not do the export for type " + ext);
501        }
502
503        if (geComp != NULL)
504            CloseComponent(geComp);
505
506        if (gw) DisposeGWorld (gw);
507        if (pixels) free(pixels);
508
509        outStream.write(*dataHandle, GetHandleSize(dataHandle));
510        DisposeHandle(dataHandle);
511    }
512
513
514    catch (QTImportExportException& e)
515    {
516        setError(e.what());
517
518        if (geComp != NULL) CloseComponent(geComp);
519        if (gw != NULL) DisposeGWorld (gw);
520        if (pixels) free(pixels);
521
522        DisposeHandle(dataHandle);
523
524    }
525
526}
Note: See TracBrowser for help on using the browser.