]> granicus.if.org Git - postgis/commitdiff
Added some primitives for CHIP management + rendering for points and lines
authorSandro Santilli <strk@keybit.net>
Tue, 30 May 2006 17:19:48 +0000 (17:19 +0000)
committerSandro Santilli <strk@keybit.net>
Tue, 30 May 2006 17:19:48 +0000 (17:19 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@2372 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/lwgeom_chip.c

index cfffeecf9faeb701d45e29b8e03dcbfcb69e714a..a3edef0b3444f6ca5bda67e615451277d3835cf2 100644 (file)
@@ -9,10 +9,15 @@
 #include "access/itup.h"
 #include "fmgr.h"
 #include "utils/elog.h"
+#include "funcapi.h"
 
 #include "lwgeom_pg.h"
 #include "liblwgeom.h"
 
+
+/* Define this to debug CHIP ops */
+/* #define DEBUG_CHIP 1 */
+
 /* Internal funcs */
 void swap_char(char *a,char *b);
 void flip_endian_double(char *d);
@@ -138,7 +143,9 @@ Datum CHIP_out(PG_FUNCTION_ARGS)
        int size_result;
        int t;
 
-/*printf("chip_out called\n"); */
+#if DEBUG_CHIP
+       lwnotice("CHIP_out: %dx%d chip, %d in size", chip->width, chip->height, chip->size);
+#endif
 
        size_result = (chip->size ) *2 +1; /* +1 for null char */
        result = palloc (size_result);
@@ -304,3 +311,905 @@ void              flip_endian_int32(char          *i)
        swap_char (i+3,i);
        swap_char (i+2,i+1);
 }
