HBITMAP to jpeg file

Can anyone offer me (preferably simple) code for saving a HBITMAP as a jpeg file with good compression?

Actually, If it's possible I ultimately need to load that jpegs contents back into memory so if I can skip writing to a file that would be good.
[263 byte] By [msg555] at [2007-11-19 19:17:15]
# 1 Re: HBITMAP to jpeg file
I have created this code to make a JPEG screen capture. Hopefully you can modify it:

#include <stdio.h>
#include <windows.h>
extern "C"
{
#include <jpeglib.h>
}

int jpegCapture(char* filename, int quality)
{
HBITMAP hBMP;
HWND desktopWnd;
int width;
int height;
RECT rc;
HDC hDC;
HDC hDCmem;

struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;
JSAMPLE* scanline;
COLORREF pixel;

desktopWnd = GetDesktopWindow();
GetWindowRect(desktopWnd, &rc);
width = rc.right - rc.left;
height = rc.bottom - rc.top;
hDC = GetDC(desktopWnd);
hDCmem = CreateCompatibleDC(hDC);
hBMP = CreateCompatibleBitmap(hDC, width, height);
if(hBMP == NULL) return -2;
SelectObject(hDCmem, hBMP);
BitBlt(hDCmem, 0, 0, width, height, hDC, rc.left, rc.top, SRCCOPY);

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
outfile = fopen(filename, "wb");
if(outfile == NULL) return -1;
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
if(quality < 0) quality = 0;
if(quality > 100) quality = 100;
jpeg_set_quality(&cinfo, quality, FALSE);
jpeg_start_compress(&cinfo, TRUE);
scanline = new JSAMPLE[width*3];
for(int posy = 0; posy < height; posy++)
{
for(int posx = 0; posx < width; posx++)
{
pixel = GetPixel(hDCmem, posx, posy);
scanline[posx*3+0] = GetRValue(pixel);
scanline[posx*3+1] = GetGValue(pixel);
scanline[posx*3+2] = GetBValue(pixel);
}
jpeg_write_scanlines(&cinfo, &scanline, 1);
}
jpeg_finish_compress(&cinfo);

jpeg_destroy_compress(&cinfo);
delete scanline;
fclose(outfile);
DeleteDC(hDCmem);
ReleaseDC(desktopWnd, hDC);
return 0;
}

The code uses libjpeg. Get it here: http://www.ijg.org/
philkr at 2007-11-10 3:49:41 >
# 2 Re: HBITMAP to jpeg file
Wow, I actually needed to capture the desktop... I guess that saves me a step.

Ok, I haven't actually tried it yet, but do you know of any way I could make this write to a memory buffer instead of to a file? I could load the file but it is kind of redundant. Also, what do I pass as the quality?
msg555 at 2007-11-10 3:50:38 >
# 3 Re: HBITMAP to jpeg file
OK, I found it. This line sets the destination of the compressed data:
jpeg_stdio_dest(&cinfo, outfile);
You can change outfile with a different stdio stream.
philkr at 2007-11-10 3:51:45 >
# 4 Re: HBITMAP to jpeg file
Here:Compressed data handling (source and destination managers)
------------------

The JPEG compression library sends its compressed data to a "destination
manager" module. The default destination manager just writes the data to a
stdio stream, but you can provide your own manager to do something else.
Similarly, the decompression library calls a "source manager" to obtain the
compressed data; you can provide your own source manager if you want the data
to come from somewhere other than a stdio stream.

In both cases, compressed data is processed a bufferload at a time: the
destination or source manager provides a work buffer, and the library invokes
the manager only when the buffer is filled or emptied. (You could define a
one-character buffer to force the manager to be invoked for each byte, but
that would be rather inefficient.) The buffer's size and location are
controlled by the manager, not by the library. For example, if you desired to
decompress a JPEG datastream that was all in memory, you could just make the
buffer pointer and length point to the original data in memory. Then the
buffer-reload procedure would be invoked only if the decompressor ran off the
end of the datastream, which would indicate an erroneous datastream.

The work buffer is defined as an array of datatype JOCTET, which is generally
"char" or "unsigned char". On a machine where char is not exactly 8 bits
wide, you must define JOCTET as a wider data type and then modify the data
source and destination modules to transcribe the work arrays into 8-bit units
on external storage.

A data destination manager struct contains a pointer and count defining the
next byte to write in the work buffer and the remaining free space:

JOCTET * next_output_byte; /* => next byte to write in buffer */
size_t free_in_buffer; /* # of byte spaces remaining in buffer */

The library increments the pointer and decrements the count until the buffer
is filled. The manager's empty_output_buffer method must reset the pointer
and count. The manager is expected to remember the buffer's starting address
and total size in private fields not visible to the library.

A data destination manager provides three methods:

init_destination (j_compress_ptr cinfo)
Initialize destination. This is called by jpeg_start_compress()
before any data is actually written. It must initialize
next_output_byte and free_in_buffer. free_in_buffer must be
initialized to a positive value.

