Windows2000/private/shell/ext/thumbvw/tngen/tnail.cpp
2020-09-30 17:12:32 +02:00

772 lines
17 KiB
C++

/******Module*Header**\
* Module Name: tnail.cpp
*
* Thumbnail API interface
*
* Created: 17-Oct-1997
* Author: Ori Gershony [orig]
*
* Copyright (c) 1996 Microsoft Corporation
\****/
#include "stdafx.h"
#include "jinclude.h"
#include "jpeglib.h"
#include "jversion.h"
#include "jerror.h"
#include "ddraw.h"
#include "jpegapi.h"
#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include "ImgUtil.h"
#include "ocmm.h"
#include "ctngen.h"
#include "tnail.h"
int vfMMXMachine=FALSE;
// WARNING: for large Thumbnail_X and Thumbnail_Y values, we will also
// need to increase INPUT_vBUF_SIZE and OUTPUT_BUF_SIZE in jdatasrc.cpp and
// jdatadst.cpp (lovely jpeg decompression code...). Also need to modify our
// own JPEG_BUFFER_SIZE in ctngen.cxx.
#define DEFAULT_THUMBNAIL_QUALITY 50
#define DEFAULT_THUMBNAIL_X 120
#define DEFAULT_THUMBNAIL_Y 120
/******Public*Routine******\
* TN_Initialize
*
* Initialize the thumbnail state
*
* Arguments:
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::TN_Initialize(
VOID
)
{
HRESULT retVal;
// Initialize class globals
Thumbnail_Quality = DEFAULT_THUMBNAIL_QUALITY;
Thumbnail_X = DEFAULT_THUMBNAIL_X;
Thumbnail_Y = DEFAULT_THUMBNAIL_Y;
m_hJpegC = m_hJpegD = NULL;
m_JPEGheaderSize = 0;
m_imageData.hbp = NULL;
m_imageData.x = 0;
m_imageData.y = 0;
// Find out if processor supports MMX technology and whether it's available
#if defined(_X86_)
// vfMMXMachine = IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE);
#endif
// Proceed with initialization of OLE and Direct Draw
if ((retVal=decoderInitialize()) != ERROR_SUCCESS)
{
return retVal;
}
// Now for JPEG initialization
if (! (m_JPEGheader = (BYTE *) LocalAlloc (0, 1024)))
{
decoderUninitialize();
return E_OUTOFMEMORY;
}
JPEGCompressHeader (m_JPEGheader,
Thumbnail_Quality,
&m_JPEGheaderSize,
&m_hJpegC,
JCS_RGBA
);
JPEGDecompressHeader(m_JPEGheader,
&m_hJpegD,
m_JPEGheaderSize
);
return ERROR_SUCCESS;
}
/******Public*Routine******\
* TN_Uninitialize
*
* Cleans up the thumbnail state
*
* Arguments:
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::TN_Uninitialize(
VOID
)
{
DestroyJPEGCompressHeader (m_hJpegC);
DestroyJPEGDecompressHeader(m_hJpegD);
LocalFree (m_JPEGheader);
decoderUninitialize();
return ERROR_SUCCESS;
}
/******Public*Routine******\
* deliverDecompressedImage
*
* This function accepts a decompressed image from the filter, reduces
* it to a thumbnail, and stores it in compressed JPEG format.
*
* Arguments:
*
* hbp -- The bitmap which stores the decompressed image. We are responsible
* for deleting it
* x -- The length of the image
* y -- The width of the image
*
* Return Value:
*
* none
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::deliverDecompressedImage(
HBITMAP hbp,
ULONG x,
ULONG y
)
{
m_imageData.hbp = hbp;
m_imageData.x = x;
m_imageData.y = y;
return S_OK;
}
/******Public*Routine******\
* createThumbnailFromImage
*
* This function takes a bitmap and exchanges it with that of a smaller
* thumbnail.
*
* Arguments:
*
* phbp -- points to a bitmap that will be freed and replaced with a
* smaller bitmap of the same image
* x -- width of image
* y -- height of image
*
*
* Return Value:
*
* none
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::createThumbnailFromImage(
HBITMAP *phbp,
ULONG *px,
ULONG *py,
INT **ppvbits
)
{
HBITMAP hbp = *phbp;
double x_reduction, y_reduction, reduction;
ULONG final_x, final_y;
// Compute dimensions of thumbnail
x_reduction = ((double) *px) / ((double) Thumbnail_X);
y_reduction = ((double) *py) / ((double) Thumbnail_Y);
if (x_reduction > y_reduction)
{
reduction = x_reduction;
}
else
{
reduction = y_reduction;
}
if (reduction < (double) 1)
{
reduction = 1; // Already the right size;
}
final_x = (ULONG) (((double) *px)/reduction);
final_y = (ULONG) (((double) *py)/reduction);
// Now Allocate buffer for thumbnail
PBITMAPINFO pbmi = (PBITMAPINFO) LocalAlloc(0, sizeof(BITMAPINFO) + 255 * sizeof(RGBQUAD));
if (!pbmi)
{
return E_OUTOFMEMORY;
}
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = final_x;
pbmi->bmiHeader.biHeight = final_y;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 32;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 0;
pbmi->bmiHeader.biYPelsPerMeter = 0;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
HBITMAP hReducedBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, (VOID **) ppvbits, NULL, 0);
if (!hReducedBitmap)
{
LocalFree(pbmi);
return E_OUTOFMEMORY;
}
// Copy image into thumbnail buffer
HDC hdc1, hdc2;
hdc1 = CreateCompatibleDC(NULL);
hdc2 = CreateCompatibleDC(NULL);
SelectObject(hdc1, hbp);
SelectObject(hdc2, hReducedBitmap);
SetStretchBltMode (hdc2, HALFTONE);
StretchBlt (hdc2, 0, 0, final_x, final_y, hdc1, 0, 0, *px, *py, SRCCOPY);
DeleteDC(hdc1);
DeleteDC(hdc2);
LocalFree(pbmi);
DeleteObject(hbp);
*phbp = hReducedBitmap;
*px = final_x;
*py = final_y;
return ERROR_SUCCESS;
}
/******Public*Routine******\
* createCompressedThumbnail
*
* This function accepts a decompressed image from the filter, reduces
* it to a thumbnail, and stores it in compressed JPEG format.
*
* Arguments:
*
* hbp -- The bitmap which stores the decompressed image. We are responsible
* for deleting it
* x -- The length of the image
* y -- The width of the image
* pJPEGBuf -- The buffer where we write the compressed
* JPEG thumbnail
* pulJPEGBufSize -- The number of bytes written to
* m_pJPEGBuf
* mtime - modification time of the file
*
*
* Return Value:
*
* none
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::createCompressedThumbnail(
HBITMAP hbp,
ULONG x,
ULONG y,
CHAR *pJPEGBuf,
ULONG *pulJPEGBufSize,
FILETIME mtime
)
{
HRESULT retVal;
INT *pvbits=NULL;
retVal = createThumbnailFromImage (&hbp, &x, &y, &pvbits);
if (SUCCEEDED(retVal))
{
retVal = EncodeThumbnail(pvbits, x, y, (VOID **) &pJPEGBuf, pulJPEGBufSize);
}
// Finally free all allocated objects
DeleteObject(hbp);
return retVal;
}
/******Public*Routine******\
* makeSquareThumbnail
*
* Converts a rectangular thumbnail into a square thumbnail while
* preserving the aspect ratio and window color
*
* Arguments:
*
* pulOutputLength - length of thumbnail
* pulOutputWidth - width of thumbnail
* phOutputBitmap - thumbnail bitmap
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::makeSquareThumbnail(
ULONG *pulOutputLength,
ULONG *pulOutputWidth,
HBITMAP *phOutputBitmap
)
{
// HACK, HACK, ugly, ugly...
// It turns out that the shell wants a square thumbnail (while preserving the aspect ratio).
// It would be really bad to store the wasted bits in the thumbnail, so instead we have to
// "squarify" the thumbnail here. The right way to do it is for us to hand the rectangular
// thumbnail to the shell along with the dimensions and have it BitBlt the correct number
// of bits to the screen. However, they are reluctant to do this and would rather us copy
// the thumbnail to a square thumbnail which we will hand over to them... Very lame!
if ((Thumbnail_X != *pulOutputLength) || (Thumbnail_Y != *pulOutputWidth))
{
// Thumbnail better be too small or something is really wrong
if ((Thumbnail_X < *pulOutputLength) || (Thumbnail_Y < *pulOutputWidth))
{
return E_FAIL;
}
// Allocate the square thumbnail
// Now Allocate buffer for thumbnail
PBITMAPINFO pbmi = (PBITMAPINFO) LocalAlloc(0, sizeof(BITMAPINFO) + 255 * sizeof(RGBQUAD));
if (!pbmi)
{
return E_OUTOFMEMORY;
}
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = Thumbnail_X;
pbmi->bmiHeader.biHeight = Thumbnail_Y;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 32;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 0;
pbmi->bmiHeader.biYPelsPerMeter = 0;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
INT *ppvbits=NULL;
HBITMAP hSquareBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, (void **)&ppvbits, NULL, 0);
// Now paint it with the window background color
HDC hdcSquareBitmap = CreateCompatibleDC(NULL);
SelectObject (hdcSquareBitmap, hSquareBitmap);
HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_WINDOW));
SelectObject (hdcSquareBitmap, hbr);
PatBlt(hdcSquareBitmap, 0, 0, Thumbnail_X, Thumbnail_Y, PATCOPY);
// Finally copy the thumbnail to it
HDC hdcMem = CreateCompatibleDC(NULL);
SelectObject (hdcMem, *phOutputBitmap);
BitBlt(hdcSquareBitmap,
(Thumbnail_X - *pulOutputLength) / 2,
(Thumbnail_Y - *pulOutputWidth) / 2,
*pulOutputLength,
*pulOutputWidth,
hdcMem,
0,
0,
SRCCOPY
);
// Fix output parameters
DeleteObject(*phOutputBitmap);
*phOutputBitmap = hSquareBitmap;
*pulOutputLength = Thumbnail_X;
*pulOutputWidth = Thumbnail_Y;
// And clean up a bit
DeleteDC (hdcSquareBitmap);
DeleteDC (hdcMem);
DeleteObject(hbr);
LocalFree(pbmi);
}
return S_OK;
}
/******Public*Routine******\
* TN_GenerateCompressedThumbnail
*
* Generates a JPEG compressed thumbnail image from an input stream in a
* format recognized by ImageDecodeFilter
*
* Arguments:
*
* pInputStream -- An input stream in a format recognized by
* ImageDecodeFilter
* pJPEGBuf -- A pointer to the buffer where the compressed JPEG
* thumbnail will be stored
* pulJPEGBufSize -- The number of bytes stored in pJPEGBuf
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::TN_GenerateCompressedThumbnail(
IStream *pInputStream,
CHAR *pJPEGBuf,
ULONG *pulJPEGBufSize,
FILETIME mtime
)
{
HRESULT retVal;
IImageDecodeEventSink *pOurEventSink;
retVal = ((IThumbnailExtractor *) this)->QueryInterface(IID_IImageDecodeEventSink,
(void **) &pOurEventSink);
if (FAILED(retVal))
{
return retVal;
}
InitializeEventSink();
// Decode the image
_try
{
retVal = DecodeImage(pInputStream, NULL, pOurEventSink);
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
// imgutil.dll can't be delay loaded--fail gracefuly.
retVal = E_FAIL;
}
// Fail if image data not delivered by the filter
if (!m_imageData.hbp)
{
retVal = E_FAIL;
}
if (SUCCEEDED(retVal))
{
// Create the compressed thumbnail
retVal = createCompressedThumbnail (m_imageData.hbp,
m_imageData.x,
m_imageData.y,
pJPEGBuf,
pulJPEGBufSize,
mtime
);
}
// Clean up old data to simplify debugging
#ifdef DEBUG
m_imageData.hbp = NULL;
m_imageData.x = NULL;
m_imageData.y = NULL;
#endif
pOurEventSink->Release();
return retVal;
}
/******Public*Routine******\
* TN_GenerateThumbnailBitmap
*
* Generates a 32BPP bitmap from an image stream.
* Note: The caller is responsible for freeing phOutputBitmap by
* calling DeleteObject!!
*
* Arguments:
*
* prgbJPEGBuf -- A pointer to a buffer holding the compressed JPEG image
* ulLength -- The requested length
* ulWidth -- the requested width
* pulOutputLength -- will hold the actual output length
* pulOutputWidth -- will hold the actual output width
* phOutputBitmap -- will hold a pointer to a bitmap handle containing the
* thumbnail image. This buffer will be freed by the caller!
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::TN_GenerateThumbnailBitmap(
IStream *pInputStream,
ULONG ulLength,
ULONG ulWidth,
PULONG pulOutputLength,
PULONG pulOutputWidth,
HBITMAP *phOutputBitmap
)
{
*phOutputBitmap = NULL;
// For now don't handle the case of size other than 120x120
if ((ulLength != Thumbnail_X) ||
(ulWidth != Thumbnail_Y))
{
return E_INVALIDARG;
}
HRESULT retVal;
InitializeEventSink();
// Decode the image
IImageDecodeEventSink *pOurEventSink;
retVal = ((IThumbnailExtractor *) this)->QueryInterface(IID_IImageDecodeEventSink,
(void **) &pOurEventSink);
if (FAILED(retVal))
{
return retVal;
}
_try
{
retVal = DecodeImage(pInputStream, NULL, pOurEventSink);
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
// imgutil.dll can't be delay loaded--fail gracefuly.
retVal = E_FAIL;
}
if (SUCCEEDED(retVal))
{
*phOutputBitmap = m_imageData.hbp;
*pulOutputLength = m_imageData.x;
*pulOutputWidth = m_imageData.y;
// Make into thumbnail
INT *pvBits;
retVal = createThumbnailFromImage(phOutputBitmap, pulOutputLength, pulOutputWidth, &pvBits);
if (SUCCEEDED(retVal))
{
// Create a square thumbnail
retVal = makeSquareThumbnail(pulOutputLength, pulOutputWidth, phOutputBitmap);
}
}
// Clean up old data to simplify debugging
m_imageData.hbp = NULL;
m_imageData.x = NULL;
m_imageData.y = NULL;
pOurEventSink->Release();
return retVal;
}
/******Public*Routine******\
* TN_GenerateThumbnailImage
*
* Generates a 32BPP bitmap from a JPEG compressed thumbnail.
* Note: The caller is responsible for freeing phOutputBitmap by
* calling DeleteObject!!
*
* Arguments:
*
* prgbJPEGBuf -- A pointer to a buffer holding the compressed JPEG image
* ulLength -- The requested length
* ulWidth -- the requested width
* pulOutputLength -- will hold the actual output length
* pulOutputWidth -- will hold the actual output width
* phOutputBitmap -- will hold a pointer to a bitmap handle containing the
* thumbnail image. This buffer will be freed by the caller!
*
* Return Value:
*
* ERROR_SUCCESS if successful, otherwise appropriate error code
*
* History:
*
* 17-Oct-1997 -by- Ori Gershony [orig]
*
\****/
STDMETHODIMP
CThumbnailFCNContainer::TN_GenerateThumbnailImage(
PCHAR prgbJPEGBuf,
ULONG ulLength,
ULONG ulWidth,
PULONG pulOutputLength,
PULONG pulOutputWidth,
HBITMAP *phOutputBitmap
)
{
HRESULT retVal;
*phOutputBitmap = NULL;
// For now don't handle the case of size other than 120x120
if ((ulLength != Thumbnail_X) ||
(ulWidth != Thumbnail_Y))
{
return E_INVALIDARG;
}
retVal = DecodeThumbnail(phOutputBitmap, pulOutputLength, pulOutputWidth, (VOID *) prgbJPEGBuf, 0);
if ((SUCCEEDED(retVal)))
{
retVal = makeSquareThumbnail(pulOutputLength, pulOutputWidth, phOutputBitmap);
}
return retVal;
}