475 lines
17 KiB
C++
475 lines
17 KiB
C++
|
//
|
||
|
// saveimag.cpp
|
||
|
//
|
||
|
// implementation of saving a file to disk via an installed graphic filter
|
||
|
//
|
||
|
#include "stdafx.h"
|
||
|
#include "pbrush.h"
|
||
|
#include "pbrusdoc.h"
|
||
|
#include "bmobject.h"
|
||
|
#include "imgwnd.h"
|
||
|
#include "imgsuprt.h"
|
||
|
#include "loadimag.h"
|
||
|
#include "saveimag.h"
|
||
|
#include "bmpstrm.h"
|
||
|
#include "imaging.h"
|
||
|
|
||
|
#include <atlbase.h>
|
||
|
|
||
|
#ifdef _X86_
|
||
|
#define _USE_IFL_API
|
||
|
#endif
|
||
|
|
||
|
extern BOOL GetHandlerForFile(BOOL bImport,LPCSTR szFile,
|
||
|
LPSTR szHandler,
|
||
|
UINT cb); // defined in loadimag.cpp
|
||
|
|
||
|
|
||
|
inline RGBTRIPLE GetPalEntry(LPVOID lpPal3, BYTE index)
|
||
|
{
|
||
|
RGBTRIPLE rgb;
|
||
|
|
||
|
rgb.rgbtRed = ((RGBTRIPLE *)lpPal3 + index)->rgbtRed;
|
||
|
rgb.rgbtGreen = ((RGBTRIPLE *)lpPal3 + index)->rgbtGreen;
|
||
|
rgb.rgbtBlue = ((RGBTRIPLE *)lpPal3 + index)->rgbtBlue;
|
||
|
|
||
|
return rgb;
|
||
|
}
|
||
|
|
||
|
inline void ConvertPalette(int bitCount, LPBYTE lpBuf, int width)
|
||
|
{
|
||
|
int j;
|
||
|
|
||
|
switch (bitCount)
|
||
|
{
|
||
|
case 4:
|
||
|
for (j=0; j<width; j++)
|
||
|
{
|
||
|
*(lpBuf+(width-1-j)*2+1) = (*(lpBuf+width-1-j) & 0x0f);
|
||
|
*(lpBuf+(width-1-j)*2) = (*(lpBuf+width-1-j) & 0xf0) >> 4;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
for (j=0; j<width; j++)
|
||
|
{
|
||
|
*(lpBuf+(width-1-j)*8+7) = (*(lpBuf+width-1-j) & 0x1);
|
||
|
*(lpBuf+(width-1-j)*8+6) = (*(lpBuf+width-1-j) & 0x2) >> 1;
|
||
|
*(lpBuf+(width-1-j)*8+5) = (*(lpBuf+width-1-j) & 0x4) >> 2;
|
||
|
*(lpBuf+(width-1-j)*8+4) = (*(lpBuf+width-1-j) & 0x8) >> 3;
|
||
|
*(lpBuf+(width-1-j)*8+3) = (*(lpBuf+width-1-j) & 0x10) >> 4;
|
||
|
*(lpBuf+(width-1-j)*8+2) = (*(lpBuf+width-1-j) & 0x20) >> 5;
|
||
|
*(lpBuf+(width-1-j)*8+1) = (*(lpBuf+width-1-j) & 0x40) >> 6;
|
||
|
*(lpBuf+(width-1-j)*8) = (*(lpBuf+width-1-j) & 0x80) >> 7;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// impossible!!!
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline BYTE SearchPalette(COLORREF crColor, LPVOID lpPal3)
|
||
|
{
|
||
|
BYTE byRed = GetRValue( crTrans );
|
||
|
BYTE byGreen = GetGValue( crTrans );
|
||
|
BYTE byBlue = GetBValue( crTrans );
|
||
|
|
||
|
for (int i = 0; i < MAX_PALETTE_COLORS; i++)
|
||
|
{
|
||
|
// note that we have to switch the colors back before
|
||
|
// attempting to compare them!!
|
||
|
if (byRed == ((RGBTRIPLE *)lpPal3 + i)->rgbtBlue &&
|
||
|
byGreen == ((RGBTRIPLE *)lpPal3 + i)->rgbtGreen &&
|
||
|
byBlue == ((RGBTRIPLE *)lpPal3 + i)->rgbtRed)
|
||
|
return (BYTE)i;
|
||
|
}
|
||
|
|
||
|
// shouldn't reach here!!
|
||
|
// (the color being searched must be in the palette)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
BOOL SaveDIBToFileA( LPCSTR szFileName,
|
||
|
REFGUID guidFormatID,
|
||
|
CBitmapObj* pBitmap )
|
||
|
{
|
||
|
IFLTYPE iflType;
|
||
|
|
||
|
if (guidFormatID == WiaImgFmt_GIF)
|
||
|
{
|
||
|
iflType = IFLT_GIF;
|
||
|
}
|
||
|
else if (guidFormatID == WiaImgFmt_BMP)
|
||
|
{
|
||
|
iflType = IFLT_BMP;
|
||
|
}
|
||
|
else if (guidFormatID == WiaImgFmt_JPEG)
|
||
|
{
|
||
|
iflType = IFLT_JPEG;
|
||
|
}
|
||
|
else if (guidFormatID == WiaImgFmt_TIFF)
|
||
|
{
|
||
|
iflType = IFLT_TIFF;
|
||
|
}
|
||
|
else if (guidFormatID == WiaImgFmt_PNG)
|
||
|
{
|
||
|
iflType = IFLT_PNG;
|
||
|
}
|
||
|
else if (guidFormatID == WiaImgFmt_PHOTOCD)
|
||
|
{
|
||
|
iflType = IFLT_PCD;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#ifdef _USE_IFL_API
|
||
|
LPBITMAPINFOHEADER lpDib = (LPBITMAPINFOHEADER) GlobalLock(pBitmap->m_hThing);
|
||
|
|
||
|
IFLCLASS iflClass = (lpDib->biBitCount == 24) ? IFLCL_RGB : IFLCL_PALETTE;
|
||
|
int iBPS = 8; // bits per sample
|
||
|
|
||
|
if (iflType == IFLT_JPEG)
|
||
|
// force it to be RGB type, otherwise the JPEG filter won't take it
|
||
|
iflClass = IFLCL_RGB;
|
||
|
|
||
|
if (iflType == IFLT_GIF && iflClass == IFLCL_RGB)
|
||
|
{
|
||
|
GlobalUnlock(pBitmap->m_hThing);
|
||
|
|
||
|
// force it to be PALETTE type, otherwise the GIF filter won't take it
|
||
|
iflClass = IFLCL_PALETTE;
|
||
|
|
||
|
// Now convert the image from RGB to palette-based. Note that
|
||
|
// the call to DibFromBitmap() will allocate new memory!!
|
||
|
DWORD dwSize;
|
||
|
lpDib = (LPBITMAPINFOHEADER) DibFromBitmap(
|
||
|
pBitmap->m_pImg->hBitmap, BI_RGB, (WORD) iBPS,
|
||
|
pBitmap->m_pImg->m_pPalette, NULL, dwSize,
|
||
|
pBitmap->m_pImg->cXPelsPerMeter, pBitmap->m_pImg->cYPelsPerMeter);
|
||
|
|
||
|
if (lpDib == NULL)
|
||
|
return FALSE; // memory allocation failed
|
||
|
|
||
|
// now replace the original
|
||
|
pBitmap->Free();
|
||
|
pBitmap->m_hThing = lpDib;
|
||
|
pBitmap->m_lMemSize = dwSize;
|
||
|
lpDib = (LPBITMAPINFOHEADER) GlobalLock(pBitmap->m_hThing);
|
||
|
}
|
||
|
|
||
|
|
||
|
IFLCOMPRESSION iflCompression = IFLCOMP_DEFAULT; // or IFLCOMP_NONE ???
|
||
|
|
||
|
IFLHANDLE iflHandle = iflCreateWriteHandle(lpDib->biWidth, lpDib->biHeight,
|
||
|
iflClass, iBPS, iflCompression, iflType);
|
||
|
|
||
|
if (iflHandle == NULL)
|
||
|
{
|
||
|
GlobalUnlock(pBitmap->m_hThing);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IFLERROR iflErr = iflOpen(iflHandle, (LPSTR)szFileName, IFLM_WRITE);
|
||
|
if (iflErr != IFLERR_NONE)
|
||
|
{
|
||
|
iflFreeHandle(iflHandle);
|
||
|
GlobalUnlock(pBitmap->m_hThing);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
LPBITMAPINFOHEADER lpHdr = lpDib;
|
||
|
|
||
|
DWORD dwHdrLen = lpHdr->biSize + PaletteSize((LPSTR)lpHdr);
|
||
|
|
||
|
LPBYTE hp = ((LPBYTE)lpDib) + dwHdrLen;
|
||
|
|
||
|
int iOutWidth = (lpDib->biBitCount == 24) ?
|
||
|
lpDib->biWidth*3 :
|
||
|
lpDib->biWidth*24/lpDib->biBitCount;
|
||
|
|
||
|
LPBYTE lpBuf = new BYTE[iOutWidth];
|
||
|
|
||
|
int i, j, k;
|
||
|
BYTE byTemp;
|
||
|
BOOL fFound;
|
||
|
|
||
|
// convert from pixels to bytes after rounding it up first
|
||
|
DWORD dwWidthInBytes = ((lpDib->biWidth * lpDib->biBitCount + 31) &~31) /8;
|
||
|
if (iflClass == IFLCL_RGB)
|
||
|
{
|
||
|
// set the transparent color on demand and only if it's been set
|
||
|
if (g_bUseTrans && crTrans != TRANS_COLOR_NONE) // not default
|
||
|
{
|
||
|
IFLCOLOR iflTransColor;
|
||
|
|
||
|
iflTransColor.wRed = GetRValue( crTrans );
|
||
|
iflTransColor.wGreen = GetGValue( crTrans );
|
||
|
iflTransColor.wBlue = GetBValue( crTrans );
|
||
|
|
||
|
// ignore any error return (if a format doesn't support
|
||
|
// transparent color, so be it)
|
||
|
iflControl(iflHandle, IFLCMD_TRANS_RGB, 0, 0, &iflTransColor);
|
||
|
}
|
||
|
|
||
|
if (lpDib->biBitCount == 24)
|
||
|
{
|
||
|
// we already have a RGB image, so just copy it out
|
||
|
LPBYTE lpBMP = hp + lpDib->biSizeImage - dwWidthInBytes;
|
||
|
|
||
|
for (i = 0;
|
||
|
i < abs(lpDib->biHeight);
|
||
|
lpBMP-=dwWidthInBytes, i++)
|
||
|
{
|
||
|
memcpy(lpBuf, lpBMP, iOutWidth);
|
||
|
|
||
|
// need to swap RED with BLUE for export
|
||
|
for (j = 0; j < iOutWidth; j+=3)
|
||
|
{
|
||
|
byTemp = *(lpBuf+j);
|
||
|
*(lpBuf+j) = *(lpBuf+j+2);
|
||
|
*(lpBuf+j+2) = byTemp;
|
||
|
}
|
||
|
|
||
|
// write out one line at a time
|
||
|
iflWrite(iflHandle, lpBuf, 1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// need to convert from palatte color
|
||
|
RGBTRIPLE Pal3[MAX_PALETTE_COLORS];
|
||
|
memset(Pal3, 255, MAX_PALETTE_COLORS*sizeof(RGBTRIPLE));
|
||
|
|
||
|
LPRGBQUAD lpPal4 = (LPRGBQUAD)((LPBYTE)lpDib + lpDib->biSize);
|
||
|
for (i = 0; i < MAX_PALETTE_COLORS; i++)
|
||
|
{
|
||
|
Pal3[i].rgbtRed = (lpPal4+i)->rgbBlue;
|
||
|
Pal3[i].rgbtGreen = (lpPal4+i)->rgbGreen;
|
||
|
Pal3[i].rgbtBlue = (lpPal4+i)->rgbRed;
|
||
|
}
|
||
|
|
||
|
LPBYTE lpBMP = hp + lpDib->biSizeImage - dwWidthInBytes;
|
||
|
|
||
|
for (i = 0;
|
||
|
i < abs(lpDib->biHeight);
|
||
|
lpBMP-=dwWidthInBytes, i++)
|
||
|
{
|
||
|
memcpy(lpBuf, lpBMP, lpDib->biWidth);
|
||
|
|
||
|
if (lpDib->biBitCount != 8)
|
||
|
ConvertPalette(lpDib->biBitCount, lpBuf, lpDib->biWidth);
|
||
|
|
||
|
for (j = 0; j < lpDib->biWidth; j++)
|
||
|
{
|
||
|
((RGBTRIPLE *)(lpBuf+(lpDib->biWidth-j-1)*3))->rgbtRed =
|
||
|
GetPalEntry(&Pal3, *(lpBuf+lpDib->biWidth-j-1)).rgbtRed;
|
||
|
((RGBTRIPLE *)(lpBuf+(lpDib->biWidth-j-1)*3))->rgbtGreen =
|
||
|
GetPalEntry(&Pal3, *(lpBuf+lpDib->biWidth-j-1)).rgbtGreen;
|
||
|
((RGBTRIPLE *)(lpBuf+(lpDib->biWidth-j-1)*3))->rgbtBlue =
|
||
|
GetPalEntry(&Pal3, *(lpBuf+lpDib->biWidth-j-1)).rgbtBlue;
|
||
|
}
|
||
|
|
||
|
// write out one line at a time
|
||
|
iflWrite(iflHandle, lpBuf, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (iflClass == IFLCL_PALETTE)
|
||
|
{
|
||
|
// first, get the color palette straight...
|
||
|
RGBTRIPLE Pal3[MAX_PALETTE_COLORS];
|
||
|
memset(Pal3, 255, MAX_PALETTE_COLORS*sizeof(RGBTRIPLE));
|
||
|
|
||
|
if (PaletteSize((LPSTR)lpDib) != 0)
|
||
|
{
|
||
|
// we have one available, so just copy it out...
|
||
|
// but not before we swap the RGB values first
|
||
|
LPRGBQUAD lpPal4 = (LPRGBQUAD)((LPBYTE)lpDib + lpDib->biSize);
|
||
|
for (i = 0; i < MAX_PALETTE_COLORS; i++)
|
||
|
{
|
||
|
Pal3[i].rgbtRed = (lpPal4+i)->rgbBlue;
|
||
|
Pal3[i].rgbtGreen = (lpPal4+i)->rgbGreen;
|
||
|
Pal3[i].rgbtBlue = (lpPal4+i)->rgbRed;
|
||
|
}
|
||
|
iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &Pal3);
|
||
|
|
||
|
if (g_bUseTrans)
|
||
|
{
|
||
|
BYTE byTransIdx = SearchPalette(crTrans, &Pal3);
|
||
|
iflControl(iflHandle, IFLCMD_TRANS_IDX, 0, 0, &byTransIdx);
|
||
|
}
|
||
|
|
||
|
LPBYTE lpBMP = hp + lpDib->biSizeImage - dwWidthInBytes;
|
||
|
|
||
|
for (i = 0;
|
||
|
i < abs(lpDib->biHeight);
|
||
|
lpBMP-=dwWidthInBytes, i++)
|
||
|
{
|
||
|
memcpy(lpBuf, lpBMP, lpDib->biWidth);
|
||
|
|
||
|
if (lpDib->biBitCount != 8)
|
||
|
ConvertPalette(lpDib->biBitCount, lpBuf, lpDib->biWidth);
|
||
|
|
||
|
// write out one line at a time
|
||
|
iflWrite(iflHandle, lpBuf, 1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we have to create our own palette...
|
||
|
for (i = 0, k = 0; i < (int)lpDib->biSizeImage; i+=3)
|
||
|
{
|
||
|
fFound = FALSE;
|
||
|
for (j = 0; j < MAX_PALETTE_COLORS; j++)
|
||
|
if (Pal3[j].rgbtRed == ((RGBTRIPLE *)(hp+i))->rgbtRed &&
|
||
|
Pal3[j].rgbtGreen == ((RGBTRIPLE *)(hp+i))->rgbtGreen &&
|
||
|
Pal3[j].rgbtBlue == ((RGBTRIPLE *)(hp+i))->rgbtBlue)
|
||
|
{
|
||
|
fFound = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!fFound && k < MAX_PALETTE_COLORS)
|
||
|
{
|
||
|
Pal3[k].rgbtRed = ((RGBTRIPLE *)(hp+i))->rgbtRed;
|
||
|
Pal3[k].rgbtGreen = ((RGBTRIPLE *)(hp+i))->rgbtGreen;
|
||
|
Pal3[k].rgbtBlue = ((RGBTRIPLE *)(hp+i))->rgbtBlue;
|
||
|
k++;
|
||
|
}
|
||
|
|
||
|
if (k >= MAX_PALETTE_COLORS)
|
||
|
// we have already filled every palette entry
|
||
|
break;
|
||
|
}
|
||
|
iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &Pal3);
|
||
|
|
||
|
LPBYTE lpBMP = hp + lpDib->biSizeImage - dwWidthInBytes;
|
||
|
for (i = 0;
|
||
|
i < abs(lpDib->biHeight);
|
||
|
lpBMP-=dwWidthInBytes, i++)
|
||
|
{
|
||
|
memcpy(lpBuf, lpBMP, lpDib->biWidth);
|
||
|
|
||
|
for (j = 0; j < lpDib->biWidth; j+=3)
|
||
|
{
|
||
|
fFound = FALSE;
|
||
|
for (k = 0; k < MAX_PALETTE_COLORS; k++)
|
||
|
{
|
||
|
if (*(lpBuf+j) == Pal3[k].rgbtRed &&
|
||
|
*(lpBuf+j+1) == Pal3[k].rgbtGreen &&
|
||
|
*(lpBuf+j+2) == Pal3[k].rgbtBlue)
|
||
|
{
|
||
|
fFound = TRUE;
|
||
|
*(lpBuf+j/3) = (BYTE) k;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if (!fFound)
|
||
|
// *(lpBuf+j/3) = 255;
|
||
|
}
|
||
|
|
||
|
// write out one line at a time
|
||
|
iflWrite(iflHandle, lpBuf, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
; // currently not supported
|
||
|
|
||
|
delete [] lpBuf;
|
||
|
|
||
|
iflClose(iflHandle);
|
||
|
iflFreeHandle(iflHandle);
|
||
|
|
||
|
GlobalUnlock(pBitmap->m_hThing);
|
||
|
|
||
|
// now update the image by loading the file just exported
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
HGLOBAL hNewDib = LoadDIBFromFile(A2CT(szFileName), &theApp.m_guidFltTypeUsed);
|
||
|
pBitmap->ReadResource(hNewDib);
|
||
|
|
||
|
theApp.m_sCurFile = szFileName;
|
||
|
return TRUE;
|
||
|
#endif // _USE_IFL_API
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL SaveDIBGdiplus(LPCTSTR szFileName, REFGUID guidFormatID, CBitmapObj* pBitmap)
|
||
|
{
|
||
|
// find a suitable export filter
|
||
|
|
||
|
CLSID ClsidEncoder;
|
||
|
|
||
|
if (GetClsidOfEncoder(guidFormatID, &ClsidEncoder))
|
||
|
{
|
||
|
// create a stream that emulates a bmp file
|
||
|
|
||
|
CComPtr<CBmpStream> pStream;
|
||
|
|
||
|
if (CBmpStream::Create(&pStream) == S_OK)
|
||
|
{
|
||
|
pStream->SetBuffer(pBitmap->m_hThing, pBitmap->m_lMemSize, pBitmap->m_dwOffBits);
|
||
|
|
||
|
// create the GDI+ object
|
||
|
|
||
|
Gdiplus::Bitmap image(pStream);
|
||
|
|
||
|
if (image.GetLastStatus() == Gdiplus::Ok)
|
||
|
{
|
||
|
// let GDI+ export the file
|
||
|
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
if (image.Save(T2CW(szFileName), &ClsidEncoder, 0) == Gdiplus::Ok)
|
||
|
{
|
||
|
// now update the image by loading the file just exported
|
||
|
|
||
|
HGLOBAL hNewDib = LoadDIBFromFile(szFileName, &theApp.m_guidFltTypeUsed);
|
||
|
|
||
|
pBitmap->ReadResource(hNewDib);
|
||
|
|
||
|
theApp.m_sCurFile = szFileName;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL SaveDIBToFile(LPCTSTR szFileName, REFGUID guidFormatID, CBitmapObj* pBitmap)
|
||
|
{
|
||
|
// Try GDI+ filters first. If it fails to convert the image or
|
||
|
// if it's not available, try the old method
|
||
|
|
||
|
BOOL bResult = FALSE;
|
||
|
|
||
|
if (theApp.GdiplusInit.StartupStatus == Gdiplus::Ok)
|
||
|
{
|
||
|
__try
|
||
|
{
|
||
|
bResult = SaveDIBGdiplus(szFileName, guidFormatID, pBitmap);
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!bResult)
|
||
|
{
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
bResult = SaveDIBToFileA(T2CA(szFileName), guidFormatID, pBitmap);
|
||
|
}
|
||
|
|
||
|
return bResult;
|
||
|
}
|