1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-12 06:13:43 +00:00
Files
MeshAgent/meshcore/KVM/MacOS/mac_tile.c
Bryan Roe e3b8cc4d2c 1. Fixed edge case bug, where TLS packet was corrupt
2. Added compile switch for KVM ALL TILES
3. Added compile switch for Debug fragmentation of Sends
4. Fixed compiler warnings
5. Added comments to makefile
2020-04-13 20:08:32 -07:00

548 lines
16 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;
#if defined(JPEGMAXBUF)
#define MAX_TILE_SIZE JPEGMAXBUF
#else
#define MAX_TILE_SIZE 65500
#endif
/******************************************************************************
* 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 MAX_TILE_SIZE > 0
if (jpeg_buffer_length > MAX_TILE_SIZE)
{
return jpeg_buffer_length;
}
else
#endif
{
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;
#if MAX_TILE_SIZE > 0
//Here we check whether the size of the coalesced bitmap is greater than the threshold (MAX_TILE_SIZE)
if ((captureWidth + TILE_WIDTH) * TILE_HEIGHT * 3 / COMPRESSION_RATIO > MAX_TILE_SIZE)
{
g_tileInfo[row][rightcol].flag = TILE_MARKED_NOT_SENT;
--rightcol;
break;
}
#endif
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
#if MAX_TILE_SIZE > 0
while ((botrow + 1 < TILE_HEIGHT_COUNT) && ((captureHeight + TILE_HEIGHT) * captureWidth * 3 / COMPRESSION_RATIO <= MAX_TILE_SIZE))
#else
while ((botrow + 1 < TILE_HEIGHT_COUNT))
#endif
{
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;
#if MAX_TILE_SIZE == 0
retval = calc_opt_compr_send(x, y, captureWidth, captureHeight, desktop, desktopsize, buffer, bufferSize);
#else
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) * (0.92 * MAX_TILE_SIZE)); //Magic number: 92% of MAX_TILE_SIZE
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);
#endif
//Set the flags to TILE_SENT
if (jpeg_buffer != NULL)
{
*bufferSize = jpeg_buffer_length + (jpeg_buffer_length > 65500 ? 16 : 8);
*buffer = malloc (*bufferSize);
if (jpeg_buffer_length > 65500)
{
((unsigned short*)*buffer)[0] = (unsigned short)htons((unsigned short)MNG_JUMBO); // Write the type
((unsigned short*)*buffer)[1] = (unsigned short)htons((unsigned short)8); // Write the size
((unsigned int*)*buffer)[1] = (unsigned int)htonl(jpeg_buffer_length + 8); // Size of the Next Packet
((unsigned short*)*buffer)[4] = (unsigned short)htons((unsigned short)MNG_KVM_PICTURE); // Write the type
((unsigned short*)*buffer)[5] = 0; // RESERVED
((unsigned short*)*buffer)[6] = (unsigned short)htons((unsigned short)x); // X position
((unsigned short*)*buffer)[7] = (unsigned short)htons((unsigned short)y); // Y position
memcpy((char *)(*buffer) + 16, jpeg_buffer, jpeg_buffer_length);
}
else
{
((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.
}