mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
602 lines
18 KiB
C++
602 lines
18 KiB
C++
/*
|
|
Copyright 2006 - 2015 Intel Corporation
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#if defined(_LINKVM)
|
|
|
|
#include <stdio.h>
|
|
#include "tile.h"
|
|
#include <gdiplus.h>
|
|
#include "meshcore/meshdefines.h"
|
|
using namespace Gdiplus;
|
|
|
|
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE)
|
|
#define _CRTDBG_MAP_ALLOC
|
|
#include <crtdbg.h>
|
|
#endif
|
|
|
|
// #define KVMDEBUGENABLED 1
|
|
|
|
#ifdef KVMDEBUGENABLED
|
|
extern "C"
|
|
{
|
|
extern void KvmCriticalLog(const char* msg, const char* file, int line, int user1, int user2);
|
|
#define KVMDEBUG(m,u) { KvmCriticalLog(m, __FILE__, __LINE__, u, GetLastError()); printf("TVMMSG: %s (%d,%d).\r\n", m, (int)u, (int)GetLastError()); }
|
|
#define KVMDEBUG2(x) x
|
|
}
|
|
#else
|
|
#define KVMDEBUG(m, u)
|
|
#define KVMDEBUG2(x)
|
|
#endif
|
|
|
|
extern "C"
|
|
{
|
|
#include "microstack/ILibCrypto.h"
|
|
extern int TILE_WIDTH;
|
|
extern int TILE_HEIGHT;
|
|
extern int SCREEN_WIDTH;
|
|
extern int SCREEN_HEIGHT;
|
|
extern int SCREEN_X;
|
|
extern int SCREEN_Y;
|
|
extern int SCALED_WIDTH;
|
|
extern int SCALED_HEIGHT;
|
|
extern int PIXEL_SIZE;
|
|
extern int TILE_WIDTH_COUNT;
|
|
extern int TILE_HEIGHT_COUNT;
|
|
extern int COMPRESSION_RATIO;
|
|
extern int SCALING_FACTOR;
|
|
extern int SCALING_FACTOR_NEW;
|
|
extern int FRAME_RATE_TIMER;
|
|
extern tileInfo_t **tileInfo;
|
|
}
|
|
|
|
// Used with setting up a GDI+ session.
|
|
GdiplusStartupInput gdiplusStartupInput;
|
|
ULONG_PTR gdiplusToken;
|
|
HDC hDesktopDC;
|
|
HDC hCaptureDC;
|
|
HBITMAP hCapturedBitmap;
|
|
//HDC hdc;
|
|
CLSID encoderClsid;
|
|
ULONG encCompression = 50; // Image compression
|
|
EncoderParameters encParam;
|
|
LPVOID tilebuffer = NULL;
|
|
unsigned int tilebuffersize = 0;
|
|
|
|
// Used to obtain the GUID for the image encoder.
|
|
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
|
|
{
|
|
unsigned int num = 0, size = 0;
|
|
ImageCodecInfo* pImageCodecInfo = NULL;
|
|
|
|
GetImageEncodersSize(&num, &size);
|
|
if (size == 0) return -1;
|
|
|
|
if ((pImageCodecInfo = (ImageCodecInfo*)(malloc(size))) == NULL) return -1;
|
|
GetImageEncoders(num, size, pImageCodecInfo);
|
|
|
|
for (unsigned int j = 0; j < num; ++j)
|
|
{
|
|
if (wcsncmp(pImageCodecInfo[j].MimeType, format, size) == 0)
|
|
{
|
|
*pClsid = pImageCodecInfo[j].Clsid;
|
|
free(pImageCodecInfo);
|
|
return j;
|
|
}
|
|
}
|
|
|
|
free(pImageCodecInfo);
|
|
return -1;
|
|
}
|
|
|
|
// Adjusts the screen size(width or height) to be exactly divisible by TILE_WIDTH
|
|
int adjust_screen_size(int pixles)
|
|
{
|
|
int extra = pixles % TILE_WIDTH; // Assuming tile width and height will remain the same.
|
|
if (extra != 0) return pixles + TILE_WIDTH - extra;
|
|
return pixles;
|
|
}
|
|
|
|
// Extracts the required tile buffer from the desktop buffer
|
|
int get_tile_buffer(int x, int y, void **buffer, void *desktop, int tilewidth, int tileheight)
|
|
{
|
|
void *target = *buffer;
|
|
for (int height = adjust_screen_size(SCALED_HEIGHT) - y - tileheight; height < adjust_screen_size(SCALED_HEIGHT) - y; height++)
|
|
{
|
|
memcpy_s(target, tilebuffersize, (const void *)((unsigned char *)desktop + (((height * adjust_screen_size(SCALED_WIDTH)) + x) * PIXEL_SIZE) ), (size_t)(tilewidth * PIXEL_SIZE));
|
|
target = (void *) ((unsigned char *)target + tilewidth * PIXEL_SIZE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int tile_crc(int x, int y, void *desktop, int tilewidth, int tileheight)
|
|
{
|
|
int crc = 0;
|
|
for (int height = adjust_screen_size(SCALED_HEIGHT) - y - tileheight; height < adjust_screen_size(SCALED_HEIGHT) - y; height++)
|
|
{
|
|
crc = util_crc(((unsigned char *)desktop + (((height * adjust_screen_size(SCALED_WIDTH)) + x) * PIXEL_SIZE) ), (size_t)(tilewidth * PIXEL_SIZE), crc);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
// This function returns 0 and *buffer != NULL if everything was good. retval = jpegsize if the captured image was too large.
|
|
int calc_opt_compr_send(int x, int y, int captureWidth, int captureHeight, void* desktop, void ** buffer, long long *bufferSize)
|
|
{
|
|
BITMAPINFO bmpInfo;
|
|
LARGE_INTEGER Offset;
|
|
BITMAPFILEHEADER bmpFileHeader;
|
|
*buffer = NULL;
|
|
*bufferSize = 0;
|
|
|
|
KVMDEBUG("calc_opt_compr_send()", 0);
|
|
|
|
// Get the bmpInfo structure
|
|
bmpInfo = get_bmp_info(captureWidth, captureHeight);
|
|
|
|
// Make sure a tile buffer is available. Most of the time, this is skipped.
|
|
if (tilebuffersize != bmpInfo.bmiHeader.biSizeImage)
|
|
{
|
|
if (tilebuffer != NULL) free(tilebuffer);
|
|
tilebuffersize = bmpInfo.bmiHeader.biSizeImage;
|
|
if ((tilebuffer = malloc(tilebuffersize)) == NULL) return 0;
|
|
}
|
|
|
|
// Get the final coalesced tile
|
|
get_tile_buffer(x, y, &tilebuffer, desktop, captureWidth, captureHeight);
|
|
|
|
bmpFileHeader.bfReserved1 = 0;
|
|
bmpFileHeader.bfReserved2 = 0;
|
|
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmpInfo.bmiHeader.biSizeImage;
|
|
bmpFileHeader.bfType = 'MB';
|
|
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
|
|
|
|
// Construct stream object.
|
|
IStream* bmpStream = NULL;
|
|
if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&bmpStream) != S_OK)
|
|
{
|
|
KVMDEBUG("CreateStreamOnHGlobal() failed", 0);
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Write entire contents of the source BMP into this stream.
|
|
bmpStream->Write(&bmpFileHeader, sizeof(BITMAPFILEHEADER), NULL);
|
|
bmpStream->Write(&bmpInfo, sizeof(BITMAPINFOHEADER), NULL);
|
|
bmpStream->Write(tilebuffer, bmpInfo.bmiHeader.biSizeImage, NULL);
|
|
|
|
// Move the stream pointer to the beginning of the stream.
|
|
Offset.QuadPart = 0;
|
|
if (bmpStream->Seek(Offset, STREAM_SEEK_SET, NULL) != S_OK)
|
|
{
|
|
KVMDEBUG("bmpStream->Seek() failed", 0);
|
|
bmpStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Construct GDI+ Image object from the BMP stream.
|
|
Gdiplus::Image* DIBImage = Gdiplus::Image::FromStream(bmpStream);
|
|
|
|
// Create stream to receive the encoded JPEG.
|
|
IStream* jpegStream = NULL;
|
|
if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&jpegStream) != S_OK)
|
|
{
|
|
KVMDEBUG("CreateStreamOnHGlobal() failed", 0);
|
|
delete DIBImage;
|
|
bmpStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Save image stream into the stream object.
|
|
Status SaveStatus = DIBImage->Save(jpegStream, &encoderClsid, &encParam);
|
|
if (SaveStatus != S_OK)
|
|
{
|
|
KVMDEBUG("DIBImage->Save() failed", 0);
|
|
delete DIBImage;
|
|
bmpStream->Release();
|
|
jpegStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Get the size of the output stream
|
|
ULARGE_INTEGER Size;
|
|
Offset.QuadPart = 0;
|
|
if (jpegStream->Seek(Offset, STREAM_SEEK_END, &Size) != S_OK)
|
|
{
|
|
KVMDEBUG("jpegStream->Save() failed", 0);
|
|
delete DIBImage;
|
|
bmpStream->Release();
|
|
jpegStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Move the image stream's pointer to its beginning.
|
|
Offset.QuadPart = 0;
|
|
if (jpegStream->Seek(Offset, STREAM_SEEK_SET, NULL) != S_OK)
|
|
{
|
|
KVMDEBUG("jpegStream->Seek() failed", 0);
|
|
delete DIBImage;
|
|
bmpStream->Release();
|
|
jpegStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Check if the tile is too large to send
|
|
DWORD jpegSize = (DWORD)Size.QuadPart;
|
|
if (jpegSize > 65500)
|
|
{
|
|
KVMDEBUG("jpegSize > 65500", jpegSize);
|
|
delete DIBImage;
|
|
*bufferSize = 0;
|
|
// ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return jpegSize;
|
|
}
|
|
|
|
// Save the image stream in memory.
|
|
char* Tile = (char*)ILibMemory_Allocate(jpegSize + 8, 0, NULL, NULL);
|
|
if (jpegStream->Read(Tile + 8, jpegSize, NULL) != S_OK)
|
|
{
|
|
KVMDEBUG("jpegStream->Read() failed", 0);
|
|
delete DIBImage;
|
|
free(Tile);
|
|
bmpStream->Release();
|
|
jpegStream->Release();
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
// Cleanup
|
|
delete DIBImage;
|
|
bmpStream->Release();
|
|
jpegStream->Release();
|
|
|
|
*buffer = (unsigned char*)Tile;
|
|
*bufferSize = jpegSize + 8;
|
|
|
|
// Place the header
|
|
((unsigned short*)*buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_PICTURE); // Write the type
|
|
((unsigned short*)*buffer)[1] = (unsigned short)htons((unsigned short)*bufferSize); // Write the size
|
|
((unsigned short*)*buffer)[2] = (unsigned short)htons((unsigned short)x); // X position
|
|
((unsigned short*)*buffer)[3] = (unsigned short)htons((unsigned short)y); // Y position
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C"
|
|
{
|
|
|
|
//Fetches the encoded jpeg tile at the given location. The neighboring tiles are coalesed to form a larger jpeg before returning.
|
|
int get_tile_at(int x, int y, void** buffer, long long *bufferSize, void *desktop, int row, int col)
|
|
{
|
|
int CRC;
|
|
int rightcol = col; // Used in coalescing. Indicates the right-most column to be coalesced.
|
|
int botrow = row; // Used in coalescing. Indicates the bottom-most row to be coalesced.
|
|
int r_x = x;
|
|
int r_y = y;
|
|
int captureWidth = TILE_WIDTH;
|
|
int captureHeight = TILE_HEIGHT;
|
|
|
|
*buffer = NULL; // If anything fails, this will be the indication.
|
|
*bufferSize = 0;
|
|
|
|
if (tileInfo[row][col].flags == (char)TILE_TODO) // First check whether the tile-crc needs to be calcualted or not.
|
|
{
|
|
// Compute CRC on the contents of the bitmap; Proceed with image encoding only if the CRC is different.
|
|
if ((CRC = tile_crc(x, y, desktop, TILE_WIDTH, TILE_HEIGHT)) == tileInfo[row][col].crc) return 0;
|
|
tileInfo[row][col].crc = CRC; // Update the tile CRC in the global data structure.
|
|
}
|
|
|
|
tileInfo[row][col].flags = (char)TILE_MARKED_NOT_SENT;
|
|
|
|
|
|
// COALESCING SECTION
|
|
|
|
// First got to the right most changed tile and record it
|
|
while (rightcol + 1 < TILE_WIDTH_COUNT)
|
|
{
|
|
rightcol++;
|
|
r_x = rightcol * TILE_WIDTH;
|
|
|
|
CRC = tileInfo[row][rightcol].crc;
|
|
|
|
if (tileInfo[row][rightcol].flags == (char)TILE_TODO) {
|
|
// Compute CRC on the contents of the bitmap.
|
|
CRC = tile_crc(r_x, y, desktop, TILE_WIDTH, TILE_HEIGHT);
|
|
}
|
|
|
|
if (CRC != tileInfo[row][rightcol].crc || tileInfo[row][rightcol].flags == (char)TILE_MARKED_NOT_SENT) // If the tile has changed, increment the capturewidth.
|
|
{
|
|
tileInfo[row][rightcol].crc = CRC;
|
|
// Here we check whether the size of the coalesced bitmap is greater than the threshold (65500)
|
|
if ((captureWidth + TILE_WIDTH) * TILE_HEIGHT * PIXEL_SIZE / COMPRESSION_RATIO > 65500) {
|
|
tileInfo[row][rightcol].flags = (char)TILE_MARKED_NOT_SENT;
|
|
--rightcol;
|
|
break;
|
|
}
|
|
|
|
tileInfo[row][rightcol].flags = (char)TILE_MARKED_NOT_SENT;
|
|
captureWidth += TILE_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
tileInfo[row][rightcol].flags = (char)TILE_DONT_SEND;
|
|
--rightcol;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// int TOLERANCE = (rightcol - col) / 4;
|
|
|
|
// Now go to the bottom tiles, check if they have changed and record them
|
|
while ((botrow + 1 < TILE_HEIGHT_COUNT) && ((captureHeight + TILE_HEIGHT) * captureWidth * PIXEL_SIZE / COMPRESSION_RATIO <= 65500))
|
|
{
|
|
botrow++;
|
|
r_y = botrow * TILE_HEIGHT;
|
|
int fail = 0;
|
|
r_x = x;
|
|
|
|
// int missCount = 0;
|
|
|
|
for (int rcol = col; rcol <= rightcol; rcol++) {
|
|
|
|
CRC = tileInfo[botrow][rcol].crc;
|
|
if (tileInfo[botrow][rcol].flags == (char)TILE_TODO)
|
|
{
|
|
// Compute CRC on the contents of the bitmap; Proceed with image encoding only if the CRC is different.
|
|
CRC = tile_crc(r_x, r_y, desktop, TILE_WIDTH, TILE_HEIGHT);
|
|
}
|
|
|
|
if (CRC != tileInfo[botrow][rcol].crc || tileInfo[botrow][rcol].flags == (char)TILE_MARKED_NOT_SENT)
|
|
{
|
|
tileInfo[botrow][rcol].flags = (char)TILE_MARKED_NOT_SENT;
|
|
tileInfo[botrow][rcol].crc = CRC;
|
|
r_x += TILE_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
/*// Keep this part commented. Adding tolerance adds to the complexity of this code.
|
|
missCount++;
|
|
|
|
if (missCount > TOLERANCE) {
|
|
fail = 1;
|
|
for (int i = col; i < rcol; i++) {
|
|
if (tileInfo[botrow][i].flags == (char)TILE_SKIPPED) {
|
|
tileInfo[botrow][i].flags = (char)TILE_DONT_SEND;
|
|
}
|
|
else {
|
|
tileInfo[botrow][i].flags = (char)TILE_MARKED_NOT_SENT;
|
|
}
|
|
}
|
|
tileInfo[botrow][rcol].flags = (char)TILE_DONT_SEND;
|
|
botrow--;
|
|
break;
|
|
}
|
|
else {
|
|
tileInfo[botrow][rcol].flags = (char)TILE_SKIPPED;
|
|
tileInfo[botrow][rcol].crc = CRC;
|
|
r_x += TILE_WIDTH;
|
|
}*/
|
|
fail = 1;
|
|
for (int i = col; i < rcol; i++)
|
|
{
|
|
tileInfo[botrow][i].flags = (char)TILE_MARKED_NOT_SENT;
|
|
}
|
|
tileInfo[botrow][rcol].flags = (char)TILE_DONT_SEND;
|
|
botrow--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fail)
|
|
{
|
|
captureHeight += TILE_HEIGHT;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
int retval = 0;
|
|
int firstTime = 1;
|
|
|
|
// This loop is used to adjust the COMPRESSION_RATIO. This loop runs only once most of the time.
|
|
do {
|
|
// retval here is 0 if everything was good. It is > 0 if it contains the size of the jpeg that was created and not sent.
|
|
retval = calc_opt_compr_send(x, y, captureWidth, captureHeight, desktop, buffer, bufferSize);
|
|
if (retval == 0 && *bufferSize == 0) break;
|
|
if (retval != 0)
|
|
{
|
|
if (firstTime)
|
|
{
|
|
// Re-adjust the compression ratio.
|
|
COMPRESSION_RATIO = (int)(((double)COMPRESSION_RATIO/(double)retval) * 60000);//Magic number: 60000 ~= 65500
|
|
if (COMPRESSION_RATIO <= 1) COMPRESSION_RATIO = 2;
|
|
firstTime = 0;
|
|
}
|
|
|
|
if (botrow > row) // First time, try reducing the height.
|
|
{
|
|
botrow = row + ((botrow - row + 1) / 2);
|
|
captureHeight = (botrow - row + 1) * TILE_HEIGHT;
|
|
}
|
|
else if (rightcol > col) // If it is not possible, reduce the width
|
|
{
|
|
rightcol = col + ((rightcol - col + 1) / 2);
|
|
captureWidth = (rightcol - col + 1) * TILE_WIDTH;
|
|
}
|
|
else
|
|
{ // This never happens, but just in case.
|
|
retval = 0;
|
|
break;
|
|
}
|
|
|
|
}
|
|
} while (*buffer == NULL);
|
|
|
|
// Set the flags to TILE_SENT
|
|
if (*buffer != NULL) {
|
|
for (int r = row; r <= botrow; r++) {
|
|
for (int c = col; c <= rightcol; c++) {
|
|
tileInfo[r][c].flags = (char)TILE_SENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
// This function captures the entire desktop buffer to scan.
|
|
int get_desktop_buffer(void **buffer, long long *bufferSize)
|
|
{
|
|
BITMAPINFO bmpInfo;
|
|
|
|
*buffer = NULL; // If anything fails, this will be the indication.
|
|
*bufferSize = 0;
|
|
|
|
if (hDesktopDC) ReleaseDC(NULL, hDesktopDC);
|
|
if ((hDesktopDC = GetDC(NULL)) == NULL) { KVMDEBUG("GetDC(NULL) returned NULL", 0); return 1; } // We need to do this incase the current desktop changes.
|
|
if (hCapturedBitmap) DeleteObject(hCapturedBitmap);
|
|
if ((hCapturedBitmap = CreateCompatibleBitmap(hDesktopDC, adjust_screen_size(SCALED_WIDTH), adjust_screen_size(SCALED_HEIGHT))) == NULL)
|
|
{
|
|
KVMDEBUG("CreateCompatibleBitmap() returned NULL", 0);
|
|
return 0;
|
|
}
|
|
|
|
if (SelectObject(hCaptureDC, hCapturedBitmap) == NULL) { KVMDEBUG("SelectObject() failed", 0); }
|
|
if (SCALING_FACTOR == 1024)
|
|
{
|
|
if (BitBlt(hCaptureDC, 0, 0, adjust_screen_size(SCREEN_WIDTH), adjust_screen_size(SCREEN_HEIGHT), hDesktopDC, SCREEN_X, SCREEN_Y, SRCCOPY | CAPTUREBLT) == FALSE)
|
|
{
|
|
KVMDEBUG("BitBlt() returned FALSE", 0);
|
|
return 1; // If the copy fails, error out.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SetStretchBltMode(hCaptureDC, HALFTONE) == 0) { KVMDEBUG("SetStretchBltMode() failed", 0); }
|
|
if (StretchBlt(hCaptureDC, 0, 0, adjust_screen_size(SCALED_WIDTH), adjust_screen_size(SCALED_HEIGHT), hDesktopDC, SCREEN_X, SCREEN_Y, adjust_screen_size(SCREEN_WIDTH), adjust_screen_size(SCREEN_HEIGHT), SRCCOPY | CAPTUREBLT) == FALSE)
|
|
{
|
|
KVMDEBUG("StretchBlt() returned FALSE", 0);
|
|
return 1; // If the copy fails, error out.
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
|
|
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
|
|
// Populates some fields in the bmpInfo struct based on the properties of the bitmap.
|
|
if (GetDIBits(hDesktopDC, hCapturedBitmap, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS) == 0)
|
|
{
|
|
KVMDEBUG("GetDIBits() failed", 0);
|
|
ILibCriticalLog(NULL, __FILE__, __LINE__, 252, GetLastError());
|
|
}
|
|
|
|
if (bmpInfo.bmiHeader.biSizeImage <= 0)
|
|
{
|
|
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * abs(bmpInfo.bmiHeader.biHeight) * (bmpInfo.bmiHeader.biBitCount + 7) / 8;
|
|
}
|
|
|
|
*bufferSize = bmpInfo.bmiHeader.biSizeImage;
|
|
PIXEL_SIZE = bmpInfo.bmiHeader.biBitCount / 8;
|
|
if ((*buffer = malloc((size_t)*bufferSize)) == NULL) { KVMDEBUG("malloc() failed", 0); return 0; }
|
|
|
|
bmpInfo.bmiHeader.biCompression = BI_RGB;
|
|
if (GetDIBits(hDesktopDC, hCapturedBitmap, 0, bmpInfo.bmiHeader.biHeight, *buffer, &bmpInfo, DIB_RGB_COLORS) == 0) { KVMDEBUG("GetDIBits() failed", 0); }
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Creates a BITMAPINFO object with required width and height
|
|
BITMAPINFO get_bmp_info(int width, int height)
|
|
{
|
|
BITMAPINFO bmpInfo;
|
|
|
|
ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
|
|
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmpInfo.bmiHeader.biBitCount = (WORD)(PIXEL_SIZE * 8);
|
|
bmpInfo.bmiHeader.biSize = 40;
|
|
bmpInfo.bmiHeader.biHeight = height;
|
|
bmpInfo.bmiHeader.biWidth = width;
|
|
bmpInfo.bmiHeader.biSizeImage = height * width * PIXEL_SIZE;
|
|
bmpInfo.bmiHeader.biPlanes = 1;
|
|
|
|
return bmpInfo;
|
|
}
|
|
|
|
short initialize_gdiplus()
|
|
{
|
|
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
|
|
|
TILE_WIDTH = 32;
|
|
TILE_HEIGHT = 32;
|
|
COMPRESSION_RATIO = 100;
|
|
FRAME_RATE_TIMER = 50;
|
|
SCALING_FACTOR = 1024;
|
|
SCALING_FACTOR_NEW = 1024;
|
|
|
|
SCALED_WIDTH = SCREEN_WIDTH = GetSystemMetrics(SM_CXSCREEN);
|
|
SCALED_HEIGHT = SCREEN_HEIGHT = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
if ((hDesktopDC = GetDC(NULL)) == NULL) { KVMDEBUG("GetDC() failed", 0); return 0; }
|
|
if ((hCaptureDC = CreateCompatibleDC(hDesktopDC)) == NULL) { KVMDEBUG("CreateCompatibleDC() failed", 0); return 0; }
|
|
if ((hCapturedBitmap = CreateCompatibleBitmap(hDesktopDC, SCALED_WIDTH, SCALED_HEIGHT)) == NULL) { KVMDEBUG("CreateCompatibleBitmap() failed", 0); return 0; }
|
|
if (SelectObject(hCaptureDC, hCapturedBitmap) == NULL) { KVMDEBUG("SelectObject() failed", 0); }
|
|
|
|
// Find encoder and setup encoder parameters
|
|
GetEncoderClsid(L"image/jpeg", &encoderClsid);
|
|
encParam.Count = 1;
|
|
encParam.Parameter[0].Guid = EncoderQuality;
|
|
encParam.Parameter[0].Type = EncoderParameterValueTypeLong;
|
|
encParam.Parameter[0].NumberOfValues = 1;
|
|
encParam.Parameter[0].Value = &encCompression;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void teardown_gdiplus()
|
|
{
|
|
if (tilebuffer) free(tilebuffer);
|
|
tilebuffersize = 0;
|
|
tilebuffer = NULL;
|
|
GdiplusShutdown(gdiplusToken);
|
|
DeleteDC(hCaptureDC);
|
|
DeleteObject(hCapturedBitmap);
|
|
if (hDesktopDC) ReleaseDC(NULL, hDesktopDC);
|
|
hDesktopDC = NULL;
|
|
}
|
|
|
|
void set_tile_compression(int type, int level)
|
|
{
|
|
encCompression = level;
|
|
if (tilebuffer == NULL) { KVMDEBUG("set_tile_compression(), tilebuffer == NULL.", 0); return; }
|
|
KVMDEBUG("set_tile_compression() type", type);
|
|
KVMDEBUG("set_tile_compression() level", level);
|
|
|
|
switch (type)
|
|
{
|
|
case 1: { GetEncoderClsid(L"image/jpeg", &encoderClsid); break; }
|
|
case 2: { GetEncoderClsid(L"image/png", &encoderClsid); break; }
|
|
case 3: { GetEncoderClsid(L"image/tiff", &encoderClsid); break; }
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif |