Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) { Imaging imOut; int x, y; /* Check arguments */ if (!imIn1 || !imIn2 || imIn1->type != IMAGING_TYPE_UINT8) return ImagingError_ModeError(); if (imIn1->type != imIn2->type || imIn1->bands != imIn2->bands || imIn1->xsize != imIn2->xsize || imIn1->ysize != imIn2->ysize) return ImagingError_Mismatch(); /* Shortcuts */ if (alpha == 0.0) return ImagingCopy(imIn1); else if (alpha == 1.0) return ImagingCopy(imIn2); imOut = ImagingNew(imIn1->mode, imIn1->xsize, imIn1->ysize); if (!imOut) return NULL; ImagingCopyInfo(imOut, imIn1); if (alpha >= 0 && alpha <= 1.0) { /* Interpolate between bands */ for (y = 0; y < imIn1->ysize; y++) { UINT8* in1 = (UINT8*) imIn1->image[y]; UINT8* in2 = (UINT8*) imIn2->image[y]; UINT8* out = (UINT8*) imOut->image[y]; for (x = 0; x < imIn1->linesize; x++) out[x] = (UINT8) ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); } } else { /* Extrapolation; must make sure to clip resulting values */ for (y = 0; y < imIn1->ysize; y++) { UINT8* in1 = (UINT8*) imIn1->image[y]; UINT8* in2 = (UINT8*) imIn2->image[y]; UINT8* out = (UINT8*) imOut->image[y]; for (x = 0; x < imIn1->linesize; x++) { float temp = ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); if (temp <= 0.0) out[x] = 0; else if (temp >= 255.0) out[x] = 255; else out[x] = (UINT8) temp; } } } return imOut; }
Imaging ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn) { /* allocate or validate output image */ if (imOut) { /* make sure images match */ if (strcmp(imOut->mode, mode) != 0 || imOut->xsize != imIn->xsize || imOut->ysize != imIn->ysize) { return ImagingError_Mismatch(); } } else { /* create new image */ imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; } return imOut; }
Imaging ImagingPutBand(Imaging imOut, Imaging imIn, int band) { int x, y; /* Check arguments */ if (!imIn || imIn->bands != 1 || !imOut) return (Imaging) ImagingError_ModeError(); if (band < 0 || band >= imOut->bands) return (Imaging) ImagingError_ValueError("band index out of range"); if (imIn->type != imOut->type || imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) return (Imaging) ImagingError_Mismatch(); /* Shortcuts */ if (imOut->bands == 1) return ImagingCopy2(imOut, imIn); /* Special case for LXXA etc */ if (imOut->bands == 2 && band == 1) band = 3; /* Insert band into image */ for (y = 0; y < imIn->ysize; y++) { UINT8* in = imIn->image8[y]; UINT8* out = (UINT8*) imOut->image[y] + band; for (x = 0; x < imIn->xsize; x++) { *out = in[x]; out += 4; } } return imOut; }
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; }
Imaging ImagingAlphaComposite(Imaging imDst, Imaging imSrc) { Imaging imOut; int x, y; /* Check arguments */ if (!imDst || !imSrc || strcmp(imDst->mode, "RGBA") || imDst->type != IMAGING_TYPE_UINT8 || imDst->bands != 4) return ImagingError_ModeError(); if (strcmp(imDst->mode, imSrc->mode) || imDst->type != imSrc->type || imDst->bands != imSrc->bands || imDst->xsize != imSrc->xsize || imDst->ysize != imSrc->ysize) return ImagingError_Mismatch(); imOut = ImagingNew(imDst->mode, imDst->xsize, imDst->ysize); if (!imOut) return NULL; ImagingCopyInfo(imOut, imDst); for (y = 0; y < imDst->ysize; y++) { rgba8* dst = (rgba8*) imDst->image[y]; rgba8* src = (rgba8*) imSrc->image[y]; rgba8* out = (rgba8*) imOut->image[y]; for (x = 0; x < imDst->xsize; x ++) { if (src->a == 0) { // Copy 4 bytes at once. *out = *dst; } else { // Integer implementation with increased precision. // Each variable has extra meaningful bits. // Divisions are rounded. // This code uses trick from Paste.c: // (a + (2 << (n-1)) - 1) / ((2 << n)-1) // almost equivalent to: // tmp = a + (2 << (n-1)), ((tmp >> n) + tmp) >> n UINT16 blend = dst->a * (255 - src->a); UINT16 outa255 = src->a * 255 + blend; // There we use 7 bits for precision. // We could use more, but we go beyond 32 bits. UINT16 coef1 = src->a * 255 * 255 * 128 / outa255; UINT16 coef2 = blend * 255 * 128 / outa255; #define SHIFTFORDIV255(a)\ ((a >> 8) + a >> 8) UINT32 tmpr = src->r * coef1 + dst->r * coef2 + (0x80 << 7); out->r = SHIFTFORDIV255(tmpr) >> 7; UINT32 tmpg = src->g * coef1 + dst->g * coef2 + (0x80 << 7); out->g = SHIFTFORDIV255(tmpg) >> 7; UINT32 tmpb = src->b * coef1 + dst->b * coef2 + (0x80 << 7); out->b = SHIFTFORDIV255(tmpb) >> 7; out->a = SHIFTFORDIV255(outa255 + 0x80); } dst++; src++; out++; } } return imOut; }
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; }
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; }