Browse Source

Add pango drawing support, and silence some warnings.

v1.x
c-spencer 13 years ago
parent
commit
35d491a284
  1. 0
      binding.gyp
  2. 2
      examples/text.js
  3. 5
      src/Canvas.h
  4. 163
      src/CanvasRenderingContext2d.cc
  5. 20
      src/CanvasRenderingContext2d.h
  6. 12
      src/Image.cc

0
binding.gyp

2
examples/text.js

@ -38,4 +38,4 @@ var out = fs.createWriteStream(__dirname + '/text.png')
stream.on('data', function(chunk){
out.write(chunk);
});
});

5
src/Canvas.h

@ -12,7 +12,12 @@
#include <node.h>
#include <node_object_wrap.h>
#include <node_version.h>
#if HAVE_PANGO
#include <pango/pangocairo.h>
#else
#include <cairo/cairo.h>
#endif
using namespace v8;
using namespace node;

163
src/CanvasRenderingContext2d.cc

@ -45,6 +45,20 @@ enum {
, TEXT_BASELINE_HANGING
};
#if HAVE_PANGO
/*
* State helper function
*/
void state_assign_fontFamily(canvas_state_t *state, const char *str) {
free(state->fontFamily);
state->fontFamily = (char *) malloc(strlen(str) + 1);
strcpy(state->fontFamily, str);
}
#endif
/*
* Initialize Context2d.
*/
@ -121,6 +135,9 @@ Context2d::Initialize(Handle<Object> target) {
Context2d::Context2d(Canvas *canvas) {
_canvas = canvas;
_context = cairo_create(canvas->surface());
#if HAVE_PANGO
_layout = pango_cairo_create_layout(_context);
#endif
cairo_set_line_width(_context, 1);
state = states[stateno = 0] = (canvas_state_t *) malloc(sizeof(canvas_state_t));
state->shadowBlur = 0;
@ -129,7 +146,7 @@ Context2d::Context2d(Canvas *canvas) {
state->textAlignment = -1;
state->fillPattern = state->strokePattern = NULL;
state->fillGradient = state->strokeGradient = NULL;
state->textBaseline = NULL;
state->textBaseline = TEXT_BASELINE_ALPHABETIC;
rgba_t transparent = { 0,0,0,1 };
rgba_t transparent_black = { 0,0,0,0 };
state->fill = transparent;
@ -137,6 +154,14 @@ Context2d::Context2d(Canvas *canvas) {
state->shadow = transparent_black;
state->patternQuality = CAIRO_FILTER_GOOD;
state->textDrawingMode = TEXT_DRAW_PATHS;
#if HAVE_PANGO
state->fontWeight = PANGO_WEIGHT_NORMAL;
state->fontStyle = PANGO_STYLE_NORMAL;
state->fontSize = 10;
state->fontFamily = NULL;
state_assign_fontFamily(state, "sans serif");
setFontFromState();
#endif
}
/*
@ -177,6 +202,10 @@ Context2d::saveState() {
if (stateno == CANVAS_MAX_STATES) return;
states[++stateno] = (canvas_state_t *) malloc(sizeof(canvas_state_t));
memcpy(states[stateno], state, sizeof(canvas_state_t));
#if HAVE_PANGO
states[stateno]->fontFamily = (char *) malloc(strlen(state->fontFamily) + 1);
strcpy(states[stateno]->fontFamily, state->fontFamily);
#endif
state = states[stateno];
}
@ -188,9 +217,15 @@ void
Context2d::restoreState() {
if (0 == stateno) return;
// Olaf (2011-02-21): Free old state data
#if HAVE_PANGO
free(states[stateno]->fontFamily);
#endif
free(states[stateno]);
states[stateno] = NULL;
state = states[--stateno];
#if HAVE_PANGO
setFontFromState();
#endif
}
/*
@ -936,6 +971,8 @@ Context2d::GetTextDrawingMode(Local<String> prop, const AccessorInfo &info) {
mode = "path";
} else if (context->state->textDrawingMode == TEXT_DRAW_GLYPHS) {
mode = "glyph";
} else {
mode = "unknown";
}
return scope.Close(String::NewSymbol(mode));
}
@ -1517,8 +1554,62 @@ Context2d::StrokeText(const Arguments &args) {
* Set text path for the given string at (x, y).
*/
// Define simple macro substitution so we can use these lazily within setTextPath
#define GET_EXTENTS() pango_layout_get_pixel_extents(_layout, &ink_rect, &logical_rect)
#define GET_METRICS() metrics = pango_context_get_metrics( \
pango_layout_get_context(_layout), \
pango_layout_get_font_description(_layout), \
pango_context_get_language(pango_layout_get_context(_layout)))
void
Context2d::setTextPath(const char *str, double x, double y) {
#if HAVE_PANGO
PangoRectangle ink_rect, logical_rect;
PangoFontMetrics *metrics = NULL;
pango_layout_set_text(_layout, str, -1);
pango_cairo_update_layout(_context, _layout);
switch (state->textAlignment) {
// center
case 0:
GET_EXTENTS();
x -= logical_rect.width / 2;
break;
// right
case 1:
GET_EXTENTS();
x -= logical_rect.width;
break;
}
switch (state->textBaseline) {
case TEXT_BASELINE_ALPHABETIC:
GET_METRICS();
y -= pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
break;
case TEXT_BASELINE_MIDDLE:
GET_METRICS();
y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics))/(2.0 * PANGO_SCALE);
break;
case TEXT_BASELINE_BOTTOM:
GET_METRICS();
y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE;
break;
}
if (metrics) pango_font_metrics_unref(metrics);
cairo_move_to(_context, x, y);
if (state->textDrawingMode == TEXT_DRAW_PATHS) {
pango_cairo_layout_path(_context, _layout);
} else if (state->textDrawingMode == TEXT_DRAW_GLYPHS) {
pango_cairo_show_layout(_context, _layout);
}
#else
cairo_text_extents_t te;
cairo_font_extents_t fe;
@ -1567,6 +1658,8 @@ Context2d::setTextPath(const char *str, double x, double y) {
} else if (state->textDrawingMode == TEXT_DRAW_GLYPHS) {
cairo_show_text(_context, str);
}
#endif
}
/*
@ -1636,8 +1729,49 @@ Context2d::SetFont(const Arguments &args) {
double size = args[2]->NumberValue();
String::AsciiValue unit(args[3]);
String::AsciiValue family(args[4]);
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
#if HAVE_PANGO
if (strlen(*family) > 0) state_assign_fontFamily(context->state, *family);
if (size > 0) context->state->fontSize = size;
if (strlen(*style) > 0) {
if (0 == strcmp("italic", *style)) {
context->state->fontStyle = PANGO_STYLE_ITALIC;
} else if (0 == strcmp("oblique", *style)) {
context->state->fontStyle = PANGO_STYLE_OBLIQUE;
}
}
if (strlen(*weight) > 0) {
if (0 == strcmp("bold", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("200", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_ULTRALIGHT;
} else if (0 == strcmp("300", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_LIGHT;
} else if (0 == strcmp("400", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_NORMAL;
} else if (0 == strcmp("500", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_MEDIUM;
} else if (0 == strcmp("600", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_SEMIBOLD;
} else if (0 == strcmp("700", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_BOLD;
} else if (0 == strcmp("800", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_ULTRABOLD;
} else if (0 == strcmp("900", *weight)) {
context->state->fontWeight = PANGO_WEIGHT_HEAVY;
}
}
context->setFontFromState();
#else
cairo_t *ctx = context->context();
// Size
@ -1658,10 +1792,33 @@ Context2d::SetFont(const Arguments &args) {
}
cairo_select_font_face(ctx, *family, s, w);
#endif
return Undefined();
}
#if HAVE_PANGO
/*
* Sets PangoLayout options from the current font state
*/
void
Context2d::setFontFromState() {
PangoFontDescription *fd = pango_font_description_new();
pango_font_description_set_family(fd, state->fontFamily);
pango_font_description_set_absolute_size(fd, state->fontSize * PANGO_SCALE);
pango_font_description_set_style(fd, state->fontStyle);
pango_font_description_set_weight(fd, state->fontWeight);
pango_layout_set_font_description(_layout, fd);
pango_font_description_free(fd);
}
#endif
/*
* Return the given text extents.
* TODO: Support for:

20
src/CanvasRenderingContext2d.h

@ -40,8 +40,20 @@ typedef struct {
double shadowOffsetX;
double shadowOffsetY;
canvas_draw_mode_t textDrawingMode;
#if HAVE_PANGO
PangoWeight fontWeight;
PangoStyle fontStyle;
double fontSize;
char *fontFamily;
#endif
} canvas_state_t;
#if HAVE_PANGO
void state_assign_fontFamily(canvas_state_t *state, const char *str);
#endif
class Context2d: public node::ObjectWrap {
public:
short stateno;
@ -134,11 +146,19 @@ class Context2d: public node::ObjectWrap {
void save();
void restore();
#if HAVE_PANGO
void setFontFromState();
inline PangoLayout *layout(){ return _layout; }
#endif
private:
~Context2d();
Canvas *_canvas;
cairo_t *_context;
cairo_path_t *_path;
#if HAVE_PANGO
PangoLayout *_layout;
#endif
};
#endif

12
src/Image.cc

@ -881,17 +881,21 @@ Image::loadJPEG(FILE *stream) {
buf = (uint8_t *) malloc(len);
if (!buf) return CAIRO_STATUS_NO_MEMORY;
fread(buf, len, 1, stream);
fclose(stream);
if ((DATA_IMAGE | DATA_MIME) == data_mode) {
if (fread(buf, len, 1, stream) != len) {
status = CAIRO_STATUS_READ_ERROR;
} else if ((DATA_IMAGE | DATA_MIME) == data_mode) {
status = loadJPEGFromBuffer(buf, len);
if (!status) status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
} else if (DATA_MIME == data_mode) {
status = decodeJPEGBufferIntoMimeSurface(buf, len);
} else {
status = CAIRO_STATUS_READ_ERROR;
}
fclose(stream);
free(buf);
#else
status = CAIRO_STATUS_READ_ERROR;
#endif
}

Loading…
Cancel
Save