C++程序  |  303行  |  9.85 KB

/** 
 **
 ** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
 ** You may obtain a copy of the License at 
 **
 **     http://www.apache.org/licenses/LICENSE-2.0 
 **
 ** Unless required by applicable law or agreed to in writing, software 
 ** distributed under the License is distributed on an "AS IS" BASIS, 
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 ** See the License for the specific language governing permissions and 
 ** limitations under the License.
 */

#ifndef _PIXELFLINGER2_IMAGE_FILE_H_
#define _PIXELFLINGER2_IMAGE_FILE_H_

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef union {	unsigned char channels[4]; unsigned int val; } Pixel;

static void SaveBMP(const char * filePath, unsigned * data, unsigned int width, unsigned int height)
{
	FILE * file = fopen(filePath, "wb");
    if (!file)
		printf("SaveMap failed fopen '%s'\n", filePath);	
		
	fwrite("BM", 2, 1, file);
	const unsigned int dataOffset = 0x36;
	const unsigned int dataSize = width * height * 4;
	const unsigned int fileSize = dataSize + dataOffset;
	fwrite((const char *)&fileSize, 4, 1, file); 
	fwrite("\0\0\0\0", 4, 1, file); // unused
	fwrite((const char *)&dataOffset, 4, 1, file);
	fwrite("\x28\0\0\0", 4, 1, file); // header size from this point
	fwrite((const char *)&width, 4, 1, file);
	fwrite((const char *)&height, 4, 1, file);
	fwrite("\x01\0", 2, 1, file); // 1 color plane
	fwrite("\x20\0", 2, 1, file); // 32bpp
	fwrite("\0\0\0\0", 4, 1, file); // BI_RGB no compression
	fwrite((const char *)&dataSize, 4, 1, file);
	fwrite("\x13\0\0\0", 4, 1, file); // horz res, 2.835 pixel/meter
	fwrite("\x13\0\0\0", 4, 1, file); // vert res
	fwrite("\0\0\0\0", 4, 1, file); // number of colours in palette
	fwrite("\0\0\0\0", 4, 1, file); // number of important colours, 0 means all are important
	
	// for non 32bpp image, need padding at end of each row for 4 byte alignment
	unsigned x, y;
	//for (y = 0; y < height; y++) // flip vertically
	//	fwrite((const char *)&data[(height - y - 1) * width], width * 4, 1, file);
	
	//fwrite(data, width * height * 4, 1, file);
	
    // vertical flip and convert rgba to bgra
    for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
        {
            unsigned pixel = data[(height - y - 1) * width + x];
            unsigned r = (pixel & 0xff) << 16;
            unsigned b = (pixel & 0xff0000) >> 16;
            pixel = (pixel & 0xff00ff00) | r | b;
            fwrite(&pixel, sizeof(pixel), 1, file);
        }
    
	fclose(file);
}

#if USE_16BPP_TEXTURE
#define TO_16BPP(rgba) ((((rgba) & 0xF8) >> 3) | (((rgba) & 0xFC00) >> 5) | (((rgba) & 0xF80000) >> 8))
#else
#define TO_16BPP(rgba) (rgba)
#endif

static int LoadTGA(const char * filePath, unsigned int * width, unsigned int * height, void ** data)
{
	FILE * file = fopen(filePath, "rb");
	if (!file)
	{
		printf("LoadTGA failed to open %s \n", filePath);
		assert(file);
		return -1;
	}
	
	struct TGAHeader 
	{
		unsigned char idLength, cMapType;
		enum TGAType { NoImageData = 0, UncompressedCM = 1, 
			UncompressedRGB = 2, UncompressedBW = 3, RLECM = 9, 
			RLERGB = 10, CompressedBW = 11, 
			CompressedCM = 32, //32  -  Compressed color-mapped data, using Huffman, Delta, and runlength encoding.
			CompressedCM4 = 33 //33  -  Compressed color-mapped data, using Huffman, Delta, and runlength encoding.  4-pass quadtree-type process.
		} type : 8;
        unsigned short cMapStart/* __attribute__ ((packed))*/;
        unsigned short cMapLength/* __attribute__ ((packed))*/;
		unsigned char cMapDepth;
        unsigned short xOffset/* __attribute__ ((packed))*/;
        unsigned short yOffset/* __attribute__ ((packed))*/;
		unsigned short width/* __attribute__ ((packed))*/;
        unsigned short height/* __attribute__ ((packed))*/;
		unsigned char bpp;
        struct { 
            unsigned attribs : 4; 
            unsigned reserved : 1;
            unsigned origin : 1; 
            enum InterleaveType { None = 0, TwoWay = 1, FourWay = 2, Reserved = 3 } interleave : 2;
        } __attribute__ ((packed)) imageDescriptor;
	} __attribute__ ((packed)) header;
	
    assert(18 == sizeof(header));
    fread(&header, sizeof(header), 1, file);
	
#if USE_16BPP_TEXTURE
    unsigned short * dest = (unsigned short *)malloc(header.width * header.height * 2);
#else
    unsigned * dest = (unsigned *)malloc(header.width * header.height * 4);
#endif
    
	*data = dest;
	*width = header.width;
	*height = header.height;
	
	if (!*data)
    {
        fclose(file);
		return -1;
	}

    Pixel pixel;
	unsigned int i;
        
	if (TGAHeader::UncompressedRGB == header.type)
	{
		if (24 == header.bpp)
		{
            pixel.channels[3] = 255;
            for (i = 0; i < (unsigned)header.width * header.height; i++)
			{
				pixel.channels[2] = fgetc(file);
                pixel.channels[1] = fgetc(file);
                pixel.channels[0] = fgetc(file);
				*dest++ = TO_16BPP(pixel.val); 
			}
		}
		else if (32 == header.bpp)
        {
            for (i = 0; i < (unsigned)header.width * header.height; i++)
            {
                pixel.channels[2] = fgetc(file);
                pixel.channels[1] = fgetc(file);
                pixel.channels[0] = fgetc(file);
                pixel.channels[3] = fgetc(file);
                *dest++ = TO_16BPP(pixel.val);
            }
			//fread(dest, header.width * header.height * 4, 1, file);
        }
	}
	else if (TGAHeader::UncompressedBW == header.type)
	{
		if (8 == header.bpp)
		{
			for (i = 0; i < (unsigned)header.width * header.height; i++)
			{
				pixel.channels[3] = pixel.channels[2] = 
				pixel.channels[1] = pixel.channels[0] = fgetc(file);
				*dest++ = TO_16BPP(pixel.val); 
			}
		}
		else
			assert(0);
	}
    else if (TGAHeader::RLERGB == header.type)
    {
        if (24 == header.bpp)
        {
            pixel.channels[3] = 255;
            for (i = 0; i < (unsigned)header.width * header.height;)
            {
                unsigned char count = fgetc(file);
                if (0x80 & count) // repeated run
                {
                    count = (0x7f & count) + 1;
                    pixel.channels[2] = fgetc(file);
                    pixel.channels[1] = fgetc(file);
                    pixel.channels[0] = fgetc(file);
                    for (unsigned j = 0; j < count; j++)
                        *dest++ = TO_16BPP(pixel.val);
                }
                else // literal run
                {
                    count += 1;
                    for (unsigned j = 0; j < count; j++)
                    {
                        pixel.channels[2] = fgetc(file);
                        pixel.channels[1] = fgetc(file);
                        pixel.channels[0] = fgetc(file);
                        *dest++ = TO_16BPP(pixel.val);
                    }
                }
                i += count;
            }
        }
        else if (32 == header.bpp)
        {
            for (i = 0; i < (unsigned)header.width * header.height;)
            {
                unsigned char count = fgetc(file);
                if (0x80 & count) // repeated run
                {
                    count = (0x7f & count) + 1;
                    pixel.channels[2] = fgetc(file);
                    pixel.channels[1] = fgetc(file);
                    pixel.channels[0] = fgetc(file);
                    pixel.channels[3] = fgetc(file);
                    for (unsigned j = 0; j < count; j++)
                        *dest++ = TO_16BPP(pixel.val);
                }
                else // literal run
                {
                    count += 1;
                    for (unsigned j = 0; j < count; j++)
                    {
                        pixel.channels[2] = fgetc(file);
                        pixel.channels[1] = fgetc(file);
                        pixel.channels[0] = fgetc(file);
                        pixel.channels[3] = fgetc(file);
                        *dest++ = TO_16BPP(pixel.val);
                    }
                }
                i += count;
            }
            
        }
        else
            assert(0);
    }
	else
		assert(0);
			
	fclose(file);
	
	return -1;
};

static unsigned GenerateMipmaps(void ** data, const unsigned width, const unsigned height)
{
    unsigned levels = 1;
    unsigned dim = (width > height ? width : height) >> 1;
    unsigned size = 0, w = width, h = height;
    while (dim)
    {
        levels++;
        w = (w + 1) / 2;
        h = (h + 1) / 2;
        size += w * h;
        dim >>= 1;
    }
    Pixel * buffer = (Pixel *)malloc(size * sizeof(*buffer));
    Pixel * previous = (Pixel *)data[0];
    w = width; h = height;
    for (unsigned i = 1; i < levels; i++)
    {
        const unsigned cw = (w + 1) / 2, ch = (h + 1) / 2;
        unsigned * current = (unsigned *)(data[i] = buffer);
        for (unsigned y = 0; y < ch; y++)
            for (unsigned x = 0; x < cw; x++)
            {
                unsigned channels[4] = {0,0,0,0};
                for (unsigned yy = 0; yy < 2; yy++)
                    for (unsigned xx = 0; xx < 2; xx++)
                    {
                        unsigned s = x * 2 + xx;
                        unsigned t = y * 2 + yy;
                        s = s < w - 1 ? s : w - 1;
                        t = t < h - 1 ? t : h - 1;
                        const Pixel * p = previous + t * w + s;
                        for (unsigned i = 0; i < 4; i++)
                            channels[i] += p->channels[i];
                    }
                for (unsigned i = 0; i < 4; i++)
                    channels[i] /= 4;
                current[y * cw + x] = channels[0] | (channels[1] << 8) |
                (channels[2] << 16) | (channels[3] << 24);

            }
        
        
        buffer += cw * ch;
        w = cw;
        h = ch;
        previous = (Pixel *)current;
    }
    
    return levels;
}

#endif // _PIXELFLINGER2_IMAGE_FILE_H_