Update of /cvsroot/freevo/freevo/lib/pyimlib2
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31637/pyimlib2
Added Files:
AUTHORS Imlib2.py Makefile README TODO display.c display.h
font.c font.h image.c image.h imlib2.c pyimlib2.spec
rawformats.c rawformats.h setup.py
Log Message:
pyimlib2, an imlib2 wrapper for python
--- NEW FILE: rawformats.h ---
unsigned int get_raw_bytes_size(char *format);
unsigned char *get_raw_bytes(char *format, unsigned char *dstbuf);
unsigned char *convert_raw_rgba_bytes(char *from_format, char *to_format,
unsigned char *from_buf, unsigned char *to_buf,
int w, int h);
void init_rgb2yuv_tables();
--- NEW FILE: display.h ---
#include <X11/Xlib.h>
#define Display_PyObject_Check(v) ((v)->ob_type == &Display_PyObject_Type)
typedef struct {
PyObject_HEAD
Display *display;
Window window;
Visual *visual;
Colormap cmap;
int depth;
} Display_PyObject;
extern PyTypeObject Display_PyObject_Type;
void Display_PyObject__dealloc(Display_PyObject *);
PyObject *Display_PyObject__getattr(Display_PyObject *, char *);
PyObject *display_new(int w, int h);
--- NEW FILE: image.h ---
#define Image_PyObject_Check(v) ((v)->ob_type == &Image_PyObject_Type)
extern key_t _imlib2_shm_key;
extern int _imlib2_shm_id;
extern char *_imlib2_shm_buf;
extern key_t _imlib2_sem_key;
extern int _imlib2_sem_id;
typedef struct {
PyObject_HEAD
Imlib_Image *image;
} Image_PyObject;
extern PyTypeObject Image_PyObject_Type;
void Image_PyObject__dealloc(Image_PyObject *);
PyObject *Image_PyObject__getattr(Image_PyObject *, char *);
--- NEW FILE: TODO ---
- Wrap the rest of the API
- Do a test suite
--- NEW FILE: display.c ---
#include <Python.h>
#include "display.h"
#include <Imlib2.h>
#include "image.h"
PyTypeObject Display_PyObject_Type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"_Imlib2.Display", /*tp_name*/
sizeof(Display_PyObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Display_PyObject__dealloc, /* tp_dealloc */
0, /*tp_print*/
(getattrfunc)Display_PyObject__getattr, /* tp_getattr */
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Imlib2 Display Object" /* tp_doc */
};
void Display_PyObject__dealloc(Display_PyObject *self)
{
PyMem_DEL(self);
}
void display_set_context(Display_PyObject *self)
{
imlib_context_set_display(self->display);
imlib_context_set_visual(self->visual);
imlib_context_set_colormap(self->cmap);
imlib_context_set_drawable(self->window);
}
PyObject *Display_PyObject__render(PyObject *self, PyObject *args)
{
Image_PyObject *img;
int dst_x = 0, dst_y = 0, src_x = 0, src_y = 0,
w = -1, h = -1, img_w, img_h, dither = 1, blend = 0;
if (!PyArg_ParseTuple(args, "O!|(ii)(ii)(ii)ii", &Image_PyObject_Type, &img,
&dst_x, &dst_y, &src_x, &src_y, &w, &h, &dither, &blend))
return NULL;
imlib_context_set_image(((Image_PyObject *)img)->image);
img_w = imlib_image_get_width();
img_h = imlib_image_get_height();
if (w == -1) w = img_w;
if (h == -1) h = img_h;
display_set_context((Display_PyObject *)self);
imlib_context_set_dither(dither);
imlib_context_set_blend(blend);
if (src_x == 0 && src_y == 0 && w == img_w && h == img_h)
imlib_render_image_on_drawable(dst_x, dst_y);
else
imlib_render_image_part_on_drawable_at_size(src_x, src_y, w, h, dst_x,
dst_y, w, h);
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef Display_PyObject_methods[] = {
{ "render", Display_PyObject__render, METH_VARARGS },
{ NULL, NULL }
};
PyObject *Display_PyObject__getattr(Display_PyObject *self, char *name)
{
return Py_FindMethod(Display_PyObject_methods, (PyObject *)self, name);
}
PyObject *display_new(int w, int h)
{
Display_PyObject *o;
int screen;
XEvent ev;
o = PyObject_NEW(Display_PyObject, &Display_PyObject_Type);
o->display = XOpenDisplay(NULL);
screen = DefaultScreen(o->display);
o->visual = DefaultVisual(o->display, screen);
o->depth = DefaultDepth(o->display, screen);
o->cmap = DefaultColormap(o->display, screen);
o->window = XCreateSimpleWindow(o->display,
DefaultRootWindow(o->display),
0, 0, w, h, 0, 0, 0);
XSelectInput(o->display, o->window, ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | ExposureMask);
XMapWindow(o->display, o->window);
do {
XNextEvent(o->display, &ev);
} while (XPending(o->display));
return (PyObject *)o;
}
// vim: ts=4
--- NEW FILE: rawformats.c ---
#define X_DISPLAY_MISSING
#include <Imlib2.h>
#include <string.h>
/* RGB to YUV conversion tables pulled from speedy.c in tvtime. Here
* are the copyright notices on that file:
*
* Copyright (c) 2002, 2003 Billy Biggs <[EMAIL PROTECTED]>.
* Copyright (C) 2001 Matthew J. Marjanovic <[EMAIL PROTECTED]>
*
* Lookup tables is not that much faster than doing the (integer)
* arithmetic in the inner loop (7% by my calculations), but it is
* presumably more correct and hey, 7% isn't that bad. :)
*/
#define rgb2y(R,G,B) ( (Y_R[ r ] + Y_G[ g ] + Y_B[ b ]) >> FP_BITS )
#define rgb2u(R,G,B) ( (U_R[ r ] + U_G[ g ] + U_B[ b ]) >> FP_BITS )
#define rgb2v(R,G,B) ( (V_R[ r ] + V_G[ g ] + V_B[ b ]) >> FP_BITS )
#define FP_BITS 18
static int Y_R[256], Y_G[256], Y_B[256],
U_R[256], U_G[256], U_B[256],
V_R[256], V_G[256], V_B[256];
static int
myround(double n)
{
if (n >= 0) return (int)(n + 0.5);
else return (int)(n - 0.5);
}
void
init_rgb2yuv_tables()
{
int i;
for (i = 0; i < 256; i++) {
Y_R[i] = myround(0.299 * (double)i * 219.0 / 255.0 *
(double)(1<<FP_BITS));
Y_G[i] = myround(0.587 * (double)i * 219.0 / 255.0 *
(double)(1<<FP_BITS));
Y_B[i] = myround((0.114 * (double)i * 219.0 / 255.0 *
(double)(1<<FP_BITS)) + (double)(1<<(FP_BITS-1)) + (16.0 * (double)(1<<FP_BITS)));
U_R[i] = myround(-0.168736 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS));
U_G[i] = myround(-0.331264 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS));
U_B[i] = myround((0.500 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS)) + (double)(1<<(FP_BITS-1)) + (128.0 * (double)(1<<FP_BITS)));
V_R[i] = myround(0.500 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS));
V_G[i] = myround(-0.418688 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS));
V_B[i] = myround((-0.081312 * (double)i * 224.0 / 255.0 *
(double)(1<<FP_BITS)) + (double)(1<<(FP_BITS-1)) + (128.0 * (double)(1<<FP_BITS)));
}
}
// End of code ripped off from speedy.c :)
///////////////////////////////////////////////////////////////////////
unsigned int
get_raw_bytes_size(char *format)
{
unsigned int w = imlib_image_get_width(),
h = imlib_image_get_height();
if (!strcmp(format, "YV12A"))
return w * h * 2 + ((w * h / 4) * 2);
else if (!strcmp(format, "YV12"))
return w * h + ((w * h / 4) * 2);
else
// assume combination of RGBA or RGB
return w * h * strlen(format);
}
unsigned char *
convert_raw_rgba_bytes(char *from_format, char *to_format,
unsigned char *from_buf, unsigned char *to_buf,
int w, int h)
{
int from_bpp, to_bpp, i;
unsigned char fr, fb, fg, fa, tr, tb, tg, ta, *from_ptr, *to_ptr;
from_bpp = strlen(from_format);
to_bpp = strlen(to_format);
if (to_buf == 0)
to_buf = (unsigned char *)malloc(w*h*to_bpp);
#define LOOP_START \
for (from_ptr = from_buf, to_ptr = to_buf; from_ptr < from_buf + w*h*from_bpp;
from_ptr += from_bpp)
// FIXME: pointless code duplication follows.
/* Hard code the common cases of BGRA -> RGB/A. This is pretty much
* as fast as memcpy. I don't think it gets much faster without
* MMX.
*/
if (!strcmp(from_format, "BGRA") && !strcmp(to_format, "RGB")) {
LOOP_START {
*(to_ptr++) = *(from_ptr + 2); *(to_ptr++) = *(from_ptr + 1);
*(to_ptr++) = *(from_ptr + 0);
}
return to_buf;
}
if (!strcmp(from_format, "BGRA") && !strcmp(to_format, "RGBA")) {
LOOP_START {
*(to_ptr++) = *(from_ptr + 2); *(to_ptr++) = *(from_ptr + 1);
*(to_ptr++) = *(from_ptr + 0); *(to_ptr++) = *(from_ptr + 3);
}
return to_buf;
}
for (i = 0; i < to_bpp; i ++) {
if (to_format[i] == 'R') tr = i;
else if (to_format[i] == 'G') tg = i;
else if (to_format[i] == 'B') tb = i;
else if (to_format[i] == 'A') ta = i;
}
for (i = 0; i < from_bpp; i ++) {
if (from_format[i] == 'R') fr = i;
else if (from_format[i] == 'G') fg = i;
else if (from_format[i] == 'B') fb = i;
else if (from_format[i] == 'A') fa = i;
}
LOOP_START {
*(to_ptr + tr) = *(from_ptr + fr);
*(to_ptr + tg) = *(from_ptr + fg);
*(to_ptr + tb) = *(from_ptr + fb);
if (to_bpp == 4)
*(to_ptr + ta) = (from_bpp==4)?*(from_ptr + fa):255;
to_ptr += to_bpp;
}
return to_buf;
}
/* Convert the imlib data (which is BGRA) to planar YV12 plus an
8bpp alpha plane. This function is ugly but it should be
reasonably tight.
*/
unsigned char *_get_yv12_image(int w, int h, unsigned char *srcbuf,
unsigned char *dstbuf)
{
unsigned char *src_ptr, *y_ptr, *u_ptr, *v_ptr, *a_ptr;
unsigned char r, g, b, a, Ar, Ab, Ag;
int pos;
y_ptr = dstbuf; pos = w * h;
u_ptr = dstbuf + pos; pos += (w * h) / 4;
v_ptr = dstbuf + pos; pos += (w * h) / 4;
a_ptr = dstbuf + pos;
int stride = w*4;
// Calculate luma plane
for (src_ptr = srcbuf; src_ptr < srcbuf + stride*h;) {
b = *(src_ptr++); g = *(src_ptr++); r = *(src_ptr++);
*(y_ptr++) = rgb2y(r, g, b);
*(a_ptr++) = *(src_ptr++);
}
/* Calculate chroma planes.
* This macro nonsense is to handle the case where the image's
* width is odd. We don't want to put the if (image is odd)
* comparison in the inner loop since that's pointlessly slow.
* Given a block of 2x2 pixels:
* A B
* C D
* We calculate the chroma values by averaging the RGB values between
* A and C. This follows MPEG2 spec, and it may not be correct in
* all cases but it's a good default.
*/
#define YV12_LOOP(expr) { \
for (src_ptr = srcbuf + stride; src_ptr < srcbuf-4+stride*h; src_ptr+=4) { \
Ab = *(src_ptr-stride); Ag = *(src_ptr-stride+1); \
Ar = *(src_ptr-stride+2); \
b = *(src_ptr++); g = *(src_ptr++); r = *(src_ptr++); src_ptr++; \
r=(r+Ar)>>1; g = (g+Ag)>>1; b = (b+Ab)>>1; \
*(u_ptr++) = rgb2u(r, g, b); \
*(v_ptr++) = rgb2v(r, g, b); \
expr; \
} }
if (w & 1)
YV12_LOOP( if ( (src_ptr-srcbuf) % stride == 0)
src_ptr += stride+4 )
else
YV12_LOOP( if ( ((src_ptr+4)-srcbuf) % stride == 0) src_ptr += stride;
)
return dstbuf;
}
unsigned char *
get_raw_bytes(char *format, unsigned char *dstbuf)
{
unsigned int w, h, bufsize;
unsigned char *srcbuf;
w = imlib_image_get_width(),
h = imlib_image_get_height(),
bufsize = get_raw_bytes_size(format);
imlib_image_set_has_alpha(1);
srcbuf = (unsigned char *)imlib_image_get_data_for_reading_only();
if (dstbuf == 0)
dstbuf = (unsigned char *)malloc(bufsize);
if (!strcmp(format, "YV12A"))
_get_yv12_image(w, h, srcbuf, dstbuf);
else if (!strcmp(format, "BGRA")) {
memcpy(dstbuf, srcbuf, bufsize);
}
else
dstbuf = convert_raw_rgba_bytes("BGRA", format, srcbuf, dstbuf, w, h);
return dstbuf;
}
// vim: ts=4
--- NEW FILE: Makefile ---
all:
python setup.py build
# Copy the module to the cwd for easier testing
cp -f build/lib*/_Imlib2module.so .
install:
python setup.py install
clean:
python setup.py clean
rm -rf build _Imlib2module.so *.pyc
--- NEW FILE: image.c ---
/* Imlib2 module for python.
Written by Jason Tackaberry <[EMAIL PROTECTED]> and released under the LGPL.
This is a quick hack. It is not complete.
See Imlib2.py for usage details.
*/
#include <Python.h>
#define X_DISPLAY_MISSING
#include <Imlib2.h>
#include "image.h"
#include "rawformats.h"
#include "font.h"
#include <sys/mman.h>
#include <fcntl.h>
static int _shm_ctr = 0;
PyTypeObject Image_PyObject_Type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"_Imlib2.Image", /*tp_name*/
sizeof(Image_PyObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Image_PyObject__dealloc, /* tp_dealloc */
0, /*tp_print*/
(getattrfunc)Image_PyObject__getattr, /* tp_getattr */
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Imlib2 Image Object" /* tp_doc */
};
void Image_PyObject__dealloc(Image_PyObject *self)
{
imlib_context_set_image(self->image);
// fprintf(stderr, "IMLIB free image %s\n", imlib_image_get_filename());
imlib_free_image();
PyMem_DEL(self);
}
PyObject *Image_PyObject__clear(PyObject *self, PyObject *args)
{
int x, y, w, h, max_w, max_h, cur_y;
unsigned char *data;
if (!PyArg_ParseTuple(args, "iiii", &x, &y, &w, &h))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
data = (unsigned char *)imlib_image_get_data();
max_w = imlib_image_get_width();
max_h = imlib_image_get_height();
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x+w > max_w) w = max_w-x;
if (y+h > max_h) h = max_h-y;
// TODO: make this faster.
for (cur_y = y; cur_y < y + h; cur_y++)
memset(&data[cur_y*max_w*4+(x*4)], 0, 4*w);
imlib_image_put_back_data((DATA32 *)data);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *Image_PyObject__scale(PyObject *self, PyObject *args)
{
int dst_w, dst_h, src_w, src_h;
Imlib_Image *image;
Image_PyObject *o;
if (!PyArg_ParseTuple(args, "ii", &dst_w, &dst_h))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
src_w = imlib_image_get_width();
src_h = imlib_image_get_height();
image = imlib_create_cropped_scaled_image(0, 0, src_w, src_h, dst_w, dst_h);
if (!image) {
PyErr_Format(PyExc_RuntimeError, "Failed scaling image (%d, %d)",
dst_w, dst_h);
return NULL;
}
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *Image_PyObject__crop(PyObject *self, PyObject *args)
{
int x, y, w, h, src_w, src_h;
Imlib_Image *image;
Image_PyObject *o;
if (!PyArg_ParseTuple(args, "iiii", &x, &y, &w, &h))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
src_w = imlib_image_get_width();
src_h = imlib_image_get_height();
if (w > src_w) w = src_w;
if (h > src_h) h = src_h;
if (x > src_w) x = 0;
if (y > src_h) y = 0;
// Errmm, why imlib_Create_cropped_image terribly broken?
//image = imlib_create_cropped_image(x, y, w, h);
image = imlib_create_cropped_scaled_image(x, y, w, h, w, h);
if (!image) {
PyErr_Format(PyExc_RuntimeError, "Failed cropping image (%d, %d), (%d,
%d)", x, y, w, h);
return NULL;
}
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *Image_PyObject__rotate(PyObject *self, PyObject *args)
{
Imlib_Image *image;
Image_PyObject *o;
double angle;
if (!PyArg_ParseTuple(args, "d", &angle))
return NULL;
fprintf(stderr, "Rotate image by %f\n", angle);
imlib_context_set_image(((Image_PyObject *)self)->image);
image = imlib_create_rotated_image(angle);
if (!image) {
PyErr_Format(PyExc_RuntimeError, "Failed rotating image (%f) degrees",
angle);
return NULL;
}
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *Image_PyObject__clone(PyObject *self, PyObject *args)
{
int dst_w, dst_h, src_w, src_h;
Imlib_Image *image;
Image_PyObject *o;
imlib_context_set_image(((Image_PyObject *)self)->image);
image = imlib_clone_image();
if (!image) {
PyErr_Format(PyExc_RuntimeError, "Failed to clone image");
return NULL;
}
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *Image_PyObject__blend(PyObject *self, PyObject *args)
{
int dst_x, dst_y, src_alpha = 255, merge_alpha = 1,
src_w, src_h, src_x = 0, src_y = 0;
Image_PyObject *src;
Imlib_Image *src_img;
Imlib_Color_Modifier cmod;
if (!PyArg_ParseTuple(args, "O!(ii)(ii)(ii)ii", &Image_PyObject_Type, &src,
&dst_x, &dst_y, &src_x, &src_y, &src_w, &src_h, &src_alpha, &merge_alpha))
return NULL;
Py_INCREF(Py_None);
if (src_alpha == 0)
return Py_None;
src_img = ((Image_PyObject *)src)->image;
// This yields incorrect results. We would need to duplicate
// the image and apply the color modifer to it for this to
// have the correct result, but that's actually slower than the
// above code.
if (src_alpha < 255) {
unsigned char a[256], linear[256];
int i;
for (i = 0; i < 256; i++) {
int temp = (i * src_alpha) + 0x80;
a[i] = ((temp + (temp >> 8)) >> 8);
linear[i] = i;
}
cmod = imlib_create_color_modifier();
imlib_context_set_color_modifier(cmod);
imlib_set_color_modifier_tables(linear, linear, linear, a);
}
imlib_context_set_image(((Image_PyObject *)self)->image);
// imlib_context_set_operation(IMLIB_OP_SUBTRACT);
imlib_context_set_blend( src_alpha == 256 ? 0 : 1);
imlib_blend_image_onto_image(src_img, merge_alpha, src_x, src_y,
src_w, src_h, dst_x, dst_y,
src_w, src_h);
imlib_context_set_blend(1);
imlib_context_set_color_modifier(NULL);
return Py_None;
}
PyObject *Image_PyObject__draw_mask(PyObject *self, PyObject *args)
{
int dst_x, dst_y, mask_w, mask_h, dst_w, dst_h;
Image_PyObject *mask;
unsigned long xpos, ypos, dst_pos, mask_pos;
unsigned char *dst_data, *mask_data;
if (!PyArg_ParseTuple(args, "O!ii", &Image_PyObject_Type, &mask, &dst_x,
&dst_y))
return NULL;
Py_INCREF(Py_None);
imlib_context_set_image(((Image_PyObject *)mask)->image);
mask_w = imlib_image_get_width();
mask_h = imlib_image_get_height();
mask_data = (unsigned char *)imlib_image_get_data_for_reading_only();
imlib_context_set_image(((Image_PyObject *)self)->image);
dst_w = imlib_image_get_width();
dst_h = imlib_image_get_height();
dst_data = (unsigned char *)imlib_image_get_data();
// Use the passed image as a mask. Again, no obvious way to do this in
// Imlib natively.
for (ypos = 0; ypos < mask_h; ypos++) {
if (ypos + dst_y >= dst_h) break;
for (xpos = 0; xpos < mask_w; xpos++) {
if (xpos + dst_x >= dst_w) break;
mask_pos = (xpos << 2) + (ypos * mask_w << 2);
dst_pos = ((dst_x + xpos) << 2) + ((dst_y + ypos) * dst_w <<
2);
unsigned char *mask_chunk = &mask_data[mask_pos],
*dst_chunk = &dst_data[dst_pos],
// Any way to optimize this?
avg = (mask_chunk[0] + mask_chunk[1] +
mask_chunk[2]) / 3;
// Blend average (grayscale) pixel from the mask with the
alpha channel of the image
int temp = (dst_chunk[3] * avg) + 0x80;
dst_chunk[3] = ((temp + (temp >> 8)) >> 8);
dst_chunk[3] = dst_chunk[3] >> 1;
}
}
imlib_image_put_back_data((DATA32 *)dst_data);
return Py_None;
}
PyObject *Image_PyObject__draw_text(PyObject *self, PyObject *args)
{
int x, y, w, h, advance_w, advance_h, r, g, b, a, descent, inset;
char *text;
Font_PyObject *font;
if (!PyArg_ParseTuple(args, "O!iis(iiii)", &Font_PyObject_Type, &font, &x, &y,
&text, &r, &g, &b, &a))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
imlib_context_set_font(((Font_PyObject *)font)->font);
imlib_context_set_color(r, g, b, a);
//descent = imlib_get_maximum_font_descent();
//inset = imlib_get_text_inset(text);
// y = y + descent + inset + 2;
//printf("TEXT INSET: %d ASC %d DESC %d\n", imlib_get_text_inset(text),
imlib_get_font_ascent(), imlib_get_font_descent());
imlib_text_draw_with_return_metrics(x, y, text, &w, &h, &advance_w,
&advance_h);
return Py_BuildValue("(llll)", w, h, advance_w, advance_h);
}
PyObject *Image_PyObject__draw_rectangle(PyObject *self, PyObject *args)
{
int x, y, w, h, r, g, b, a, fill = 0;
if (!PyArg_ParseTuple(args, "iiii(iiii)|i", &x, &y, &w, &h, &r, &g, &b, &a,
&fill))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
imlib_image_set_has_alpha(1);
// imlib_context_set_operation(IMLIB_OP_SUBTRACT);
imlib_context_set_color(r, g, b, a);
if (!fill)
imlib_image_draw_rectangle(x, y, w, h);
else
imlib_image_fill_rectangle(x, y, w, h);
// imlib_image_set_has_alpha(1);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *Image_PyObject__draw_ellipse(PyObject *self, PyObject *args)
{
int xc, yc, ea, eb, r, g, b, a, fill = 0;
if (!PyArg_ParseTuple(args, "iiii(iiii)|i", &xc, &yc, &ea, &eb, &r, &g, &b,
&a, &fill))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
imlib_context_set_color(r, g, b, a);
imlib_context_set_anti_alias(1);
if (!fill)
imlib_image_draw_ellipse(xc, yc, ea, eb);
else
imlib_image_fill_ellipse(xc, yc, ea, eb);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *Image_PyObject__copy_rect(PyObject *self, PyObject *args)
{
int src_x, src_y, w, h, dst_x, dst_y;
if (!PyArg_ParseTuple(args, "(ii)(ii)(ii)", &src_x, &src_y, &w, &h, &dst_x,
&dst_y))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
imlib_image_copy_rect(src_x, src_y, w, h, dst_x, dst_y);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *Image_PyObject__move_to_shmem(PyObject *self, PyObject *args)
{
char *shmem_name, *buf, *format = "BGRA";
unsigned long size, w, h;
int fd;
if (!PyArg_ParseTuple(args, "|ss", &format, &shmem_name))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
size = get_raw_bytes_size(format);
fd = shm_open(shmem_name, O_RDWR|O_CREAT, 0777);
if (fd == -1) {
Py_INCREF(Py_None);
return Py_None;
}
ftruncate(fd, size);
buf = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (!buf)
return NULL;
get_raw_bytes(format, buf);
// FIXME: need some error checking here.
close(fd);
munmap(buf, size);
return Py_BuildValue("s", shmem_name);
}
PyObject *Image_PyObject__get_bytes(PyObject *self, PyObject *args)
{
unsigned char *format = "BGRA", *buffer;
unsigned long size, w, h;
PyObject *ret;
if (!PyArg_ParseTuple(args, "|s", &format))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
size = get_raw_bytes_size(format);
if (!strcmp(format, "BGRA")) {
unsigned char *bytes = imlib_image_get_data_for_reading_only();
// Imlib2 docs say we're not supposed to do this. It seems to
// work though. :)
ret = PyBuffer_FromReadWriteMemory(bytes, size);
return Py_BuildValue("(Ol)", ret, 0);
}
buffer = get_raw_bytes(format, NULL);
ret = PyBuffer_FromMemory(buffer, size);
// XXX: WARNING! This function creates a buffer from memory allocated
// by get_raw_bytes(). It's the responsibility of the wrapper to
// free this buffer by calling _Imlib2.free_buffer().
return Py_BuildValue("(Ol)", ret, buffer);
}
////////////////////////////////////////////////////////////////////////////
PyObject *Image_PyObject__save(PyObject *self, PyObject *args)
{
unsigned char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
imlib_context_set_image(((Image_PyObject *)self)->image);
// TODO: call imlib_save_image_with_error_return
imlib_save_image(filename);
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef Image_PyObject_methods[] = {
{ "draw_rectangle", Image_PyObject__draw_rectangle, METH_VARARGS },
{ "draw_ellipse", Image_PyObject__draw_ellipse, METH_VARARGS },
{ "draw_text", Image_PyObject__draw_text, METH_VARARGS },
{ "draw_mask", Image_PyObject__draw_mask, METH_VARARGS },
{ "clear", Image_PyObject__clear, METH_VARARGS },
{ "copy_rect", Image_PyObject__copy_rect, METH_VARARGS },
{ "clone", Image_PyObject__clone, METH_VARARGS },
{ "scale", Image_PyObject__scale, METH_VARARGS },
{ "crop", Image_PyObject__crop, METH_VARARGS },
{ "rotate", Image_PyObject__rotate, METH_VARARGS },
{ "blend", Image_PyObject__blend, METH_VARARGS },
{ "move_to_shmem", Image_PyObject__move_to_shmem, METH_VARARGS },
{ "get_bytes", Image_PyObject__get_bytes, METH_VARARGS },
{ "save", Image_PyObject__save, METH_VARARGS },
{ NULL, NULL }
};
PyObject *Image_PyObject__getattr(Image_PyObject *self, char *name)
{
imlib_context_set_image(self->image);
if (!strcmp(name, "width"))
return Py_BuildValue("l", imlib_image_get_width());
else if (!strcmp(name, "height"))
return Py_BuildValue("l", imlib_image_get_height());
else if (!strcmp(name, "format"))
return Py_BuildValue("s", imlib_image_format());
else if (!strcmp(name, "mode"))
return Py_BuildValue("s", "BGRA");
else if (!strcmp(name, "filename"))
return Py_BuildValue("s", imlib_image_get_filename());
return Py_FindMethod(Image_PyObject_methods, (PyObject *)self, name);
}
// vim: ts=4
--- NEW FILE: imlib2.c ---
/* Imlib2 module for python.
Written by Jason Tackaberry <[EMAIL PROTECTED]> and released under the LGPL.
This is a quick hack. It is not complete.
See Imlib2.py for usage details.
*/
#include <Python.h>
#define X_DISPLAY_MISSING
#include <Imlib2.h>
#include "image.h"
#include "rawformats.h"
#include "font.h"
#include "display.h"
PyObject *imlib2_create(PyObject *self, PyObject *args)
{
int w, h, num_bytes;
unsigned char *bytes = NULL, *from_format = "BGRA";
Imlib_Image *image;
Image_PyObject *o;
if (!PyArg_ParseTuple(args, "(ii)|s#s", &w, &h, &bytes, &num_bytes,
&from_format))
return NULL;
if (bytes) {
printf("Convert from format: %s\n", from_format);
if (!strcmp(from_format, "BGRA"))
image = imlib_create_image_using_copied_data(w, h, (void
*)bytes);
else {
bytes = convert_raw_rgba_bytes(from_format, "BGRA", bytes,
NULL, w, h);
image = imlib_create_image_using_copied_data(w, h, (void
*)bytes);
free(bytes);
}
imlib_context_set_image(image);
imlib_image_set_has_alpha(1);
} else {
image = imlib_create_image(w, h);
imlib_context_set_image(image);
imlib_image_set_has_alpha(1);
imlib_image_clear_color(0, 0, 0, 0);
}
if (!image) {
PyErr_Format(PyExc_RuntimeError, "Failed to create image");
return NULL;
}
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *imlib2_open(PyObject *self, PyObject *args)
{
char *file;
Imlib_Image *image;
Image_PyObject *o;
if (!PyArg_ParseTuple(args, "s", &file))
return NULL;
// FIXME: use imlib_load_image_with_error_return instead.
image = imlib_load_image(file);
if (!image) {
PyErr_Format(PyExc_IOError, "Could not open %s", file);
return NULL;
}
imlib_context_set_image(image);
imlib_image_set_has_alpha(1);
imlib_image_get_data_for_reading_only();
o = PyObject_NEW(Image_PyObject, &Image_PyObject_Type);
o->image = image;
return (PyObject *)o;
}
PyObject *imlib2_add_font_path(PyObject *self, PyObject *args)
{
char *font_path;
if (!PyArg_ParseTuple(args, "s", &font_path))
return NULL;
imlib_add_path_to_font_path(font_path);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *imlib2_load_font(PyObject *self, PyObject *args)
{
char *font_spec;
Imlib_Font *font;
Font_PyObject *o;
if (!PyArg_ParseTuple(args, "s", &font_spec))
return NULL;
font = imlib_load_font(font_spec);
if (!font) {
PyErr_Format(PyExc_IOError, "Couldn't open font: %s", font_spec);
return NULL;
}
o = PyObject_NEW(Font_PyObject, &Font_PyObject_Type);
o->font = font;
return (PyObject *)o;
}
PyObject *imlib2_new_display(PyObject *self, PyObject *args)
{
int w, h;
if (!PyArg_ParseTuple(args, "ii", &w, &h))
return NULL;
return display_new(w, h);
}
PyObject *imlib2__shm_unlink(PyObject *self, PyObject *args)
{
char *name;
if (!PyArg_ParseTuple(args, "s", &name))
return NULL;
shm_unlink(name);
Py_INCREF(Py_None);
return Py_None;
}
PyObject *imlib2__free_buffer(PyObject *self, PyObject *args)
{
void *buffer;
if (!PyArg_ParseTuple(args, "l", &buffer))
return NULL;
if (buffer)
free(buffer);
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef Imlib2_methods[] = {
{ "new_display", imlib2_new_display, METH_VARARGS },
{ "add_font_path", imlib2_add_font_path, METH_VARARGS },
{ "load_font", imlib2_load_font, METH_VARARGS },
{ "create", imlib2_create, METH_VARARGS },
{ "open", imlib2_open, METH_VARARGS },
{ "_shm_unlink", imlib2__shm_unlink, METH_VARARGS },
{ "_free_buffer", imlib2__free_buffer, METH_VARARGS },
{ NULL }
};
void init_Imlib2()
{
PyObject *m;
init_rgb2yuv_tables();
m = Py_InitModule("_Imlib2", Imlib2_methods);
Image_PyObject_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&Image_PyObject_Type) < 0)
return;
PyModule_AddObject(m, "Image", (PyObject *)&Image_PyObject_Type);
imlib_set_cache_size(1024*1024*4);
imlib_set_font_cache_size(1024*1024*2);
}
// vim: ts=4
--- NEW FILE: Imlib2.py ---
import _Imlib2, types, math, os
# Counter for auto-generated shared memory names.
_imlib2_shmem_ctr = 0
def utf8(text):
"""
Returns a UTF-8 string, converting from latin-1 if necessary. This does a
pretty good job Doing the Right Thing, converting only when it's really
latin-1. Of course it's not foolproof, but it works in practice.
"""
try:
text.decode("utf-8")
except:
try:
text = text.decode("latin-1").encode("utf-8")
except:
pass
return text
class Image:
def __init__(self, image_or_filename):
"""
Create a new Image object.
Arguments:
image_or_filename: Instantiate the image from another Image
instance, an instance of the backend's image
class or type, or a file name from which to load
the image.
"""
if type(image_or_filename) in types.StringTypes:
self._image = _Imlib2.open(image_or_filename)
elif isinstance(image_or_filename, Image):
self._image = image_or_filename.copy()._image
elif type(image_or_filename) == _Imlib2.Image:
self._image = image_or_filename
else:
raise ValueError, "Unsupported image type", type(image)
self.font = None
def __getattr__(self, attr):
"""
Supports these attributes:
size: tuple containing the width and height of the image
width: width of the image
height: height of the image
format: format of the image if loaded from file (e.g. PNG, JPEG)
filename: filename if loaded from file
"""
if attr == "width":
return self._image.width
elif attr == "height":
return self._image.height
elif attr == "size":
return (self._image.width, self._image.height)
elif attr == "format":
return self._image.format
elif attr == "mode":
return self._image.mode
elif attr == "filename":
return self._image.filename
if attr not in self.__dict__:
raise AttributeError, attr
return self.__dict__[attr]
# Functions for pickling.
def __getstate__(self):
return (self.size, str(self.get_bytes()))
def __setstate__(self, state):
self._image = _Imlib2.create(state[0], state[1])
def get_bytes(self, format = "BGRA"):
"""
Returns raw image data.
Arguments:
format: pixel format of the raw data to be returned. If 'format' is
not a supported format, ValueError is raised. Format
can be any combination of RGB or RGBA. YV12A is also
supported, which returns a planar 4:2:0 plus a 8bpp
alpha plane. (YV12A isn't a fourcc notation; I just
made it up.)
Returns: A buffer object containing the raw image data.
"""
bytes, buffer_addr = self._image.get_bytes(format)
if buffer_addr == 0:
return bytes
return BufferProxy(bytes, _Imlib2._free_buffer, buffer_addr)
def scale(self, (w, h)):
"""
Scale the image and return a new image.
Arguments:
w, h: the width and height of the new image. If either argument
is -1, that dimension is calculated from the other dimension
while retaining the original aspect ratio.
Returns: a new Image instance representing the scaled image.
"""
aspect = float(self.width) / float(self.height)
if w == -1:
w = round(h * aspect)
elif h == -1:
h = round(w / aspect)
return Image(self._image.scale(int(w), int(h)))
def crop(self, (x, y), (w, h)):
"""
Crop the image and return a new image.
Arguments:
x, y, w, h: represents the left, top, width, height region in
the image.
Returns: a new Image instance representing the cropped image.
"""
return Image(self._image.crop(int(x), int(y), int(w), int(h)))
def rotate(self, angle):
"""
Rotate the image and return a new image.
Arguments:
angle: the angle in degrees by which to rotate the image.
Return: a new Image instance representing the rotated image.
FIXME: imlib2's rotate works all wonky. Doesn't act how I expect.
"""
return Image(self._image.rotate(angle * math.pi / 180))
def scale_preserve_aspect(self, (w, h)):
"""
Scales the image while retaining the original aspect ratio and return
a new image.
Arguments:
w, h: the maximum size of the new image. The new image will be as
large as possible, using w, h as the upper limits, while
retaining the original aspect ratio.
Return: a new Image insatnce represented the scaled image.
"""
aspect = float(self.width) / float(self.height)
if aspect >= 1.0:
img = self._image.scale(w, h / aspect)
else:
img = self._image.scale(w * aspect, h)
return img
def thumbnail(self, (w, h)):
"""
Generates a thumbnail of the image, REPLACING the current image.
This simulates the PIL thumbnail function.
"""
if self.width < w and self.height < h:
return
self._image = self.scale_preserve_aspect( (w, h) )
def copy_rect(self, src_pos, size, dst_pos):
"""
Copies a region within the image.
Arguments:
src_pos: a tuple holding the x, y coordinates marking the top left
of the region to be moved.
size: a tuple holding the width and height of the region to move.
If either dimension is -1, then that dimension extends to
the far edge of the image.
dst_pos: a tuple holding the x, y coordinates within the image
where the region will be moved to.
Returns: None
"""
return self._image.copy_rect(src_pos, size, dst_pos)
def blend(self, src, dst_pos = (0, 0), src_pos = (0, 0),
src_size = (-1, -1), alpha = 255, merge_alpha = True):
"""
Blends one image onto another.
Arguments:
src: the image being blended onto 'self'
dst_pos: a tuple holding the x, y coordinates where the source
image will be blended onto the destination image.
src_pos: a tuple holding the x, y coordinates within the source
image where blending will start.
src_size: a tuple holding the width and height of the source
image to be blended. A value of -1 for either one
indicates the full dimension of the source image.
alpha: the "layer" alpha that is applied to all pixels of the
image. If an individual pixel has an alpha of 128 and
this value is 128, the resulting pixel will have an
alpha of 64 before it is blended to the destination
image. 0 is fully transparent and 255 is fully opaque,
and 256 is a special value that means alpha blending is
disabled.
merge_alpha: if True, the alpha channel is also blended. If False,
the destination image's alpha channel is untouched and
the RGB values are compensated
Returns: None.
"""
if src_size[0] == -1:
src_size = src.width, src_size[1]
if src_size[1] == -1:
src_size = src_size[0], src.height
return self._image.blend(src._image, dst_pos, src_pos, src_size,
int(alpha), merge_alpha)
def clear(self, (x, y) = (0, 0), (w, h) = (-1, -1)):
"""
Clears the image at the specified rectangle, resetting all pixels in
that rectangle to fully transparent.
Arguments:
x, y: left and top coordinates of the rectangle to be cleared.
Default is the top left corner.
w, h: width and height of the rectangle to be cleared. If either
value is -1 then the image is cleared to the far edge.
Returns: None
"""
x = max(0, min(self.width, x))
y = max(0, min(self.height, y))
if w == -1: w = self.width - x
if h == -1: h = self.height - y
w = min(w, self.width-x)
h = min(h, self.height-y)
self._image.clear(x, y, w, h)
def draw_mask(self, maskimg, (x, y)):
"""
Applies the luma channel of maskimg to the alpha channel of the
the current image.
Arguments:
maskimg: the image from which to read the luma channel
x, y: the top left coordinates within the current image where the
alpha channel will be modified. The mask is drawn to the
full width/height of maskimg.
Returns: None
"""
return self._image.draw_mask(maskimg._image, int(x), int(y))
def copy(self):
"""
Creates a copy of the current image.
Returns: a new Image instance with a copy of the current image.
"""
return Image(self._image.clone())
def set_font(self, font):
"""
Sets the font context to font_or_font_name. Subsequent calls to
draw_text() will be rendered using this font.
Arguments:
font_or_fontname: either a Font object, or a string containing the
font's name and size. This string is in the
form "Fontname/Size" such as "Arial/16"
Returns: a Font instance represent the specified font. It
'font_or_fontname' is already a Font instance, it is simply
returned back to the caller.
"""
if type(font) in types.StringTypes:
self.font = Font(font)
else:
self.font = font
return self.font
def get_font(self):
"""
Gets the current Font context.
Returns: A Font instance as created by set_font() or None if no font
context is defined.
"""
return self.font
def draw_text(self, (x, y), text, color = None, font_or_fontname = None):
"""
Draws text on the image.
Arguments:
x, y: the left/top coordinates within the current image
where the text will be rendered.
text: a string holding the text to be rendered.
color: a 3- or 4-tuple holding the red, green, blue, and
alpha values of the color in which to render the
font. If color is a 3-tuple, the implied alpha
is 255. If color is None, the color of the font
context, as specified by set_font(), is used.
font_or_fontname: either a Font object, or a string containing the
font's name and size. This string is in the
form "Fontname/Size" such as "Arial/16". If this
parameter is none, the font context is used, as
specified by set_font().
Returns: a 4-tuple representing the width, height, horizontal advance,
and vertical advance of the rendered text.
"""
if not font_or_fontname:
font = self.font
elif type(font_or_fontname) in types.StringTypes:
font = Font(font)
else:
font = font_or_fontname
if not color:
color = font.color
if len(color) == 3:
color = color + (255,)
return self._image.draw_text(font._font, int(x), int(y),
utf8(text), color)
def draw_rectangle(self, (x, y), (w, h), color, fill = True):
"""
Draws a rectangle on the image.
Arguments:
x, y: the top left corner of the rectangle.
w, h: the width and height of the rectangle.
color: a 3- or 4-tuple holding the red, green, blue, and alpha
values of the color in which to draw the rectangle. If
color is a 3-tuple, the implied alpha is 255.
fill: whether the rectangle should be filled or not. The default
is true.
Returns: None
"""
if len(color) == 3:
color = color + (255,)
return self._image.draw_rectangle(int(x), int(y), int(w), int(h),
color, fill)
def draw_ellipse(self, (xc, yc), (a, b), color, fill = True):
"""
Draws an ellipse on the image.
Arguments:
xc, yc: the x, y coordinates of the center of the ellipse.
a, b: the horizontal and veritcal amplitude of the ellipse.
color: a 3- or 4-tuple holding the red, green, blue, and alpha
values of the color in which to draw the ellipse. If
color is a 3-tuple, the implied alpha is 255.
fill: whether the ellipse should be filled or not. The default
is true.
Returns: None
"""
if len(color) == 3:
color = color + (255,)
return self._image.draw_ellipse(int(xc), int(yc), int(a), int(b),
color, fill)
def move_to_shmem(self, format = "BGRA", id = None):
"""
Creates a POSIX shared memory object and copy the image's raw data.
Arguments:
format: the format of the raw data to copy to shared memory. If
the specified format is not supported, raises ValueError.
id: the name of the shared memory object (as passed to
shm_open(3)). If id is None, a suitable unique id is
generated.
Returns: the id of the shared memory object.
"""
if not id:
global _imlib2_shmem_ctr
id = "pyimlib2-image-%d-%d" % (os.getpid(), _imlib2_shmem_ctr)
_imlib2_shmem_ctr += 1
return self._image.move_to_shmem(format, id)
def save(self, filename, format = None):
"""
Saves the image to a file.
Arguments:
format: the format of the written file (jpg, png, etc.). If format
is None, the format is gotten from the filename extension.
Returns: None.
"""
return self._image.save(filename)
class Display:
def __init__(self, (w, h), dither = True, blend = False):
self._display = _Imlib2.new_display(w, h)
self.size = (w, h)
self.width = w
self.height = h
self.blend = blend
self.dither = dither
def render(self, image, dst_pos = (0, 0), src_pos = (0, 0),
src_size = (-1, -1), dither = None, blend = None):
if blend == None:
blend = self.blend
if dither == None:
dither = self.dither
if not isinstance(image, Image):
raise ValueError, image
return self._display.render(image._image, dst_pos, src_pos, src_size,
dither, blend)
class Font:
def __init__(self, fontdesc, color=(255,255,255,255)):
"""
Create a new Font object.
Arguments:
fontdesc: the description of the font, in the form "Fontname/Size".
Only TrueType fonts are supported, and the .ttf file must
exist in a registered font path. Font paths can be
registered by calling Imlib2.add_font_path().
color: a 3- or 4-tuple holding the red, green, blue, and alpha
values of the color in which to render text with this
font context. If color is a 3-tuple, the implied alpha
is 255. If color is not specified, the default is fully
opaque white.
"""
self._font = _Imlib2.load_font(fontdesc)
sep = fontdesc.index("/")
self.fontname = fontdesc[:sep]
self.size = fontdesc[sep + 1:]
self.set_color(color)
def get_text_size(self, text):
"""
Get the font metrics for the specified text as rendered by the
current font.
Arguments:
text: the text for which to retrieve the metric.
Returns: a 4-tuple containing the width, height, horizontal advance,
and vertical advance of the text when rendered.
"""
return self._font.get_text_size(utf8(text))
def set_color(self, color):
"""
Sets the default color for text rendered with this font.
Arguments:
color: a 3- or 4-tuple holding the red, green, blue, and alpha
values of the color in which to render text with this
font context. If color is a 3-tuple, the implied alpha
is 255.
"""
if len(color) == 3:
self.color = color + (255,)
else:
self.color = color
def __getattr__(self, attr):
"""
These attributes are available:
ascent: the current font's ascent value in pixels.
descent: the current font's descent value in pixels.
max_descent: the current font's maximum descent extent.
max_ascent: the current font's maximum ascent extent.
"""
if attr == "ascent":
return self._font.ascent
elif attr == "descent":
return self._font.descent
elif attr == "max_ascent":
return self._font.max_ascent
elif attr == "max_descent":
return self._font.max_descent
if attr not in self.__dict__:
raise AttributeError, attr
return self.__dict__[attr]
class BufferProxy:
def __init__(self, buffer, callback = None, args = None):
self._buffer = buffer
self._callback = callback
self._cbargs = args
def __del__(self):
if self._callback:
self._callback(self._cbargs)
def __repr__(self):
return "<Proxy for %s>" % repr(self._buffer)
def __str__(self):
return str(self._buffer)
def __getitem__(self, index):
return self._buffer[index]
def __getslice__(self, start, end):
return self._buffer[start:end]
def __len__(self):
return len(self._buffer)
# Implement a crude image cache.
#
# Imlib2 maintains its own cache, but I don't think it caches
# the raw image data, since this ends up being faster. So think
# of this as L1 cache, and Imlib's cache as L2 cache. (Of course
# our L1 cache is much bigger :))
_image_cache = {
"images": {},
"order": [],
"size": 0,
"max-size": 16*1024 # 16 MB
}
def open(file):
"""
Create a new image object from the file 'file'.
"""
if file in _image_cache["images"]:
return _image_cache["images"][file].copy()
image = Image(file)
_image_cache["images"][file] = image
_image_cache["order"].insert(0, file)
_image_cache["size"] += image.width * image.height * 4 / 1024
while _image_cache["size"] > _image_cache["max-size"]:
file = _image_cache["order"].pop()
expired = _image_cache["images"][file]
del _image_cache["images"][file]
_image_cache["size"] -= expired.width * expired.height * 4 / 1024
return image
def new(size, bytes = None, from_format = "BGRA"):
"""
Generates a new Image of size 'size', which is a tuple holding the width
and height. If 'bytes' is specified, the image is initialized from the
raw BGRA data.
"""
if bytes:
if isinstance(bytes, BufferProxy):
bytes = bytes._buffer
if False in map(lambda x: x in "RGBA", list(from_format)):
raise ValueError, "Converting from unsupported format:",
from_format
if len(bytes) < size[0]*size[1]*len(from_format):
raise ValueError, "Not enough bytes for converted format:
expected %d, got %d" % (size[0]*size[1]*len(from_format), len(bytes))
return Image(_Imlib2.create(size, bytes, from_format))
else:
return Image(_Imlib2.create(size))
def add_font_path(path):
"""
Add the given path to the list of paths to scan when loading fonts.
"""
_Imlib2.add_font_path(path)
def load_font(font, size):
"""
Return a Font object from the given font specified in the form
"FontName/Size", such as "Arial/16"
"""
return Font(font + "/" + str(size))
def clean_stale_shmem():
"""
Clean stale shmem segments left over from processes that might have
crashed.
FIXME: this is actually broken, because it could end up deleting shmem
objects from processes that are still running.
Must test the existence of pid before deleting the shmem object.
It's not perfect, but it's better.
"""
import glob, os
for file in glob.glob("/dev/shm/pyimlib2*"):
path, file = os.path.split(file)
_Imlib2._shm_unlink(file)
clean_stale_shmem()
# vim: ts=4
--- NEW FILE: pyimlib2.spec ---
Summary: Python wrapper for Imlib2
Name: pyimlib2
Version: 0.0.6
Release: 1
License: LGPL
Group: System Environment/Libraries
Source: http://sault.org/mebox/downloads/pyimlib2/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/root-%{name}-%{version}
Prefix: %{_prefix}
BuildRequires: imlib2-devel >= 1.1.0
BuildRequires: python >= 2.2
%description
pyimlib2 is small python module partially wrapping Imlib2.
Imlib2 is an advanced replacement library for libraries like libXpm that
provides many more features with much greater flexibility and speed than
standard libraries, including font rasterization, rotation, RGBA space
rendering and blending, dynamic binary filters, scripting, and more.
%prep
%setup
%build
python setup.py build
%install
%{__rm} -rf %{buildroot}
python setup.py install --root=%{buildroot} --record=INSTALLED_FILES
cat >>INSTALLED_FILES << EOF
%doc README
EOF
%clean
%{__rm} -rf %{buildroot}
%files -f INSTALLED_FILES
%defattr(-, root, root, 0755)
%changelog
* Tue Feb 24 2004 Jason Tackaberry <[EMAIL PROTECTED]> - 0.0.1-1
- Initial package
--- NEW FILE: font.h ---
#define Font_PyObject_Check(v) ((v)->ob_type == &Font_PyObject_Type)
typedef struct {
PyObject_HEAD
Imlib_Font *font;
} Font_PyObject;
extern PyTypeObject Font_PyObject_Type;
void Font_PyObject__dealloc(Font_PyObject *);
PyObject *Font_PyObject__getattr(Font_PyObject *, char *);
--- NEW FILE: README ---
pyimlib2 - python module for Imlib2
=========================================
NOTE: This isn't meant to be a formal release but rather just a demo
of some of the stuff I'm working on. As a result, it may suck
unbelievably or be outright broken.
About
-----
Pyimlib2 is a python module for Imlib2. It is terribly incomplete. Its
main raison d'ĂȘtre is to provide image manipulation routines for MeBox.
PyImlib2 requires Imlib2 1.1.1 or later, which can be gotten here:
http://sourceforge.net/project/showfiles.php?group_id=2&package_id=11130
I've packaged RPMs for both Imlib2 and pyimlib2 for convenience. You can
download them, as well as the most recent version of pyimlib2 at:
http://sault.org/mebox/downloads.php
Only a small part of the Imlib2 API has been wrapped. If you want to help by
adding more methods, patches will be happily accepted.
Installation
------------
Assuming you already have Imlib2 installed, you can install pyimlib2 with:
make && make install
License
-------
This code is released under the GNU GPL version 2. Read it here:
http://www.gnu.org/licenses/gpl.html
- Jason Tackaberry <[EMAIL PROTECTED]>
--- NEW FILE: setup.py ---
from distutils.core import setup, Extension
setup(name="_Imlib2", version="0.0.7",
ext_modules=[
Extension("_Imlib2module",
["imlib2.c", "image.c", "font.c", "rawformats.c",
"display.c"],
library_dirs=["/usr/X11R6/lib"],
libraries=["Imlib2", "rt", "X11"])
],
py_modules=["Imlib2"]
)
# vim: ts=4
--- NEW FILE: AUTHORS ---
Main Developer:
Jason Tackaberry <[EMAIL PROTECTED]>
--- NEW FILE: font.c ---
/* Imlib2 module for python.
Written by Jason Tackaberry <[EMAIL PROTECTED]> and released under the LGPL.
This is a quick hack. It is not complete.
See Imlib2.py for usage details.
*/
#include <Python.h>
#define X_DISPLAY_MISSING
#include <Imlib2.h>
#include "font.h"
PyTypeObject Font_PyObject_Type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"_Imlib2.Font", /*tp_name*/
sizeof(Font_PyObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Font_PyObject__dealloc, /* tp_dealloc */
0, /*tp_print*/
(getattrfunc)Font_PyObject__getattr, /* tp_getattr */
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Imlib2 Font Object" /* tp_doc */
};
void Font_PyObject__dealloc(Font_PyObject *self)
{
imlib_context_set_font(self->font);
imlib_free_font();
PyMem_DEL(self);
}
PyObject *Font_PyObject__get_text_size(PyObject *self, PyObject *args)
{
char *text;
int w, h, advance_w, advance_h;
if (!PyArg_ParseTuple(args, "s", &text))
return NULL;
imlib_context_set_font( ((Font_PyObject *)self)->font );
imlib_get_text_size(text, &w, &h);
imlib_get_text_advance(text, &advance_w, &advance_h);
return Py_BuildValue("(llll)", w, h, advance_w, advance_h);
}
PyMethodDef Font_PyObject_methods[] = {
{ "get_text_size", Font_PyObject__get_text_size, METH_VARARGS },
{ NULL, NULL }
};
PyObject *Font_PyObject__getattr(Font_PyObject *self, char *name)
{
imlib_context_set_font(self->font);
if (!strcmp(name, "descent"))
return Py_BuildValue("l", imlib_get_font_descent());
else if (!strcmp(name, "ascent"))
return Py_BuildValue("l", imlib_get_font_ascent());
else if (!strcmp(name, "max_ascent"))
return Py_BuildValue("l", imlib_get_maximum_font_ascent());
else if (!strcmp(name, "max_descent"))
return Py_BuildValue("l", imlib_get_maximum_font_descent());
return Py_FindMethod(Font_PyObject_methods, (PyObject *)self, name);
}
// vim: ts=4
-------------------------------------------------------
SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media
100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33
Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift.
http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285
_______________________________________________
Freevo-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog