root/OpenSceneGraph/trunk/src/osgPlugins/bmp/ReaderWriterBMP.cpp @ 13041

Revision 13041, 19.7 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// -*-c++-*-
2
3/*
4 * $Id$
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21#include <osg/Image>
22#include <osg/Notify>
23#include <osg/Image>
24#include <osg/GL>
25#include <osg/Endian>
26
27#include <osgDB/Registry>
28#include <osgDB/FileNameUtils>
29#include <osgDB/FileUtils>
30
31#include <vector>
32
33#include <stdio.h>
34#include <string.h>
35#include <stdlib.h>
36#include <assert.h>
37
38
39/****************************************************************************
40 *
41 * Follows is code written by GWM and translated to fit with the OSG Ethos.
42 *
43 * Ported into the OSG as a plugin, Geoff Michel October 2001.
44 * For patches, bugs and new features
45 * please send them direct to the OSG dev team.
46 *
47 **********************************************************************/
48
49// find least-significant (lowest) bit position in 16-bit mask
50static unsigned int findLeastSignificantBit(unsigned short mask)
51{
52    unsigned int shift = 1;
53    while ((mask & 0x01) == 0)
54    {
55        mask >>= 1;
56        ++shift;
57    }
58    return shift;
59}
60
61// find most-significant (highest) bit position in 16-bit mask
62static unsigned int findMostSignificantBit(unsigned short mask)
63{
64    unsigned int shift = 16;
65    while ((mask & 0x8000) == 0)
66    {
67        mask <<= 1;
68        --shift;
69    }
70    return shift;
71}
72
73
74/*
75 * BMP header
76 */
77const unsigned short BMP_MAGIC_BM = 0x424D; // 'BM'
78const unsigned short BMP_MAGIC_MB = 0x4D42; // 'MB'
79
80struct BMPHeader {
81    unsigned short magic; // stored 'BM', but read as ushort
82    unsigned int fileSize;
83    unsigned short reserved1;
84    unsigned short reserved2;
85    unsigned int imageOffset;
86};
87
88/*
89 * Windows v3 header
90 */
91enum BMPCOMPRESSION {
92    BI_RGB = 0,
93    BI_RLE8,
94    BI_RLE4,
95    BI_BITFIELDS,
96    BI_JPEG,
97    BI_PNG
98};
99struct BITMAPINFOHEADER {
100    //unsigned int hdrSize;
101    int width, height;
102    unsigned short colorPlanes;
103    unsigned short bitsPerPixel;
104    unsigned int compression;
105    unsigned int imageSize;
106    int horizontalPixelPerMeter;
107    int verticalPixelPerMeter;
108    unsigned int numColorsInPalette;
109    unsigned int numImportantColors;
110};
111
112/*
113 * OS/2 v1 header
114 */
115struct BITMAPCOREHEADER {
116    //unsigned int hdrSize;
117    unsigned short width, height;
118    unsigned short colorPlanes;
119    unsigned short bitsPerPixel;
120};
121
122static unsigned char* bmp_load(std::istream& fin,
123        int& width_ret, int& height_ret, int& numComponents_ret)
124{
125    // actual file size
126    fin.seekg(0, std::ios::end);
127    size_t actFileSize = fin.tellg();
128    fin.seekg(0, std::ios::beg);
129
130    bool swap;
131
132    BMPHeader bmp;
133    {
134        /*
135         * read each part individually to avoid struct packing issues (and #pragma)
136         */
137        fin.read((char*) &bmp.magic, sizeof(bmp.magic));
138        fin.read((char*) &bmp.fileSize, sizeof(bmp.fileSize));
139        fin.read((char*) &bmp.reserved1, sizeof(bmp.reserved1));
140        fin.read((char*) &bmp.reserved2, sizeof(bmp.reserved2));
141        fin.read((char*) &bmp.imageOffset, sizeof(bmp.imageOffset));
142
143        if (bmp.magic != BMP_MAGIC_BM && bmp.magic != BMP_MAGIC_MB)
144        {
145            OSG_WARN << "Invalid BMP magic\n";
146            return 0;
147        }
148
149        swap = (bmp.magic == BMP_MAGIC_BM); // means machine is big-endian and must swap
150        if (swap)
151        {
152            OSG_DEBUG << "swap=" << swap << std::endl;
153            osg::swapBytes4((char*) &bmp.fileSize);
154            osg::swapBytes4((char*) &bmp.imageOffset);
155        }
156
157        if (bmp.fileSize != actFileSize)
158        {
159            OSG_DEBUG << "Stored BMP fileSize=" << bmp.fileSize << " != actual=" << actFileSize << std::endl;
160            bmp.fileSize = actFileSize;
161        }
162    }
163
164    BITMAPINFOHEADER dib;
165    unsigned int dibHdrSize;
166
167    /*
168     * read DIB header
169     */
170    fin.read((char*) &dibHdrSize, sizeof(dibHdrSize));
171    if (swap)
172        osg::swapBytes4((char*) &dibHdrSize);
173
174    if (dibHdrSize == 12)
175    {
176        /*
177         * OS/2 v1
178         */
179        BITMAPCOREHEADER hdr;
180
181        unsigned int expectHdrSize = sizeof(hdr) + sizeof(dibHdrSize);
182        if (expectHdrSize != dibHdrSize)
183        {
184            OSG_WARN << "Invalid BMP OS/2 v1 header size " << expectHdrSize << " != " << dibHdrSize << std::endl;
185            return 0;
186        }
187
188        fin.read((char*) &hdr, sizeof(hdr));
189        if (swap)
190        {
191            osg::swapBytes2((char*) &hdr.width);
192            osg::swapBytes2((char*) &hdr.height);
193            osg::swapBytes2((char*) &hdr.colorPlanes);
194            osg::swapBytes2((char*) &hdr.bitsPerPixel);
195        }
196
197        // store to BITMAPINFOHEADER
198        memset(&dib, 0, sizeof(dib));
199        dib.width = hdr.width;
200        dib.height = hdr.height;
201        dib.colorPlanes = hdr.colorPlanes;
202        dib.bitsPerPixel = hdr.bitsPerPixel;
203    }
204    else if (dibHdrSize == 40 || dibHdrSize == 108 || dibHdrSize == 124)
205    {
206        /*
207         * Windows v3/v4/v5 header
208         * reads only the common part (i.e. v3) and skips over the rest
209         */
210        fin.read((char*) &dib, sizeof(dib));
211        if (swap)
212        {
213            osg::swapBytes4((char*) &dib.width);
214            osg::swapBytes4((char*) &dib.height);
215            osg::swapBytes2((char*) &dib.colorPlanes);
216            osg::swapBytes2((char*) &dib.bitsPerPixel);
217            osg::swapBytes4((char*) &dib.compression);
218            osg::swapBytes4((char*) &dib.imageSize);
219            osg::swapBytes4((char*) &dib.numColorsInPalette);
220            osg::swapBytes4((char*) &dib.numImportantColors);
221        }
222    }
223    else
224    {
225        OSG_WARN << "Unsupported BMP/DIB header size=" << dibHdrSize << std::endl;
226        return 0;
227    }
228
229    // sanity checks
230    if (dib.height < 0)
231    {
232        OSG_DEBUG << "BMP Image is upside-down\n";
233        dib.height *= -1;
234    }
235    if (dib.colorPlanes != 1)
236    {
237        OSG_WARN << "Invalid BMP number of color planes=" << dib.colorPlanes << std::endl;
238        return 0;
239    }
240    if (dib.bitsPerPixel == 0)
241    {
242        OSG_WARN << "Invalid BMP bits/pixel=" << dib.bitsPerPixel << std::endl;
243        return 0;
244    }
245    if (dib.compression != BI_RGB && dib.compression != BI_BITFIELDS)
246    {
247        OSG_WARN << "Unsupported BMP compression=" << dib.compression << std::endl;
248        return 0;
249    }
250
251    /*
252     * color masks
253     */
254    unsigned int redMask, greenMask, blueMask;
255    if (dib.bitsPerPixel == 16 && dib.compression == BI_BITFIELDS)
256    {
257        fin.read((char*) &redMask, sizeof(redMask));
258        fin.read((char*) &greenMask, sizeof(greenMask));
259        fin.read((char*) &blueMask, sizeof(blueMask));
260        if (swap)
261        {
262            osg::swapBytes4((char*) &redMask);
263            osg::swapBytes4((char*) &greenMask);
264            osg::swapBytes4((char*) &blueMask);
265        }
266    }
267    else
268    {
269        redMask = 0x7c00;
270        greenMask = 0x03e0;
271        blueMask = 0x001f;
272    }
273
274    // determine shift width...
275    unsigned int redShift = findLeastSignificantBit(redMask) - 1;
276    unsigned int greenShift = findLeastSignificantBit(greenMask) - 1;
277    unsigned int blueShift = findLeastSignificantBit(blueMask) - 1;
278
279    // determine mask width
280    unsigned int redMaskWidth = findMostSignificantBit(redMask) - redShift;
281    unsigned int greenMaskWidth = findMostSignificantBit(greenMask) - greenShift;
282    unsigned int blueMaskWidth = findMostSignificantBit(blueMask) - blueShift;
283
284#if 0
285    printf("redMask=%04x/%d/%d greenMask=%04x/%d/%d blueMask=%04x/%d/%d\n",
286            redMask, redMaskWidth, redShift,
287            greenMask, greenMaskWidth, greenShift,
288            blueMask, blueMaskWidth, blueShift);
289#endif
290
291    unsigned int imageBytesPerPixel = 0;
292
293    /*
294     * color palette
295     */
296    std::vector<unsigned char> colorPalette;
297    if (dib.bitsPerPixel < 16)
298    {
299        // defaults to 2^n
300        if (dib.numColorsInPalette == 0)
301            dib.numColorsInPalette = 1 << dib.bitsPerPixel;
302
303        // allocate/read color palette
304        imageBytesPerPixel = (dibHdrSize == 12 ? 3 : 4); // OS/2 v1 stores RGB, else RGBA
305        colorPalette.resize(dib.numColorsInPalette * imageBytesPerPixel);
306        fin.read((char*) &*colorPalette.begin(), colorPalette.size());
307    }
308    else
309    {
310        if (dib.bitsPerPixel == 16)
311            imageBytesPerPixel = 3;
312        else if (dib.bitsPerPixel == 24 || dib.bitsPerPixel == 32)
313            imageBytesPerPixel = dib.bitsPerPixel / 8;
314        else
315        {
316            OSG_WARN << "Unsupported BMP bit depth " << dib.bitsPerPixel << std::endl;
317            return 0;
318        }
319    }
320
321    unsigned int where = fin.tellg();
322    if (where != bmp.imageOffset)
323    {
324        // this can happen because we don't fully parse v4/v5 headers
325        OSG_DEBUG << "BMP streampos out-of-sync where=" << where << " imageOffset=" << bmp.imageOffset << std::endl;
326        fin.seekg(bmp.imageOffset, std::ios::beg); // seek to imageOffset and hope for the best
327    }
328
329    /*
330     * image data
331     */
332    const unsigned int imageBytesPerRow = dib.width * imageBytesPerPixel;
333    const unsigned int imageBufferSize = imageBytesPerRow * dib.height;
334    unsigned char* imageBuffer = new unsigned char[imageBufferSize];
335    //printf("imageBytesPerPixel=%u imageBytesPerRow=%u\n", imageBytesPerPixel, imageBytesPerRow);
336
337    // byte/row in BMP image data
338    unsigned int bytesPerPixel;
339    unsigned int bytesPerRow;
340    if (dib.bitsPerPixel >= 8)
341    {
342        bytesPerPixel = dib.bitsPerPixel / 8;
343        bytesPerRow = dib.width * bytesPerPixel;
344    }
345    else
346    {
347        bytesPerPixel = 1;
348        bytesPerRow = (unsigned int) ceilf(dib.width * (dib.bitsPerPixel / 8.0f));
349    }
350    const unsigned int bytesPerRowAlign = (unsigned int) ceilf(bytesPerRow / 4.0f) * 4;
351    //printf("bytesPerPixel=%u bytesPerRow=%u bytesPerRowAlign=%u\n", bytesPerPixel, bytesPerRow, bytesPerRowAlign);
352
353    std::vector<unsigned char> rowBuffer;
354    rowBuffer.resize(bytesPerRowAlign);
355
356    if (dib.bitsPerPixel >= 16)
357    {
358        unsigned char* imgp = imageBuffer;
359        for (int i = 0; i < dib.height; ++i)
360        {
361            // read row
362            unsigned char* rowp = &*rowBuffer.begin();
363            fin.read((char*) rowp, rowBuffer.size());
364
365            // copy to image buffer, swap/unpack BGR to RGB(A)
366            for (unsigned int j = 0; j < bytesPerRow; j += bytesPerPixel)
367            {
368                if (dib.bitsPerPixel == 16)
369                {
370                    // 16-bit RGB -> 24-bit RGB
371                    unsigned short rgb16 = (rowp[1] << 8) | rowp[0];
372                    if (swap)
373                        osg::swapBytes2((char*) &rgb16);
374
375                    imgp[0] = (rgb16 & redMask) >> redShift;
376                    imgp[1] = (rgb16 & greenMask) >> greenShift;
377                    imgp[2] = (rgb16 & blueMask) >> blueShift;
378
379                    // expand range
380                    imgp[0] <<= (8-redMaskWidth);
381                    imgp[1] <<= (8-greenMaskWidth);
382                    imgp[2] <<= (8-blueMaskWidth);
383                }
384                else
385                {
386                    // BGR -> RGB(A)
387                    imgp[0] = rowp[2];
388                    imgp[1] = rowp[1];
389                    imgp[2] = rowp[0];
390                    if (imageBytesPerPixel == 4)
391                    {
392                        imgp[3] = 0xff;
393                    }
394                }
395                imgp += imageBytesPerPixel;
396                rowp += bytesPerPixel;
397            }
398        }
399    }
400    else
401    {
402        const int idxPerByte = 8 / dib.bitsPerPixel; // color indices per byte
403        const int idxMask = (1 << dib.bitsPerPixel) - 1; // index mask
404        //printf("idxPerByte=%d idxMask=%02x\n", idxPerByte, idxMask);
405
406        unsigned char* imgp = imageBuffer;
407        for (int i = 0; i < dib.height; ++i)
408        {
409            // read row
410            unsigned char* rowp = &*rowBuffer.begin();
411            fin.read((char*) rowp, rowBuffer.size());
412
413            int j = 0;
414            while (j < dib.width)
415            {
416                // unpack bytes/indices to image buffer
417                unsigned char val = rowp[0];
418                for (int k = 0; k < idxPerByte && j < dib.width; ++k, ++j)
419                {
420                    unsigned int idx = (val >> ((idxPerByte-1-k) * dib.bitsPerPixel)) & idxMask;
421                    idx *= imageBytesPerPixel;
422                    imgp[0] = colorPalette[idx+2];
423                    imgp[1] = colorPalette[idx+1];
424                    imgp[2] = colorPalette[idx+0];
425                    if (imageBytesPerPixel == 4)
426                    {
427                        imgp[3] = 0xff;
428                    }
429                    imgp += imageBytesPerPixel;
430                }
431                ++rowp;
432            }
433        }
434    }
435
436    // return result
437    width_ret = dib.width;
438    height_ret = dib.height;
439    numComponents_ret = imageBytesPerPixel;
440
441    return imageBuffer;
442}
443
444static bool bmp_save(const osg::Image& img, std::ostream& fout)
445{
446    BMPHeader bmp;
447    const unsigned int bmpHdrSize = 14;
448
449    BITMAPINFOHEADER dib;
450    assert(sizeof(dib) == 36);
451    const unsigned int dibHdrSize = sizeof(dib) + 4;
452
453    const unsigned int bytesPerRowAlign = ((img.s() * 3 + 3) / 4) * 4;
454
455    bool swap = (osg::getCpuByteOrder() == osg::BigEndian);
456
457    // BMP header
458    {
459        bmp.magic = BMP_MAGIC_BM;
460        bmp.reserved1 = bmp.reserved2 = 0;
461        bmp.imageOffset = bmpHdrSize + dibHdrSize;
462        bmp.fileSize = bmp.imageOffset + bytesPerRowAlign * img.t();
463#if 0
464        printf("sizeof(bmp)=%u sizeof(dib)=%u dibHdrSize=%u\n", sizeof(bmp), sizeof(dib), dibHdrSize);
465        printf("fileSize=%u imageOffset=%u\n", bmp.fileSize, bmp.imageOffset);
466        printf("s=%u t=%u bytesPerRowAlign=%u\n", img.s(), img.t(), bytesPerRowAlign);
467#endif
468
469        if (swap)
470        {
471            // big-endian must swap everything except magic
472            osg::swapBytes4((char*) &bmp.fileSize);
473            osg::swapBytes4((char*) &bmp.imageOffset);
474        }
475        else
476        {
477            // little-endian must swap the magic
478            osg::swapBytes2((char*) &bmp.magic);
479        }
480
481        fout.write((char*) &bmp.magic, sizeof(bmp.magic));
482        fout.write((char*) &bmp.fileSize, sizeof(bmp.fileSize));
483        fout.write((char*) &bmp.reserved1, sizeof(bmp.reserved1));
484        fout.write((char*) &bmp.reserved2, sizeof(bmp.reserved2));
485        fout.write((char*) &bmp.imageOffset, sizeof(bmp.imageOffset));
486    }
487
488    // DIB header
489    {
490        dib.width = img.s();
491        dib.height = img.t();
492        dib.colorPlanes = 1;
493        dib.bitsPerPixel = 24;
494        dib.compression = BI_RGB;
495        dib.imageSize = bytesPerRowAlign * img.t();
496        dib.horizontalPixelPerMeter = 1000;
497        dib.verticalPixelPerMeter = 1000;
498        dib.numColorsInPalette = 0;
499        dib.numImportantColors = 0;
500
501        if (swap) {
502            osg::swapBytes4((char*) &dibHdrSize);
503            osg::swapBytes4((char*) &dib.width);
504            osg::swapBytes4((char*) &dib.height);
505            osg::swapBytes2((char*) &dib.colorPlanes);
506            osg::swapBytes2((char*) &dib.bitsPerPixel);
507            osg::swapBytes4((char*) &dib.imageSize);
508            osg::swapBytes4((char*) &dib.horizontalPixelPerMeter);
509            osg::swapBytes4((char*) &dib.verticalPixelPerMeter);
510        }
511
512        fout.write((char*) &dibHdrSize, sizeof(dibHdrSize));
513        fout.write((char*) &dib, sizeof(dib));
514    }
515
516    unsigned int pixelFormat = img.getPixelFormat();
517
518    unsigned int r = 0, g = 1, b = 2;
519    if ( pixelFormat == GL_BGR || pixelFormat == GL_BGRA )
520    {
521        r = 2;
522        b = 0;
523    }
524
525    const unsigned int channelsPerPixel = img.computeNumComponents(pixelFormat);
526
527    std::vector<unsigned char> rowBuffer(bytesPerRowAlign);
528    for (int y = 0; y < img.t(); ++y)
529    {
530        const unsigned char* imgp = img.data(0, y);
531        for (int x = 0; x < img.s(); ++x)
532        {
533            // RGB -> BGR
534            unsigned int rowOffs = x * 3, imgOffs = x * channelsPerPixel;
535            rowBuffer[rowOffs + 2] = imgp[imgOffs + r];
536            rowBuffer[rowOffs + 1] = imgp[imgOffs + g];
537            rowBuffer[rowOffs + 0] = imgp[imgOffs + b];
538        }
539        fout.write((char*) &*rowBuffer.begin(), rowBuffer.size());
540    }
541
542    return true;
543}
544
545
546class ReaderWriterBMP : public osgDB::ReaderWriter
547{
548    public:
549
550        ReaderWriterBMP()
551        {
552            supportsExtension("bmp","BMP Image format");
553        }
554
555        const char* className() const { return "BMP Image Reader"; }
556
557
558        ReadResult readObject(std::istream& fin, const Options* options = 0) const
559        {
560            return readImage(fin, options);
561        }
562
563        ReadResult readObject(const std::string& file, const Options* options = 0) const
564        {
565            return readImage(file, options);
566        }
567
568
569        ReadResult readImage(std::istream& fin, const Options* = 0) const
570        {
571            return readBMPStream(fin);
572        }
573
574        ReadResult readImage(const std::string& file, const Options* options = 0) const
575        {
576            std::string ext = osgDB::getLowerCaseFileExtension(file);
577            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
578
579            std::string fileName = osgDB::findDataFile(file, options);
580            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
581
582            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
583            if(!istream) return ReadResult::FILE_NOT_HANDLED;
584
585            ReadResult rr = readBMPStream(istream);
586            if(rr.validImage()) rr.getImage()->setFileName(file);
587
588            return rr;
589        }
590
591
592        WriteResult writeImage(const osg::Image& image, std::ostream& fout, const Options* = 0) const
593        {
594            if (bmp_save(image, fout))
595                return WriteResult::FILE_SAVED;
596            else
597                return WriteResult::ERROR_IN_WRITING_FILE;
598        }
599
600        WriteResult writeImage(const osg::Image& img, const std::string& fileName, const Options* options = 0) const
601        {
602            std::string ext = osgDB::getFileExtension(fileName);
603            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
604
605            osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
606            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;
607
608            return writeImage(img, fout, options);
609        }
610
611    private:
612        static ReadResult readBMPStream(std::istream& fin)
613        {
614            int s, t;
615            int internalFormat;
616
617            unsigned char *imageData = bmp_load(fin, s, t, internalFormat);
618            if (imageData == 0) return ReadResult::ERROR_IN_READING_FILE;
619
620            unsigned int pixelFormat;
621            switch (internalFormat)
622            {
623            case 1:
624                pixelFormat = GL_LUMINANCE;
625                break;
626            case 2:
627                pixelFormat = GL_LUMINANCE_ALPHA;
628                break;
629            case 3:
630                pixelFormat = GL_RGB;
631                break;
632            default:
633                pixelFormat = GL_RGBA;
634                break;
635            }
636
637            osg::Image* img = new osg::Image;
638            img->setImage(s, t, 1,
639                internalFormat, pixelFormat, GL_UNSIGNED_BYTE, imageData,
640                osg::Image::USE_NEW_DELETE);
641
642            return img;
643        }
644};
645
646// now register with Registry to instantiate the above
647// reader/writer.
648REGISTER_OSGPLUGIN(bmp, ReaderWriterBMP)
Note: See TracBrowser for help on using the browser.