sfftools/sfftobmp3/src/output.cpp

307 lines
9.7 KiB
C++

// Output classes
//
// This file is part of sfftobmp, a program to convert
// structured fax files (sff) to windows bitmap files (bmp),
// portable bitmap graphics (pbm), tagged image file format (tiff)
// or JPEG (jpg).
//
// Copyright (C) 1998-2012 Peter Schaefer-Hutter and contributors ("THE AUTHORS")
//
// Permission to use, copy, modify, distribute, and sell this software and
// its documentation for any purpose is hereby granted without fee.
//
// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
// INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
// THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE
// OR PERFORMANCE OF THIS SOFTWARE.
//
// Contributor(s):
// Gernot Hillier <ghillie@suse.de> (StdOut output support)
// Ulf Zibis <ulf.zibis@gmx.de> (Resolution preserving output, Cleanups,
// inheritance from CFile, destructor handling, preserving filetime)
//
// You can contact the original author by email at peter.schaefer@gmx.de.
//
// I'm always pleased to hear that somebody is actually using my software.
// If you can manage it, e-mail me a quick notice. Thanks!
//
/*-RCS-Info----------------------------------------------------
$Id: output.cpp,v 1.5 2009/04/28 19:02:08 pschaefer Exp $
---RCS-Info--------------------------------------------------*/
#include <cstring>
#include <cassert>
#include <iostream>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
#ifdef WIN32
#include <io.h>
#endif
#include "sfftypes.h"
#include "common.h"
extern "C"
{
#define XMD_H
#include <jpeglib.h>
#include <tiffio.h>
}
#include "codes.h"
#include "decoder.h"
#include "output.h"
using namespace std;
namespace fs = boost::filesystem;
//-Constants-------------------------------------------------------
TBitMapFileHeader CBMPFilter::m_bmpHeader =
{
0x4d42, /* magic 'BM' */
0, /* filesize */
0, /* reserved */
0, /* reserved */
sizeof(TBitMapFileHeader), /* offset to bitmap data */
0x28, /* length of bitmap info header 0x28 -> Windows BMP */
0, /* width in pixels */
0, /* height in pixels */
1, /* no. of planes */
1, /* bpp -> monochrome */
0, /* compression -> none */
0, /* size of bitmap data in bytes rounded to 4 byte boundary */
0, /* horz. resolution in pixels/meter */
0, /* vert. resolution in pixels/meter */
2, /* no. of colours */
2, /* no. of important colours */
{ 255,255,255, 0 }, /* palette */
{ 0, 0, 0, 0 }
};
//-----------------------------------------------------------------
CBitSink& COutputFilter::GetBitSink()
{
m_sink.Reset();
return m_sink;
}
void COutputFilter::BlankLine()
{
::memset(m_abBuffer, 0x00, sizeof(m_abBuffer));
}
void COutputFilter::Init(CFile *pFile)
{
m_pFile = pFile;
}
void COutputFilter::EndPage()
{
}
void COutputFilter::Finalize()
{
// base class does nothing
}
//-----------------------------------------------------------------
void CBMPFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
sff_dword aHeight, sff_word aDpi,
sff_word aLpi)
{
assert(m_pFile);
m_bmpHeader.dwPixelWidth = aWidth;
m_bmpHeader.dwPixelHeight = aHeight;
m_bmpHeader.dwPixelWidthPerMeter = (aDpi*10000+127)/254;
m_bmpHeader.dwPixelHeightPerMeter = (aLpi*10000+127)/254;
m_bmpHeader.dwSizeImage = (m_bmpHeader.dwPixelWidth >> 3) * m_bmpHeader.dwPixelHeight;
m_nLineToWrite = aHeight;
m_pFile->Seek(0, CFile::sk_from_start);
m_pFile->Write(&m_bmpHeader, sizeof(m_bmpHeader));
}
void CBMPFilter::WriteLine()
{
assert(m_pFile);
sff_dword dwBytesPerLine = (m_bmpHeader.dwPixelWidth >> 3);
/* we write lines from the end to the top of the file */
if (m_nLineToWrite-- > 0) {
m_pFile->Seek(dwBytesPerLine * m_nLineToWrite + sizeof(m_bmpHeader), CFile::sk_from_start);
m_pFile->Write(m_abBuffer, dwBytesPerLine);
}
}
//-----------------------------------------------------------------
void CPBMFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
sff_dword aHeight, sff_word aDpi,
sff_word aLpi)
{
char acBuf[50];
assert(m_pFile);
m_Width = aWidth;
m_pFile->Seek(0, CFile::sk_from_start);
sprintf(acBuf, "%s", "P4\n");
m_pFile->Write(acBuf, strlen(acBuf));
sprintf(acBuf, "%s", "# generated with SffToBmp\n");
m_pFile->Write(acBuf, strlen(acBuf));
sprintf(acBuf, "%ld %ld\n", (long int) aWidth, (long int) aHeight);
m_pFile->Write(acBuf, strlen(acBuf));
}
void CPBMFilter::WriteLine()
{
assert(m_pFile);
m_pFile->Write(m_abBuffer, m_Width >> 3);
}
//-----------------------------------------------------------------
void CJPEGFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
sff_dword aHeight, sff_word aDpi,
sff_word aLpi)
{
assert(m_pFile);
memset(&m_cinfo, 0, sizeof(m_cinfo));
m_cinfo.err = jpeg_std_error(&m_jerr);
jpeg_create_compress(&m_cinfo);
m_cinfo.in_color_space = JCS_GRAYSCALE;
jpeg_set_defaults(&m_cinfo);
m_cinfo.image_width = aWidth;
m_cinfo.image_height = aHeight;
m_cinfo.input_components = 1;
m_cinfo.dct_method = JDCT_FLOAT;
m_cinfo.density_unit = 1;
m_cinfo.X_density = aDpi;
m_cinfo.Y_density = aLpi;
jpeg_stdio_dest(&m_cinfo, m_pFile->GetFP());
jpeg_set_quality(&m_cinfo, m_quality, FALSE);
jpeg_set_colorspace(&m_cinfo, JCS_GRAYSCALE);
jpeg_start_compress(&m_cinfo, TRUE);
}
void CJPEGFilter::WriteLine()
{
m_row_pointer[0] = m_abBuffer;
jpeg_write_scanlines(&m_cinfo, m_row_pointer, 1);
}
void CJPEGFilter::EndPage()
{
assert(m_pFile);
jpeg_finish_compress(&m_cinfo);
jpeg_destroy_compress(&m_cinfo);
}
CBitSink& CJPEGFilter::GetBitSink()
{
m_bytesink.Reset();
return m_bytesink;
}
void CJPEGFilter::BlankLine()
{
::memset(m_abBuffer, 0xFF, sizeof(m_abBuffer));
}
//-----------------------------------------------------------------
void CTIFFFilter::Init(CFile *pFile)
{
COutputFilter::Init(pFile);
}
void CTIFFFilter::BeginPage(sff_dword aPage, sff_dword aWidth,
sff_dword aHeight, sff_word aDpi,
sff_word aLpi)
{
tm *pTime;
char acTime[25];
if (!m_tiffFile) {
#ifndef WIN32
m_tiffFile = TIFFFdOpen(m_pFile->GetFN(), m_pFile->GetFileName(), "w");
#else
m_tiffFile = TIFFFdOpen(_get_osfhandle(m_pFile->GetFN()),
m_pFile->GetFileName(), "w");
#endif
}
m_nRow = 0;
// --- TIFF required:
TIFFSetField(m_tiffFile, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
TIFFSetField(m_tiffFile, TIFFTAG_IMAGEWIDTH, aWidth);
// TIFFTAG_IMAGELENGTH ...automaticaly set by TIFFWriteScanline()
// TIFFTAG_STRIPOFFSETS ...automaticaly set by TIFFWriteScanline()
TIFFSetField(m_tiffFile, TIFFTAG_ROWSPERSTRIP, aHeight);
// TIFFTAG_STRIPBYTECOUNTS ...automaticaly set by TIFFWriteScanline()
TIFFSetField(m_tiffFile, TIFFTAG_XRESOLUTION, (float)aDpi);
TIFFSetField(m_tiffFile, TIFFTAG_YRESOLUTION, (float)aLpi);
TIFFSetField(m_tiffFile, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
// --- Class B required:
TIFFSetField(m_tiffFile, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(m_tiffFile, TIFFTAG_COMPRESSION, m_wCompression);
TIFFSetField(m_tiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
TIFFSetField(m_tiffFile, TIFFTAG_SAMPLESPERPIXEL, 1);
// --- Class F required:
TIFFSetField(m_tiffFile, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
TIFFSetField(m_tiffFile, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
if (m_wCompression == COMPRESSION_CCITTFAX3) {
TIFFSetField(m_tiffFile, TIFFTAG_GROUP3OPTIONS, GROUP3OPT_FILLBITS|GROUP3OPT_2DENCODING);
}
TIFFSetField(m_tiffFile, TIFFTAG_PAGENUMBER, aPage, m_nPageCount);
// --- Class F recommended:
TIFFSetField(m_tiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(m_tiffFile, TIFFTAG_SOFTWARE, "SffToBmp");
// - Some viewers interpret this as fax sender
TIFFSetField(m_tiffFile, TIFFTAG_IMAGEDESCRIPTION, "");
// - Some viewers interpret this as receive time
// - Has to be exactly 19 chars: 2007-01-01 24:00:00
if ((pTime = ::gmtime(&m_nModTime))) {
strftime(acTime, 20, "%Y-%m-%d %H:%M:%S", pTime);
}
acTime[19]='\0';
TIFFSetField(m_tiffFile, TIFFTAG_DATETIME, acTime);
// - not recommended, but required by TIFFWriteScanline(); why ?:
TIFFSetField(m_tiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
//TIFFSetWarningHandler(NULL);
}
void CTIFFFilter::WriteLine()
{
if (m_tiffFile) {
TIFFWriteScanline(m_tiffFile, m_abBuffer, m_nRow++,0);
}
}
void CTIFFFilter::EndPage()
{
TIFFWriteDirectory(m_tiffFile);
}
void CTIFFFilter::Finalize()
{
if (m_tiffFile) {
TIFFClose(m_tiffFile);
}
m_tiffFile = NULL;
}