spice/client/canvas.h
Izik Eidus 4a208b3644 libspice: add off screens support
Signed-off-by: Izik Eidus <ieidus@redhat.com>
2010-04-03 05:35:31 +03:00

453 lines
11 KiB
C++

/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_CANVAS
#define _H_CANVAS
#include "common.h"
#include "debug.h"
#include "region.h"
#include <spice/protocol.h>
#include "cache.hpp"
#include "shared_cache.hpp"
#include "canvas_base.h"
#include "canvas_utils.h"
#include "glz_decoded_image.h"
#include "glz_decoder.h"
enum CanvasType {
CANVAS_TYPE_INVALID,
CANVAS_TYPE_CAIRO,
CANVAS_TYPE_GL,
CANVAS_TYPE_GDI,
};
template <class T, int HASH_SIZE, class Base = EmptyBase>
class CHash : public Base {
public:
CHash()
{
memset(_hash, 0, sizeof(_hash));
}
~CHash()
{
}
void add(uint32_t id, T* data)
{
Item** item = &_hash[key(id)];
while (*item) {
PANIC_ON((*item)->id == id);
item = &(*item)->next;
}
*item = new Item(id, data);
}
bool is_present(uint32_t id)
{
Item* item = _hash[key(id)];
for (;;) {
if (!item) {
return false;
}
if (item->id != id) {
item = item->next;
continue;
}
return true;
}
}
T* get(uint32_t id)
{
Item* item = _hash[key(id)];
for (;;) {
PANIC_ON(!item);
if (item->id != id) {
item = item->next;
continue;
}
return item->data;
}
}
void remove(uint32_t id)
{
Item** item = &_hash[key(id)];
while (*item) {
if ((*item)->id == id) {
Item *rm_item = *item;
*item = rm_item->next;
delete rm_item;
return;
}
item = &(*item)->next;
}
THROW("id %lu, not found", id);
}
private:
inline uint32_t key(uint32_t id) {return id % HASH_SIZE;}
private:
class Item {
public:
Item(uint32_t in_id, T* data)
: id (in_id)
, next (NULL)
, data (data) {}
~Item()
{
}
uint64_t id;
Item* next;
T* data;
};
Item* _hash[HASH_SIZE];
};
class PixmapCacheTreat {
public:
static inline pixman_image_t *get(pixman_image_t *surf)
{
return pixman_image_ref(surf);
}
static inline void release(pixman_image_t *surf)
{
pixman_image_unref(surf);
}
static const char* name() { return "pixmap";}
};
class SpiceImageCacheBase;
typedef SharedCache<pixman_image_t, PixmapCacheTreat, 1024, SpiceImageCacheBase> PixmapCache;
class SpiceImageCacheBase {
public:
SpiceImageCache base;
static void op_put(SpiceImageCache *c, uint64_t id, pixman_image_t *surface)
{
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
cache->add(id, surface);
}
static pixman_image_t* op_get(SpiceImageCache *c, uint64_t id)
{
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
return cache->get(id);
}
SpiceImageCacheBase()
{
static SpiceImageCacheOps cache_ops = {
op_put,
op_get
};
base.ops = &cache_ops;
}
};
class SpiceImageSurfacesBase;
typedef CHash<SpiceCanvas, 1024, SpiceImageSurfacesBase> CSurfaces;
class SpiceImageSurfacesBase {
public:
SpiceImageSurfaces base;
static void op_put(SpiceImageSurfaces *c, uint32_t surface_id, SpiceCanvas *surface)
{
CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
cache->add(surface_id, surface);
}
static SpiceCanvas* op_get(SpiceImageSurfaces *s, uint32_t surface_id)
{
CSurfaces* cache = reinterpret_cast<CSurfaces*>(s);
return cache->get(surface_id);
}
static void op_del(SpiceImageSurfaces *c, uint32_t surface_id)
{
CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
cache->remove(surface_id);
}
SpiceImageSurfacesBase()
{
static SpiceImageSurfacesOps cache_ops = {
op_get
};
base.ops = &cache_ops;
}
};
class Canvas;
typedef CHash<Canvas, 1024, SpiceImageSurfacesBase> CCanvases;
class CachedPalette {
public:
CachedPalette(SpicePalette* palette)
: _refs(1)
{
int size = sizeof(SpicePalette) + palette->num_ents * sizeof(uint32_t);
CachedPalette **ptr = (CachedPalette **)new uint8_t[size + sizeof(CachedPalette *)];
*ptr = this;
_palette = (SpicePalette*)(ptr + 1);
memcpy(_palette, palette, size);
}
CachedPalette* ref()
{
_refs++;
return this;
}
void unref()
{
if (--_refs == 0) {
delete this;
}
}
static void unref(SpicePalette *pal)
{
CachedPalette **ptr = (CachedPalette **)pal;
(*(ptr - 1))->unref();
}
SpicePalette* palette() { return _palette;}
private:
~CachedPalette()
{
delete[] (uint8_t *)((CachedPalette **)_palette - 1);
}
private:
int _refs;
SpicePalette* _palette;
};
class PaletteCacheTreat {
public:
static inline CachedPalette* get(CachedPalette* palette)
{
return palette->ref();
}
static inline void release(CachedPalette* palette)
{
palette->unref();
}
static const char* name() { return "palette";}
};
class SpicePaletteCacheBase;
typedef Cache<CachedPalette, PaletteCacheTreat, 1024, SpicePaletteCacheBase> PaletteCache;
class SpicePaletteCacheBase {
public:
SpicePaletteCache base;
static void op_put(SpicePaletteCache *c, SpicePalette *palette)
{
PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
AutoRef<CachedPalette> cached_palette(new CachedPalette(palette));
cache->add(palette->unique, *cached_palette);
}
static SpicePalette* op_get(SpicePaletteCache *c, uint64_t id)
{
PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
return cache->get(id)->palette();
}
static void op_release (SpicePaletteCache *c,
SpicePalette *palette)
{
CachedPalette::unref(palette);
}
SpicePaletteCacheBase()
{
static SpicePaletteCacheOps cache_ops = {
op_put,
op_get,
op_release
};
base.ops = &cache_ops;
}
};
/* Lz decoder related classes */
class GlzDecodedSurface: public GlzDecodedImage {
public:
GlzDecodedSurface(uint64_t id, uint64_t win_head_id, uint8_t *data, int size,
int bytes_per_pixel, pixman_image_t *surface)
: GlzDecodedImage(id, win_head_id, data, size, bytes_per_pixel)
, _surface (surface)
{
pixman_image_ref(_surface);
}
virtual ~GlzDecodedSurface()
{
pixman_image_unref(_surface);
}
private:
pixman_image_t *_surface;
};
class GlzDecodeSurfaceHandler: public GlzDecodeHandler {
public:
virtual GlzDecodedImage *alloc_image(void *opaque_usr_info, uint64_t image_id,
uint64_t image_win_head_id, LzImageType type,
int width, int height, int gross_pixels,
int n_bytes_per_pixel, bool top_down)
{
pixman_image_t *surface = alloc_lz_image_surface((LzDecodeUsrData *)opaque_usr_info,
type, width, height, gross_pixels,
top_down);
uint8_t *data = (uint8_t *)pixman_image_get_data(surface);
if (!top_down) {
data = data - (gross_pixels / height) * n_bytes_per_pixel * (height - 1);
}
return (new GlzDecodedSurface(image_id, image_win_head_id, data,
gross_pixels, n_bytes_per_pixel, surface));
}
};
/* TODO: unite with the window debug callbacks? */
class GlzDecoderCanvasDebug: public GlzDecoderDebug {
public:
virtual void error(const std::string& str)
{
throw Exception(str);
}
virtual void warn(const std::string& str)
{
LOG_WARN("%s", str.c_str());
}
virtual void info(const std::string& str)
{
LOG_INFO("%s", str.c_str());
}
};
class Canvas {
public:
Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces);
virtual ~Canvas();
virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
const PixmapHeader* pixmap) = 0;
virtual void copy_pixels(const QRegion& region, RedDrawable& dc) = 0;
virtual void thread_touch() = 0;
void clear();
void draw_fill(SpiceMsgDisplayDrawFill& fill, int size);
void draw_text(SpiceMsgDisplayDrawText& text, int size);
void draw_opaque(SpiceMsgDisplayDrawOpaque& opaque, int size);
void draw_copy(SpiceMsgDisplayDrawCopy& copy, int size);
void draw_transparent(SpiceMsgDisplayDrawTransparent& transparent, int size);
void draw_alpha_blend(SpiceMsgDisplayDrawAlphaBlend& alpha_blend, int size);
void copy_bits(SpiceMsgDisplayCopyBits& copy_bits, int size);
void draw_blend(SpiceMsgDisplayDrawBlend& blend, int size);
void draw_blackness(SpiceMsgDisplayDrawBlackness& blackness, int size);
void draw_whiteness(SpiceMsgDisplayDrawWhiteness& whiteness, int size);
void draw_invers(SpiceMsgDisplayDrawInvers& invers, int size);
void draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size);
void draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size);
void put_image(
#ifdef WIN32
HDC dc,
#endif
const PixmapHeader& image,
const SpiceRect& dest, const QRegion* clip);
virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
virtual SpiceCanvas *get_internal_canvas() { return _canvas; }
protected:
virtual void touched_bbox(const SpiceRect *bbox) {};
PixmapCache& pixmap_cache() { return _pixmap_cache;}
PaletteCache& palette_cache() { return _palette_cache;}
CSurfaces& csurfaces() { return _csurfaces; }
GlzDecoder& glz_decoder() {return _glz_decoder;}
private:
void access_test(void* ptr, size_t size);
void localalize_ptr(SPICE_ADDRESS* data);
void localalize_image(SPICE_ADDRESS* in_bitmap);
void localalize_brush(SpiceBrush& brush);
void localalize_attr(SpiceLineAttr& attr);
void localalize_mask(SpiceQMask& mask);
void begin_draw(SpiceMsgDisplayBase& base, int size, size_t min_size);
protected:
SpiceCanvas* _canvas;
CSurfaces _surfaces;
private:
PixmapCache& _pixmap_cache;
PaletteCache& _palette_cache;
GlzDecodeSurfaceHandler _glz_handler;
GlzDecoderCanvasDebug _glz_debug;
GlzDecoder _glz_decoder;
CSurfaces& _csurfaces;
unsigned long _base;
unsigned long _max;
};
#endif