empty_output_buffer (j_compress_ptr cinfo)
This is called whenever the buffer has filled (free_in_buffer
reaches zero). In typical applications, it should write out the
*entire* buffer (use the saved start address and buffer length;
ignore the current state of next_output_byte and free_in_buffer).
Then reset the pointer & count to the start of the buffer, and
return TRUE indicating that the buffer has been dumped.
free_in_buffer must be set to a positive value when TRUE is
returned. A FALSE return should only be used when I/O suspension is
desired (this operating mode is discussed in the next section).

term_destination (j_compress_ptr cinfo)
Terminate destination -- called by jpeg_finish_compress() after all
data has been written. In most applications, this must flush any
data remaining in the buffer. Use either next_output_byte or
free_in_buffer to determine how much data is in the buffer.

term_destination() is NOT called by jpeg_abort() or jpeg_destroy(). If you
want the destination manager to be cleaned up during an abort, you must do it
yourself.

You will also need code to create a jpeg_destination_mgr struct, fill in its
method pointers, and insert a pointer to the struct into the "dest" field of
the JPEG compression object. This can be done in-line in your setup code if
you like, but it's probably cleaner to provide a separate routine similar to
the jpeg_stdio_dest() routine of the supplied destination manager.
philkr at 2007-11-10 3:52:37 >
# 5 Re: HBITMAP to jpeg file
Hmm... I'll see what I can do, I don't work with this type of code alot. To start off with, I didn't have the header files, so I downloaded them, but now have a bunch of linker errors. What library do I have to add to my project? Does every computer have this library?
msg555 at 2007-11-10 3:53:46 >
# 6 Re: HBITMAP to jpeg file
I think CxImage will solve your problem,you can use it to compress bitmap to jpeg file.
http://www.codeproject.com/bitmap/cximage.asp

Hope it is useful.

Cindy
-----------------------
For high quality flow/diagram MFC/C++ Visio Like visualization Source Code,download XD++ at:
http://www.ucancode.net
cindyonlyone at 2007-11-10 3:54:43 >
# 7 Re: HBITMAP to jpeg file
Alternatively, you can use GDI+ library (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/GDIPlus.asp) which provides an easy an powerful way to handle different graphic file formats (bitmap, JPEG, TIFF, GIF and PNG).
You can find a lot of examples using GDI+ in dev-archive articles (http://www.dev-archive.com/Cpp/G-M/gdi/).
ovidiucucu at 2007-11-10 3:55:47 >
# 8 Re: HBITMAP to jpeg file
Hmm... I'll see what I can do, I don't work with this type of code alot. To start off with, I didn't have the header files, so I downloaded them, but now have a bunch of linker errors. What library do I have to add to my project? Does every computer have this library?
I gave you a link to the library, but it isn't compiled yet. So you have to compile it yourself or search the internet for a precompiled version.
philkr at 2007-11-10 3:56:50 >
# 9 Re: HBITMAP to jpeg file
Sorry, I'm not used to using other people's code in general so I'm having difficulty getting anything to work.

Furthermore, this is a really small project and I would hate to have to include a dll for anyone using it, so it'ld be nice if I could find something like a single header file that can do a simple jpeg compression.
msg555 at 2007-11-10 3:57:44 >
# 10 Re: HBITMAP to jpeg file
Sorry, I'm not used to using other people's code in general so I'm having difficulty getting anything to work.

Furthermore, this is a really small project and I would hate to have to include a dll for anyone using it, so it'ld be nice if I could find something like a single header file that can do a simple jpeg compression.
It is a static library, so there is no DLL. All you need to do is add "libjpeg.lib" to the additional dependancies in the linker options and include the header file, like I already showed in my code excerpt.

I found a compiled version for the lib file:
philkr at 2007-11-10 3:58:43 >
# 11 Re: HBITMAP to jpeg file
Ok, so I now have the library functioning properly, but now I'm struggling with the data destination manager.

I kind of get what the quoted text is talking about but I don't even know where to start making a ddm on my own.
msg555 at 2007-11-10 3:59:52 >
# 12 Re: HBITMAP to jpeg file
Take a look at the source code in "jdatadst.c". This is the source code for the standard destination manager which uses a FILE* for output.
You just have to copy the code which defines the destination manager into your program and modify it to output to memory instead to a FILE*. Then you have to set cinfo->dest to the pointer to your destination manager object instead of the call to jpeg_stdio_dest().
philkr at 2007-11-10 4:00:47 >
# 13 Re: HBITMAP to jpeg file
Philkr i'm having trouble creating my own destination manager to store the resulting jpg in memory. Could you provide an example?
clown at 2007-11-10 4:01:54 >
# 14 Re: HBITMAP to jpeg file
Well I got it to work using the following code for memory to memory compression.

#include <stdio.h>
#include "jpeg/jpeglib.h"

/*
This a custom destination manager for jpeglib that
enables the use of memory to memory compression.

See IJG documentation for details.
*/
typedef struct {
struct jpeg_destination_mgr pub; /* base class */
JOCTET* buffer; /* buffer start address */
int bufsize; /* size of buffer */
size_t datasize; /* final size of compressed data */
int* outsize; /* user pointer to datasize */
int errcount; /* counts up write errors due to
buffer overruns */
} memory_destination_mgr;

typedef memory_destination_mgr* mem_dest_ptr;

/* ------------------- */
/* MEMORY DESTINATION INTERFACE METHODS */
/* ------------------- */

/* This function is called by the library before any data gets written */
METHODDEF(void)
init_destination (j_compress_ptr cinfo)
{
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;

dest->pub.next_output_byte = dest->buffer; /* set destination buffer */
dest->pub.free_in_buffer = dest->bufsize; /* input buffer size */
dest->datasize = 0; /* reset output size */
dest->errcount = 0; /* reset error count */
}

/* This function is called by the library if the buffer fills up

I just reset destination pointer and buffer size here.
Note that this behavior, while preventing seg faults
will lead to invalid output streams as data is over-
written.
*/
METHODDEF(boolean)
empty_output_buffer (j_compress_ptr cinfo)
{
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = dest->bufsize;
++dest->errcount; /* need to increase error count */

return TRUE;
}

/* Usually the library wants to flush output here.

I will calculate output buffer size here.
Note that results become incorrect, once
empty_output_buffer was called.
This situation is notified by errcount.
*/
METHODDEF(void)
term_destination (j_compress_ptr cinfo)
{
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
dest->datasize = dest->bufsize - dest->pub.free_in_buffer;
if (dest->outsize) *dest->outsize += (int)dest->datasize;
}

/* Override the default destination manager initialization
provided by jpeglib. Since we want to use memory-to-memory
compression, we need to use our own destination manager.
*/
GLOBAL(void)
jpeg_memory_dest (j_compress_ptr cinfo, JOCTET* buffer, int bufsize, int* outsize)
{
mem_dest_ptr dest;

/* first call for this instance - need to setup */
if (cinfo->dest == 0) {
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof (memory_destination_mgr));
}

dest = (mem_dest_ptr) cinfo->dest;
dest->bufsize = bufsize;
dest->buffer = buffer;
dest->outsize = outsize;
/* set method callbacks */
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
}

