FreeRDP/libfreerdp/cache/glyph.c

634 lines
17 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Glyph Cache
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <winpr/crt.h>
#include <freerdp/freerdp.h>
#include <winpr/stream.h>
#include <freerdp/log.h>
#include <freerdp/cache/glyph.h>
#define TAG FREERDP_TAG("cache.glyph")
static rdpGlyph* glyph_cache_get(rdpGlyphCache* glyph_cache, UINT32 id,
UINT32 index);
static void glyph_cache_put(rdpGlyphCache* glyph_cache, UINT32 id, UINT32 index,
rdpGlyph* entry);
static void* glyph_cache_fragment_get(rdpGlyphCache* glyph, UINT32 index,
UINT32* count);
static void glyph_cache_fragment_put(rdpGlyphCache* glyph, UINT32 index,
UINT32 count, void* entry);
static void update_process_glyph(rdpContext* context, const BYTE* data,
UINT32* index, UINT32* x, UINT32* y,
UINT32 cacheId, UINT32 ulCharInc, UINT32 flAccel)
{
UINT32 offset;
rdpGlyph* glyph;
UINT32 cacheIndex;
rdpGraphics* graphics;
rdpGlyphCache* glyph_cache;
graphics = context->graphics;
glyph_cache = context->cache->glyph;
cacheIndex = data[*index];
glyph = glyph_cache_get(glyph_cache, cacheId, cacheIndex);
if ((ulCharInc == 0) && (!(flAccel & SO_CHAR_INC_EQUAL_BM_BASE)))
{
/* Contrary to fragments, the offset is added before the glyph. */
(*index)++;
offset = data[*index];
if (offset & 0x80)
{
offset = data[*index + 1] | ((int)((signed char)data[*index + 2]) << 8);
(*index)++;
(*index)++;
}
if (flAccel & SO_VERTICAL)
*y += offset;
else
*x += offset;
}
if (glyph != NULL)
{
Glyph_Draw(context, glyph, glyph->x + *x, glyph->y + *y);
if (flAccel & SO_CHAR_INC_EQUAL_BM_BASE)
*x += glyph->cx;
}
}
static BOOL update_process_glyph_fragments(rdpContext* context,
const BYTE* data,
UINT32 length, UINT32 cacheId,
UINT32 ulCharInc, UINT32 flAccel,
UINT32 bgcolor, UINT32 fgcolor,
UINT32 x, UINT32 y,
UINT32 bkX, UINT32 bkY,
UINT32 bkWidth, UINT32 bkHeight,
UINT32 opX, UINT32 opY,
UINT32 opWidth, UINT32 opHeight,
BOOL fOpRedundant)
{
UINT32 n;
UINT32 id;
UINT32 size;
UINT32 index = 0;
BYTE* fragments;
rdpGraphics* graphics;
rdpGlyphCache* glyph_cache;
graphics = context->graphics;
glyph_cache = context->cache->glyph;
if (opX + opWidth > context->settings->DesktopWidth)
{
/**
* Some Microsoft servers send erroneous high values close to the
* sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
* FastGlyph drawing orders, probably a result of applications trying to
* clear the text line to the very right end.
* One example where this can be seen is typing in notepad.exe within
* a RDP session to Windows XP Professional SP3.
* This workaround prevents resulting problems in the UI callbacks.
*/
opWidth = context->settings->DesktopWidth - opX;
}
if (opWidth > 0 && opHeight > 0)
{
if (!Glyph_BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor,
fOpRedundant))
return FALSE;
}
else
{
if (fOpRedundant)
{
if (!Glyph_BeginDraw(context, bkX, bkY, bkWidth, bkHeight, bgcolor, fgcolor,
fOpRedundant))
return FALSE;
}
else
{
if (!Glyph_BeginDraw(context, 0, 0, 0, 0, bgcolor, fgcolor, fOpRedundant))
return FALSE;
}
}
while (index < (int) length)
{
switch (data[index])
{
case GLYPH_FRAGMENT_USE:
if (index + 2 > (int) length)
{
/* at least one byte need to follow */
index = length = 0;
break;
}
id = data[index + 1];
fragments = (BYTE*) glyph_cache_fragment_get(glyph_cache, id, &size);
if (fragments != NULL)
{
for (n = 0; n < (int) size; n++)
{
update_process_glyph(context, fragments, &n, &x, &y, cacheId, ulCharInc,
flAccel);
}
/* Contrary to glyphs, the offset is added after the fragment. */
if ((ulCharInc == 0) && (!(flAccel & SO_CHAR_INC_EQUAL_BM_BASE)))
{
if (flAccel & SO_VERTICAL)
y += data[index + 2];
else
x += data[index + 2];
}
}
index += (index + 2 < (int) length) ? 3 : 2;
length -= index;
data = &(data[index]);
index = 0;
break;
case GLYPH_FRAGMENT_ADD:
if (index + 3 > (int) length)
{
/* at least two bytes need to follow */
index = length = 0;
break;
}
id = data[index + 1];
size = data[index + 2];
fragments = (BYTE*) malloc(size);
if (!fragments)
return FALSE;
CopyMemory(fragments, data, size);
glyph_cache_fragment_put(glyph_cache, id, size, fragments);
index += 3;
length -= index;
data = &(data[index]);
index = 0;
break;
default:
update_process_glyph(context, data, &index, &x, &y, cacheId, ulCharInc,
flAccel);
index++;
break;
}
}
if (opWidth > 0 && opHeight > 0)
return Glyph_EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor);
return Glyph_EndDraw(context, bkX, bkY, bkWidth, bkHeight, bgcolor, fgcolor);
}
static BOOL update_gdi_glyph_index(rdpContext* context,
GLYPH_INDEX_ORDER* glyphIndex)
{
rdpGlyphCache* glyph_cache;
int bkWidth, bkHeight, opWidth, opHeight;
glyph_cache = context->cache->glyph;
bkWidth = glyphIndex->bkRight - glyphIndex->bkLeft;
opWidth = glyphIndex->opRight - glyphIndex->opLeft;
bkHeight = glyphIndex->bkBottom - glyphIndex->bkTop;
opHeight = glyphIndex->opBottom - glyphIndex->opTop;
return update_process_glyph_fragments(context, glyphIndex->data,
glyphIndex->cbData,
glyphIndex->cacheId, glyphIndex->ulCharInc, glyphIndex->flAccel,
glyphIndex->backColor, glyphIndex->foreColor, glyphIndex->x, glyphIndex->y,
glyphIndex->bkLeft, glyphIndex->bkTop, bkWidth, bkHeight,
glyphIndex->opLeft, glyphIndex->opTop, opWidth, opHeight,
glyphIndex->fOpRedundant);
}
static BOOL update_gdi_fast_index(rdpContext* context,
const FAST_INDEX_ORDER* fastIndex)
{
INT32 x, y;
INT32 opLeft, opTop;
INT32 opRight, opBottom;
rdpGlyphCache* glyph_cache;
glyph_cache = context->cache->glyph;
opLeft = fastIndex->opLeft;
opTop = fastIndex->opTop;
opRight = fastIndex->opRight;
opBottom = fastIndex->opBottom;
x = fastIndex->x;
y = fastIndex->y;
if (opBottom == -32768)
{
BYTE flags = (BYTE)(opTop & 0x0F);
if (flags & 0x01)
opBottom = fastIndex->bkBottom;
if (flags & 0x02)
opRight = fastIndex->bkRight;
if (flags & 0x04)
opTop = fastIndex->bkTop;
if (flags & 0x08)
opLeft = fastIndex->bkLeft;
}
if (opLeft == 0)
opLeft = fastIndex->bkLeft;
if (opRight == 0)
opRight = fastIndex->bkRight;
/* Server can send a massive number (32766) which appears to be
* undocumented special behavior for "Erase all the way right".
* X11 has nondeterministic results asking for a draw that wide. */
if (opRight > context->instance->settings->DesktopWidth)
opRight = context->instance->settings->DesktopWidth;
if (x == -32768)
x = fastIndex->bkLeft;
if (y == -32768)
y = fastIndex->bkTop;
return update_process_glyph_fragments(context, fastIndex->data,
fastIndex->cbData,
fastIndex->cacheId, fastIndex->ulCharInc, fastIndex->flAccel,
fastIndex->backColor, fastIndex->foreColor, x, y,
fastIndex->bkLeft, fastIndex->bkTop,
fastIndex->bkRight - fastIndex->bkLeft, fastIndex->bkBottom - fastIndex->bkTop,
opLeft, opTop,
opRight - opLeft, opBottom - opTop,
FALSE);
}
static BOOL update_gdi_fast_glyph(rdpContext* context,
const FAST_GLYPH_ORDER* fastGlyph)
{
INT32 x, y;
rdpGlyph* glyph;
BYTE text_data[2];
INT32 opLeft, opTop;
INT32 opRight, opBottom;
const GLYPH_DATA_V2* glyphData;
rdpCache* cache = context->cache;
opLeft = fastGlyph->opLeft;
opTop = fastGlyph->opTop;
opRight = fastGlyph->opRight;
opBottom = fastGlyph->opBottom;
x = fastGlyph->x;
y = fastGlyph->y;
if (opBottom == -32768)
{
BYTE flags = (BYTE)(opTop & 0x0F);
if (flags & 0x01)
opBottom = fastGlyph->bkBottom;
if (flags & 0x02)
opRight = fastGlyph->bkRight;
if (flags & 0x04)
opTop = fastGlyph->bkTop;
if (flags & 0x08)
opLeft = fastGlyph->bkLeft;
}
if (opLeft == 0)
opLeft = fastGlyph->bkLeft;
if (opRight == 0)
opRight = fastGlyph->bkRight;
/* See update_gdi_fast_index opRight comment. */
if (opRight > context->instance->settings->DesktopWidth)
opRight = context->instance->settings->DesktopWidth;
if (x == -32768)
x = fastGlyph->bkLeft;
if (y == -32768)
y = fastGlyph->bkTop;
if ((fastGlyph->cbData > 1) && (fastGlyph->glyphData.aj))
{
/* got option font that needs to go into cache */
glyphData = &fastGlyph->glyphData;
glyph = Glyph_Alloc(context);
if (!glyph)
return FALSE;
glyph->x = glyphData->x;
glyph->y = glyphData->y;
glyph->cx = glyphData->cx;
glyph->cy = glyphData->cy;
glyph->cb = glyphData->cb;
glyph->aj = malloc(glyphData->cb);
if (!glyph->aj)
goto error_aj;
CopyMemory(glyph->aj, glyphData->aj, glyph->cb);
if (!Glyph_New(context, glyph))
goto error_glyph_new;
glyph_cache_put(cache->glyph, fastGlyph->cacheId, fastGlyph->data[0], glyph);
}
text_data[0] = fastGlyph->data[0];
text_data[1] = 0;
return update_process_glyph_fragments(context, text_data, 1,
fastGlyph->cacheId, fastGlyph->ulCharInc, fastGlyph->flAccel,
fastGlyph->backColor, fastGlyph->foreColor, x, y,
fastGlyph->bkLeft, fastGlyph->bkTop,
fastGlyph->bkRight - fastGlyph->bkLeft, fastGlyph->bkBottom - fastGlyph->bkTop,
opLeft, opTop,
opRight - opLeft, opBottom - opTop,
FALSE);
error_glyph_new:
free(glyph->aj);
glyph->aj = NULL;
error_aj:
Glyph_Free(context, glyph);
return FALSE;
}
static BOOL update_gdi_cache_glyph(rdpContext* context,
const CACHE_GLYPH_ORDER* cacheGlyph)
{
UINT32 i;
rdpGlyph* glyph;
const GLYPH_DATA* glyph_data;
rdpCache* cache = context->cache;
for (i = 0; i < cacheGlyph->cGlyphs; i++)
{
glyph_data = &cacheGlyph->glyphData[i];
glyph = Glyph_Alloc(context);
if (!glyph)
return FALSE;
glyph->x = glyph_data->x;
glyph->y = glyph_data->y;
glyph->cx = glyph_data->cx;
glyph->cy = glyph_data->cy;
glyph->cb = glyph_data->cb;
glyph->aj = glyph_data->aj;
if (!Glyph_New(context, glyph))
{
Glyph_Free(context, glyph);
return FALSE;
}
glyph_cache_put(cache->glyph, cacheGlyph->cacheId, glyph_data->cacheIndex,
glyph);
}
return TRUE;
}
static BOOL update_gdi_cache_glyph_v2(rdpContext* context,
const CACHE_GLYPH_V2_ORDER* cacheGlyphV2)
{
UINT32 i;
rdpGlyph* glyph;
const GLYPH_DATA_V2* glyphData;
rdpCache* cache = context->cache;
for (i = 0; i < cacheGlyphV2->cGlyphs; i++)
{
glyphData = &cacheGlyphV2->glyphData[i];
glyph = Glyph_Alloc(context);
if (!glyph)
{
/* TODO: cleanup perviosly allocated glyph memory in error case */
return FALSE;
}
glyph->x = glyphData->x;
glyph->y = glyphData->y;
glyph->cx = glyphData->cx;
glyph->cy = glyphData->cy;
glyph->cb = glyphData->cb;
glyph->aj = glyphData->aj;
Glyph_New(context, glyph);
glyph_cache_put(cache->glyph, cacheGlyphV2->cacheId, glyphData->cacheIndex,
glyph);
}
return TRUE;
}
rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index)
{
rdpGlyph* glyph;
WLog_DBG(TAG, "GlyphCacheGet: id: %d index: %d", id, index);
if (id > 9)
{
WLog_ERR(TAG, "invalid glyph cache id: %d", id);
return NULL;
}
if (index > glyphCache->glyphCache[id].number)
{
WLog_ERR(TAG, "index %d out of range for cache id: %d", index, id);
return NULL;
}
glyph = glyphCache->glyphCache[id].entries[index];
if (!glyph)
WLog_ERR(TAG, "no glyph found at cache index: %d in cache id: %d", index, id);
return glyph;
}
void glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index,
rdpGlyph* glyph)
{
rdpGlyph* prevGlyph;
if (id > 9)
{
WLog_ERR(TAG, "invalid glyph cache id: %d", id);
return;
}
if (index > glyphCache->glyphCache[id].number)
{
WLog_ERR(TAG, "invalid glyph cache index: %d in cache id: %d", index, id);
return;
}
WLog_DBG(TAG, "GlyphCachePut: id: %d index: %d", id, index);
prevGlyph = glyphCache->glyphCache[id].entries[index];
if (prevGlyph)
{
Glyph_Free(glyphCache->context, prevGlyph);
free(prevGlyph->aj);
free(prevGlyph);
}
glyphCache->glyphCache[id].entries[index] = glyph;
}
void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index,
UINT32* size)
{
void* fragment;
if (index > 255)
{
WLog_ERR(TAG, "invalid glyph cache fragment index: %d", index);
return NULL;
}
fragment = glyphCache->fragCache.entries[index].fragment;
*size = (BYTE) glyphCache->fragCache.entries[index].size;
WLog_DBG(TAG, "GlyphCacheFragmentGet: index: %d size: %d", index, *size);
if (!fragment)
WLog_ERR(TAG, "invalid glyph fragment at index:%d", index);
return fragment;
}
void glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index,
UINT32 size, void* fragment)
{
void* prevFragment;
if (index > 255)
{
WLog_ERR(TAG, "invalid glyph cache fragment index: %d", index);
return;
}
WLog_DBG(TAG, "GlyphCacheFragmentPut: index: %d size: %d", index, size);
prevFragment = glyphCache->fragCache.entries[index].fragment;
glyphCache->fragCache.entries[index].fragment = fragment;
glyphCache->fragCache.entries[index].size = size;
free(prevFragment);
}
void glyph_cache_register_callbacks(rdpUpdate* update)
{
update->primary->GlyphIndex = update_gdi_glyph_index;
update->primary->FastIndex = update_gdi_fast_index;
update->primary->FastGlyph = update_gdi_fast_glyph;
update->secondary->CacheGlyph = update_gdi_cache_glyph;
update->secondary->CacheGlyphV2 = update_gdi_cache_glyph_v2;
}
rdpGlyphCache* glyph_cache_new(rdpSettings* settings)
{
rdpGlyphCache* glyphCache;
glyphCache = (rdpGlyphCache*) calloc(1, sizeof(rdpGlyphCache));
if (glyphCache)
{
int i;
WLog_Init();
glyphCache->log = WLog_Get("com.freerdp.cache.glyph");
glyphCache->settings = settings;
glyphCache->context = ((freerdp*) settings->instance)->update->context;
for (i = 0; i < 10; i++)
{
glyphCache->glyphCache[i].number = settings->GlyphCache[i].cacheEntries;
glyphCache->glyphCache[i].maxCellSize =
settings->GlyphCache[i].cacheMaximumCellSize;
glyphCache->glyphCache[i].entries = (rdpGlyph**) calloc(
glyphCache->glyphCache[i].number, sizeof(rdpGlyph*));
}
glyphCache->fragCache.entries = calloc(256, sizeof(FRAGMENT_CACHE_ENTRY));
}
return glyphCache;
}
void glyph_cache_free(rdpGlyphCache* glyphCache)
{
if (glyphCache)
{
int i;
for (i = 0; i < 10; i++)
{
int j;
for (j = 0; j < (int) glyphCache->glyphCache[i].number; j++)
{
rdpGlyph* glyph;
glyph = glyphCache->glyphCache[i].entries[j];
if (glyph)
{
Glyph_Free(glyphCache->context, glyph);
free(glyph->aj);
free(glyph);
glyphCache->glyphCache[i].entries[j] = NULL;
}
}
free(glyphCache->glyphCache[i].entries);
glyphCache->glyphCache[i].entries = NULL;
}
for (i = 0; i < 256; i++)
{
free(glyphCache->fragCache.entries[i].fragment);
glyphCache->fragCache.entries[i].fragment = NULL;
}
free(glyphCache->fragCache.entries);
free(glyphCache);
}
}