VALUE oily_png_encode_png_image_pass_to_stream(VALUE self, VALUE stream, VALUE color_mode, VALUE bit_depth, VALUE filtering) { UNUSED_PARAMETER(bit_depth); // Get the data char depth = (char) FIX2INT(bit_depth); long width = FIX2LONG(rb_funcall(self, rb_intern("width"), 0)); long height = FIX2LONG(rb_funcall(self, rb_intern("height"), 0)); VALUE pixels = rb_funcall(self, rb_intern("pixels"), 0); if (RARRAY_LEN(pixels) != width * height) { rb_raise(rb_eRuntimeError, "The number of pixels does not match the canvas dimensions."); } // Get the encoding palette if we're encoding to an indexed bytestream. VALUE encoding_palette = Qnil; if (FIX2INT(color_mode) == OILY_PNG_COLOR_INDEXED) { encoding_palette = oily_png_encode_palette(self); } char pixel_size = oily_png_pixel_bytesize(FIX2INT(color_mode), depth); long line_size = oily_png_scanline_bytesize(FIX2INT(color_mode), depth, width); long pass_size = oily_png_pass_bytesize(FIX2INT(color_mode), depth, width, height); // Allocate memory for the byte array. BYTE* bytes = ALLOC_N(BYTE, pass_size); // Get the scanline encoder function. scanline_encoder_func scanline_encoder = oily_png_encode_scanline_func(FIX2INT(color_mode), depth); if (scanline_encoder == NULL) { rb_raise(rb_eRuntimeError, "No encoder for color mode %d and bit depth %d", FIX2INT(color_mode), depth); } long y, pos; for (y = height - 1; y >= 0; y--) { pos = line_size * y; bytes[pos] = (BYTE) FIX2INT(filtering); scanline_encoder(bytes + pos + 1, pixels, y, width, encoding_palette); } if (FIX2INT(filtering) != OILY_PNG_FILTER_NONE) { // Get the scanline filter function void (*scanline_filter)(BYTE*, long, long, char) = NULL; switch (FIX2INT(filtering)) { case OILY_PNG_FILTER_SUB: scanline_filter = &oily_png_encode_filter_sub; break; case OILY_PNG_FILTER_UP: scanline_filter = &oily_png_encode_filter_up; break; case OILY_PNG_FILTER_AVERAGE: scanline_filter = &oily_png_encode_filter_average; break; case OILY_PNG_FILTER_PAETH: scanline_filter = &oily_png_encode_filter_paeth; break; default: rb_raise(rb_eRuntimeError, "Unsupported filter type: %d", FIX2INT(filtering)); } for (y = height - 1; y >= 0; y--) { scanline_filter(bytes, line_size * y, line_size, pixel_size); } } // Append to encoded image pass to the output stream. rb_str_cat(stream, (char*) bytes, pass_size); xfree(bytes); return Qnil; }
/*! * Reduces an image using the interpolation filter and zoom specified in * @@selectZoomFilter@. ^ * [slines] - array of line pointers for the input image ^ * [aXsize], [aYsize] - size of input image ^ * [aXoff], [aYoff] - coordinate in the input image at which the lower left edge of the * lower left pixel of the output starts, where input coordinates are zero at the lower * left edge of the first input pixel ^ * [bXsize], [bYsize] - size of output image to be created, potentially from a subset of * the input in X or Y and into a subset of the output array ^ * [bXdim] - X dimension of the full output image array ^ * [bXoff] - Index of first pixel in X to fill in output array ^ * [dtype] - Type of data, a SLICE_MODE_... value. BYTE, FLOAT, RGB, SHORT, and USHORT * are allowed ^ * [outData] - Output array, or address of first line to fill in a larger output array. * The array is the same data type as the input unless mapping is being done, in which * case it must be unsigned integers. ^ * [cindex] - Unsigned integer RGBA values to map byte or short data into, or NULL for * no mapping ^ * [bindex] - Byte values to map RGB data into, or NULL for no mapping. Each channel is * mapped the same and a fourth channel is added with zero. ^ * Returns 1 if no filter has been selected, 2 for an unsupported data type, 3 for an * attempt to map float data, 4 if needed input coordinates to compose the output image go * out of range, or 5 for a memory allocation error. ^ * This function is parallelized with OpenMP and uses the same formula as @cubinterp for * reducing the number of threads for small images. */ int zoomWithFilter(unsigned char **slines, int aXsize, int aYsize, float aXoff, float aYoff, int bXsize, int bYsize, int bXdim, int bXoff, int dtype, void *outData, b3dUInt32 *cindex, unsigned char *bindex) { Weighttab *xweights; /* sampled filter at each dst x pos; for xfilt*/ b3dInt16 *xweightSbuf, *xwp; /* big block of memory addressed by xweights */ Weighttab yweight[MAX_THREADS]; /* a single sampled filter for current y pos */ b3dFloat *xweightFbuf, *xwfp; b3dInt32 *accumBuf[MAX_THREADS]; unsigned char *filtBuf[MAX_THREADS]; unsigned char *lineb, *obufb; int mapping = 0; int numThreads; int psizeAccum, psizeWgt, psizeFilt, bx, by, i, ayf, thr; float fweight = 0.; b3dInt16 sweight = 0; if (!sFilt_func) return 1; psizeAccum = 4; psizeWgt = 2; switch (dtype) { case SLICE_MODE_BYTE: if (cindex) mapping = 1; psizeFilt = 1; break; case SLICE_MODE_RGB: if (bindex) mapping = 1; psizeAccum = 12; psizeFilt = 3; break; case SLICE_MODE_FLOAT: if (cindex || bindex) return 3; psizeFilt = 4; psizeWgt = 4; break; case SLICE_MODE_SHORT: case SLICE_MODE_USHORT: if (cindex) mapping = 1; psizeFilt = 2; psizeWgt = 4; break; default: return 2; } if (aXoff < 0. || aYoff < 0. || (int)(aXoff + bXsize * sXscale) > aXsize || (int)(aYoff + bYsize * sYscale) > aYsize) { /* printf("axo %f bxs %d sc %f xe %f axs %d ayo %f bys %d ye %f ays %d\n", aXoff, bXsize, sXscale, aXoff + bXsize * sXscale, aXsize, aYoff, bYsize, aYoff + bYsize * sYscale, aYsize); */ return 4; } numThreads = B3DNINT(0.04 * sqrt((double)aXsize * aYsize)); numThreads = numOMPthreads(B3DMIN(numThreads, MAX_THREADS)); /* Allocate accumulation, filter output, and weight buffers */ xweights = B3DMALLOC(Weighttab, bXsize); xweightSbuf = (b3dInt16 *)malloc(bXsize * sXwidth * psizeWgt); if (!xweightSbuf || !xweights) { B3DFREE(xweights); B3DFREE(xweightSbuf); return 5; } for (i = 0; i < numThreads; i++) { filtBuf[i] = NULL; accumBuf[i] = (b3dInt32 *)malloc(aXsize * psizeAccum); yweight[i].weight.s = (b3dInt16 *)malloc(sYwidth * psizeWgt); if (mapping) filtBuf[i] = (unsigned char *)malloc(bXsize * psizeFilt); if (!accumBuf[i] || !yweight[i].weight.s || (mapping && !filtBuf[i])) { for (by = 0; by <= i; by++) { B3DFREE(yweight[by].weight.s); B3DFREE(accumBuf[by]); B3DFREE(filtBuf[by]); } return 5; } } xweightFbuf = (b3dFloat *)xweightSbuf; /* * prepare a weighttab (a sampled filter for source pixels) for * each dest x position */ xwfp = xweightFbuf; xwp = xweightSbuf; for (bx = 0; bx < bXsize; bx++, xwp += sXwidth, xwfp += sXwidth) { if (psizeWgt == 4) xweights[bx].weight.f = xwfp; else xweights[bx].weight.s = xwp; make_weighttab(bx, aXoff + (bx + 0.5) * sXscale, aXsize, sXscale, sXsupport, dtype, &xweights[bx]); } #pragma omp parallel for num_threads(numThreads) \ shared(bYsize, aYoff, sYscale, aYsize, dtype, yweight, psizeAccum, accumBuf, \ aXsize, slines, outData, psizeFilt, bXsize, mapping, filtBuf, xweights, \ cindex, bindex, sValueScaling, sYsupport) \ private(by, thr, i, ayf, sweight, fweight, lineb, obufb) for (by = 0; by < bYsize; by++) { thr = b3dOMPthreadNum(); /* prepare a weighttab for dest y position by */ make_weighttab(by, aYoff + (by + 0.5) * sYscale, aYsize, sYscale, sYsupport, dtype, &yweight[thr]); /* Zero the scanline accum buffer */ for (i = 0; i < aXsize * psizeAccum / 4; i++) accumBuf[thr][i] = 0; /* loop over source scanlines that influence this dest scanline */ for (ayf = yweight[thr].i0; ayf < yweight[thr].i1; ayf++) { if (psizeWgt == 2) sweight = yweight[thr].weight.s[ayf-yweight[thr].i0]; else fweight = yweight[thr].weight.f[ayf-yweight[thr].i0] * sValueScaling; lineb = (unsigned char *)slines[ayf]; /* add weighted tbuf into accum (these do yfilt) */ scanline_accum(lineb, dtype, aXsize, accumBuf[thr], sweight, fweight); } /* and filter it into the appropriate line of output or into filtBuf */ obufb = (unsigned char *)outData + psizeFilt * (by * bXdim + bXoff); if (mapping) obufb = filtBuf[thr]; /*for (i = 30; i < 45; i++) printf("%.1f ", *((float *)accumBuf[thr] + i)); printf("\n"); */ scanline_filter(accumBuf[thr], dtype, aXsize, obufb, bXsize, xweights, FINALSHIFT); /* Map to RGBA output, always 4 bytes, if index tables provided */ if (mapping) { obufb = (unsigned char *)outData + 4 * (by * bXdim + bXoff); scanline_remap(filtBuf[thr], dtype, bXsize, obufb, cindex, bindex); } } B3DFREE(xweights); B3DFREE(xweightSbuf); for (by = 0; by < numThreads; by++) { B3DFREE(yweight[by].weight.s); B3DFREE(accumBuf[by]); B3DFREE(filtBuf[by]); } return 0; }