/* ------------------- */
/* MEMORY SOURCE INTERFACE METHODS */
/* ------------------- */

/* Called before data is read */
METHODDEF(void)
init_source (j_decompress_ptr dinfo)
{
/* nothing to do here, really. I mean. I'm not lazy or something, but...
we're actually through here. */
}

/* Called if the decoder wants some bytes that we cannot provide... */
METHODDEF(boolean)
fill_input_buffer (j_decompress_ptr dinfo)
{
/* we can't do anything about this. This might happen if the provided
buffer is either invalid with regards to its content or just a to
small bufsize has been given. */

/* fail. */
return FALSE;
}

/* From IJG docs: "it's not clear that being smart is worth much trouble"
So I save myself some trouble by ignoring this bit.
*/
METHODDEF(void)
skip_input_data (j_decompress_ptr dinfo, INT32 num_bytes)
{
/* There might be more data to skip than available in buffer.
This clearly is an error, so screw this mess. */
if ((size_t)num_bytes > dinfo->src->bytes_in_buffer) {
dinfo->src->next_input_byte = 0; /* no buffer byte */
dinfo->src->bytes_in_buffer = 0; /* no input left */
} else {
dinfo->src->next_input_byte += num_bytes;
dinfo->src->bytes_in_buffer -= num_bytes;
}
}

/* Finished with decompression */
METHODDEF(void)
term_source (j_decompress_ptr dinfo)
{
/* Again. Absolute laziness. Nothing to do here. Boring. */
}

GLOBAL(void)
jpeg_memory_src (j_decompress_ptr dinfo, unsigned char* buffer, size_t size)
{
struct jpeg_source_mgr* src;

/* first call for this instance - need to setup */
if (dinfo->src == 0) {
dinfo->src = (struct jpeg_source_mgr *)
(*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
sizeof (struct jpeg_source_mgr));
}

src = dinfo->src;
src->next_input_byte = buffer;
src->bytes_in_buffer = size;
src->init_source = init_source;
src->fill_input_buffer = fill_input_buffer;
src->skip_input_data = skip_input_data;
src->term_source = term_source;
/* IJG recommend to use their function - as I don't know ****
about how to do better, I follow this recommendation */
src->resync_to_restart = jpeg_resync_to_restart;
}

And in your main compression function replace the jpeg_stdio_dest with

int numBytes = 0; //size of jpeg after compression
char * storage = new char[150000]; //storage buffer
JOCTET *jpgbuff = (JOCTET*)storage; //JOCTET pointer to buffer
jpeg_memory_dest(&cinfo,jpgbuff,150000,&numBytes);

The 150000 is a static size buffer, you probably will have images that will exceed it, so allocate accordingly.
clown at 2007-11-10 4:02:47 >