Imaging ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel, FLOAT32 offset) { Imaging imOut; ImagingSectionCookie cookie; if ( ! im || im->type != IMAGING_TYPE_UINT8) return (Imaging) ImagingError_ModeError(); if (im->xsize < xsize || im->ysize < ysize) return ImagingCopy(im); if ((xsize != 3 && xsize != 5) || xsize != ysize) return (Imaging) ImagingError_ValueError("bad kernel size"); imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) return NULL; ImagingSectionEnter(&cookie); if (xsize == 3) { /* 3x3 kernel. */ ImagingFilter3x3(imOut, im, kernel, offset); } else { /* 5x5 kernel. */ ImagingFilter5x5(imOut, im, kernel, offset); } ImagingSectionLeave(&cookie); return imOut; }
static PyObject* _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) { UINT8* buf; int status; ImagingSectionCookie cookie; /* Encode to a file handle */ int fh; int bufsize = 16384; if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize)) return NULL; /* Allocate an encoder buffer */ buf = (UINT8*) malloc(bufsize); if (!buf) return PyErr_NoMemory(); ImagingSectionEnter(&cookie); do { /* This replaces the inner loop in the ImageFile _save function. */ status = encoder->encode(encoder->im, &encoder->state, buf, bufsize); if (status > 0) if (write(fh, buf, status) < 0) { ImagingSectionLeave(&cookie); free(buf); return PyErr_SetFromErrno(PyExc_IOError); } } while (encoder->state.errcode == 0); ImagingSectionLeave(&cookie); free(buf); return Py_BuildValue("i", encoder->state.errcode); }
Imaging ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) { Imaging imOut; int x, y; ImagingSectionCookie cookie; if (xmargin < 0 && ymargin < 0) return (Imaging) ImagingError_ValueError("bad kernel size"); imOut = ImagingNewDirty( imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin); if (!imOut) return NULL; #define EXPAND_LINE(type, image, yin, yout) {\ for (x = 0; x < xmargin; x++)\ imOut->image[yout][x] = imIn->image[yin][0];\ for (x = 0; x < imIn->xsize; x++)\ imOut->image[yout][x+xmargin] = imIn->image[yin][x];\ for (x = 0; x < xmargin; x++)\ imOut->image[yout][xmargin+imIn->xsize+x] =\ imIn->image[yin][imIn->xsize-1];\ } #define EXPAND(type, image) {\ for (y = 0; y < ymargin; y++)\ EXPAND_LINE(type, image, 0, y);\ for (y = 0; y < imIn->ysize; y++)\ EXPAND_LINE(type, image, y, y+ymargin);\ for (y = 0; y < ymargin; y++)\ EXPAND_LINE(type, image, imIn->ysize-1, ymargin+imIn->ysize+y);\ } ImagingSectionEnter(&cookie); if (imIn->image8) { EXPAND(UINT8, image8); } else { EXPAND(INT32, image32); } ImagingSectionLeave(&cookie); ImagingCopyPalette(imOut, imIn); return imOut; }
void ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; double ss; int xx, yy, y, ymin, ymax; double *k; ImagingSectionEnter(&cookie); switch(imIn->type) { case IMAGING_TYPE_INT32: for (yy = 0; yy < imOut->ysize; yy++) { ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = 0; y < ymax; y++) ss += IMAGING_PIXEL_I(imIn, xx, y + ymin) * k[y]; IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); } } break; case IMAGING_TYPE_FLOAT32: for (yy = 0; yy < imOut->ysize; yy++) { ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = 0; y < ymax; y++) ss += IMAGING_PIXEL_F(imIn, xx, y + ymin) * k[y]; IMAGING_PIXEL_F(imOut, xx, yy) = ss; } } break; } ImagingSectionLeave(&cookie); }
void ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; double ss; int xx, yy, x, xmin, xmax; double *k; ImagingSectionEnter(&cookie); switch(imIn->type) { case IMAGING_TYPE_INT32: for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { xmin = bounds[xx * 2 + 0]; xmax = bounds[xx * 2 + 1]; k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) ss += IMAGING_PIXEL_I(imIn, x + xmin, yy + offset) * k[x]; IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); } } break; case IMAGING_TYPE_FLOAT32: for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { xmin = bounds[xx * 2 + 0]; xmax = bounds[xx * 2 + 1]; k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) ss += IMAGING_PIXEL_F(imIn, x + xmin, yy + offset) * k[x]; IMAGING_PIXEL_F(imOut, xx, yy) = ss; } } break; } ImagingSectionLeave(&cookie); }
static PyObject* _decode(ImagingDecoderObject* decoder, PyObject* args) { UINT8* buffer; int bufsize, status; ImagingSectionCookie cookie; if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) return NULL; if (!decoder->pulls_fd) { ImagingSectionEnter(&cookie); } status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); if (!decoder->pulls_fd) { ImagingSectionLeave(&cookie); } return Py_BuildValue("ii", status, decoder->state.errcode); }
Imaging ImagingFill(Imaging im, const void* colour) { int x, y; ImagingSectionCookie cookie; if (im->type == IMAGING_TYPE_SPECIAL) { /* use generic API */ ImagingAccess access = ImagingAccessNew(im); if (access) { for (y = 0; y < im->ysize; y++) for (x = 0; x < im->xsize; x++) access->put_pixel(im, x, y, colour); ImagingAccessDelete(im, access); } else { /* wipe the image */ for (y = 0; y < im->ysize; y++) memset(im->image[y], 0, im->linesize); } } else { ImagingSectionEnter(&cookie); INT32 c = 0L; memcpy(&c, colour, im->pixelsize); if (im->image32 && c != 0L) { for (y = 0; y < im->ysize; y++) for (x = 0; x < im->xsize; x++) im->image32[y][x] = c; } else { unsigned char cc = (unsigned char) *(UINT8*) colour; for (y = 0; y < im->ysize; y++) memset(im->image[y], cc, im->linesize); } ImagingSectionLeave(&cookie); } return im; }
int ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, int dx0, int dy0, int dx1, int dy1) { ImagingSectionCookie cookie; int xsize, ysize; int pixelsize; int sx0, sy0; if (!imOut || !ink) { (void) ImagingError_ModeError(); return -1; } pixelsize = imOut->pixelsize; xsize = dx1 - dx0; ysize = dy1 - dy0; if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) { (void) ImagingError_Mismatch(); return -1; } /* Determine which region to fill */ sx0 = sy0 = 0; if (dx0 < 0) xsize += dx0, sx0 = -dx0, dx0 = 0; if (dx0 + xsize > imOut->xsize) xsize = imOut->xsize - dx0; if (dy0 < 0) ysize += dy0, sy0 = -dy0, dy0 = 0; if (dy0 + ysize > imOut->ysize) ysize = imOut->ysize - dy0; if (xsize <= 0 || ysize <= 0) return 0; if (!imMask) { ImagingSectionEnter(&cookie); fill(imOut, ink, dx0, dy0, xsize, ysize, pixelsize); ImagingSectionLeave(&cookie); } else if (strcmp(imMask->mode, "1") == 0) { ImagingSectionEnter(&cookie); fill_mask_1(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize); ImagingSectionLeave(&cookie); } else if (strcmp(imMask->mode, "L") == 0) { ImagingSectionEnter(&cookie); fill_mask_L(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize); ImagingSectionLeave(&cookie); } else if (strcmp(imMask->mode, "RGBA") == 0) { ImagingSectionEnter(&cookie); fill_mask_RGBA(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize); ImagingSectionLeave(&cookie); } else if (strcmp(imMask->mode, "RGBa") == 0) { ImagingSectionEnter(&cookie); fill_mask_RGBa(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize); ImagingSectionLeave(&cookie); } else { (void) ImagingError_ValueError("bad transparency mask"); return -1; } return 0; }
void ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *prekk) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; int xx, yy, y, ymin, ymax; INT32 *k, *kk; // use the same buffer for normalized coefficients kk = (INT32 *) prekk; normalize_coeffs_8bpc(imOut->ysize, ksize, prekk); ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * ksize]; ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) ss0 += ((UINT8) imIn->image8[y + ymin][xx]) * k[y]; imOut->image8[yy][xx] = clip8(ss0); } } } else if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == 2) { for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * ksize]; ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss3 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; } ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( clip8(ss0), 0, 0, clip8(ss3)); } } } else if (imIn->bands == 3) { for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * ksize]; ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y]; ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; } ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( clip8(ss0), clip8(ss1), clip8(ss2), 0); } } } else { for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * ksize]; ymin = bounds[yy * 2 + 0]; ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y]; ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; } ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3)); } } } } ImagingSectionLeave(&cookie); }
Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter) { /* FIXME: this is a quick and straightforward translation from a python prototype. might need some further C-ification... */ ImagingSectionCookie cookie; struct filter *filterp; float support, scale, filterscale; float center, ww, ss, ymin, ymax, xmin, xmax; int xx, yy, x, y, b; float *k; /* check modes */ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); /* check filter */ switch (filter) { case IMAGING_TRANSFORM_NEAREST: filterp = &NEAREST; break; case IMAGING_TRANSFORM_ANTIALIAS: filterp = &ANTIALIAS; break; case IMAGING_TRANSFORM_BILINEAR: filterp = &BILINEAR; break; case IMAGING_TRANSFORM_BICUBIC: filterp = &BICUBIC; break; default: return (Imaging) ImagingError_ValueError( "unsupported resampling filter" ); } if (imIn->ysize == imOut->ysize) { /* prepare for horizontal stretch */ filterscale = scale = (float) imIn->xsize / imOut->xsize; } else if (imIn->xsize == imOut->xsize) { /* prepare for vertical stretch */ filterscale = scale = (float) imIn->ysize / imOut->ysize; } else return (Imaging) ImagingError_Mismatch(); /* determine support size (length of resampling filter) */ support = filterp->support; if (filterscale < 1.0) { filterscale = 1.0; support = 0.5; } support = support * filterscale; /* coefficient buffer (with rounding safety margin) */ k = malloc(((int) support * 2 + 10) * sizeof(float)); if (!k) return (Imaging) ImagingError_MemoryError(); ImagingSectionEnter(&cookie); if (imIn->xsize == imOut->xsize) { /* vertical stretch */ for (yy = 0; yy < imOut->ysize; yy++) { center = (yy + 0.5) * scale; ww = 0.0; ss = 1.0 / filterscale; /* calculate filter weights */ ymin = floor(center - support); if (ymin < 0.0) ymin = 0.0; ymax = ceil(center + support); if (ymax > (float) imIn->ysize) ymax = (float) imIn->ysize; for (y = (int) ymin; y < (int) ymax; y++) { float w = filterp->filter((y - center + 0.5) * ss) * ss; k[y - (int) ymin] = w; ww = ww + w; } if (ww == 0.0) ww = 1.0; else ww = 1.0 / ww; if (imIn->image8) { /* 8-bit grayscale */ for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = (int) ymin; y < (int) ymax; y++) ss = ss + imIn->image8[y][xx] * k[y - (int) ymin]; ss = ss * ww + 0.5; if (ss < 0.5) imOut->image8[yy][xx] = 0; else if (ss >= 255.0) imOut->image8[yy][xx] = 255; else imOut->image8[yy][xx] = (UINT8) ss; } } else switch(imIn->type) { case IMAGING_TYPE_UINT8: /* n-bit grayscale */ for (xx = 0; xx < imOut->xsize*4; xx++) { /* FIXME: skip over unused pixels */ ss = 0.0; for (y = (int) ymin; y < (int) ymax; y++) ss = ss + (UINT8) imIn->image[y][xx] * k[y-(int) ymin]; ss = ss * ww + 0.5; if (ss < 0.5) imOut->image[yy][xx] = (UINT8) 0; else if (ss >= 255.0) imOut->image[yy][xx] = (UINT8) 255; else imOut->image[yy][xx] = (UINT8) ss; } break; case IMAGING_TYPE_INT32: /* 32-bit integer */ for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = (int) ymin; y < (int) ymax; y++) ss = ss + IMAGING_PIXEL_I(imIn, xx, y) * k[y - (int) ymin]; IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww; } break; case IMAGING_TYPE_FLOAT32: /* 32-bit float */ for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = (int) ymin; y < (int) ymax; y++) ss = ss + IMAGING_PIXEL_F(imIn, xx, y) * k[y - (int) ymin]; IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww; } break; default: ImagingSectionLeave(&cookie); return (Imaging) ImagingError_ModeError(); } } } else { /* horizontal stretch */ for (xx = 0; xx < imOut->xsize; xx++) { center = (xx + 0.5) * scale; ww = 0.0; ss = 1.0 / filterscale; xmin = floor(center - support); if (xmin < 0.0) xmin = 0.0; xmax = ceil(center + support); if (xmax > (float) imIn->xsize) xmax = (float) imIn->xsize; for (x = (int) xmin; x < (int) xmax; x++) { float w = filterp->filter((x - center + 0.5) * ss) * ss; k[x - (int) xmin] = w; ww = ww + w; } if (ww == 0.0) ww = 1.0; else ww = 1.0 / ww; if (imIn->image8) { /* 8-bit grayscale */ for (yy = 0; yy < imOut->ysize; yy++) { ss = 0.0; for (x = (int) xmin; x < (int) xmax; x++) ss = ss + imIn->image8[yy][x] * k[x - (int) xmin]; ss = ss * ww + 0.5; if (ss < 0.5) imOut->image8[yy][xx] = (UINT8) 0; else if (ss >= 255.0) imOut->image8[yy][xx] = (UINT8) 255; else imOut->image8[yy][xx] = (UINT8) ss; } } else switch(imIn->type) { case IMAGING_TYPE_UINT8: /* n-bit grayscale */ for (yy = 0; yy < imOut->ysize; yy++) { for (b = 0; b < imIn->bands; b++) { if (imIn->bands == 2 && b) b = 3; /* hack to deal with LA images */ ss = 0.0; for (x = (int) xmin; x < (int) xmax; x++) ss = ss + (UINT8) imIn->image[yy][x*4+b] * k[x - (int) xmin]; ss = ss * ww + 0.5; if (ss < 0.5) imOut->image[yy][xx*4+b] = (UINT8) 0; else if (ss >= 255.0) imOut->image[yy][xx*4+b] = (UINT8) 255; else imOut->image[yy][xx*4+b] = (UINT8) ss; } } break; case IMAGING_TYPE_INT32: /* 32-bit integer */ for (yy = 0; yy < imOut->ysize; yy++) { ss = 0.0; for (x = (int) xmin; x < (int) xmax; x++) ss = ss + IMAGING_PIXEL_I(imIn, x, yy) * k[x - (int) xmin]; IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww; } break; case IMAGING_TYPE_FLOAT32: /* 32-bit float */ for (yy = 0; yy < imOut->ysize; yy++) { ss = 0.0; for (x = (int) xmin; x < (int) xmax; x++) ss = ss + IMAGING_PIXEL_F(imIn, x, yy) * k[x - (int) xmin]; IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww; } break; default: ImagingSectionLeave(&cookie); return (Imaging) ImagingError_ModeError(); } } } ImagingSectionLeave(&cookie); free(k); return imOut; }
Imaging ImagingPointTransform(Imaging imIn, double scale, double offset) { /* scale/offset transform */ ImagingSectionCookie cookie; Imaging imOut; int x, y; if (!imIn || (strcmp(imIn->mode, "I") != 0 && strcmp(imIn->mode, "I;16") != 0 && strcmp(imIn->mode, "F") != 0)) return (Imaging) ImagingError_ModeError(); imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; ImagingCopyInfo(imOut, imIn); switch (imIn->type) { case IMAGING_TYPE_INT32: ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) { INT32* in = imIn->image32[y]; INT32* out = imOut->image32[y]; /* FIXME: add clipping? */ for (x = 0; x < imIn->xsize; x++) out[x] = in[x] * scale + offset; } ImagingSectionLeave(&cookie); break; case IMAGING_TYPE_FLOAT32: ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) { FLOAT32* in = (FLOAT32*) imIn->image32[y]; FLOAT32* out = (FLOAT32*) imOut->image32[y]; for (x = 0; x < imIn->xsize; x++) out[x] = in[x] * scale + offset; } ImagingSectionLeave(&cookie); break; case IMAGING_TYPE_SPECIAL: if (strcmp(imIn->mode,"I;16") == 0) { ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) { UINT16* in = (UINT16 *)imIn->image[y]; UINT16* out = (UINT16 *)imOut->image[y]; /* FIXME: add clipping? */ for (x = 0; x < imIn->xsize; x++) out[x] = in[x] * scale + offset; } ImagingSectionLeave(&cookie); break; } /* FALL THROUGH */ default: ImagingDelete(imOut); return (Imaging) ImagingError_ValueError("internal error"); } return imOut; }
Imaging ImagingPoint(Imaging imIn, const char* mode, const void* table) { /* lookup table transform */ ImagingSectionCookie cookie; Imaging imOut; im_point_context context; void (*point)(Imaging imIn, Imaging imOut, im_point_context* context); if (!imIn) return (Imaging) ImagingError_ModeError(); if (!mode) mode = imIn->mode; if (imIn->type != IMAGING_TYPE_UINT8) { if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0) goto mode_mismatch; } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0) goto mode_mismatch; imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; /* find appropriate handler */ if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == imOut->bands && imIn->type == imOut->type) { switch (imIn->bands) { case 1: point = im_point_8_8; break; case 2: point = im_point_2x8_2x8; break; case 3: point = im_point_3x8_3x8; break; case 4: point = im_point_4x8_4x8; break; default: /* this cannot really happen */ point = im_point_8_8; break; } } else point = im_point_8_32; } else point = im_point_32_8; ImagingCopyInfo(imOut, imIn); ImagingSectionEnter(&cookie); context.table = table; point(imOut, imIn, &context); ImagingSectionLeave(&cookie); return imOut; mode_mismatch: return (Imaging) ImagingError_ValueError( "point operation not supported for this mode" ); }
ImagingHistogram ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) { ImagingSectionCookie cookie; int x, y, i; ImagingHistogram h; INT32 imin, imax; FLOAT32 fmin, fmax, scale; if (!im) return ImagingError_ModeError(); if (imMask) { /* Validate mask */ if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) return ImagingError_Mismatch(); if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) return ImagingError_ValueError("bad transparency mask"); } h = ImagingHistogramNew(im); if (imMask) { /* mask */ if (im->image8) { ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) for (x = 0; x < im->xsize; x++) if (imMask->image8[y][x] != 0) h->histogram[im->image8[y][x]]++; ImagingSectionLeave(&cookie); } else { /* yes, we need the braces. C isn't Python! */ if (im->type != IMAGING_TYPE_UINT8) return ImagingError_ModeError(); ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { UINT8* in = (UINT8*) im->image32[y]; for (x = 0; x < im->xsize; x++) if (imMask->image8[y][x] != 0) { h->histogram[(*in++)]++; h->histogram[(*in++)+256]++; h->histogram[(*in++)+512]++; h->histogram[(*in++)+768]++; } else in += 4; } ImagingSectionLeave(&cookie); } } else { /* mask not given; process pixels in image */ if (im->image8) { ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) for (x = 0; x < im->xsize; x++) h->histogram[im->image8[y][x]]++; ImagingSectionLeave(&cookie); } else { switch (im->type) { case IMAGING_TYPE_UINT8: ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { UINT8* in = (UINT8*) im->image[y]; for (x = 0; x < im->xsize; x++) { h->histogram[(*in++)]++; h->histogram[(*in++)+256]++; h->histogram[(*in++)+512]++; h->histogram[(*in++)+768]++; } } ImagingSectionLeave(&cookie); break; case IMAGING_TYPE_INT32: if (!minmax) return ImagingError_ValueError("min/max not given"); if (!im->xsize || !im->ysize) break; imin = ((INT32*) minmax)[0]; imax = ((INT32*) minmax)[1]; if (imin >= imax) break; ImagingSectionEnter(&cookie); scale = 255.0F / (imax - imin); for (y = 0; y < im->ysize; y++) { INT32* in = im->image32[y]; for (x = 0; x < im->xsize; x++) { i = (int) (((*in++)-imin)*scale); if (i >= 0 && i < 256) h->histogram[i]++; } } ImagingSectionLeave(&cookie); break; case IMAGING_TYPE_FLOAT32: if (!minmax) return ImagingError_ValueError("min/max not given"); if (!im->xsize || !im->ysize) break; fmin = ((FLOAT32*) minmax)[0]; fmax = ((FLOAT32*) minmax)[1]; if (fmin >= fmax) break; ImagingSectionEnter(&cookie); scale = 255.0F / (fmax - fmin); for (y = 0; y < im->ysize; y++) { FLOAT32* in = (FLOAT32*) im->image32[y]; for (x = 0; x < im->xsize; x++) { i = (int) (((*in++)-fmin)*scale); if (i >= 0 && i < 256) h->histogram[i]++; } } ImagingSectionLeave(&cookie); break; } } } return h; }
int ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { ZIPSTATE* context = (ZIPSTATE*) state->context; int err; int compress_level, compress_type; UINT8* ptr; int i, bpp, s, sum; ImagingSectionCookie cookie; if (!state->state) { /* Initialization */ /* Valid modes are ZIP_PNG, ZIP_PNG_PALETTE, and ZIP_TIFF */ /* overflow check for malloc */ if (state->bytes > INT_MAX - 1) { state->errcode = IMAGING_CODEC_MEMORY; return -1; } /* Expand standard buffer to make room for the filter selector, and allocate filter buffers */ free(state->buffer); /* malloc check ok, overflow checked above */ state->buffer = (UINT8*) malloc(state->bytes+1); context->previous = (UINT8*) malloc(state->bytes+1); context->prior = (UINT8*) malloc(state->bytes+1); context->up = (UINT8*) malloc(state->bytes+1); context->average = (UINT8*) malloc(state->bytes+1); context->paeth = (UINT8*) malloc(state->bytes+1); if (!state->buffer || !context->previous || !context->prior || !context->up || !context->average || !context->paeth) { free(context->paeth); free(context->average); free(context->up); free(context->prior); free(context->previous); state->errcode = IMAGING_CODEC_MEMORY; return -1; } /* Initialise filter buffers */ state->buffer[0] = 0; context->prior[0] = 1; context->up[0] = 2; context->average[0] = 3; context->paeth[0] = 4; /* Initialise previous buffer to black */ memset(context->previous, 0, state->bytes+1); /* Setup compression context */ context->z_stream.zalloc = (alloc_func)0; context->z_stream.zfree = (free_func)0; context->z_stream.opaque = (voidpf)0; context->z_stream.next_in = 0; context->z_stream.avail_in = 0; compress_level = (context->optimize) ? Z_BEST_COMPRESSION : context->compress_level; if (context->compress_type == -1) { compress_type = (context->mode == ZIP_PNG) ? Z_FILTERED : Z_DEFAULT_STRATEGY; } else { compress_type = context->compress_type; } err = deflateInit2(&context->z_stream, /* compression level */ compress_level, /* compression method */ Z_DEFLATED, /* compression memory resources */ 15, 9, /* compression strategy (image data are filtered)*/ compress_type); if (err < 0) { state->errcode = IMAGING_CODEC_CONFIG; return -1; } if (context->dictionary && context->dictionary_size > 0) { err = deflateSetDictionary(&context->z_stream, (unsigned char *)context->dictionary, context->dictionary_size); if (err < 0) { state->errcode = IMAGING_CODEC_CONFIG; return -1; } } /* Ready to decode */ state->state = 1; } /* Setup the destination buffer */ context->z_stream.next_out = buf; context->z_stream.avail_out = bytes; if (context->z_stream.next_in && context->z_stream.avail_in > 0) { /* We have some data from previous round, deflate it first */ err = deflate(&context->z_stream, Z_NO_FLUSH); if (err < 0) { /* Something went wrong inside the compression library */ if (err == Z_DATA_ERROR) state->errcode = IMAGING_CODEC_BROKEN; else if (err == Z_MEM_ERROR) state->errcode = IMAGING_CODEC_MEMORY; else state->errcode = IMAGING_CODEC_CONFIG; free(context->paeth); free(context->average); free(context->up); free(context->prior); free(context->previous); deflateEnd(&context->z_stream); return -1; } } ImagingSectionEnter(&cookie); for (;;) { switch (state->state) { case 1: /* Compress image data */ while (context->z_stream.avail_out > 0) { if (state->y >= state->ysize) { /* End of image; now flush compressor buffers */ state->state = 2; break; } /* Stuff image data into the compressor */ state->shuffle(state->buffer+1, (UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize); state->y++; context->output = state->buffer; if (context->mode == ZIP_PNG) { /* Filter the image data. For each line, select the filter that gives the least total distance from zero for the filtered data (taken from LIBPNG) */ bpp = (state->bits + 7) / 8; /* 0. No filter */ for (i = 1, sum = 0; i <= state->bytes; i++) { UINT8 v = state->buffer[i]; sum += (v < 128) ? v : 256 - v; } /* 2. Up. We'll test this first to save time when an image line is identical to the one above. */ if (sum > 0) { for (i = 1, s = 0; i <= state->bytes; i++) { UINT8 v = state->buffer[i] - context->previous[i]; context->up[i] = v; s += (v < 128) ? v : 256 - v; } if (s < sum) { context->output = context->up; sum = s; /* 0 if line was duplicated */ } } /* 1. Prior */ if (sum > 0) { for (i = 1, s = 0; i <= bpp; i++) { UINT8 v = state->buffer[i]; context->prior[i] = v; s += (v < 128) ? v : 256 - v; } for (; i <= state->bytes; i++) { UINT8 v = state->buffer[i] - state->buffer[i-bpp]; context->prior[i] = v; s += (v < 128) ? v : 256 - v; } if (s < sum) { context->output = context->prior; sum = s; /* 0 if line is solid */ } } /* 3. Average (not very common in real-life images, so its only used with the optimize option) */ if (context->optimize && sum > 0) { for (i = 1, s = 0; i <= bpp; i++) { UINT8 v = state->buffer[i] - context->previous[i]/2; context->average[i] = v; s += (v < 128) ? v : 256 - v; } for (; i <= state->bytes; i++) { UINT8 v = state->buffer[i] - (state->buffer[i-bpp] + context->previous[i])/2; context->average[i] = v; s += (v < 128) ? v : 256 - v; } if (s < sum) { context->output = context->average; sum = s; } } /* 4. Paeth */ if (sum > 0) { for (i = 1, s = 0; i <= bpp; i++) { UINT8 v = state->buffer[i] - context->previous[i]; context->paeth[i] = v; s += (v < 128) ? v : 256 - v; } for (; i <= state->bytes; i++) { UINT8 v; int a, b, c; int pa, pb, pc; /* fetch pixels */ a = state->buffer[i-bpp]; b = context->previous[i]; c = context->previous[i-bpp]; /* distances to surrounding pixels */ pa = abs(b - c); pb = abs(a - c); pc = abs(a + b - 2*c); /* pick predictor with the shortest distance */ v = state->buffer[i] - ((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c); context->paeth[i] = v; s += (v < 128) ? v : 256 - v; } if (s < sum) { context->output = context->paeth; sum = s; } } } /* Compress this line */ context->z_stream.next_in = context->output; context->z_stream.avail_in = state->bytes+1; err = deflate(&context->z_stream, Z_NO_FLUSH); if (err < 0) { /* Something went wrong inside the compression library */ if (err == Z_DATA_ERROR) state->errcode = IMAGING_CODEC_BROKEN; else if (err == Z_MEM_ERROR) state->errcode = IMAGING_CODEC_MEMORY; else state->errcode = IMAGING_CODEC_CONFIG; free(context->paeth); free(context->average); free(context->up); free(context->prior); free(context->previous); deflateEnd(&context->z_stream); ImagingSectionLeave(&cookie); return -1; } /* Swap buffer pointers */ ptr = state->buffer; state->buffer = context->previous; context->previous = ptr; } if (context->z_stream.avail_out == 0) break; /* Buffer full */ case 2: /* End of image data; flush compressor buffers */ while (context->z_stream.avail_out > 0) { err = deflate(&context->z_stream, Z_FINISH); if (err == Z_STREAM_END) { free(context->paeth); free(context->average); free(context->up); free(context->prior); free(context->previous); deflateEnd(&context->z_stream); state->errcode = IMAGING_CODEC_END; break; } if (context->z_stream.avail_out == 0) break; /* Buffer full */ } } ImagingSectionLeave(&cookie); return bytes - context->z_stream.avail_out; } /* Should never ever arrive here... */ state->errcode = IMAGING_CODEC_CONFIG; ImagingSectionLeave(&cookie); return -1; }
static Imaging gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) { ImagingSectionCookie cookie; float *maskData = NULL; int y = 0; int x = 0; float z = 0; float sum = 0.0; float dev = 0.0; float *buffer = NULL; int *line = NULL; UINT8 *line8 = NULL; int pix = 0; float newPixel[4]; int channel = 0; int offset = 0; INT32 newPixelFinals; int radius = 0; float remainder = 0.0; int i; /* Do the gaussian blur */ /* For a symmetrical gaussian blur, instead of doing a radius*radius matrix lookup, you get the EXACT same results by doing a radius*1 transform, followed by a 1*radius transform. This reduces the number of lookups exponentially (10 lookups per pixel for a radius of 5 instead of 25 lookups). So, we blur the lines first, then we blur the resulting columns. */ /* first, round radius off to the next higher integer and hold the remainder this is used so we can support float radius values properly. */ remainder = floatRadius - ((int) floatRadius); floatRadius = ceil(floatRadius); /* Next, double the radius and offset by 2.0... that way "0" returns the original image instead of a black one. We multiply it by 2.0 so that it is a true "radius", not a diameter (the results match other paint programs closer that way too). */ radius = (int) ((floatRadius * 2.0) + 2.0); /* create the maskData for the gaussian curve */ maskData = malloc(radius * sizeof(float)); /* FIXME: error checking */ for (x = 0; x < radius; x++) { z = ((float) (x + 2) / ((float) radius)); dev = 0.5 + (((float) (radius * radius)) * 0.001); /* you can adjust this factor to change the shape/center-weighting of the gaussian */ maskData[x] = (float) pow((1.0 / sqrt(2.0 * 3.14159265359 * dev)), ((-(z - 1.0) * -(x - 1.0)) / (2.0 * dev))); } /* if there's any remainder, multiply the first/last values in MaskData it. this allows us to support float radius values. */ if (remainder > 0.0) { maskData[0] *= remainder; maskData[radius - 1] *= remainder; } for (x = 0; x < radius; x++) { /* this is done separately now due to the correction for float radius values above */ sum += maskData[x]; } for (i = 0; i < radius; i++) { maskData[i] *= (1.0 / sum); /* printf("%f\n", maskData[i]); */ } /* create a temporary memory buffer for the data for the first pass memset the buffer to 0 so we can use it directly with += */ /* don't bother about alpha/padding */ buffer = calloc((size_t) (im->xsize * im->ysize * channels), sizeof(float)); if (buffer == NULL) return ImagingError_MemoryError(); /* be nice to other threads while you go off to lala land */ ImagingSectionEnter(&cookie); /* memset(buffer, 0, sizeof(buffer)); */ newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; /* perform a blur on each line, and place in the temporary storage buffer */ for (y = 0; y < im->ysize; y++) { if (channels == 1 && im->image8 != NULL) { line8 = (UINT8 *) im->image8[y]; } else { line = im->image32[y]; } for (x = 0; x < im->xsize; x++) { newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; /* for each neighbor pixel, factor in its value/weighting to the current pixel */ for (pix = 0; pix < radius; pix++) { /* figure the offset of this neighbor pixel */ offset = (int) ((-((float) radius / 2.0) + (float) pix) + 0.5); if (x + offset < 0) offset = -x; else if (x + offset >= im->xsize) offset = im->xsize - x - 1; /* add (neighbor pixel value * maskData[pix]) to the current pixel value */ if (channels == 1) { buffer[(y * im->xsize) + x] += ((float) ((UINT8 *) & line8[x + offset])[0]) * (maskData[pix]); } else { for (channel = 0; channel < channels; channel++) { buffer[(y * im->xsize * channels) + (x * channels) + channel] += ((float) ((UINT8 *) & line[x + offset]) [channel]) * (maskData[pix]); } } } } } /* perform a blur on each column in the buffer, and place in the output image */ for (x = 0; x < im->xsize; x++) { for (y = 0; y < im->ysize; y++) { newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; /* for each neighbor pixel, factor in its value/weighting to the current pixel */ for (pix = 0; pix < radius; pix++) { /* figure the offset of this neighbor pixel */ offset = (int) (-((float) radius / 2.0) + (float) pix + 0.5); if (y + offset < 0) offset = -y; else if (y + offset >= im->ysize) offset = im->ysize - y - 1; /* add (neighbor pixel value * maskData[pix]) to the current pixel value */ for (channel = 0; channel < channels; channel++) { newPixel[channel] += (buffer [((y + offset) * im->xsize * channels) + (x * channels) + channel]) * (maskData[pix]); } } /* if the image is RGBX or RGBA, copy the 4th channel data to newPixel, so it gets put in imOut */ if (strcmp(im->mode, "RGBX") == 0 || strcmp(im->mode, "RGBA") == 0) { newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3]; } /* pack the channels into an INT32 so we can put them back in the PIL image */ newPixelFinals = 0; if (channels == 1) { newPixelFinals = clip(newPixel[0]); } else { /* for RGB, the fourth channel isn't used anyways, so just pack a 0 in there, this saves checking the mode for each pixel. */ /* this doesn't work on little-endian machines... fix it! */ newPixelFinals = clip(newPixel[0]) | clip(newPixel[1]) << 8 | clip(newPixel[2]) << 16 | clip(newPixel[3]) << 24; } /* set the resulting pixel in imOut */ if (channels == 1) { imOut->image8[y][x] = (UINT8) newPixelFinals; } else { imOut->image32[y][x] = newPixelFinals; } } } /* free the buffer */ free(buffer); /* get the GIL back so Python knows who you are */ ImagingSectionLeave(&cookie); return imOut; }
Imaging ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, int threshold) { ImagingSectionCookie cookie; Imaging result; int channel = 0; int channels = 0; int padding = 0; int x = 0; int y = 0; int *lineIn = NULL; int *lineOut = NULL; UINT8 *lineIn8 = NULL; UINT8 *lineOut8 = NULL; int diff = 0; INT32 newPixel = 0; if (strcmp(im->mode, "RGB") == 0) { channels = 3; padding = 1; } else if (strcmp(im->mode, "RGBA") == 0) { channels = 3; padding = 1; } else if (strcmp(im->mode, "RGBX") == 0) { channels = 3; padding = 1; } else if (strcmp(im->mode, "CMYK") == 0) { channels = 4; padding = 0; } else if (strcmp(im->mode, "L") == 0) { channels = 1; padding = 0; } else return ImagingError_ModeError(); /* first, do a gaussian blur on the image, putting results in imOut temporarily */ result = gblur(im, imOut, radius, channels, padding); if (!result) return NULL; /* now, go through each pixel, compare "normal" pixel to blurred pixel. if the difference is more than threshold values, apply the OPPOSITE correction to the amount of blur, multiplied by percent. */ ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { if (channels == 1) { lineIn8 = im->image8[y]; lineOut8 = imOut->image8[y]; } else { lineIn = im->image32[y]; lineOut = imOut->image32[y]; } for (x = 0; x < im->xsize; x++) { newPixel = 0; /* compare in/out pixels, apply sharpening */ if (channels == 1) { diff = ((UINT8 *) & lineIn8[x])[0] - ((UINT8 *) & lineOut8[x])[0]; if (abs(diff) > threshold) { /* add the diff*percent to the original pixel */ imOut->image8[y][x] = clip((((UINT8 *) & lineIn8[x])[0]) + (diff * ((float) percent) / 100.0)); } else { /* newPixel is the same as imIn */ imOut->image8[y][x] = ((UINT8 *) & lineIn8[x])[0]; } } else { for (channel = 0; channel < channels; channel++) { diff = (int) ((((UINT8 *) & lineIn[x])[channel]) - (((UINT8 *) & lineOut[x])[channel])); if (abs(diff) > threshold) { /* add the diff*percent to the original pixel this may not work for little-endian systems, fix it! */ newPixel = newPixel | clip((float) (((UINT8 *) & lineIn[x])[channel]) + (diff * (((float) percent / 100.0)))) << (channel * 8); } else { /* newPixel is the same as imIn this may not work for little-endian systems, fix it! */ newPixel = newPixel | ((UINT8 *) & lineIn[x])[channel] << (channel * 8); } } if (strcmp(im->mode, "RGBX") == 0 || strcmp(im->mode, "RGBA") == 0) { /* preserve the alpha channel this may not work for little-endian systems, fix it! */ newPixel = newPixel | ((UINT8 *) & lineIn[x])[channel] << 24; } imOut->image32[y][x] = newPixel; } } } ImagingSectionLeave(&cookie); return imOut; }