windows-nt/Source/XPSP1/NT/shell/osshell/accesory/mspaint/saveimag.cpp
2020-09-26 16:20:57 +08:00

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;
}