Pillow
330 строк · 9.7 Кб
1#include "Imaging.h"2
3#define MAX(x, y) (((x) > (y)) ? (x) : (y))4#define MIN(x, y) (((x) < (y)) ? (x) : (y))5
6typedef UINT8 pixel[4];7
8void static inline ImagingLineBoxBlur32(9pixel *lineOut,10pixel *lineIn,11int lastx,12int radius,13int edgeA,14int edgeB,15UINT32 ww,16UINT32 fw
17) {18int x;19UINT32 acc[4];20UINT32 bulk[4];21
22#define MOVE_ACC(acc, subtract, add) \23acc[0] += lineIn[add][0] - lineIn[subtract][0]; \24acc[1] += lineIn[add][1] - lineIn[subtract][1]; \25acc[2] += lineIn[add][2] - lineIn[subtract][2]; \26acc[3] += lineIn[add][3] - lineIn[subtract][3];27
28#define ADD_FAR(bulk, acc, left, right) \29bulk[0] = (acc[0] * ww) + (lineIn[left][0] + lineIn[right][0]) * fw; \30bulk[1] = (acc[1] * ww) + (lineIn[left][1] + lineIn[right][1]) * fw; \31bulk[2] = (acc[2] * ww) + (lineIn[left][2] + lineIn[right][2]) * fw; \32bulk[3] = (acc[3] * ww) + (lineIn[left][3] + lineIn[right][3]) * fw;33
34#define SAVE(x, bulk) \35lineOut[x][0] = (UINT8)((bulk[0] + (1 << 23)) >> 24); \36lineOut[x][1] = (UINT8)((bulk[1] + (1 << 23)) >> 24); \37lineOut[x][2] = (UINT8)((bulk[2] + (1 << 23)) >> 24); \38lineOut[x][3] = (UINT8)((bulk[3] + (1 << 23)) >> 24);39
40/* Compute acc for -1 pixel (outside of image):41From "-radius-1" to "-1" get first pixel,
42then from "0" to "radius-1". */
43acc[0] = lineIn[0][0] * (radius + 1);44acc[1] = lineIn[0][1] * (radius + 1);45acc[2] = lineIn[0][2] * (radius + 1);46acc[3] = lineIn[0][3] * (radius + 1);47/* As radius can be bigger than xsize, iterate to edgeA -1. */48for (x = 0; x < edgeA - 1; x++) {49acc[0] += lineIn[x][0];50acc[1] += lineIn[x][1];51acc[2] += lineIn[x][2];52acc[3] += lineIn[x][3];53}54/* Then multiply remainder to last x. */55acc[0] += lineIn[lastx][0] * (radius - edgeA + 1);56acc[1] += lineIn[lastx][1] * (radius - edgeA + 1);57acc[2] += lineIn[lastx][2] * (radius - edgeA + 1);58acc[3] += lineIn[lastx][3] * (radius - edgeA + 1);59
60if (edgeA <= edgeB) {61/* Subtract pixel from left ("0").62Add pixels from radius. */
63for (x = 0; x < edgeA; x++) {64MOVE_ACC(acc, 0, x + radius);65ADD_FAR(bulk, acc, 0, x + radius + 1);66SAVE(x, bulk);67}68/* Subtract previous pixel from "-radius".69Add pixels from radius. */
70for (x = edgeA; x < edgeB; x++) {71MOVE_ACC(acc, x - radius - 1, x + radius);72ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1);73SAVE(x, bulk);74}75/* Subtract previous pixel from "-radius".76Add last pixel. */
77for (x = edgeB; x <= lastx; x++) {78MOVE_ACC(acc, x - radius - 1, lastx);79ADD_FAR(bulk, acc, x - radius - 1, lastx);80SAVE(x, bulk);81}82} else {83for (x = 0; x < edgeB; x++) {84MOVE_ACC(acc, 0, x + radius);85ADD_FAR(bulk, acc, 0, x + radius + 1);86SAVE(x, bulk);87}88for (x = edgeB; x < edgeA; x++) {89MOVE_ACC(acc, 0, lastx);90ADD_FAR(bulk, acc, 0, lastx);91SAVE(x, bulk);92}93for (x = edgeA; x <= lastx; x++) {94MOVE_ACC(acc, x - radius - 1, lastx);95ADD_FAR(bulk, acc, x - radius - 1, lastx);96SAVE(x, bulk);97}98}99
100#undef MOVE_ACC101#undef ADD_FAR102#undef SAVE103}
104
105void static inline ImagingLineBoxBlur8(106UINT8 *lineOut,107UINT8 *lineIn,108int lastx,109int radius,110int edgeA,111int edgeB,112UINT32 ww,113UINT32 fw
114) {115int x;116UINT32 acc;117UINT32 bulk;118
119#define MOVE_ACC(acc, subtract, add) acc += lineIn[add] - lineIn[subtract];120
121#define ADD_FAR(bulk, acc, left, right) \122bulk = (acc * ww) + (lineIn[left] + lineIn[right]) * fw;123
124#define SAVE(x, bulk) lineOut[x] = (UINT8)((bulk + (1 << 23)) >> 24)125
126acc = lineIn[0] * (radius + 1);127for (x = 0; x < edgeA - 1; x++) {128acc += lineIn[x];129}130acc += lineIn[lastx] * (radius - edgeA + 1);131
132if (edgeA <= edgeB) {133for (x = 0; x < edgeA; x++) {134MOVE_ACC(acc, 0, x + radius);135ADD_FAR(bulk, acc, 0, x + radius + 1);136SAVE(x, bulk);137}138for (x = edgeA; x < edgeB; x++) {139MOVE_ACC(acc, x - radius - 1, x + radius);140ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1);141SAVE(x, bulk);142}143for (x = edgeB; x <= lastx; x++) {144MOVE_ACC(acc, x - radius - 1, lastx);145ADD_FAR(bulk, acc, x - radius - 1, lastx);146SAVE(x, bulk);147}148} else {149for (x = 0; x < edgeB; x++) {150MOVE_ACC(acc, 0, x + radius);151ADD_FAR(bulk, acc, 0, x + radius + 1);152SAVE(x, bulk);153}154for (x = edgeB; x < edgeA; x++) {155MOVE_ACC(acc, 0, lastx);156ADD_FAR(bulk, acc, 0, lastx);157SAVE(x, bulk);158}159for (x = edgeA; x <= lastx; x++) {160MOVE_ACC(acc, x - radius - 1, lastx);161ADD_FAR(bulk, acc, x - radius - 1, lastx);162SAVE(x, bulk);163}164}165
166#undef MOVE_ACC167#undef ADD_FAR168#undef SAVE169}
170
171Imaging
172ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) {173ImagingSectionCookie cookie;174
175int y;176
177int radius = (int)floatRadius;178UINT32 ww = (UINT32)(1 << 24) / (floatRadius * 2 + 1);179UINT32 fw = ((1 << 24) - (radius * 2 + 1) * ww) / 2;180
181int edgeA = MIN(radius + 1, imIn->xsize);182int edgeB = MAX(imIn->xsize - radius - 1, 0);183
184UINT32 *lineOut = calloc(imIn->xsize, sizeof(UINT32));185if (lineOut == NULL) {186return ImagingError_MemoryError();187}188
189// printf(">>> %d %d %d\n", radius, ww, fw);190
191ImagingSectionEnter(&cookie);192
193if (imIn->image8) {194for (y = 0; y < imIn->ysize; y++) {195ImagingLineBoxBlur8(196(imIn == imOut ? (UINT8 *)lineOut : imOut->image8[y]),197imIn->image8[y],198imIn->xsize - 1,199radius,200edgeA,201edgeB,202ww,203fw
204);205if (imIn == imOut) {206// Commit.207memcpy(imOut->image8[y], lineOut, imIn->xsize);208}209}210} else {211for (y = 0; y < imIn->ysize; y++) {212ImagingLineBoxBlur32(213imIn == imOut ? (pixel *)lineOut : (pixel *)imOut->image32[y],214(pixel *)imIn->image32[y],215imIn->xsize - 1,216radius,217edgeA,218edgeB,219ww,220fw
221);222if (imIn == imOut) {223// Commit.224memcpy(imOut->image32[y], lineOut, imIn->xsize * 4);225}226}227}228
229ImagingSectionLeave(&cookie);230
231free(lineOut);232
233return imOut;234}
235
236Imaging
237ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) {238int i;239Imaging imTransposed;240
241if (n < 1) {242return ImagingError_ValueError("number of passes must be greater than zero");243}244if (xradius < 0 || yradius < 0) {245return ImagingError_ValueError("radius must be >= 0");246}247
248if (strcmp(imIn->mode, imOut->mode) || imIn->type != imOut->type ||249imIn->bands != imOut->bands || imIn->xsize != imOut->xsize ||250imIn->ysize != imOut->ysize) {251return ImagingError_Mismatch();252}253
254if (imIn->type != IMAGING_TYPE_UINT8) {255return ImagingError_ModeError();256}257
258if (!(strcmp(imIn->mode, "RGB") == 0 || strcmp(imIn->mode, "RGBA") == 0 ||259strcmp(imIn->mode, "RGBa") == 0 || strcmp(imIn->mode, "RGBX") == 0 ||260strcmp(imIn->mode, "CMYK") == 0 || strcmp(imIn->mode, "L") == 0 ||261strcmp(imIn->mode, "LA") == 0 || strcmp(imIn->mode, "La") == 0)) {262return ImagingError_ModeError();263}264
265/* Apply blur in one dimension.266Use imOut as a destination at first pass,
267then use imOut as a source too. */
268
269if (xradius != 0) {270ImagingHorizontalBoxBlur(imOut, imIn, xradius);271for (i = 1; i < n; i++) {272ImagingHorizontalBoxBlur(imOut, imOut, xradius);273}274}275if (yradius != 0) {276imTransposed = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);277if (!imTransposed) {278return NULL;279}280
281/* Transpose result for blur in another direction. */282ImagingTranspose(imTransposed, xradius == 0 ? imIn : imOut);283
284/* Reuse imTransposed as a source and destination there. */285for (i = 0; i < n; i++) {286ImagingHorizontalBoxBlur(imTransposed, imTransposed, yradius);287}288/* Restore original orientation. */289ImagingTranspose(imOut, imTransposed);290
291ImagingDelete(imTransposed);292}293if (xradius == 0 && yradius == 0) {294if (!ImagingCopy2(imOut, imIn)) {295return NULL;296}297}298
299return imOut;300}
301
302static float303_gaussian_blur_radius(float radius, int passes) {304float sigma2, L, l, a;305
306sigma2 = radius * radius / passes;307// from https://www.mia.uni-saarland.de/Publications/gwosdek-ssvm11.pdf308// [7] Box length.309L = sqrt(12.0 * sigma2 + 1.0);310// [11] Integer part of box radius.311l = floor((L - 1.0) / 2.0);312// [14], [Fig. 2] Fractional part of box radius.313a = (2 * l + 1) * (l * (l + 1) - 3 * sigma2);314a /= 6 * (sigma2 - (l + 1) * (l + 1));315
316return l + a;317}
318
319Imaging
320ImagingGaussianBlur(321Imaging imOut, Imaging imIn, float xradius, float yradius, int passes322) {323return ImagingBoxBlur(324imOut,325imIn,326_gaussian_blur_radius(xradius, passes),327_gaussian_blur_radius(yradius, passes),328passes
329);330}
331