+
+/********************************************************************
+ *
+ * NEW chip code
+ * 
+ ********************************************************************/
+
+#define CHIP_BOUNDS_CHECKS 1
+
+/* Return the size of values for pixel of the given datatype */
+size_t chip_pixel_value_size(int datatype);
+
+/********************************************************************
+ *
+ * IO primitives
+ * 
+ ********************************************************************/
+
+#define PIXELOP_OVERWRITE 1
+#define PIXELOP_ADD 2
+#define PIXELOP_MULTIPLY 3
+
+char *pixelop_name[] = {
+       "Unknown",
+       "Overwrite",
+       "Add",
+       "Multiply"
+};
+
+const char*
+pixelOpName(int op)
+{
+       if ( op < 0 || op > 3 )
+       {
+               return "Invalid";
+       }
+       return pixelop_name[op];
+}
+
+
+typedef struct RGB_T {
+       uchar red;
+       uchar green;
+       uchar blue;
+} RGB;
+
+typedef unsigned short int UINT16;
+typedef float FLOAT32;
+
+typedef struct PIXEL_T {
+       int type; /* 1=float32, 5=int24, 6=int16 */
+       uchar val[4];
+} PIXEL;
+
+const char*
+pixelHEX(PIXEL* p)
+{
+       static char buf[256];
+       size_t ps = chip_pixel_value_size(p->type);
+       int i,j;
+       static const char* hex = "0123456789ABCDEF";
+
+       for (i=0, j=0; i<ps; ++i)
+       {
+               uchar val = p->val[i];
+               int upper = (val & 0xF0) >> 4;
+               int lower = val & 0x0F;
+
+               buf[j++] = hex[ upper ];
+               buf[j++] = hex[ lower ];
+       }
+       buf[j]=0;
+
+       return buf;
+}
+
+UINT16
+pixel_readUINT16(PIXEL *p)
+{
+       UINT16 i=0;
+       memcpy(&i, &(p->val), 2);
+       //i = p->val[0] | (p->val[1] << 8);
+       return i;
+}
+
+void
+pixel_writeUINT16(PIXEL *p, UINT16 i)
+{
+       //memcpy(&(p->val), &i, 2);
+       p->val[0] = i&0xFF;
+       p->val[1] = i>>8;
+       p->val[2] = 0;
+       p->val[3] = 0;
+}
+
+/*
+ * Write text representation of a PIXEL value into provided
+ * buffer.
+ */
+void
+pixel_writeval(PIXEL *p, char *buf, size_t maxlen)
+{
+       FLOAT32 f32=0.0;
+       UINT16 i16=0;
+
+       switch (p->type)
+       {
+               case 1: /* float32 */
+                       memcpy(&f32, p->val, 4);
+                       sprintf(buf, "%g", f32);
+                       break;
+               case 5: /* int24 (rgb) */
+                       buf[0] = '#';
+                       deparse_hex(p->val[0], &buf[1]);
+                       deparse_hex(p->val[1], &buf[3]);
+                       deparse_hex(p->val[2], &buf[5]);
+                       buf[7]='\0';
+                       break;
+               case 6: /* int16 */
+                       i16=pixel_readUINT16(p);
+                       snprintf(buf, maxlen, "%u", i16);
+                       break;
+               default:
+                       lwerror("Unsupported PIXEL value %d", p->type);
+       }
+}
+
+PIXEL
+pixel_readval(char *buf)
+{
+       FLOAT32 f32=0.0;
+       long int i16=0;
+       RGB rgb;
+       char *ptr;
+       PIXEL p;
+
+       /* RGB */
+       if ( buf[0] == '#' )
+       {
+               if ( strlen(buf) < 7 ) lwerror("RGB value too short");
+               p.type = 5; /* rgb */
+               ptr=buf+1;
+               rgb.red = parse_hex(ptr);
+               ptr+=2;
+               rgb.green = parse_hex(ptr);
+               ptr+=2;
+               rgb.blue = parse_hex(ptr);
+               memcpy(p.val, &rgb, 3);
+
+               return p;
+       }
+
+       /* float32 */
+       if ( strchr(buf, '.') )
+       {
+               f32 = strtod(buf, &ptr);
+               if ( ptr != buf+strlen(buf) )
+                       lwerror("Malformed float value");
+               p.type = 1; /* float32 */
+               memcpy(p.val, &f32, 4);
+
+               return p;
+       }
+
+       /* int16 */
+       i16 = strtol(buf, &ptr, 0);
+       if ( ptr != buf+strlen(buf) ) lwerror("Malformed integer value");
+       if ( i16 > 65535 ) lwerror("Integer too high for an int16");
+       p.type = 6; /* int16 */
+       memcpy(p.val, &i16, 2);
+       p.val[2]=0;
+       p.val[3]=0;
+       return p;
+}
+
+void
+pixel_add(PIXEL *where, PIXEL *what)
+{
+       if ( where->type != what->type )
+               lwerror("Can't add pixels of different types");
+
+       FLOAT32 f1=0.0, f2=0.0;
+       UINT16 i1, i2;
+       RGB rgb1, rgb2;
+       unsigned int red, green, blue;
+       unsigned int tmp;
+
+       switch (where->type)
+       {
+               case 1: /*float32*/
+                       memcpy(&f1, where->val, 4);
+                       memcpy(&f2, what->val, 4);
+                       f1 += f2;
+                       memcpy(where->val, &f1, 4);
+                       break;
+
+               case 5: /*int24*/
+                       memcpy(&rgb1, where->val, 3);
+                       memcpy(&rgb2, what->val, 3);
+                       red = rgb1.red + rgb2.red;
+                       green = rgb1.green + rgb2.green;
+                       blue = rgb1.blue + rgb2.blue;
+                       if ( red > 255 )
+                       {
+                               lwnotice("Red channel saturated by add operation");
+                               red = 255;
+                       }
+                       if ( green > 255 )
+                       {
+                               lwnotice("Green channel saturated by add operation");
+                               green = 255;
+                       }
+                       if ( blue > 255 )
+                       {
+                               lwnotice("Blue channel saturated by add operation");
+                               blue = 255;
+                       }
+                       rgb1.red = red;
+                       rgb1.green = green;
+                       rgb1.blue = blue;
+                       memcpy(where->val, &rgb1, 3);
+                       break;
+
+               case 6: /*int16*/
+                       i1 = pixel_readUINT16(where);
+                       i2 = pixel_readUINT16(what);
+                       tmp = (unsigned long)i1 + i2;
+                       if ( tmp > 65535 )
+                       {
+                               lwnotice("Pixel saturated by add operation (%u)", tmp);
+                               tmp = 65535;
+                       }
+                       i1 = tmp;
+                       pixel_writeUINT16(where, i1);
+                       break;
+
+               default:
+                       lwerror("pixel_add: unkown pixel type %d",
+                               where->type);
+       }
+
+}
+
+/*
+ * Return size of pixel values in bytes
+ */
+size_t
+chip_pixel_value_size(int datatype)
+{
+       switch(datatype)
+       {
+               case 1:
+               case 101:
+                       return 4;
+               case 5:
+               case 105:
+                       return 3;
+               case 6:
+               case 106:
+                       return 2;
+               default:
+                       lwerror("Unknown CHIP datatype: %d", datatype);
+                       return 0;
+       }
+}
+
+/*
+ * Returns offset of pixel at X,Y
+ */
+#define CHIP_XY_OFF(c, x, y) ((x)+((y)*(c)->width))
+size_t
+chip_xy_off(CHIP *c, size_t x, size_t y)
+{
+#ifdef CHIP_BOUNDS_CHECKS
+       if ( x < 0 || x >= c->width || y < 0 || y >= c->height )
+       {
+               lwerror("Coordinates ouf of range");
+               return 0;
+       }
+#endif
+       return x+(y*c->width);
+}
+
+/*
+ * Set pixel value.
+ *
+ * CHIP is assumed to be:
+ *     - uncompressed
+ *     - in machine's byte order
+ */
+void
+chip_setPixel(CHIP *c, int x, int y, PIXEL *p)
+{
+       void *where;
+       size_t ps;
+       size_t off;
+
+#if DEBUG_CHIP
+       lwnotice("chip_setPixel([CHIP %p], %d, %d) called", c, x, y);
+#endif
+       if ( c->datatype != p->type ) lwerror("Pixel datatype mismatch");
+
+       ps = chip_pixel_value_size(c->datatype);
+       off = chip_xy_off(c, x, y)*ps;
+       if ( off > c->size + sizeof(CHIP) )
+       {
+               lwerror("Pixel offset out of CHIP size bounds");
+       }
+
+       where = ((char*)&(c->data))+off;
+
+#if DEBUG_CHIP
+       lwnotice("Writing %d bytes (%s) at offset %d (%p)",
+               ps, pixelHEX(p), off, where);
+#endif
+
+       memcpy(where, &(p->val), ps);
+}
+
+/*
+ * Get pixel value.
+ *
+ * CHIP is assumed to be:
+ *     - uncompressed
+ *     - in machine's byte order
+ */
+PIXEL
+chip_getPixel(CHIP *c, int x, int y)
+{
+       PIXEL p;
+       size_t ps = chip_pixel_value_size(c->datatype);
+       void *where = ((char*)&(c->data))+chip_xy_off(c, x, y)*ps;
+       p.type = c->datatype;
+       memset(p.val, '\0', 4);
+       memcpy(p.val, where, ps);
+
+       return p;
+}
+
+
+/********************************************************************
+ *
+ * Drawing primitives
+ * 
+ ********************************************************************/
+
+void
+chip_draw_pixel(CHIP *chip, int x, int y, PIXEL *pixel, int op)
+{
+
+#if DEBUG_CHIP
+       lwnotice("chip_draw_pixel([CHIP %p], %d, %d, [PIXEL %p], %s) called",
+               (void*)chip, x, y, (void*)pixel, pixelOpName(op));
+#endif
+
+       if ( x < 0 || x >= chip->width || y < 0 || y >= chip->height )
+       {
+lwnotice("chip_draw_pixel called with out-of-range coordinates");
+return;
+       }
+
+       PIXEL p;
+
+       switch ( op )
+       {
+               case PIXELOP_OVERWRITE:
+                       chip_setPixel(chip, x, y, pixel);
+                       break;
+
+               case PIXELOP_ADD:
+                       p = chip_getPixel(chip, x, y);
+                       pixel_add(&p, pixel);
+                       chip_setPixel(chip, x, y, &p);
+                       break;
+
+               default:
+                       lwerror("Unsupported PIXELOP: %d", op);
+       }
+
+}
+
+/* 
+ * Bresenham Line Algorithm
+ *  http://www.edepot.com/linebresenham.html
+ */
+void
+chip_draw_segment(CHIP *chip,
+       int x1, int y1, int x2, int y2,
+       PIXEL *pixel, int op)
+{
+       int x, y;
+       int dx, dy;
+       int incx, incy;
+       int balance;
+
+
+       if (x2 >= x1)
+       {
+               dx = x2 - x1;
+               incx = 1;
+       }
+       else
+       {
+               dx = x1 - x2;
+               incx = -1;
+       }
+
+       if (y2 >= y1)
+       {
+               dy = y2 - y1;
+               incy = 1;
+       }
+       else
+       {
+               dy = y1 - y2;
+               incy = -1;
+       }
+
+       x = x1;
+       y = y1;
+
+       if (dx >= dy)
+       {
+               dy <<= 1;
+               balance = dy - dx;
+               dx <<= 1;
+
+               while (x != x2)
+               {
+                       chip_draw_pixel(chip, x, y, pixel, op);
+                       if (balance >= 0)
+                       {
+                               y += incy;
+                               balance -= dx;
+                       }
+                       balance += dy;
+                       x += incx;
+               } chip_draw_pixel(chip, x, y, pixel, op);
+       }
+       else
+       {
+               dx <<= 1;
+               balance = dx - dy;
+               dy <<= 1;
+
+               while (y != y2)
+               {
+                       chip_draw_pixel(chip, x, y, pixel, op);
+                       if (balance >= 0)
+                       {
+                               x += incx;
+                               balance -= dy;
+                       }
+                       balance += dx;
+                       y += incy;
+               } chip_draw_pixel(chip, x, y, pixel, op);
+       }
+}
+
+void
+chip_fill(CHIP *chip, PIXEL *pixel, int op)
+{
+       int x, y;
+
+#if DEBUG_CHIP
+       lwnotice("chip_fill called");
+#endif
+
+       for (x=0; x<chip->width; x++)
+       {
+               for (y=0; y<chip->height; y++)
+               {
+                       chip_draw_pixel(chip, x, y, pixel, op);
+               }
+       }
+}
+
+/********************************************************************
+ *
+ * CHIP constructors
+ * 
+ ********************************************************************/
+
+CHIP *
+pgchip_construct(BOX3D *bvol, int SRID, int width, int height,
+       int datatype, PIXEL *initvalue)
+{
+       size_t pixsize = chip_pixel_value_size(datatype);
+       size_t datasize = pixsize*width*height;
+       size_t size = sizeof(CHIP)-sizeof(void*)+datasize;
+       CHIP *chip = lwalloc(size);
+
+#if DEBUG_CHIP
+       lwnotice(" sizeof(CHIP):%d, pixelsize:%d, datasize:%d, total size:%d", sizeof(CHIP), pixsize, datasize, size);
+#endif
+
+       chip->size=size;
+       chip->endian_hint=1;
+       memcpy(&(chip->bvol), bvol, sizeof(BOX3D));
+       chip->SRID=SRID;
+       memset(chip->future, '\0', 4);
+       chip->factor=1.0;
+       chip->datatype=datatype;
+       chip->height=height;
+       chip->width=width;
+       chip->compression=0; // no compression
+       if ( ! initvalue ) {
+               memset(&(chip->data), '\0', datasize);
+       } else {
+               chip_fill(chip, initvalue, PIXELOP_OVERWRITE);
+       }
+       return chip;
+} 
+
+
+/********************************************************************
+ *
+ * Drawing functions
+ * 
+ ********************************************************************/
+
+void
+chip_draw_ptarray(CHIP *chip, POINTARRAY *pa, PIXEL *pixel, int op)
+{
+       POINT2D A, B;
+       int i;
+
+       for (i=1; i<pa->npoints; i++)
+       {
+               getPoint2d_p(pa, i-1, &A);
+               getPoint2d_p(pa, i, &B);
+               chip_draw_segment(chip, A.x, A.y, B.x, B.y, pixel, op);
+       }
+}
+
+void
+chip_draw_lwgeom(CHIP *chip, LWGEOM *lwgeom, PIXEL *pixel, int op)
+{
+       POINTARRAY *pa;
+       POINT2D point;
+       int i;
+       LWCOLLECTION *coll;
+
+       /* Check wheter we should completely skip this geometry */
+       if ( lwgeom->bbox )
+       {
+               if ( chip->bvol.xmax < lwgeom->bbox->xmin ) return;
+               if ( chip->bvol.xmin > lwgeom->bbox->xmax ) return;
+               if ( chip->bvol.ymax < lwgeom->bbox->ymin ) return;
+               if ( chip->bvol.ymin > lwgeom->bbox->ymax ) return;
+       }
+
+       switch (TYPE_GETTYPE(lwgeom->type) )
+       {
+               case POINTTYPE:
+                       pa = ((LWPOINT *)lwgeom)->point;
+                       getPoint2d_p(pa, 0, &point);
+                       chip_draw_pixel(chip, point.x, point.y, pixel, op);
+                       return;
+               case LINETYPE:
+                       pa = ((LWLINE *)lwgeom)->points;
+                       chip_draw_ptarray(chip, pa, pixel, op);
+                       return;
+               case POLYGONTYPE:
+                       lwerror("%s geometry unsupported by draw operation",
+                               lwgeom_typename(TYPE_GETTYPE(lwgeom->type)));
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+               case COLLECTIONTYPE:
+                       coll = (LWCOLLECTION *)lwgeom;
+                       for (i=0; i<coll->ngeoms; i++)
+                       {
+                               chip_draw_lwgeom(chip, coll->geoms[i],
+                                       pixel, op);
+                       }
+                       return;
+               default:
+                       lwerror("Unknown geometry type: %d", lwgeom->type);
+       }
+}
+
+/********************************************************************
+ *
+ * PGsql interfaces
+ * 
+ ********************************************************************/
+
+typedef struct CHIPDUMPSTATE_T {
+       CHIP *chip;
+       int x;
+       int y;
+       char *values[3];
+       char fmt[8];
+} CHIPDUMPSTATE;
+
+/*
+ * Convert a TEXT to a C-String.
+ * Remember to lwfree the returned object.
+ */
+char *
+text_to_cstring(text *t)
+{
+       char *s;
+       size_t len;
+
+       len = VARSIZE(t)-VARHDRSZ;
+       s = lwalloc(len+1);
+       memcpy(s, VARDATA(t), len);
+       s[len] = '\0';
+
+       return s;
+}
+
+PG_FUNCTION_INFO_V1(CHIP_dump);
+Datum CHIP_dump(PG_FUNCTION_ARGS)
+{
+       CHIP *chip;
+       FuncCallContext *funcctx;
+       MemoryContext oldcontext, newcontext;
+       CHIPDUMPSTATE *state;
+       TupleDesc tupdesc;
+       AttInMetadata *attinmeta;
+       HeapTuple tuple;
+       TupleTableSlot *slot;
+       Datum result;
+
+       if (SRF_IS_FIRSTCALL())
+       {
+               funcctx = SRF_FIRSTCALL_INIT();
+               newcontext = funcctx->multi_call_memory_ctx;
+
+               oldcontext = MemoryContextSwitchTo(newcontext);
+
+               chip = (CHIP *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+
+               /* Create function state */
+               state = lwalloc(sizeof(CHIPDUMPSTATE));
+               state->chip = chip;
+               state->x=0;
+               state->y=0;
+               state->values[0] = lwalloc(256);
+               state->values[1] = lwalloc(256);
+               state->values[2] = lwalloc(256);
+
+               funcctx->user_fctx = state;
+
+               /*
+                * Build a tuple description for an
+                * geometry_dump tuple
+                */
+               tupdesc = RelationNameGetTupleDesc("chip_dump");
+
+               /* allocate a slot for a tuple with this tupdesc */
+               slot = TupleDescGetSlot(tupdesc);
+
+               /* allocate a slot for a tuple with this tupdesc */
+               slot = TupleDescGetSlot(tupdesc);
+
+               /* assign slot to function context */
+               funcctx->slot = slot;
+
+               /*
+                * generate attribute metadata needed later to produce
+                * tuples from raw C strings
+                */
+               attinmeta = TupleDescGetAttInMetadata(tupdesc);
+               funcctx->attinmeta = attinmeta;
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /* stuff done on every call of the function */
+       funcctx = SRF_PERCALL_SETUP();
+       newcontext = funcctx->multi_call_memory_ctx;
+
+       /* get state */
+       state = funcctx->user_fctx;
+
+       /* Handled simple geometries */
+       while ( state->y < state->chip->height &&
+               state->x < state->chip->width )
+       {
+               char buf[256];
+
+               if ( ! state->chip ) lwerror("state->chip corrupted");
+               PIXEL p = chip_getPixel(state->chip, state->x, state->y);
+               pixel_writeval(&p, buf, 255);
+
+               sprintf(state->values[0], "%d", state->x);
+               sprintf(state->values[1], "%d", state->y);
+               sprintf(state->values[2], "%s", buf);
+
+               tuple = BuildTupleFromCStrings(funcctx->attinmeta,
+                       state->values);
+               result = TupleGetDatum(funcctx->slot, tuple);
+               
+               if ( state->x < state->chip->width-1 )
+               {
+                       state->x++;
+               }
+               else
+               {
+                       state->x=0;
+                       state->y++;
+               }
+
+               SRF_RETURN_NEXT(funcctx, result);
+
+       }
+
+       SRF_RETURN_DONE(funcctx);
+}
+
+PG_FUNCTION_INFO_V1(CHIP_construct);
+Datum CHIP_construct(PG_FUNCTION_ARGS)
+{
+       CHIP *chip;
+       BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
+       int SRID = PG_GETARG_INT32(1);
+       int width = PG_GETARG_INT32(2);
+       int height = PG_GETARG_INT32(3);
+       text *pixel_text = PG_GETARG_TEXT_P(4);
+       char *pixel = text_to_cstring(pixel_text);
+       PIXEL pix = pixel_readval(pixel);
+
+#if DEBUG_CHIP
+       lwnotice("CHIP_construct called");
+#endif
+
+       if ( width <= 0 || height <= 0 )
+       {
+               lwerror("Invalid values for width or height");
+               PG_RETURN_NULL();
+       }
+
+       chip = pgchip_construct(box, SRID, width, height, pix.type, &pix);
+
+#if DEBUG_CHIP
+       lwnotice("Created %dx%d chip type", chip->width, chip->height);
+#endif
+
+       PG_RETURN_POINTER(chip);
+}
+
+PG_FUNCTION_INFO_V1(CHIP_getpixel);
+Datum CHIP_getpixel(PG_FUNCTION_ARGS)
+{
+       CHIP *chip = (CHIP *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int x = PG_GETARG_INT32(1);
+       int y = PG_GETARG_INT32(2);
+       char buf[256];
+       size_t len;
+       text *ret;
+       PIXEL p;
+
+       if ( x < 0 || x >= chip->width )
+       {
+               lwerror("X out of range %d..%d",
+                       0, chip->width-1);
+               PG_RETURN_NULL();
+       }
+       if ( y < 0 || y >= chip->height )
+       {
+               lwerror("Y out of range %d..%d",
+                       0, chip->height-1);
+               PG_RETURN_NULL();
+       }
+
+       p = chip_getPixel(chip, x, y);
+       pixel_writeval(&p, buf, 255);
+       len = strlen(buf);
+       ret = lwalloc(len+VARHDRSZ);
+       VARATT_SIZEP(ret) = len+VARHDRSZ;
+       memcpy(VARDATA(ret), buf, len);
+
+       PG_RETURN_POINTER(ret);
+}
+
+PG_FUNCTION_INFO_V1(CHIP_setpixel);
+Datum CHIP_setpixel(PG_FUNCTION_ARGS)
+{
+       CHIP *chip = (CHIP *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+       int x = PG_GETARG_INT32(1);
+       int y = PG_GETARG_INT32(2);
+       text *init_val_text = PG_GETARG_TEXT_P(3);
+       char *init_val;
+       PIXEL pixel;
+
+       /* Parse pixel */
+       init_val = text_to_cstring(init_val_text);
+       pixel = pixel_readval(init_val);
+
+       if ( chip->datatype != pixel.type )
+       {
+               lwerror("Pixel datatype %d mismatches chip datatype %d",
+                       pixel.type, chip->datatype);
+       }
+
+       chip_setPixel(chip, x, y, &pixel);
+
+       PG_RETURN_POINTER(chip);
+
+}
+
+PG_FUNCTION_INFO_V1(CHIP_draw);
+Datum CHIP_draw(PG_FUNCTION_ARGS)
+{
+       CHIP *chip = (CHIP *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+       PG_LWGEOM *geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       LWGEOM *lwgeom = pglwgeom_deserialize(geom);
+       text *pixel_text = PG_GETARG_TEXT_P(2);
+       char *pixel_str;
+       text *pixelop_text;
+       char *pixelop_str;
+       int pixelop = PIXELOP_OVERWRITE;
+       PIXEL pixel;
+
+       /* Check SRID match */
+       if ( chip->SRID != lwgeom->SRID )
+       {
+               lwerror("Operation on mixed SRID objects");
+       }
+
+       if ( PG_NARGS() > 3 )
+       {
+               pixelop_text = PG_GETARG_TEXT_P(3);
+               pixelop_str = text_to_cstring(pixelop_text);
+               if ( pixelop_str[0] == 'o' )
+               {
+                       pixelop = PIXELOP_OVERWRITE;
+               }
+               else if ( pixelop_str[0] == 'a' )
+               {
+                       pixelop = PIXELOP_ADD;
+               }
+               else
+               {
+                       lwerror("Unsupported pixel operation %s", pixelop_str);
+               }
+       }
+
+       /* Parse pixel */
+       pixel_str = text_to_cstring(pixel_text);
+       pixel = pixel_readval(pixel_str);
+       lwfree(pixel_str);
+
+       if ( pixel.type != chip->datatype )
+       {
+               lwerror("Pixel/Chip datatype mismatch");
+       }
+
+       /* Perform drawing */
+       chip_draw_lwgeom(chip, lwgeom, &pixel, pixelop);
+
+       PG_RETURN_POINTER(chip);
+}
+
+PG_FUNCTION_INFO_V1(CHIP_fill);
+Datum CHIP_fill(PG_FUNCTION_ARGS)
+{
+       CHIP *chip = (CHIP *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+       text *pixel_text = PG_GETARG_TEXT_P(1);
+       char *pixel_str;
+       text *pixelop_text;
+       char *pixelop_str;
+       int pixelop = PIXELOP_OVERWRITE;
+       PIXEL pixel;
+
+       if ( PG_NARGS() > 2 )
+       {
+               pixelop_text = PG_GETARG_TEXT_P(2);
+               pixelop_str = text_to_cstring(pixelop_text);
+               if ( pixelop_str[0] == 'o' )
+               {
+                       pixelop = PIXELOP_OVERWRITE;
+               }
+               else if ( pixelop_str[0] == 'a' )
+               {
+                       pixelop = PIXELOP_ADD;
+               }
+               else
+               {
+                       lwerror("Unsupported pixel operation %s", pixelop_str);
+               }
+       }
+
+       /* Parse pixel */
+       pixel_str = text_to_cstring(pixel_text);
+       pixel = pixel_readval(pixel_str);
+       lwfree(pixel_str);
+
+       if ( pixel.type != chip->datatype )
+       {
+               lwerror("Pixel/Chip datatype mismatch");
+       }
+
+       /* Perform fill */
+       chip_fill(chip, &pixel, pixelop);
+
+       PG_RETURN_POINTER(chip);
+}