Example #1
0
// Takes an image from the frame buffer and compresses it
int writeFrame(FIMEMORY *fiBuffer, FIBITMAP *fiImage, unsigned char imageType) {
	/*
	char msg[1024];
	sprintf_s(msg, 1024, "imageType: %i", imageType);
	MessageBox(NULL,msg,"ADES DEBUG", MB_OK);
	*/
	imageType = 2;
	int errStatus = 1;
	u_short imageWidth, imageHeight;

	unsigned width, height, pitch, line;
	BYTE *bits;

	// Package image using correct compression
	switch(imageType) {
	case 0: // Send a raw frame
		// Get image characteristics
		width = FreeImage_GetWidth(fiImage);
		height = FreeImage_GetHeight(fiImage);
		pitch = FreeImage_GetPitch(fiImage);
		line = FreeImage_GetLine(fiImage);

		// Write out width and height
		errStatus = FreeImage_SeekMemory(fiBuffer, 0, SEEK_SET);
		if (errStatus != 1) break;

		imageWidth = htons(width);
		errStatus = FreeImage_WriteMemory( &imageWidth, 2, 1, fiBuffer );
		if (errStatus != 1) break;
		
		imageHeight = htons(height);
		errStatus = FreeImage_WriteMemory( &imageHeight, 2, 1, fiBuffer );
		if (errStatus != 1) break;

		// Write out image (convert the bitmap to raw bits, top-left pixel first)
		bits = (BYTE*)malloc(height * pitch);
		FreeImage_ConvertToRawBits(bits, fiImage, pitch, 24, 
			FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, TRUE);
		errStatus = FreeImage_WriteMemory( bits, height*pitch*sizeof(BYTE), 1, fiBuffer );
		free(bits);
		if (errStatus != 1) break;
		
		break;
	default: // Send a jpg frame
		errStatus = FreeImage_SeekMemory(fiBuffer, 0, SEEK_SET);
		if (errStatus != 1) break;

		errStatus = FreeImage_SaveToMemory(FIF_JPEG, fiImage, fiBuffer, convertToJpegFlag(imageType));
		if (errStatus != 1) break;
		break;
	}
	
	// Clean up and exit
	return errStatus;
}
Example #2
0
static FIBITMAP*
mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) {
    long offset = 0;
    FIBITMAP *dib = NULL;

    if(hmem) {
        // seek to the start of the stream
        FreeImage_SeekMemory(hmem, offset, SEEK_SET);

        // check the file signature and deduce its format
        // (the second argument is currently not used by FreeImage)
        FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
        if(fif != FIF_UNKNOWN) {
            dib = FreeImage_LoadFromMemory(fif, hmem, flags);
        }
    }

    return dib;
}
Example #3
0
/**
   Insert a chunk just before the inNextChunkName chunk
   @param hPngMemory PNG stream handle
   @param start_pos Start position of the inNextChunkName chunk
   @param next_pos Start position of the next chunk
   @return Returns TRUE if successfull, returns FALSE otherwise
*/
static BOOL
mng_CopyInsertChunks(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, DWORD inChunkLength, DWORD start_pos, DWORD next_pos) {
    BYTE *data = NULL;
    DWORD size_in_bytes = 0;

    // length of the chunk to check
    DWORD chunk_length = next_pos - start_pos;
    if(chunk_length == 0) {
        return TRUE;
    }

    // get a pointer to the stream buffer
    FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
    if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
        // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
        return FALSE;
    }

    // new file length
    unsigned buffer_size = inChunkLength + size_in_bytes;

    BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
    if(!buffer) {
        return FALSE;
    }
    unsigned p = 0;
    memcpy(&buffer[p], &data[0], start_pos);
    p += start_pos;
    memcpy(&buffer[p], inInsertChunk, inChunkLength);
    p += inChunkLength;
    memcpy(&buffer[p], &data[start_pos], size_in_bytes - start_pos);

    // seek to the start of the stream
    FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
    // re-write the stream
    FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);

    free(buffer);

    return TRUE;
}
Example #4
0
void testSaveMemIO(const char *lpszPathName) {
    FIMEMORY *hmem = NULL;

    // load a regular file
    FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(lpszPathName);
    FIBITMAP *dib = FreeImage_Load(fif, lpszPathName, 0);

    // open a memory handle
    hmem = FreeImage_OpenMemory();

    // save the file to memory
    FreeImage_SaveToMemory(fif, dib, hmem, 0);

    // at this point, hmem contains the entire PNG data in memory.
    // the amount of space used by the memory is equal to file_size
    long file_size = FreeImage_TellMemory(hmem);
    printf("File size : %ld\n", file_size);


    // its easy load an image from memory as well

    // seek to the start of the memory stream
    FreeImage_SeekMemory(hmem, 0L, SEEK_SET);

    // get the file type
    FREE_IMAGE_FORMAT mem_fif = FreeImage_GetFileTypeFromMemory(hmem, 0);

    // load an image from the memory handle
    FIBITMAP *check = FreeImage_LoadFromMemory(mem_fif, hmem, 0);

    // save as a regular file
    FreeImage_Save(FIF_PNG, check, "dump.png", PNG_DEFAULT);

    // make sure to free the data since FreeImage_SaveToMemory
    // will cause it to be malloc'd
    FreeImage_CloseMemory(hmem);

    FreeImage_Unload(check);
    FreeImage_Unload(dib);
}
BOOL fipMemoryIO::seek(long offset, int origin) {
	return FreeImage_SeekMemory(_hmem, offset, origin);
}
Example #6
0
/**
   Load a FIBITMAP from a MNG or a JNG stream
   @param format_id ID of the caller
   @param io Stream i/o functions
   @param handle Stream handle
   @param Offset Start of the first chunk
   @param flags Loading flags
   @return Returns a dib if successful, returns NULL otherwise
*/
FIBITMAP*
mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) {
    DWORD mLength = 0;
    BYTE mChunkName[5];
    BYTE *mChunk = NULL;
    DWORD crc_file;
    long LastOffset;
    long mOrigPos;
    BYTE *PLTE_file_chunk = NULL;   // whole PLTE chunk (lentgh, name, array, crc)
    DWORD PLTE_file_size = 0;       // size of PLTE chunk

    BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk
    unsigned m_TotalBytesOfChunks = 0;
    FIBITMAP *dib = NULL;
    FIBITMAP *dib_alpha = NULL;

    FIMEMORY *hJpegMemory = NULL;
    FIMEMORY *hPngMemory = NULL;
    FIMEMORY *hIDATMemory = NULL;

    // ---
    DWORD jng_width = 0;
    DWORD jng_height = 0;
    BYTE jng_color_type = 0;
    BYTE jng_image_sample_depth = 0;
    BYTE jng_image_compression_method = 0;

    BYTE jng_alpha_sample_depth = 0;
    BYTE jng_alpha_compression_method = 0;
    BYTE jng_alpha_filter_method = 0;
    BYTE jng_alpha_interlace_method = 0;

    DWORD mng_frame_width = 0;
    DWORD mng_frame_height = 0;
    DWORD mng_ticks_per_second = 0;
    DWORD mng_nominal_layer_count = 0;
    DWORD mng_nominal_frame_count = 0;
    DWORD mng_nominal_play_time = 0;
    DWORD mng_simplicity_profile = 0;


    DWORD res_x = 2835; // 72 dpi
    DWORD res_y = 2835; // 72 dpi
    RGBQUAD rgbBkColor = {0, 0, 0, 0};
    WORD bk_red, bk_green, bk_blue;
    BOOL hasBkColor = FALSE;
    BOOL mHasIDAT = FALSE;

    tEXtMAP key_value_pair;

    // ---

    BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;

    // get the file size
    const long mLOF = mng_LOF(io, handle);
    // go to the first chunk
    io->seek_proc(handle, Offset, SEEK_SET);

    try {
        BOOL mEnd = FALSE;

        while(mEnd == FALSE) {
            // start of the chunk
            LastOffset = io->tell_proc(handle);
            // read length
            mLength = 0;
            io->read_proc(&mLength, 1, sizeof(mLength), handle);
            mng_SwapLong(&mLength);
            // read name
            io->read_proc(&mChunkName[0], 1, 4, handle);
            mChunkName[4] = '\0';

            if(mLength > 0) {
                mChunk = (BYTE*)realloc(mChunk, mLength);
                if(!mChunk) {
                    FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
                    throw (const char*)NULL;
                }
                Offset = io->tell_proc(handle);
                if(Offset + (long)mLength > mLOF) {
                    FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName);
                    throw (const char*)NULL;
                }
                // read chunk
                io->read_proc(mChunk, 1, mLength, handle);
            }
            // read crc
            io->read_proc(&crc_file, 1, sizeof(crc_file), handle);
            mng_SwapLong(&crc_file);
            // check crc
            DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4);
            crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength);
            if(crc_check != crc_file) {
                FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName);
                throw (const char*)NULL;
            }

            switch( mng_GetChunckType(mChunkName) ) {
                case MHDR:
                    // The MHDR chunk is always first in all MNG datastreams except for those
                    // that consist of a single PNG or JNG datastream with a PNG or JNG signature.
                    if(mLength == 28) {
                        memcpy(&mng_frame_width, &mChunk[0], 4);
                        memcpy(&mng_frame_height, &mChunk[4], 4);
                        memcpy(&mng_ticks_per_second, &mChunk[8], 4);
                        memcpy(&mng_nominal_layer_count, &mChunk[12], 4);
                        memcpy(&mng_nominal_frame_count, &mChunk[16], 4);
                        memcpy(&mng_nominal_play_time, &mChunk[20], 4);
                        memcpy(&mng_simplicity_profile, &mChunk[24], 4);

                        mng_SwapLong(&mng_frame_width);
                        mng_SwapLong(&mng_frame_height);
                        mng_SwapLong(&mng_ticks_per_second);
                        mng_SwapLong(&mng_nominal_layer_count);
                        mng_SwapLong(&mng_nominal_frame_count);
                        mng_SwapLong(&mng_nominal_play_time);
                        mng_SwapLong(&mng_simplicity_profile);

                    } else {
                        FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength);
                    }
                    break;

                case MEND:
                    mEnd = TRUE;
                    break;

                case LOOP:
                case ENDL:
                    break;
                case DEFI:
                    break;
                case SAVE:
                case SEEK:
                case TERM:
                    break;
                case BACK:
                    break;

                    // Global "PLTE" and "tRNS" (if any).  PNG "PLTE" will be of 0 byte, as it uses global data.
                case PLTE:  // Global
                    m_HasGlobalPalette = TRUE;
                    PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4)
                    PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size);
                    if(!PLTE_file_chunk) {
                        FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
                        throw (const char*)NULL;
                    } else {
                        mOrigPos = io->tell_proc(handle);
                        // seek to the start of the chunk
                        io->seek_proc(handle, LastOffset, SEEK_SET);
                        // load the whole chunk
                        io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle);
                        // go to the start of the next chunk
                        io->seek_proc(handle, mOrigPos, SEEK_SET);
                    }
                    break;

                case tRNS:  // Global
                    break;

                case IHDR:
                    Offset = LastOffset;
                    // parse the PNG file and get its file size
                    if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) {
                        // reach an unexpected end of file
                        mEnd = TRUE;
                        FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName);
                        break;
                    }

                    // wrap the { IHDR, ..., IEND } chunks as a PNG stream
                    if(hPngMemory == NULL) {
                        hPngMemory = FreeImage_OpenMemory();
                    }

                    mOrigPos = io->tell_proc(handle);

                    // write PNG file signature
                    FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
                    FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);

                    mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks);
                    if(!mChunk) {
                        FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
                        throw (const char*)NULL;
                    }

                    // on calling CountPNGChunks earlier, we were in Offset pos,
                    // go back there
                    io->seek_proc(handle, Offset, SEEK_SET);
                    io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle);
                    // Put back to original pos
                    io->seek_proc(handle, mOrigPos, SEEK_SET);
                    // write the PNG chunks
                    FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory);

                    // plug in global PLTE if local PLTE exists
                    if(m_HasGlobalPalette) {
                        // ensure we remove some local chunks, so that global
                        // "PLTE" can be inserted right before "IDAT".
                        mng_RemoveChunk(hPngMemory, mng_PLTE);
                        mng_RemoveChunk(hPngMemory, mng_tRNS);
                        mng_RemoveChunk(hPngMemory, mng_bKGD);
                        // insert global "PLTE" chunk in its entirety before "IDAT"
                        mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size);
                    }

                    if(dib) FreeImage_Unload(dib);
                    dib = mng_LoadFromMemoryHandle(hPngMemory, flags);

                    // stop after the first image
                    mEnd = TRUE;
                    break;

                case JHDR:
                    if(mLength == 16) {
                        memcpy(&jng_width, &mChunk[0], 4);
                        memcpy(&jng_height, &mChunk[4], 4);
                        mng_SwapLong(&jng_width);
                        mng_SwapLong(&jng_height);

                        jng_color_type = mChunk[8];
                        jng_image_sample_depth = mChunk[9];
                        jng_image_compression_method = mChunk[10];
                        //BYTE jng_image_interlace_method = mChunk[11]; // for debug only

                        jng_alpha_sample_depth = mChunk[12];
                        jng_alpha_compression_method = mChunk[13];
                        jng_alpha_filter_method = mChunk[14];
                        jng_alpha_interlace_method = mChunk[15];
                    } else {
                        FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName);
                        throw (const char*)NULL;
                    }
                    break;

                case JDAT:
                    if(hJpegMemory == NULL) {
                        hJpegMemory = FreeImage_OpenMemory();
                    }
                    // as there may be several JDAT chunks, concatenate them
                    FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory);
                    break;

                case IDAT:
                    if(!header_only && (jng_alpha_compression_method == 0)) {
                        // PNG grayscale IDAT format
                        if(hIDATMemory == NULL) {
                            hIDATMemory = FreeImage_OpenMemory();
                            mHasIDAT = TRUE;
                        }
                        // as there may be several IDAT chunks, concatenate them
                        FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory);
                    }
                    break;

                case IEND:
                    if(!hJpegMemory) {
                        mEnd = TRUE;
                        break;
                    }
                    // load the JPEG
                    if(dib) {
                        FreeImage_Unload(dib);
                    }
                    dib = mng_LoadFromMemoryHandle(hJpegMemory, flags);

                    // load the PNG alpha layer
                    if(mHasIDAT) {
                        BYTE *data = NULL;
                        DWORD size_in_bytes = 0;

                        // get a pointer to the IDAT buffer
                        FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes);
                        if(data && size_in_bytes) {
                            // wrap the IDAT chunk as a PNG stream
                            if(hPngMemory == NULL) {
                                hPngMemory = FreeImage_OpenMemory();
                            }
                            mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory);
                            // load the PNG
                            if(dib_alpha) {
                                FreeImage_Unload(dib_alpha);
                            }
                            dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags);
                        }
                    }
                    // stop the parsing
                    mEnd = TRUE;
                    break;

                case JDAA:
                    break;

                case gAMA:
                    break;

                case pHYs:
                    // unit is pixels per meter
                    memcpy(&res_x, &mChunk[0], 4);
                    mng_SwapLong(&res_x);
                    memcpy(&res_y, &mChunk[4], 4);
                    mng_SwapLong(&res_y);
                    break;

                case bKGD:
                    memcpy(&bk_red, &mChunk[0], 2);
                    mng_SwapShort(&bk_red);
                    rgbBkColor.rgbRed = (BYTE)bk_red;
                    memcpy(&bk_green, &mChunk[2], 2);
                    mng_SwapShort(&bk_green);
                    rgbBkColor.rgbGreen = (BYTE)bk_green;
                    memcpy(&bk_blue, &mChunk[4], 2);
                    mng_SwapShort(&bk_blue);
                    rgbBkColor.rgbBlue = (BYTE)bk_blue;
                    hasBkColor = TRUE;
                    break;

                case tEXt:
                    mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength);
                    break;

                case UNKNOWN_CHUNCK:
                default:
                    break;


            } // switch( GetChunckType )
        } // while(!mEnd)

        FreeImage_CloseMemory(hJpegMemory);
        FreeImage_CloseMemory(hPngMemory);
        FreeImage_CloseMemory(hIDATMemory);
        free(mChunk);
        free(PLTE_file_chunk);

        // convert to 32-bit if a transparent layer is available
        if(!header_only && dib_alpha) {
            FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib);
            if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) {
                FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA);
            } else {
                FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha);
                FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA);
                FreeImage_Unload(dst_alpha);
            }
            FreeImage_Unload(dib);
            dib = dst;
        }
        FreeImage_Unload(dib_alpha);

        if(dib) {
            // set metadata
            FreeImage_SetDotsPerMeterX(dib, res_x);
            FreeImage_SetDotsPerMeterY(dib, res_y);
            if(hasBkColor) {
                FreeImage_SetBackgroundColor(dib, &rgbBkColor);
            }
            if(key_value_pair.size()) {
                for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) {
                    std::string key = (*j).first;
                    std::string value = (*j).second;
                    mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str());
                }
            }
        }

        return dib;

    } catch(const char *text) {
        FreeImage_CloseMemory(hJpegMemory);
        FreeImage_CloseMemory(hPngMemory);
        FreeImage_CloseMemory(hIDATMemory);
        free(mChunk);
        free(PLTE_file_chunk);
        FreeImage_Unload(dib);
        FreeImage_Unload(dib_alpha);
        if(text) {
            FreeImage_OutputMessageProc(format_id, text);
        }
        return NULL;
    }
}
Example #7
0
bool IPLFileIO::loadMemory(void* mem, IPLImage*& image)
{
    FIMEMORY* hmem = (FIMEMORY*) mem;
    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
    FreeImage_SeekMemory(hmem, 0L, SEEK_SET);
    fif = FreeImage_GetFileTypeFromMemory(hmem, 0);

    if(fif == FIF_UNKNOWN)
    {
        // always close the memory stream
        FreeImage_CloseMemory(hmem);
        return false;
    }

    FIBITMAP *dib = FreeImage_LoadFromMemory(fif, hmem);
    int width = FreeImage_GetWidth(dib);
    int height = FreeImage_GetHeight(dib);

    // all files need to be flipped
    FreeImage_FlipVertical(dib);

    if(FreeImage_GetBPP(dib) == 8)
    {
        // grayscale images

        // convert to 32 bit
        FIBITMAP *dib2 = FreeImage_ConvertToGreyscale(dib);

        // clear old image
        delete image;
        // create new instance with the right dimensions
        image = new IPLImage(IPLData::IMAGE_GRAYSCALE, width, height);

        for(int y = 0; y < height; y++)
        {
            for(int x = 0; x < width; x++)
            {
                BYTE value;
                FreeImage_GetPixelIndex(dib2, x, y, &value);
                image->plane(0)->p(x, y) = (value  * FACTOR_TO_FLOAT);
            }
        }

        FreeImage_Unload(dib2);
    }
    else
    {
        // color images

        // convert to 32 bit
        FIBITMAP *dib2 = FreeImage_ConvertTo32Bits(dib);

        // clear old image
        delete image;
        // create new instance with the right dimensions
        image = new IPLImage(IPLData::IMAGE_COLOR, width, height);

        for(int y = 0; y < height; y++)
        {
            for(int x = 0; x < width; x++)
            {
                RGBQUAD rgb;
                FreeImage_GetPixelColor(dib2, x, y, &rgb);
                image->plane(0)->p(x, y) = (rgb.rgbRed   * FACTOR_TO_FLOAT);    // R
                image->plane(1)->p(x, y) = (rgb.rgbGreen * FACTOR_TO_FLOAT);    // G
                image->plane(2)->p(x, y) = (rgb.rgbBlue  * FACTOR_TO_FLOAT);    // B
            }
        }

        FreeImage_Unload(dib2);
    }

    // always close the memory stream
    FreeImage_CloseMemory(hmem);


    // free temporary memory
    FreeImage_Unload(dib);

    return true;
}
Example #8
0
/**
Write a metadata model as a TIF IFD to a FIMEMORY handle.
The entries in the TIF IFD are sorted in ascending order by tag id.	
The last entry is written as 0 (4 bytes) which means no more IFD to follow. 
Supported metadata models are
<ul>
<li>FIMD_EXIF_MAIN
<li>FIMD_EXIF_EXIF
<li>FIMD_EXIF_GPS
<li>FIMD_EXIF_INTEROP
</ul>
The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset)

@param dib Input FIBITMAP
@param md_model Metadata model to write
@param hmem Memory handle
@return Returns TRUE if successful, FALSE otherwise
@see tiff_get_ifd_profile
*/
static BOOL
tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) {
	FITAG *tag = NULL;
	FIMETADATA *mdhandle = NULL;
	std::vector<FITAG*> vTagList;
	TagLib::MDMODEL internal_md_model;

	DWORD ifd_offset = 0;	// WORD-aligned IFD value offset

	const BYTE empty_byte = 0;

	// start of the file
	const long start_of_file = FreeImage_TellMemory(hmem);

	// get the metadata count
	unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib);
	if(metadata_count == 0) {
		return FALSE;
	}

	TagLib& s = TagLib::instance();

	// check for supported metadata models
	switch(md_model) {
		case FIMD_EXIF_MAIN:
			internal_md_model = TagLib::EXIF_MAIN;
			break;
		case FIMD_EXIF_EXIF:
			internal_md_model = TagLib::EXIF_EXIF;
			break;
		case FIMD_EXIF_GPS:
			internal_md_model = TagLib::EXIF_GPS;
			break;
		case FIMD_EXIF_INTEROP:
			internal_md_model = TagLib::EXIF_INTEROP;
			break;
		default:
			return FALSE;
	}

	try {
		// 1) according to the TIFF specifications, 
		// the entries in a TIF IFD must be sorted in ascending order by tag id

		// store the tags into a vector
		vTagList.reserve(metadata_count);
		mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag);
		if(mdhandle) {
			// parse the tags and store them inside vTagList
			do {
				// rewrite the tag id using FreeImage internal database
				// (in case the tag id is wrong or missing)
				const char *key = FreeImage_GetTagKey(tag);
				int tag_id = s.getTagID(internal_md_model, key);
				if(tag_id != -1) {
					// this is a known tag, set the tag ID
					FreeImage_SetTagID(tag, (WORD)tag_id);
					// record the tag
					vTagList.push_back(tag);
				}
				// else ignore this tag
			} while(FreeImage_FindNextMetadata(mdhandle, &tag));

			FreeImage_FindCloseMetadata(mdhandle);

			// sort the vector by tag id
			std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare());

			// update the metadata_count
			metadata_count = (unsigned)vTagList.size();

		} else {
			throw(1);
		}

		// 2) prepare the place for each IFD entries.

		/*
		An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), 
		followed by a sequence of 12-byte field entries, 
		followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD.
		*/

		{		
			// prepare place for 2 bytes for number of entries + 12 bytes for each entry
			unsigned ifd_size = 2 + 12 * metadata_count;
			FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem);
			// record the offset used to write values > 4-bytes
			ifd_offset = FreeImage_TellMemory(hmem);
			// rewind
			FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET);
		}

		// 3) write each IFD entry in tag id ascending order

		// number of directory entries
		WORD nde = (WORD)metadata_count;
		FreeImage_WriteMemory(&nde, 1, 2, hmem);

		// for each entry ...
		for(unsigned i = 0; i < metadata_count; i++) {
			FITAG *tag = vTagList[i];
			// tag id
			WORD tag_id = FreeImage_GetTagID(tag);
			FreeImage_WriteMemory(&tag_id, 1, 2, hmem);
			// tag type (compliant with TIFF specification)
			WORD tag_type = (WORD)FreeImage_GetTagType(tag);
			FreeImage_WriteMemory(&tag_type, 1, 2, hmem);
			// tag count
			DWORD tag_count = FreeImage_GetTagCount(tag);
			FreeImage_WriteMemory(&tag_count, 1, 4, hmem);
			// tag value or offset (results are in BYTE's units)
			unsigned tag_length = FreeImage_GetTagLength(tag);
			if(tag_length <= 4) {
				// 4 bytes or less, write the value (left justified)
				const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag);
				FreeImage_WriteMemory(tag_value, 1, tag_length, hmem);
				for(unsigned k = tag_length; k < 4; k++) {
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
			} else {
				// write an offset
				FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem);
				// write the value
				long current_position = FreeImage_TellMemory(hmem);
				FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
				FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem);
				if(tag_length & 1) {
					// align to the next WORD boundary
					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
				}
				// next offset to use
				ifd_offset = FreeImage_TellMemory(hmem);
				// rewind
				FreeImage_SeekMemory(hmem, current_position, SEEK_SET);
			}
		}

		// end-of-IFD or next IFD (0 == none)
		FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
		FreeImage_WriteMemory(&empty_byte, 1, 4, hmem);

		return TRUE;
	}
	catch(int) {
		return FALSE;
	}
}
Example #9
0
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
    TIFF *faxTIFF = NULL;
    FIBITMAP *dib = NULL;
    FIMEMORY *memory = NULL;

    //int verbose = 0;
    int stretch = 0;
    int rows;
    float resX = 204.0;
    float resY = 196.0;

    uint32 xsize = G3_DEFAULT_WIDTH;
    int compression_in = COMPRESSION_CCITTFAX3;
    int fillorder_in = FILLORDER_LSB2MSB;
    uint32 group3options_in = 0;    // 1d-encoded
    uint32 group4options_in = 0;    // compressed
    int photometric_in = PHOTOMETRIC_MINISWHITE;

    if(handle==NULL) return NULL;

    try {
        // set default load options

        compression_in = COMPRESSION_CCITTFAX3;         // input is g3-encoded
        group3options_in &= ~GROUP3OPT_2DENCODING;      // input is 1d-encoded (g3 only)
        fillorder_in = FILLORDER_MSB2LSB;               // input has msb-to-lsb fillorder

        /*
          Original input-related fax2tiff options

          while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) {
          switch (c) {
          // input-related options
          case '3':       // input is g3-encoded
          compression_in = COMPRESSION_CCITTFAX3;
          break;
          case '4':       // input is g4-encoded
          compression_in = COMPRESSION_CCITTFAX4;
          break;
          case 'U':       // input is uncompressed (g3 and g4)
          group3options_in |= GROUP3OPT_UNCOMPRESSED;
          group4options_in |= GROUP4OPT_UNCOMPRESSED;
          break;
          case '1':       // input is 1d-encoded (g3 only)
          group3options_in &= ~GROUP3OPT_2DENCODING;
          break;
          case '2':       // input is 2d-encoded (g3 only)
          group3options_in |= GROUP3OPT_2DENCODING;
          break;
          case 'P':   // input has not-aligned EOL (g3 only)
          group3options_in &= ~GROUP3OPT_FILLBITS;
          break;
          case 'A':       // input has aligned EOL (g3 only)
          group3options_in |= GROUP3OPT_FILLBITS;
          break;
          case 'W':       // input has 0 mean white
          photometric_in = PHOTOMETRIC_MINISWHITE;
          break;
          case 'B':       // input has 0 mean black
          photometric_in = PHOTOMETRIC_MINISBLACK;
          break;
          case 'L':       // input has lsb-to-msb fillorder
          fillorder_in = FILLORDER_LSB2MSB;
          break;
          case 'M':       // input has msb-to-lsb fillorder
          fillorder_in = FILLORDER_MSB2LSB;
          break;
          case 'R':       // input resolution
          resY = (float) atof(optarg);
          break;
          case 'X':       // input width
          xsize = (uint32) atoi(optarg);
          break;

          // output-related options
          case 's':       // stretch image by dup'ng scanlines
          stretch = 1;
          break;
          case 'v':       // -v for info
          verbose++;
          break;
          }
          }

        */

        // open a temporary memory buffer to save decoded scanlines
        memory = FreeImage_OpenMemory();
        if(!memory) throw FI_MSG_ERROR_MEMORY;

        // wrap the raw fax file
        faxTIFF = TIFFClientOpen("(FakeInput)", "w",
                                 // TIFFClientOpen() fails if we don't set existing value here
                                 NULL,
                                 _g3ReadProc, _g3WriteProc,
                                 _g3SeekProc, _g3CloseProc,
                                 _g3SizeProc, _g3MapProc,
                                 _g3UnmapProc);

        if (faxTIFF == NULL) {
            throw "Can not create fake input file";
        }
        TIFFSetMode(faxTIFF, O_RDONLY);
        TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
        TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
        TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
        TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
        TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
        TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
        TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);

        // NB: this must be done after directory info is setup
        TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
        if (compression_in == COMPRESSION_CCITTFAX3)
            TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
        else if (compression_in == COMPRESSION_CCITTFAX4)
            TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);

        resX = 204;
        if (!stretch) {
            TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
        } else {
            resY = 196;
        }

        // decode the raw fax data
        rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory);
        if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options";


        // allocate the output dib
        dib = FreeImage_Allocate(xsize, rows, 1);
        unsigned pitch = FreeImage_GetPitch(dib);
        uint32 linesize = TIFFhowmany8(xsize);

        // fill the bitmap structure ...
        // ... palette
        RGBQUAD *pal = FreeImage_GetPalette(dib);
        if(photometric_in == PHOTOMETRIC_MINISWHITE) {
            pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
            pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
        } else {
            pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
            pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
        }
        // ... resolution
        FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5));
        FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5));

        // read the decoded scanline and fill the bitmap data
        FreeImage_SeekMemory(memory, 0, SEEK_SET);
        BYTE *bits = FreeImage_GetScanLine(dib, rows - 1);
        for(int k = 0; k < rows; k++) {
            FreeImage_ReadMemory(bits, linesize, 1, memory);
            bits -= pitch;
        }

        // free the TIFF wrapper
        TIFFClose(faxTIFF);

        // free the memory buffer
        FreeImage_CloseMemory(memory);

    } catch(const char *message) {
        if(memory) FreeImage_CloseMemory(memory);
        if(faxTIFF) TIFFClose(faxTIFF);
        if(dib) FreeImage_Unload(dib);
        FreeImage_OutputMessageProc(s_format_id, message);
        return NULL;
    }

    return dib;

}