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

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

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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) (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.