/* BSD-2-Clause license * * Copyright (c) 2018-2023 NST , sss . * */ #include #include #include #include #include #include #include #include #include #include #include "rdp_backend_api.h" #include "rdp_impl.h" #include "rdp_png.h" #define RGB_555_888(_r, _g, _b) \ _r = (_r << 3 & ~0x7) | (_r >> 2); \ _g = (_g << 3 & ~0x7) | (_g >> 2); \ _b = (_b << 3 & ~0x7) | (_b >> 2); #define RGB_565_888(_r, _g, _b) \ _r = (_r << 3 & ~0x7) | (_r >> 2); \ _g = (_g << 2 & ~0x3) | (_g >> 4); \ _b = (_b << 3 & ~0x7) | (_b >> 2); #define GetRGB_555(_r, _g, _b, _p) \ _r = (_p & 0x7C00) >> 10; \ _g = (_p & 0x3E0) >> 5; \ _b = (_p & 0x1F); #define GetRGB_565(_r, _g, _b, _p) \ _r = (_p & 0xF800) >> 11; \ _g = (_p & 0x7E0) >> 5; \ _b = (_p & 0x1F); #define GetRGB15(_r, _g, _b, _p) \ GetRGB_555(_r, _g, _b, _p); \ RGB_555_888(_r, _g, _b); #define GetRGB16(_r, _g, _b, _p) \ GetRGB_565(_r, _g, _b, _p); \ RGB_565_888(_r, _g, _b); #define RGB24(_r, _g, _b) ((_r << 16) | (_g << 8) | _b) #define GetRGB24(_r, _g, _b, _p) \ _r = (_p & 0xFF0000) >> 16; \ _g = (_p & 0xFF00) >> 8; \ _b = (_p & 0xFF); #define GetRGB32(_r, _g, _b, _p) \ _r = (_p & 0xFF0000) >> 16; \ _g = (_p & 0xFF00) >> 8; \ _b = (_p & 0xFF); #define ARGB32(_a, _r, _g, _b) (_a << 24) | (_r << 16) | (_g << 8) | _b #define RGB16(_r, _g, _b) \ (((_r >> 3) & 0x1F) << 11) | (((_g >> 2) & 0x3F) << 5) \ | ((_b >> 3) & 0x1F) #define RGB15(_r, _g, _b) \ (((_r >> 3) & 0x1F) << 10) | (((_g >> 3) & 0x1F) << 5) \ | ((_b >> 3) & 0x1F) #define GetARGB32(_a, _r, _g, _b, _p) \ _a = (_p & 0xFF000000) >> 24; \ _r = (_p & 0xFF0000) >> 16; \ _g = (_p & 0xFF00) >> 8; \ _b = (_p & 0xFF); HCLRCONV freerdp_clrconv_new(UINT32 flags) { HCLRCONV clrconv; clrconv = (CLRCONV *)calloc(1, sizeof(CLRCONV)); if (!clrconv) { perror("calloc"); return 0; } clrconv->alpha = (flags & CLRCONV_ALPHA) ? TRUE : FALSE; clrconv->invert = (flags & CLRCONV_INVERT) ? TRUE : FALSE; clrconv->rgb555 = (flags & CLRCONV_RGB555) ? TRUE : FALSE; clrconv->palette = (rdpPalette *)calloc(1, sizeof(rdpPalette)); if (!clrconv->palette) { perror("calloc"); free(clrconv); return 0; } return clrconv; } void freerdp_clrconv_free(HCLRCONV clrconv) { if (clrconv) { free(clrconv->palette); free(clrconv); } } static int freerdp_get_pixel(BYTE *data, int x, int y, int width, int height, int bpp) { int start; int shift; UINT16 *src16; UINT32 *src32; int red, green, blue; switch (bpp) { case 1: width = (width + 7) / 8; start = (y * width) + x / 8; shift = x % 8; return (data[start] & (0x80 >> shift)) != 0; case 8: return data[y * width + x]; case 15: case 16: src16 = (UINT16 *)data; return src16[y * width + x]; case 24: data += y * width * 3; data += x * 3; red = data[0]; green = data[1]; blue = data[2]; return RGB24(red, green, blue); case 32: src32 = (UINT32 *)data; return src32[y * width + x]; default: break; } return 0; } static void freerdp_set_pixel( BYTE *data, int x, int y, int width, int height, int bpp, int pixel) { int start; int shift; int *dst32; if (bpp == 1) { width = (width + 7) / 8; start = (y * width) + x / 8; shift = x % 8; if (pixel) data[start] = data[start] | (0x80 >> shift); else data[start] = data[start] & ~(0x80 >> shift); } else if (bpp == 32) { dst32 = (int *)data; dst32[y * width + x] = pixel; } } static void freerdp_color_split_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, BYTE *blue, BYTE *alpha, HCLRCONV clrconv) { *red = *green = *blue = 0; *alpha = (clrconv->alpha) ? 0xFF : 0x00; switch (bpp) { case 32: if (clrconv->alpha) { GetARGB32(*alpha, *red, *green, *blue, *color); } else { GetRGB32(*red, *green, *blue, *color); } break; case 24: GetRGB24(*red, *green, *blue, *color); break; case 16: GetRGB16(*red, *green, *blue, *color); break; case 15: GetRGB15(*red, *green, *blue, *color); break; case 8: *color &= 0xFF; *red = clrconv->palette->entries[*color].red; *green = clrconv->palette->entries[*color].green; *blue = clrconv->palette->entries[*color].blue; break; case 1: if (*color != 0) { *red = 0xFF; *green = 0xFF; *blue = 0xFF; } break; default: break; } } static void freerdp_color_make_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, BYTE *blue, BYTE *alpha, HCLRCONV clrconv) { switch (bpp) { case 32: *color = ARGB32(*alpha, *red, *green, *blue); break; case 24: *color = RGB24(*red, *green, *blue); break; case 16: if (clrconv->rgb555) { *color = RGB15(*red, *green, *blue); } else { *color = RGB16(*red, *green, *blue); } break; case 15: *color = RGB15(*red, *green, *blue); break; case 8: *color = RGB24(*red, *green, *blue); break; case 1: if ((*red != 0) || (*green != 0) || (*blue != 0)) *color = 1; break; default: break; } } static UINT32 freerdp_color_convert_rgb( UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv) { BYTE red = 0; BYTE green = 0; BYTE blue = 0; BYTE alpha = 0xFF; UINT32 dstColor = 0; freerdp_color_split_rgb( &srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv); freerdp_color_make_rgb( &dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv); return dstColor; } static void freerdp_alpha_cursor_convert(BYTE *alphaData, BYTE *xorMask, BYTE *andMask, int width, int height, int bpp, HCLRCONV clrconv) { UINT32 xorPixel; UINT32 andPixel; UINT32 x, y, jj; for (y = 0; y < height; y++) { jj = (bpp == 1) ? y : (height - 1) - y; for (x = 0; x < width; x++) { xorPixel = freerdp_get_pixel( xorMask, x, jj, width, height, bpp); xorPixel = freerdp_color_convert_rgb( xorPixel, bpp, 32, clrconv); andPixel = freerdp_get_pixel( andMask, x, jj, width, height, 1); if (andPixel) { if ((xorPixel & 0xFFFFFF) == 0xFFFFFF) { /* use pattern (not solid black) for xor * area */ xorPixel = (x & 1) == (y & 1); xorPixel = xorPixel ? 0xFFFFFF : 0; xorPixel |= 0xFF000000; } else if (xorPixel == 0xFF000000) { xorPixel = 0; } } freerdp_set_pixel( alphaData, x, y, width, height, 32, xorPixel); } } } static void freerdp_bitmap_flip(BYTE *src, BYTE *dst, int scanLineSz, int height) { int i; BYTE *bottomLine = dst + (scanLineSz * (height - 1)); BYTE *topLine = src; /* Special processing if called for flip-in-place. */ if (src == dst) { /* Allocate a scanline buffer. * (FIXME: malloc / xfree below should be replaced by "get/put * scanline buffer from a pool/Q of fixed buffers" to reuse * fixed size buffers (of max scanline size (or adaptative?) ) * -- would be much faster). */ BYTE *tmpBfr = (BYTE *)winpr_aligned_malloc(scanLineSz, 16); if (!tmpBfr) { return; } int half = height / 2; /* Flip buffer in place by line permutations through the temp * scan line buffer. * Note that if height has an odd number of line, we don't need * to move the center scanline anyway. * Also note that in place flipping takes three memcpy() calls * to process two scanlines while src to distinct dest would * only requires two memcpy() calls for two scanlines. */ height--; for (i = 0; i < half; i++) { CopyMemory(tmpBfr, topLine, scanLineSz); CopyMemory(topLine, bottomLine, scanLineSz); CopyMemory(bottomLine, tmpBfr, scanLineSz); topLine += scanLineSz; bottomLine -= scanLineSz; height--; } winpr_aligned_free(tmpBfr); } /* Flip from source buffer to destination buffer. */ else { for (i = 0; i < height; i++) { if (bottomLine && topLine && scanLineSz) { CopyMemory(bottomLine, topLine, scanLineSz); topLine += scanLineSz; bottomLine -= scanLineSz; } } } } static BYTE * freerdp_image_flip(BYTE *srcData, BYTE *dstData, int width, int height, int bpp) { int scanline; scanline = width * ((bpp + 7) / 8); if (!dstData) dstData = (BYTE *)winpr_aligned_malloc( width * height * ((bpp + 7) / 8), 16); if (!dstData) { return NULL; } freerdp_bitmap_flip(srcData, dstData, scanline, height); return dstData; } /* outgoing rdp protocol to client implementation */ static BOOL Pointer_New(rdpContext *context, rdpPointer *pointer) { my_rdp_context *c = (my_rdp_context *)context; HCLRCONV hclrconv = c->my_internals->clrconv; size_t psize = pointer->width * pointer->height * 4; my_rdp_pointer *p = (my_rdp_pointer *)pointer; p->id = c->my_internals->m_ptrId++; uint8_t *pixels = malloc(sizeof(uint8_t) * psize); if (!pixels) { perror("malloc"); return FALSE; } memset(pixels, 0, psize); if ((pointer->andMaskData != 0) && (pointer->xorMaskData != 0)) { //XXX freerdp_alpha_cursor_convert(pixels, pointer->xorMaskData, pointer->andMaskData, pointer->width, pointer->height, pointer->xorBpp, hclrconv); } //check if the cursor is fully transparent bool transparent = TRUE; for (int y = 0; y < pointer->height; y++) { for (int x = 0; x < pointer->width; x++) { if (pixels[0 + x * 4 + y * 4 * pointer->width] != 0) { transparent = FALSE; } } } if (transparent) { pixels[3] = 1; } rdp_png_buf png_buf; memset(&png_buf, 0, sizeof(png_buf)); png_buf.buf_size = (pointer->width * pointer->height * sizeof(uint32_t)) + 8 /* allocating fullsize 32but argb pixels + png header */; png_buf.buf = malloc(png_buf.buf_size); if (!png_buf.buf) { free(pixels); perror("malloc"); return FALSE; } png_generate_from_argb( pointer->width, pointer->height, pixels, &png_buf); wrdp_core_display_cursor cur = {0x00, 0x00, 0x02, 0x00, 0x01, 0x00, (unsigned char)pointer->width, (unsigned char)pointer->height, 0x00, 0x00, (unsigned char)pointer->xPos, (unsigned char)(pointer->xPos >> 8), (unsigned char)pointer->yPos, (unsigned char)(pointer->yPos >> 8), (unsigned char)png_buf.written, (unsigned char)(png_buf.written >> 8), (unsigned char)(png_buf.written >> 16), (unsigned char)(png_buf.written >> 24), 0x16, 0x00, 0x00, 0x00}; cur.data = png_buf.buf; wrdp_core_display_cursor_info cursor_info = {p->id, pointer->xPos, pointer->yPos, png_buf.written, &cur}; c->my_internals->core->api_paint->send_ptr_new( &cursor_info, c->my_internals->task_info); free(png_buf.buf); free(pixels); return TRUE; } static void Pointer_Free(rdpContext *context, rdpPointer *pointer) { my_rdp_pointer *p = (my_rdp_pointer *)pointer; my_rdp_context *c = (my_rdp_context *)context; if (p->id) { c->my_internals->core->api_paint->send_ptr_free( p->id, c->my_internals->task_info); } } static BOOL Pointer_Set(rdpContext *context, rdpPointer *pointer) { my_rdp_pointer *p = (my_rdp_pointer *)pointer; my_rdp_context *c = (my_rdp_context *)context; c->my_internals->core->api_paint->send_ptr_set( p->id, c->my_internals->task_info); return TRUE; } static BOOL Pointer_SetNull(rdpContext *context) { my_rdp_context *c = (my_rdp_context *)context; c->my_internals->core->api_paint->send_ptr_set_null( c->my_internals->task_info); return TRUE; } static BOOL Pointer_SetDefault(rdpContext *context) { my_rdp_context *c = (my_rdp_context *)context; c->my_internals->core->api_paint->send_ptr_set_default( c->my_internals->task_info); return TRUE; } void register_pointer(rdpPointer *p) { p->New = Pointer_New; p->Free = Pointer_Free; p->Set = Pointer_Set; p->SetNull = Pointer_SetNull; p->SetDefault = Pointer_SetDefault; } static BOOL BeginPaint(rdpContext *context) { my_rdp_context *c = (my_rdp_context *)context; c->my_internals->core->api_paint->send_begin_paint( c->my_internals->task_info); return TRUE; } static BOOL EndPaint(rdpContext *context) { my_rdp_context *c = (my_rdp_context *)context; c->my_internals->core->api_paint->send_end_paint( c->my_internals->task_info); return TRUE; } static BOOL SetBounds(rdpContext *context, const rdpBounds *bounds) { my_rdp_context *c = (my_rdp_context *)context; wrdp_core_display_bounds lB; if (bounds) { lB.left = bounds->left; lB.right = bounds->right + 1; lB.top = bounds->top; lB.bottom = bounds->bottom + 1; } else { memset(&lB, 0, sizeof(wrdp_core_display_bounds)); } c->my_internals->core->api_paint->send_set_bounds( &lB, c->my_internals->task_info); return TRUE; } static BOOL Synchronize(rdpContext *context) { return TRUE; } static BOOL DesktopResize(rdpContext *context) { my_rdp_context *c = (my_rdp_context *)context; char buf[64]; snprintf(buf, 63, "R:%dx%d", context->settings->DesktopWidth, context->settings->DesktopHeight); c->my_internals->core->api_msgs->send_text_msg( buf, c->my_internals->task_info); return TRUE; } static BOOL BitmapUpdate(rdpContext *context, const BITMAP_UPDATE *bitmap) { my_rdp_context *c = (my_rdp_context *)context; int i; BITMAP_DATA *bmd; for (i = 0; i < (int)bitmap->number; i++) { bmd = &bitmap->rectangles[i]; wrdp_core_display_bmp bmp = {bmd->destLeft, bmd->destTop, bmd->width, bmd->height, bmd->destRight - bmd->destLeft + 1, bmd->destBottom - bmd->destTop + 1, bmd->bitsPerPixel, bmd->compressed, bmd->bitmapLength}; if (!bmd->compressed) { freerdp_image_flip(bmd->bitmapDataStream, bmd->bitmapDataStream, bmd->width, bmd->height, bmd->bitsPerPixel); } c->my_internals->core->api_paint->send_bitmap( &bmp, bmd->bitmapDataStream, c->my_internals->task_info); } return TRUE; } static BOOL Palette(rdpContext *c, const PALETTE_UPDATE *p) { return TRUE; } static BOOL PlaySound(rdpContext *c, const PLAY_SOUND_UPDATE *s) { return TRUE; } static BOOL RefreshRect(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) { return TRUE; } static BOOL SuppressOutput(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) { return TRUE; } /*static BOOL SurfaceCommand(rdpContext* c, wStream* s) { return TRUE; }*/ static BOOL SurfaceBits(rdpContext *c, const SURFACE_BITS_COMMAND *sbc) { return TRUE; } /*static BOOL SurfaceFrameMarker(rdpContext* c, SURFACE_FRAME_MARKER* sfm) { return TRUE; } */ void RegisterUpdate(freerdp *rdp) { rdpUpdate *u = rdp->context->update; u->BeginPaint = BeginPaint; u->EndPaint = EndPaint; u->SetBounds = SetBounds; u->Synchronize = Synchronize; u->DesktopResize = DesktopResize; u->BitmapUpdate = BitmapUpdate; u->Palette = Palette; u->PlaySound = PlaySound; u->SurfaceBits = SurfaceBits; u->RefreshRect = RefreshRect; u->SuppressOutput = SuppressOutput; } static BOOL DstBlt(rdpContext *c, const DSTBLT_ORDER *_do) { return TRUE; } static BOOL PatBlt(rdpContext *ctx, PATBLT_ORDER *po) { my_rdp_context *c = (my_rdp_context *)ctx; uint32_t rop3 = gdi_rop3_code(po->bRop); if (GDI_BS_SOLID == po->brush.style) { wrdp_core_display_patblt_order patblt = {po->nLeftRect, po->nTopRect, po->nWidth, po->nHeight, FreeRDPConvertColor(po->foreColor, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL), //XXX rop3}; c->my_internals->core->api_paint->send_pat_blt( &patblt, c->my_internals->task_info); } return TRUE; } static BOOL ScrBlt(rdpContext *ctx, const SCRBLT_ORDER *sbo) { my_rdp_context *c = (my_rdp_context *)ctx; uint32_t rop3 = gdi_rop3_code(sbo->bRop); wrdp_core_display_scr_blt scr_blt = {rop3, sbo->nLeftRect, sbo->nTopRect, sbo->nWidth, sbo->nHeight, sbo->nXSrc, sbo->nYSrc}; c->my_internals->core->api_paint->send_scr_blt( &scr_blt, c->my_internals->task_info); return TRUE; } static BOOL OpaqueRect(rdpContext *context, const OPAQUE_RECT_ORDER *oro) { my_rdp_context *c = (my_rdp_context *)context; wrdp_core_display_opaque_rect_order oro2; oro2.color = oro->color; oro2.nHeight = oro->nHeight; oro2.nLeftRect = oro->nLeftRect; oro2.nTopRect = oro->nTopRect; oro2.nWidth = oro->nWidth; oro2.color = FreeRDPConvertColor( oro2.color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); c->my_internals->core->api_paint->send_opaque_rect_order( &oro2, c->my_internals->task_info); return TRUE; } static BOOL DrawNineGrid(rdpContext *c, const DRAW_NINE_GRID_ORDER *dngo) { return TRUE; } static BOOL MultiDstBlt(rdpContext *c, const MULTI_DSTBLT_ORDER *mdo) { return TRUE; } static BOOL MultiPatBlt(rdpContext *c, const MULTI_PATBLT_ORDER *mpo) { return TRUE; } static BOOL MultiScrBlt(rdpContext *c, const MULTI_SCRBLT_ORDER *mso) { return TRUE; } static BOOL MultiOpaqueRect(rdpContext *context, const MULTI_OPAQUE_RECT_ORDER *moro) { my_rdp_context *c = (my_rdp_context *)context; wrdp_core_display_m_opaque_rect mrect; mrect.color = FreeRDPConvertColor( moro->color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); mrect.num_rect = moro->numRectangles; mrect.rects = (wrdp_core_display_delta_rect *)(moro->rectangles); c->my_internals->core->api_paint->send_multi_opaque_rect( &mrect, c->my_internals->task_info); return TRUE; } static BOOL MultiDrawNineGrid(rdpContext *c, const MULTI_DRAW_NINE_GRID_ORDER *mdngo) { return TRUE; } static BOOL LineTo(rdpContext *c, const LINE_TO_ORDER *lto) { return TRUE; } static BOOL Polyline(rdpContext *c, const POLYLINE_ORDER *po) { return TRUE; } static BOOL MemBlt(rdpContext *c, MEMBLT_ORDER *mo) { return TRUE; } static BOOL Mem3Blt(rdpContext *c, MEM3BLT_ORDER *mo) { return TRUE; } static BOOL SaveBitmap(rdpContext *c, const SAVE_BITMAP_ORDER *sbo) { return TRUE; } static BOOL GlyphIndex(rdpContext *c, GLYPH_INDEX_ORDER *gio) { return TRUE; } static BOOL FastIndex(rdpContext *c, const FAST_INDEX_ORDER *fio) { return TRUE; } static BOOL FastGlyph(rdpContext *c, const FAST_GLYPH_ORDER *fgo) { return TRUE; } static BOOL PolygonSC(rdpContext *c, const POLYGON_SC_ORDER *pso) { return TRUE; } static BOOL PolygonCB(rdpContext *c, POLYGON_CB_ORDER *pco) { return TRUE; } static BOOL EllipseSC(rdpContext *c, const ELLIPSE_SC_ORDER *eso) { return TRUE; } static BOOL EllipseCB(rdpContext *c, const ELLIPSE_CB_ORDER *eco) { return TRUE; } void RegisterPrimary(freerdp *rdp) { rdpPrimaryUpdate *p = rdp->context->update->primary; p->DstBlt = DstBlt; p->PatBlt = PatBlt; p->ScrBlt = ScrBlt; p->OpaqueRect = OpaqueRect; p->DrawNineGrid = DrawNineGrid; p->MultiDstBlt = MultiDstBlt; p->MultiPatBlt = MultiPatBlt; p->MultiScrBlt = MultiScrBlt; p->MultiOpaqueRect = MultiOpaqueRect; p->MultiDrawNineGrid = MultiDrawNineGrid; p->LineTo = LineTo; p->Polyline = Polyline; p->MemBlt = MemBlt; p->Mem3Blt = Mem3Blt; p->SaveBitmap = SaveBitmap; p->GlyphIndex = GlyphIndex; p->FastIndex = FastIndex; p->FastGlyph = FastGlyph; p->PolygonSC = PolygonSC; p->PolygonCB = PolygonCB; p->EllipseSC = EllipseSC; p->EllipseCB = EllipseCB; }