1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-15 07:43:50 +00:00
Files
MeshAgent/meshcore/KVM/MacOS/mac_tile.c
2020-03-16 04:46:52 -04:00

509 lines
15 KiB
C

/*
* mac_tile.c
*
*
* Created by Ylian Saint-Hilaire on 8/18/11.
* Copyright 2011 __MyCompanyName__. All rights reserved.
*
*/
#include "mac_tile.h"
#include "../../meshinfo.h"
#include "../../meshdefines.h"
extern CGDirectDisplayID SCREEN_NUM;
extern int SCREEN_WIDTH;
extern int SCREEN_HEIGHT;
extern int SCREEN_SCALE;
extern int SCREEN_DEPTH;
extern int TILE_WIDTH;
extern int TILE_HEIGHT;
extern int TILE_WIDTH_COUNT;
extern int TILE_HEIGHT_COUNT;
extern int COMPRESSION_RATIO;
extern struct tileInfo_t **g_tileInfo;
extern unsigned char *jpeg_buffer;
extern int jpeg_buffer_length;
int tilebuffersize = 0;
void* tilebuffer = NULL;
int COMPRESSION_QUALITY = 50;
/******************************************************************************
* INTERNAL FUNCTIONS
******************************************************************************/
//Extracts the required tile buffer from the desktop buffer
int get_tile_buffer(int x, int y, void **buffer, long long bufferSize, void *desktop, long long desktopsize, int tilewidth, int tileheight)
{
char *target = *buffer;
int height = 0;
for (height = y; height < y + tileheight; height++) {
memcpy(target, (const void *)(((char *)desktop) + (3 * ((height * adjust_screen_size(SCREEN_WIDTH)) + x))), (size_t)(tilewidth * 3));
target = (char *) (target + (3 * tilewidth));
}
return 0;
}
//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, long long desktopsize, void ** buffer, long long *bufferSize)
{
*buffer = NULL;
*bufferSize = 0;
// Make sure a tile buffer is available. Most of the time, this is skipped.
if (tilebuffersize != captureWidth * captureHeight * 3)
{
if (tilebuffer != NULL) free(tilebuffer);
tilebuffersize = captureWidth * captureHeight * 3;
if ((tilebuffer = malloc(tilebuffersize)) == NULL) return 0;
}
//Get the final coalesced tile
get_tile_buffer(x, y, &tilebuffer, tilebuffersize, desktop, desktopsize, captureWidth, captureHeight);
write_JPEG_buffer(tilebuffer, captureWidth, captureHeight, COMPRESSION_QUALITY);
if (jpeg_buffer_length > 65500) {
return jpeg_buffer_length;
}
else {
return 0;
}
}
#if 0
void dump32bit (const XImage * input)
{
int row, col;
static char head[256];
static FILE *fp2 = NULL;
char *ptr2, *output;
long size;
register unsigned int
rm = input->red_mask,
gm = input->green_mask,
bm = input->blue_mask,
rs = 16,
gs = 8,
bs = 0, *p32 = (unsigned int *) input->data;
printf("%x %x %x\n", rm, gm, bm);
sprintf (head, "P6\n%d %d\n%d\n", input->width, input->height, 255);
size = ((input->bytes_per_line * input->height) / 4) * 3;
output = malloc (size);
ptr2 = output;
for (row = 0; row < input->height; row++) {
for (col = 0; col < input->width; col++) {
*output++ = ((*p32 & rm) >> rs);
*output++ = ((*p32 & gm) >> gs);
*output++ = ((*p32 & bm) >> bs);
p32++; // ignore alpha values
}
//
// eat padded bytes, for better speed we use shifting,
// (bytes_per_line - bits_per_pixel / 8 * width ) / 4
//
p32 += (input->bytes_per_line - (input->bits_per_pixel >> 3)
* input->width) >> 2;
}
fp2 = fopen ("/tmp/pic.rgb.pnm", "w");
fwrite (head, strlen (head), 1, fp2);
fwrite (ptr2, size, 1, fp2);
fclose (fp2);
free (ptr2);
}
#endif
// Really fast CRC-like method. Used for the KVM.
int util_crc(int x, int y, long long bufferSize, void *desktop, long long desktopsize, int tilewidth, int tileheight)
{
int hval = 0;
int *bp = NULL;
int *be = NULL;
int height = 0;
for (height = y; height < y + tileheight; height++) {
bp = (int *)(((char *)desktop) + (3 * ((height * adjust_screen_size(SCREEN_WIDTH)) + x)));
be = (int *)(((char *)desktop) + (3 * ((height * adjust_screen_size(SCREEN_WIDTH)) + x + tilewidth)));
while ((bp + 1) <= be)
{
//hval *= 0x01000193;
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
hval ^= *bp++;
}
/*if ((int)be - (int)bp >= 0) {
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
hval ^= (*(int *)(((int)be) - 3));
}*/
}
return hval;
}
/******************************************************************************
* EXTERNAL FUNCTIONS
******************************************************************************/
//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;
}
// Reset the tile info structure
int reset_tile_info(int old_height_count) {
int row, col;
if (g_tileInfo != NULL)
{
for (row = 0; row < old_height_count; row++) { free(g_tileInfo[row]); }
free(g_tileInfo);
g_tileInfo = NULL;
}
g_tileInfo = (struct tileInfo_t **) malloc(sizeof(struct tileInfo_t *) * TILE_HEIGHT_COUNT);
for (row = 0; row < TILE_HEIGHT_COUNT; row++) {
g_tileInfo[row] = (struct tileInfo_t *) calloc (TILE_WIDTH_COUNT, sizeof(struct tileInfo_t));
for (col = 0; col < TILE_WIDTH_COUNT; col++) { g_tileInfo[row][col].crc = 0xff; }
}
return 0;
}
//Fetches the encoded jpeg tile at the given location. The neighboring tiles are coalesced to form a larger jpeg before returning.
int getTileAt(int x, int y, void** buffer, long long *bufferSize, void *desktop, long long desktopsize, int row, int col)
{
int CRC, rcol, i, r, c;
int rightcol = col; //Used in coalescing. Indicates the rightmost 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 (g_tileInfo[row][col].flag == TILE_TODO) { //First check whether the tile-crc needs to be calculated or not.
if ((CRC = util_crc(x, y, TILE_HEIGHT * TILE_WIDTH * 3, desktop, desktopsize, TILE_WIDTH, TILE_HEIGHT)) == g_tileInfo[row][col].crc) return 0;
g_tileInfo[row][col].crc = CRC; //Update the tile CRC in the global data structure.
}
g_tileInfo[row][col].flag = 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 = g_tileInfo[row][rightcol].crc;
if (g_tileInfo[row][rightcol].flag == TILE_TODO) {
CRC = util_crc(r_x, y, TILE_HEIGHT * TILE_WIDTH * 3, desktop, desktopsize, TILE_WIDTH, TILE_HEIGHT);
}
if (CRC != g_tileInfo[row][rightcol].crc || g_tileInfo[row][rightcol].flag == TILE_MARKED_NOT_SENT) { //If the tile has changed, increment the capturewidth.
g_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 * 3 / COMPRESSION_RATIO > 65500) {
g_tileInfo[row][rightcol].flag = TILE_MARKED_NOT_SENT;
--rightcol;
break;
}
g_tileInfo[row][rightcol].flag = TILE_MARKED_NOT_SENT;
captureWidth += TILE_WIDTH;
}
else {
g_tileInfo[row][rightcol].flag = TILE_DONT_SEND;
--rightcol;
break;
}
}
//int TOLERANCE = (rightcol - col) / 3;
// Now go to the bottom tiles, check if they have changed and record them
while ((botrow + 1 < TILE_HEIGHT_COUNT) && ((captureHeight + TILE_HEIGHT) * captureWidth * 3 / COMPRESSION_RATIO <= 65500)) {
botrow++;
r_y = botrow * TILE_HEIGHT;
int fail = 0;
r_x = x;
//int missCount = 0;
for (rcol = col; rcol <= rightcol; rcol++) {
CRC = g_tileInfo[botrow][rcol].crc;
if (g_tileInfo[botrow][rcol].flag == TILE_TODO) {
CRC = util_crc(r_x, r_y, TILE_HEIGHT * TILE_WIDTH * 3, desktop, desktopsize, TILE_WIDTH, TILE_HEIGHT);
}
if (CRC != g_tileInfo[botrow][rcol].crc || g_tileInfo[botrow][rcol].flag == TILE_MARKED_NOT_SENT) {
g_tileInfo[botrow][rcol].flag = TILE_MARKED_NOT_SENT;
g_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 (g_tileInfo[botrow][i].flag == TILE_SKIPPED) {
g_tileInfo[botrow][i].flag = TILE_DONT_SEND;
}
else {
g_tileInfo[botrow][i].flag = TILE_MARKED_NOT_SENT;
}
}
g_tileInfo[botrow][rcol].flag = TILE_DONT_SEND;
botrow--;
break;
}
else {
g_tileInfo[botrow][rcol].flag = TILE_SKIPPED;
g_tileInfo[botrow][rcol].crc = CRC;
r_x += TILE_WIDTH;
}*/
fail = 1;
for (i = col; i < rcol; i++) {
g_tileInfo[botrow][i].flag = TILE_MARKED_NOT_SENT;
}
g_tileInfo[botrow][rcol].flag = 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, desktopsize, buffer, bufferSize);
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 in any case.
retval = 0;
break;
}
}
} while (retval != 0);
//Set the flags to TILE_SENT
if (jpeg_buffer != NULL) {
*bufferSize = jpeg_buffer_length + 8;
*buffer = malloc (*bufferSize);
((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
memcpy((char *)(*buffer) + 8, jpeg_buffer, jpeg_buffer_length);
free(jpeg_buffer);
jpeg_buffer = NULL;
jpeg_buffer_length = 0;
for (r = row; r <= botrow; r++) {
for (c = col; c <= rightcol; c++) {
g_tileInfo[r][c].flag = TILE_SENT;
}
}
}
return retval;
}
// Get screen buffer from the CGImageRef structure
int getScreenBuffer(unsigned char **desktop, long long *desktopsize, CGImageRef image)
{
unsigned int row, col, bpp, len, width_padding_size, height_padding_size, i;
unsigned char *output;
int height = CGImageGetHeight(image);
int width = CGImageGetWidth(image);
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);
CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(image));
const unsigned char *sourceBytesPtr = CFDataGetBytePtr(dataRef);
len = CFDataGetLength(dataRef);
if (*desktopsize != len) {
if (*desktop != NULL) { free(*desktop); }
*desktopsize = len;
*desktop = (unsigned char *) malloc (*desktopsize);
}
output = *desktop;
bpp = CGImageGetBitsPerPixel(image);
width_padding_size = (adjust_screen_size(SCREEN_WIDTH) - width) * 3;
switch(bpp) {
case 16:
{
const unsigned short *tmpPtr = (const unsigned short *)sourceBytesPtr;
if(alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaFirst) {
for (row = 0; row < height; row++) {
for (col = 0; col < width; col++) {
*output++ = (*tmpPtr & 0x7C00) >> 7;
*output++ = (*tmpPtr & 0x3E0) >> 2;
*output++ = (*tmpPtr & 0x1F) << 3;
tmpPtr++;
}
if (width_padding_size != 0) {
for (i = 0; i < width_padding_size; i++) {
*output++ = 0;
}
}
tmpPtr += (CGImageGetBytesPerRow(image) - (bpp >> 3) * width) >> 2;
}
}
else if (alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipLast ||
alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaLast) {
for (row = 0; row < height; row++) {
for (col = 0; col < width; col++) {
*output++ = (*tmpPtr & 0xF800) >> 8;
*output++ = (*tmpPtr & 0x7C0) >> 3;
*output++ = (*tmpPtr & 0x3E) << 2;
tmpPtr++;
}
if (width_padding_size != 0) {
for (i = 0; i < width_padding_size; i++) {
*output++ = 0;
}
}
tmpPtr += (CGImageGetBytesPerRow(image) - (bpp >> 3) * width) >> 2;
}
}
}
break;
case 32:
{
const unsigned int *tmpPtr1 = (const unsigned int *)sourceBytesPtr;
if(alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaFirst) {
for (row = 0; row < height; row++) {
for (col = 0; col < width; col++) {
*output++ = (*tmpPtr1 & 0x0ff0000) >> 16;
*output++ = (*tmpPtr1 & 0x0ff00) >> 8;
*output++ = (*tmpPtr1 & 0x0FF);
tmpPtr1++;
}
if (width_padding_size > 0) {
for (i = 0; i < width_padding_size; i++) {
*output++ = 0;
}
}
tmpPtr1 += (CGImageGetBytesPerRow(image) - (bpp >> 3) * width) >> 2;
}
}
else if (alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipLast ||
alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaLast) {
for (row = 0; row < height; row++) {
for (col = 0; col < width; col++) {
*output++ = (*tmpPtr1 & 0xFF000000) >> 24;
*output++ = (*tmpPtr1 & 0x0ff0000) >> 16;
*output++ = (*tmpPtr1 & 0x0ff00) >> 8;
tmpPtr1++;
}
if (width_padding_size != 0) {
for (i = 0; i < width_padding_size; i++) {
*output++ = 0;
}
}
tmpPtr1 += (CGImageGetBytesPerRow(image) - (bpp >> 3) * width) >> 2;
}
}
}
break;
default:
fprintf(stderr, "This image depth is not supported.\n");
return -1;
}
height_padding_size = adjust_screen_size(SCREEN_HEIGHT) - height;
if (height_padding_size > 0) {
for (row = 0; row < height_padding_size; row++) {
for (col = 0; col < (width * 3) + width_padding_size; col++) {
*output++ = 0;
}
}
}
CFRelease(dataRef);
return 0;
}
// Set the compression quality
void set_tile_compression(int type, int level)
{
if (level > 0 && level <= 100) {
COMPRESSION_QUALITY = level;
}
else {
COMPRESSION_QUALITY = 60;
}
// TODO Make sure the all the types are handled. We ignore the type variable for now.
}