You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/lib/goffice/graph/gog-renderer-pixbuf.c

879 lines
24 KiB

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gog-renderer-pixbuf.c :
*
* Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <goffice/goffice-config.h>
#include <goffice/graph/gog-renderer-pixbuf.h>
#include <goffice/graph/gog-renderer-impl.h>
#include <goffice/graph/gog-style.h>
#include <goffice/graph/gog-view.h>
#include <goffice/utils/go-color.h>
#include <goffice/utils/go-font.h>
#include <goffice/utils/go-marker.h>
#include <goffice/utils/go-units.h>
#include <goffice/utils/go-math.h>
#include <libart_lgpl/art_render_gradient.h>
#include <libart_lgpl/art_render_svp.h>
#include <libart_lgpl/art_render_mask.h>
#include <pango/pangoft2.h>
#include <gsf/gsf-impl-utils.h>
//#include <src/application.h>
#include <application.h>
#include <math.h>
struct _GogRendererPixbuf {
GogRenderer base;
int w, h;
int x_offset, y_offset;
GdkPixbuf *buffer;
guchar *pixels; /* from pixbuf */
int rowstride;
PangoContext *pango_context;
PangoLayout *pango_layout;
};
typedef GogRendererClass GogRendererPixbufClass;
static GObjectClass *parent_klass;
static void
gog_renderer_pixbuf_finalize (GObject *obj)
{
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (obj);
if (prend->buffer != NULL) {
g_object_unref (prend->buffer);
prend->buffer = NULL;
}
if (prend->pango_layout != NULL) {
g_object_unref (prend->pango_layout);
prend->pango_layout = NULL;
}
if (prend->pango_context != NULL) {
#ifdef HAVE_PANGO_CONTEXT_GET_FONT_MAP
/* See http://bugzilla.gnome.org/show_bug.cgi?id=143542 */
go_pango_fc_font_map_cache_clear (PANGO_FC_FONT_MAP (pango_context_get_font_map (prend->pango_context)));
#endif
g_object_unref (prend->pango_context);
prend->pango_context = NULL;
}
(*parent_klass->finalize) (obj);
}
typedef struct
{
GdkPixbuf *buffer;
double x_offset;
double y_offset;
} ClipData;
static void
gog_renderer_pixbuf_clip_push (GogRenderer *rend, GogRendererClip *clip)
{
ClipData *clip_data;
GdkRectangle graph_rect, clip_rect, res_rect;
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
clip->data = g_new (ClipData, 1);
clip_data = (ClipData *) clip->data;
clip_data->x_offset = prend->x_offset;
clip_data->y_offset = prend->y_offset;
clip_data->buffer = NULL;
graph_rect.x = graph_rect.y = 0;
graph_rect.width = gdk_pixbuf_get_width (prend->buffer);
graph_rect.height = gdk_pixbuf_get_height (prend->buffer);
clip_rect.x = floor (clip->area.x - prend->x_offset + 0.5);
clip_rect.y = floor (clip->area.y - prend->y_offset + 0.5);
clip_rect.width = floor (clip->area.x - prend->x_offset + clip->area.w + 0.5) - clip_rect.x;
clip_rect.height = floor (clip->area.y -prend->y_offset + clip->area.h + 0.5) - clip_rect.y;
if (gdk_rectangle_intersect (&graph_rect, &clip_rect, &res_rect)) {
clip_data->buffer = prend->buffer;
prend->buffer = gdk_pixbuf_new_subpixbuf (clip_data->buffer,
res_rect.x, res_rect.y,
res_rect.width, res_rect.height);
prend->x_offset += res_rect.x;
prend->y_offset += res_rect.y;
}
if (prend->buffer == NULL)
g_warning ("Pixbuf renderer: invalid clipping region");
prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
prend->w = gdk_pixbuf_get_width (prend->buffer);
prend->h = gdk_pixbuf_get_height (prend->buffer);
prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
}
static void
gog_renderer_pixbuf_clip_pop (GogRenderer *rend, GogRendererClip *clip)
{
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
ClipData *clip_data = clip->data;
if (clip_data->buffer != NULL) {
if (prend->buffer != NULL)
g_object_unref (prend->buffer);
prend->buffer = clip_data->buffer;
}
prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
prend->w = gdk_pixbuf_get_width (prend->buffer);
prend->h = gdk_pixbuf_get_height (prend->buffer);
prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
prend->x_offset = clip_data->x_offset;
prend->y_offset = clip_data->y_offset;
g_free (clip->data);
clip->data = NULL;
}
static ArtSVP *
clip_path (GogViewAllocation const *bound)
{
ArtVpath path[6];
path[0].code = ART_MOVETO;
path[1].code = ART_LINETO;
path[2].code = ART_LINETO;
path[3].code = ART_LINETO;
path[4].code = ART_LINETO;
path[5].code = ART_END;
path[0].x = path[1].x = path[4].x = bound->x;
path[2].x = path[3].x = path[0].x + bound->w;
path[0].y = path[3].y = path[4].y = bound->y;
path[1].y = path[2].y = path[0].y + bound->h;
return art_svp_from_vpath ((ArtVpath *)path);
}
static double
line_size (GogRenderer const *rend, double width)
{
if (go_sub_epsilon (width) <= 0.) /* cheesy version of hairline */
return 1.;
width *= rend->scale;
if (width <= 1.)
return width;
return floor (width);
}
static double
gog_renderer_pixbuf_line_size (GogRenderer const *rend, double width)
{
double size = line_size (rend, width);
if (size < 1.0)
return ceil (size);
return size;
}
static void
gog_renderer_pixbuf_sharp_path (GogRenderer *rend, ArtVpath *path, double line_width)
{
ArtVpath *iter = path;
if (((int) (rint (line_width)) % 2 == 0) && line_width > 1.0)
while (iter->code != ART_END) {
iter->x = floor (iter->x + .5);
iter->y = floor (iter->y + .5);
iter++;
}
else
while (iter->code != ART_END) {
iter->x = floor (iter->x) + .5;
iter->y = floor (iter->y) + .5;
iter++;
}
}
static void
gog_renderer_pixbuf_draw_path (GogRenderer *rend, ArtVpath const *path,
GogViewAllocation const *bound)
{
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
GogStyle const *style = rend->cur_style;
double width = line_size (rend, style->line.width);
ArtSVP *svp;
ArtVpath *dashed_path;
switch (style->line.dash_type) {
case GO_LINE_NONE:
return;
case GO_LINE_SOLID:
svp = art_svp_vpath_stroke ((ArtVpath *) path,
ART_PATH_STROKE_JOIN_MITER,
ART_PATH_STROKE_CAP_BUTT,
width, 4, 0.5);
break;
default:
dashed_path = go_line_dash_vpath (path, rend->line_dash,
rend->cur_clip != NULL ? &rend->cur_clip->area : NULL);
if (dashed_path == NULL)
return;
svp = art_svp_vpath_stroke (dashed_path,
ART_PATH_STROKE_JOIN_MITER,
ART_PATH_STROKE_CAP_BUTT,
width, 4, 0.5);
g_free (dashed_path);
}
if (bound != NULL) {
ArtSVP *orig = svp;
ArtSVP *clip = clip_path (bound);
svp = art_svp_intersect (clip, orig);
art_svp_free (clip);
art_svp_free (orig);
}
go_color_render_svp (style->line.color, svp,
prend->x_offset,
prend->y_offset,
prend->w + prend->x_offset,
prend->h + prend->y_offset,
prend->pixels, prend->rowstride);
art_svp_free (svp);
}
static ArtRender *
gog_art_renderer_new (GogRendererPixbuf *prend)
{
return art_render_new (prend->x_offset,
prend->y_offset,
prend->w + prend->x_offset,
prend->h + prend->y_offset,
prend->pixels, prend->rowstride,
gdk_pixbuf_get_n_channels (prend->buffer) - 1,
8, ART_ALPHA_SEPARATE, NULL);
}
static void
gog_renderer_pixbuf_draw_polygon (GogRenderer *rend, ArtVpath const *path,
gboolean narrow, GogViewAllocation const *bound)
{
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
GogStyle const *style = rend->cur_style;
ArtVpath *dashed_path;
ArtRender *render;
ArtSVP *fill, *outline = NULL;
ArtDRect bbox;
ArtGradientLinear gradient;
ArtGradientStop stops[2];
GdkPixbuf *image;
gint i, j, imax, jmax, h, w;
double width = line_size (rend, style->outline.width);
if (!narrow) {
switch (style->outline.dash_type) {
case GO_LINE_NONE:
break;
case GO_LINE_SOLID:
outline = art_svp_vpath_stroke ((ArtVpath *) path,
ART_PATH_STROKE_JOIN_MITER,
ART_PATH_STROKE_CAP_BUTT,
width, 4, 0.5);
break;
default:
dashed_path = go_line_dash_vpath (path, rend->outline_dash,
rend->cur_clip != NULL ? &rend->cur_clip->area : NULL);
if (dashed_path != NULL) {
outline = art_svp_vpath_stroke (dashed_path,
ART_PATH_STROKE_JOIN_MITER,
ART_PATH_STROKE_CAP_BUTT,
width, 4, 0.5);
g_free (dashed_path);
}
}
if (bound != NULL && outline != NULL) {
ArtSVP *orig = outline;
ArtSVP *clip = clip_path (bound);
outline = art_svp_intersect (clip, orig);
art_svp_free (clip);
art_svp_free (orig);
}
}
if (style->fill.type != GOG_FILL_STYLE_NONE) {
fill = art_svp_from_vpath ((ArtVpath *)path);
if (bound != NULL) {
ArtSVP *orig = fill;
ArtSVP *clip = clip_path (bound);
fill = art_svp_intersect (clip, orig);
art_svp_free (clip);
art_svp_free (orig);
}
#if 0 /* art_svp_minus is not implemented */
if (outline != NULL) {
ArtSVP *tmp = art_svp_minus (fill, outline);
art_svp_free (fill);
fill = tmp;
}
#endif
switch (style->fill.type) {
case GOG_FILL_STYLE_PATTERN:
go_pattern_render_svp (&style->fill.pattern,
fill,
prend->x_offset,
prend->y_offset,
prend->w + prend->x_offset,
prend->h + prend->y_offset,
prend->pixels, prend->rowstride);
break;
case GOG_FILL_STYLE_GRADIENT: {
art_vpath_bbox_drect ((ArtVpath *)path, &bbox);
render = gog_art_renderer_new (prend);
art_render_svp (render, fill);
go_gradient_setup (&gradient,
style->fill.gradient.dir,
style->fill.pattern.back, style->fill.pattern.fore,
bbox.x0, bbox.y0, bbox.x1, bbox.y1,
stops);
art_render_gradient_linear (render,
&gradient, ART_FILTER_NEAREST);
art_render_invoke (render);
break;
}
case GOG_FILL_STYLE_IMAGE: {
GdkRectangle path_rect, clip_rect, dest_rect;
image = style->fill.image.image;
if (image == NULL)
break;
art_vpath_bbox_drect (path, &bbox);
path_rect.x = bbox.x0 - prend->x_offset;
path_rect.y = bbox.y0 - prend->y_offset;
path_rect.width = bbox.x1 - bbox.x0;
path_rect.height = bbox.y1 - bbox.y0;
clip_rect.x = clip_rect.y = 0;
clip_rect.width = prend->w;
clip_rect.height = prend->h;
if (gdk_rectangle_intersect (&path_rect, &clip_rect, &dest_rect)) {
switch (style->fill.image.type) {
case GOG_IMAGE_CENTERED:
w = ((bbox.x1 - bbox.x0) - gdk_pixbuf_get_width (image)) / 2.;
if (w < 0.) w = 0.;
h = ((bbox.y1 - bbox.y0) - gdk_pixbuf_get_height (image)) / 2.;
if (h < 0.) h = 0.;
gdk_pixbuf_composite (image, prend->buffer,
dest_rect.x + w, dest_rect.y + h,
gdk_pixbuf_get_width (image),
gdk_pixbuf_get_height (image),
path_rect.x + w, path_rect.y + h,
1., 1.,
GDK_INTERP_BILINEAR, 255);
break;
case GOG_IMAGE_STRETCHED:
gdk_pixbuf_composite (image, prend->buffer,
dest_rect.x, dest_rect.y,
dest_rect.width, dest_rect.height,
path_rect.x, path_rect.y,
path_rect.width /
(double)gdk_pixbuf_get_width (image),
path_rect.height /
(double)gdk_pixbuf_get_height (image),
GDK_INTERP_BILINEAR, 255);
break;
case GOG_IMAGE_WALLPAPER: {
GdkRectangle image_rect, copy_rect;
imax = path_rect.width /
(image_rect.width = gdk_pixbuf_get_width (image));
jmax = path_rect.height /
(image_rect.height = gdk_pixbuf_get_height (image));
image_rect.x = path_rect.x;
for (i = 0; i <= imax; i++)
{
image_rect.y = path_rect.y;
for (j = 0; j <= jmax; j++) {
if (gdk_rectangle_intersect (&image_rect,
&dest_rect,
&copy_rect))
gdk_pixbuf_copy_area (image,
copy_rect.x - image_rect.x,
copy_rect.y - image_rect.y,
copy_rect.width,
copy_rect.height,
prend->buffer,
copy_rect.x,
copy_rect.y);
image_rect.y += image_rect.height;
}
image_rect.x +=image_rect.width;
}
break;
}
}
}
break;
}
case GOG_FILL_STYLE_NONE:
break; /* impossible */
}
if (fill != NULL)
art_svp_free (fill);
}
if (outline != NULL) {
go_color_render_svp (style->outline.color, outline,
prend->x_offset,
prend->y_offset,
prend->w + prend->x_offset,
prend->h + prend->y_offset,
prend->pixels, prend->rowstride);
art_svp_free (outline);
}
}
static PangoContext *
gog_renderer_pixbuf_get_pango_context (GogRendererPixbuf *prend)
{
PangoFT2FontMap *font_map;
if (prend->pango_context != NULL)
return prend->pango_context;
font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
pango_ft2_font_map_set_resolution (font_map,
gnm_app_display_dpi_get (TRUE),
gnm_app_display_dpi_get (FALSE));
prend->pango_context = pango_ft2_font_map_create_context (font_map);
g_object_unref (font_map);
return prend->pango_context;
}
static PangoLayout *
gog_renderer_pixbuf_get_pango_layout (GogRendererPixbuf *prend)
{
PangoContext *context;
PangoAttribute *attr;
PangoAttrList *attrs = NULL;
PangoFontDescription const *fd = prend->base.cur_style->font.font->desc;
// +jsled: testing font display issues.
if (0)
{
char *pfdStr;
pfdStr = pango_font_description_to_string( fd );
printf( "pfd=[%s]\n", pfdStr );
g_free( pfdStr );
}
// -jsled
if (prend->pango_layout != NULL)
return prend->pango_layout;
context = gog_renderer_pixbuf_get_pango_context (prend);
prend->pango_layout = pango_layout_new (context);
pango_layout_set_font_description (prend->pango_layout, fd);
/*
* Manually scale the font size to compensate for
* Before the fix to http://bugzilla.gnome.org/show_bug.cgi?id=121543
* the scale would otherwise be ignored.
*/
attr = pango_attr_size_new (prend->base.zoom *
pango_font_description_get_size (fd));
attr->start_index = 0;
attr->end_index = -1;
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (prend->pango_layout, attrs);
pango_attr_list_unref (attrs);
return prend->pango_layout;
}
static void
gog_renderer_pixbuf_draw_text (GogRenderer *rend, char const *text,
GogViewAllocation const *pos, GtkAnchorType anchor,
GogViewAllocation *result)
{
FT_Bitmap ft_bitmap;
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
PangoRectangle rect;
PangoLayout *layout;
guint8 r, g, b, a, alpha, *dst, *src;
int h, w, i, x, y;
GogStyle const *style = rend->cur_style;
layout = gog_renderer_pixbuf_get_pango_layout ((GogRendererPixbuf *) rend);
pango_layout_set_text (layout, text, -1);
pango_layout_get_extents (layout, NULL, &rect);
rect.x = PANGO_PIXELS (rect.x);
rect.y = PANGO_PIXELS (rect.y);
x = (int)((pos->x - prend->x_offset) * PANGO_SCALE);
y = (int)((pos->y - prend->y_offset) * PANGO_SCALE);
switch (anchor) {
case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
x -= rect.width / 2;
break;
case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
x -= rect.width;
break;
default : break;
}
x = (x > 0) ? (x + PANGO_SCALE / 2) / PANGO_SCALE : 0;
w = (rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
/* Makes rendering inconsitent with gnome-print and svg renderer */
/* if (w > pos->w && pos->w >= 0)*/
/* w = pos->w;*/
if ((x + w) > prend->w)
w = prend->w - x;
switch (anchor) {
case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
y -= rect.height / 2;
break;
case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
y -= rect.height;
break;
default : break;
}
y = (y > 0) ? (y + PANGO_SCALE / 2) / PANGO_SCALE : 0;
h = (rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
/* Makes rendering inconsitent with gnome-print and svg renderer */
/* if (h > pos->h && pos->h >= 0)*/
/* h = pos->h;*/
if ((y + h) > prend->h)
h = prend->h - y;
if (result != NULL) {
result->x = x;
result->y = y;
result->w = w;
result->h = h;
}
if (w <= 0 || h <= 0)
return;
ft_bitmap.rows = h;
ft_bitmap.width = w;
ft_bitmap.pitch = (w+3) & ~3;
ft_bitmap.buffer = g_malloc0 (ft_bitmap.rows * ft_bitmap.pitch);
ft_bitmap.num_grays = 256;
ft_bitmap.pixel_mode = ft_pixel_mode_grays;
ft_bitmap.palette_mode = 0;
ft_bitmap.palette = NULL;
pango_ft2_render_layout (&ft_bitmap, layout, -rect.x, -rect.y);
r = UINT_RGBA_R (style->font.color);
g = UINT_RGBA_G (style->font.color);
b = UINT_RGBA_B (style->font.color);
a = UINT_RGBA_A (style->font.color);
/* do the compositing manually, ArtRender as used in librsvg is dog
* slow, and I do not feel like leaping through 20 different data
* structures to composite 1 byte images, onto rgba */
dst = prend->pixels;
dst += (y * prend->rowstride);
dst += (x + rect.x)* 4;
src = ft_bitmap.buffer;
while (h--) {
for (i = w; i-- > 0 ; dst += 4, src++) {
/* FIXME: Do the libart thing instead of divide by 255 */
alpha = (a * (*src)) / 255;
dst[0] = (dst[0] * (255 - alpha) + r * alpha) / 255;
dst[1] = (dst[1] * (255 - alpha) + g * alpha) / 255;
dst[2] = (dst[2] * (255 - alpha) + b * alpha) / 255;
dst[3] = (dst[3] * (255 - alpha) + a * alpha) / 255;
}
dst += prend->rowstride - w*4;
src += ft_bitmap.pitch - w;
}
g_free (ft_bitmap.buffer);
}
static void
gog_renderer_pixbuf_draw_marker (GogRenderer *rend, double x, double y)
{
GdkRectangle r1, r2, dest;
GogStyle const *style = rend->cur_style;
GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
GdkPixbuf const *marker_pixbuf = go_marker_get_pixbuf (style->marker.mark, rend->scale);
if (marker_pixbuf == NULL)
return;
r2.x = r2.y = 0;
r2.width = prend->w;
r2.height = prend->h;
r1.width = gdk_pixbuf_get_width (marker_pixbuf);
r1.height = gdk_pixbuf_get_height (marker_pixbuf);
r1.x = floor (floor (x + .5) - r1.width / 2.0 - prend->x_offset);
r1.y = floor (floor (y + .5) - r1.height / 2.0 - prend->y_offset);
if (gdk_rectangle_intersect (&r1, &r2, &dest))
gdk_pixbuf_composite (marker_pixbuf, prend->buffer,
dest.x, dest.y,
dest.width, dest.height,
r1.x, r1.y,
1.0, 1.0,
GDK_INTERP_NEAREST,
255);
}
static void
gog_renderer_pixbuf_measure_text (GogRenderer *rend,
char const *text, GogViewRequisition *size)
{
PangoRectangle logical;
PangoLayout *layout;
layout = gog_renderer_pixbuf_get_pango_layout ((GogRendererPixbuf *) rend);
pango_layout_set_text (layout, text, -1);
pango_layout_get_pixel_extents (layout, NULL, &logical);
size->w = logical.width;
size->h = logical.height;
}
static void
gog_renderer_pixbuf_pop_style (GogRenderer *rend)
{
GogRendererPixbuf *prend = (GogRendererPixbuf *) rend;
if (prend->pango_layout != NULL)
{
g_object_unref (prend->pango_layout);
prend->pango_layout = NULL;
}
}
static void
gog_renderer_pixbuf_push_style (GogRenderer *rend, GogStyle const *style)
{
GogRendererPixbuf *prend = (GogRendererPixbuf *) rend;
if (prend->pango_layout != NULL)
{
g_object_unref (prend->pango_layout);
prend->pango_layout = NULL;
}
}
static void
gog_renderer_pixbuf_class_init (GogRendererClass *rend_klass)
{
GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
parent_klass = g_type_class_peek_parent (rend_klass);
gobject_klass->finalize = gog_renderer_pixbuf_finalize;
rend_klass->push_style = gog_renderer_pixbuf_push_style;
rend_klass->pop_style = gog_renderer_pixbuf_pop_style;
rend_klass->clip_push = gog_renderer_pixbuf_clip_push;
rend_klass->clip_pop = gog_renderer_pixbuf_clip_pop;
rend_klass->sharp_path = gog_renderer_pixbuf_sharp_path;
rend_klass->draw_path = gog_renderer_pixbuf_draw_path;
rend_klass->draw_polygon = gog_renderer_pixbuf_draw_polygon;
rend_klass->draw_text = gog_renderer_pixbuf_draw_text;
rend_klass->draw_marker = gog_renderer_pixbuf_draw_marker;
rend_klass->measure_text = gog_renderer_pixbuf_measure_text;
rend_klass->line_size = gog_renderer_pixbuf_line_size;
}
static void
gog_renderer_pixbuf_init (GogRendererPixbuf *prend)
{
prend->buffer = NULL;
prend->w = prend->h = 1; /* just in case */
prend->x_offset = prend->y_offset = 0;
prend->pango_layout = NULL;
prend->pango_context = NULL;
}
GSF_CLASS (GogRendererPixbuf, gog_renderer_pixbuf,
gog_renderer_pixbuf_class_init, gog_renderer_pixbuf_init,
GOG_RENDERER_TYPE)
GdkPixbuf *
gog_renderer_pixbuf_get (GogRendererPixbuf *prend)
{
g_return_val_if_fail (prend != NULL, NULL);
return prend->buffer;
}
#if 0 /* An initial non-working attempt to use different dpi to render
different zooms */
/* fontmaps are reasonably expensive use a cache to share them */
static GHashTable *fontmap_cache = NULL; /* PangoFT2FontMap hashed by y_dpi */
static gboolean
cb_remove_entry (gpointer key, PangoFT2FontMap *value, PangoFT2FontMap *target)
{
return value == target;
}
static void
cb_map_is_gone (gpointer data, GObject *where_the_object_was)
{
g_warning ("fontmap %p is gone",where_the_object_was);
g_hash_table_foreach_steal (fontmap_cache,
(GHRFunc) cb_remove_entry, where_the_object_was);
}
static void
cb_weak_unref (GObject *fontmap)
{
g_object_weak_unref (fontmap, cb_map_is_gone, NULL);
}
static PangoFT2FontMap *
fontmap_from_cache (double x_dpi, double y_dpi)
{
PangoFT2FontMap *fontmap = NULL;
int key_dpi = floor (y_dpi + .5);
gpointer key = GUINT_TO_POINTER (key_dpi);
if (fontmap_cache != NULL)
fontmap = g_hash_table_lookup (fontmap_cache, key);
else
fontmap_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) cb_weak_unref);
if (fontmap == NULL) {
fontmap = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
pango_ft2_font_map_set_resolution (fontmap, x_dpi, y_dpi);
g_object_weak_ref (G_OBJECT (fontmap), cb_map_is_gone, NULL);
g_hash_table_insert (fontmap_cache, key, fontmap);
} else
g_object_ref (fontmap);
g_warning ("fontmap %d = %p", key_dpi, fontmap);
return fontmap;
}
#endif
/**
* gog_renderer_update :
* @prend :
* @w :
* @h :
*
* Returns TRUE if the size actually changed.
**/
gboolean
gog_renderer_pixbuf_update (GogRendererPixbuf *prend, int w, int h, double zoom)
{
gboolean redraw = TRUE;
GogView *view;
GogViewAllocation allocation;
g_return_val_if_fail (prend != NULL, FALSE);
g_return_val_if_fail (prend->base.view != NULL, FALSE);
view = prend->base.view;
allocation.x = allocation.y = 0.;
allocation.w = w;
allocation.h = h;
if (prend->w != w || prend->h != h) {
double dpi_x, dpi_y;
prend->w = w;
prend->h = h;
prend->base.scale_x = w / prend->base.logical_width_pts;
prend->base.scale_y = h / prend->base.logical_height_pts;
prend->base.scale = MIN (prend->base.scale_x, prend->base.scale_y);
prend->base.zoom = zoom;
dpi_x = gog_renderer_pt2r_x (&prend->base, GO_IN_TO_PT ((double)1.))
/ zoom;
dpi_y = gog_renderer_pt2r_y (&prend->base, GO_IN_TO_PT ((double)1.))
/ zoom;
if (prend->buffer != NULL) {
g_object_unref (prend->buffer);
prend->buffer = NULL;
}
if (prend->pango_layout != NULL) {
g_object_unref (prend->pango_layout);
prend->pango_layout = NULL;
}
if (prend->pango_context != NULL) {
g_object_unref (prend->pango_context);
prend->pango_context = NULL;
}
/* make sure we dont try to queue an update while updating */
prend->base.needs_update = TRUE;
/* scale just changed need to recalculate sizes */
gog_renderer_invalidate_size_requests (&prend->base);
gog_view_size_allocate (view, &allocation);
} else if (w != view->allocation.w || h != view->allocation.h)
gog_view_size_allocate (view, &allocation);
else
redraw = gog_view_update_sizes (view);
redraw |= prend->base.needs_update;
prend->base.needs_update = FALSE;
gog_debug (0, g_warning ("rend_pixbuf:update = %d", redraw););
if (redraw) {
if (prend->buffer == NULL) {
prend->buffer = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
prend->w, prend->h);
if (prend->buffer == NULL) {
g_warning ("Chart is too large");
return FALSE;
}
prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
}
gdk_pixbuf_fill (prend->buffer, 0);
gog_view_render (view, NULL);
}
return redraw;
}