void ReplaceColorFilter::applyToGrayscale(FilterManager* filterMgr) { const uint16_t* src_address = (uint16_t*)filterMgr->getSourceAddress(); uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); int w = filterMgr->getWidth(); int src_k, src_a; int dst_k, dst_a; int x, c; dst_k = _graya_getv(m_from); dst_a = _graya_geta(m_from); for (x=0; x<w; x++) { if (filterMgr->skipPixel()) { ++src_address; ++dst_address; continue; } c = *(src_address++); src_k = _graya_getv(c); src_a = _graya_geta(c); if ((ABS(src_k-dst_k) <= m_tolerance) && (ABS(src_a-dst_a) <= m_tolerance)) *(dst_address++) = m_to; else *(dst_address++) = c; } }
// static Color Color::fromImage(PixelFormat pixelFormat, int c) { Color color = Color::fromMask(); switch (pixelFormat) { case IMAGE_RGB: if (_rgba_geta(c) > 0) { color = Color::fromRgb(_rgba_getr(c), _rgba_getg(c), _rgba_getb(c)); } break; case IMAGE_GRAYSCALE: if (_graya_geta(c) > 0) { color = Color::fromGray(_graya_getv(c)); } break; case IMAGE_INDEXED: color = Color::fromIndex(c); break; } return color; }
void InvertColorFilter::applyToGrayscale(FilterManager* filterMgr) { const uint16_t* src_address = (uint16_t*)filterMgr->getSourceAddress(); uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); int w = filterMgr->getWidth(); Target target = filterMgr->getTarget(); int x, c, k, a; for (x=0; x<w; x++) { if (filterMgr->skipPixel()) { ++src_address; ++dst_address; continue; } c = *(src_address++); k = _graya_getv(c); a = _graya_geta(c); if (target & TARGET_GRAY_CHANNEL) k ^= 0xff; if (target & TARGET_ALPHA_CHANNEL) a ^= 0xff; *(dst_address++) = _graya(k, a); } }
static inline bool color_equal_16(uint16_t c1, uint16_t c2, int tolerance) { if (tolerance == 0) return (c1 == c2) || (_graya_geta(c1) == 0 && _graya_geta(c2) == 0); else { int k1 = _graya_getv(c1); int a1 = _graya_geta(c1); int k2 = _graya_getv(c2); int a2 = _graya_geta(c2); if (a1 == 0 && a2 == 0) return true; return ((ABS(k1-k2) <= tolerance) && (ABS(a1-a2) <= tolerance)); } }
void write_scanline(GrayscaleTraits::address_t address, int w, uint8_t* buffer) { for (int x=0; x<w; ++x) { *(buffer++) = _graya_getv(*address); *(buffer++) = _graya_geta(*address); ++address; } }
inline void operator()(RgbTraits::address_t& scanline_address, RgbTraits::address_t& dst_address, GrayscaleTraits::address_t& src_address, int opacity) { if (*src_address != m_mask_color) { int v = _graya_getv(*src_address); *scanline_address = (*m_blend_color)(*dst_address, _rgba(v, v, v, _graya_geta(*src_address)), opacity); } else *scanline_address = *dst_address; }
void ConvolutionMatrixFilter::applyToGrayscale(FilterManager* filterMgr) { if (!m_matrix) return; const Image* src = filterMgr->getSourceImage(); uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); Target target = filterMgr->getTarget(); uint16_t color; GetPixelsDelegateGrayscale delegate; int x = filterMgr->getX(); int x2 = x+filterMgr->getWidth(); int y = filterMgr->getY(); for (; x<x2; ++x) { // Avoid the non-selected region if (filterMgr->skipPixel()) { ++dst_address; continue; } delegate.reset(m_matrix); get_neighboring_pixels<GrayscaleTraits>(src, x, y, m_matrix->getWidth(), m_matrix->getHeight(), m_matrix->getCenterX(), m_matrix->getCenterY(), m_tiledMode, delegate); color = image_getpixel_fast<GrayscaleTraits>(src, x, y); if (delegate.div == 0) { *(dst_address++) = color; continue; } if (target & TARGET_GRAY_CHANNEL) { delegate.v = delegate.v / delegate.div + m_matrix->getBias(); delegate.v = MID(0, delegate.v, 255); } else delegate.v = _graya_getv(color); if (target & TARGET_ALPHA_CHANNEL) { delegate.a = delegate.a / m_matrix->getDiv() + m_matrix->getBias(); delegate.a = MID(0, delegate.a, 255); } else delegate.a = _graya_geta(color); *(dst_address++) = _graya(delegate.v, delegate.a); } }
void MedianFilter::applyToGrayscale(FilterManager* filterMgr) { const Image* src = filterMgr->getSourceImage(); uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); Target target = filterMgr->getTarget(); int color, k, a; GetPixelsDelegateGrayscale delegate(m_channel); int x = filterMgr->getX(); int x2 = x+filterMgr->getWidth(); int y = filterMgr->getY(); for (; x<x2; ++x) { // Avoid the non-selected region if (filterMgr->skipPixel()) { ++dst_address; continue; } delegate.reset(); get_neighboring_pixels<GrayscaleTraits>(src, x, y, m_width, m_height, m_width/2, m_height/2, m_tiledMode, delegate); color = image_getpixel_fast<GrayscaleTraits>(src, x, y); if (target & TARGET_GRAY_CHANNEL) { std::sort(m_channel[0].begin(), m_channel[0].end()); k = m_channel[0][m_ncolors/2]; } else k = _graya_getv(color); if (target & TARGET_ALPHA_CHANNEL) { std::sort(m_channel[1].begin(), m_channel[1].end()); a = m_channel[1][m_ncolors/2]; } else a = _graya_geta(color); *(dst_address++) = _graya(k, a); } }
/** * Resizes the source image @a src to the destination image @a dst. * * @warning If you are using the RESIZE_METHOD_BILINEAR, it is * recommended to use @ref image_fixup_transparent_colors function * over the source image @a src before using this routine. */ void image_resize(const Image* src, Image* dst, ResizeMethod method, const Palette* pal, const RgbMap* rgbmap) { switch (method) { // TODO optimize this case RESIZE_METHOD_NEAREST_NEIGHBOR: { uint32_t color; double u, v, du, dv; int x, y; u = v = 0.0; du = src->w * 1.0 / dst->w; dv = src->h * 1.0 / dst->h; for (y=0; y<dst->h; ++y) { for (x=0; x<dst->w; ++x) { color = src->getpixel(MID(0, u, src->w-1), MID(0, v, src->h-1)); dst->putpixel(x, y, color); u += du; } u = 0.0; v += dv; } break; } // TODO optimize this case RESIZE_METHOD_BILINEAR: { uint32_t color[4], dst_color = 0; double u, v, du, dv; int u_floor, u_floor2; int v_floor, v_floor2; int x, y; u = v = 0.0; du = (src->w-1) * 1.0 / (dst->w-1); dv = (src->h-1) * 1.0 / (dst->h-1); for (y=0; y<dst->h; ++y) { for (x=0; x<dst->w; ++x) { u_floor = floor(u); v_floor = floor(v); if (u_floor > src->w-1) { u_floor = src->w-1; u_floor2 = src->w-1; } else if (u_floor == src->w-1) u_floor2 = u_floor; else u_floor2 = u_floor+1; if (v_floor > src->h-1) { v_floor = src->h-1; v_floor2 = src->h-1; } else if (v_floor == src->h-1) v_floor2 = v_floor; else v_floor2 = v_floor+1; // get the four colors color[0] = src->getpixel(u_floor, v_floor); color[1] = src->getpixel(u_floor2, v_floor); color[2] = src->getpixel(u_floor, v_floor2); color[3] = src->getpixel(u_floor2, v_floor2); // calculate the interpolated color double u1 = u - u_floor; double v1 = v - v_floor; double u2 = 1 - u1; double v2 = 1 - v1; switch (dst->getPixelFormat()) { case IMAGE_RGB: { int r = ((_rgba_getr(color[0])*u2 + _rgba_getr(color[1])*u1)*v2 + (_rgba_getr(color[2])*u2 + _rgba_getr(color[3])*u1)*v1); int g = ((_rgba_getg(color[0])*u2 + _rgba_getg(color[1])*u1)*v2 + (_rgba_getg(color[2])*u2 + _rgba_getg(color[3])*u1)*v1); int b = ((_rgba_getb(color[0])*u2 + _rgba_getb(color[1])*u1)*v2 + (_rgba_getb(color[2])*u2 + _rgba_getb(color[3])*u1)*v1); int a = ((_rgba_geta(color[0])*u2 + _rgba_geta(color[1])*u1)*v2 + (_rgba_geta(color[2])*u2 + _rgba_geta(color[3])*u1)*v1); dst_color = _rgba(r, g, b, a); break; } case IMAGE_GRAYSCALE: { int v = ((_graya_getv(color[0])*u2 + _graya_getv(color[1])*u1)*v2 + (_graya_getv(color[2])*u2 + _graya_getv(color[3])*u1)*v1); int a = ((_graya_geta(color[0])*u2 + _graya_geta(color[1])*u1)*v2 + (_graya_geta(color[2])*u2 + _graya_geta(color[3])*u1)*v1); dst_color = _graya(v, a); break; } case IMAGE_INDEXED: { int r = ((_rgba_getr(pal->getEntry(color[0]))*u2 + _rgba_getr(pal->getEntry(color[1]))*u1)*v2 + (_rgba_getr(pal->getEntry(color[2]))*u2 + _rgba_getr(pal->getEntry(color[3]))*u1)*v1); int g = ((_rgba_getg(pal->getEntry(color[0]))*u2 + _rgba_getg(pal->getEntry(color[1]))*u1)*v2 + (_rgba_getg(pal->getEntry(color[2]))*u2 + _rgba_getg(pal->getEntry(color[3]))*u1)*v1); int b = ((_rgba_getb(pal->getEntry(color[0]))*u2 + _rgba_getb(pal->getEntry(color[1]))*u1)*v2 + (_rgba_getb(pal->getEntry(color[2]))*u2 + _rgba_getb(pal->getEntry(color[3]))*u1)*v1); int a = (((color[0] == 0 ? 0: 255)*u2 + (color[1] == 0 ? 0: 255)*u1)*v2 + ((color[2] == 0 ? 0: 255)*u2 + (color[3] == 0 ? 0: 255)*u1)*v1); dst_color = a > 127 ? rgbmap->mapColor(r, g, b): 0; break; } case IMAGE_BITMAP: { int g = ((255*color[0]*u2 + 255*color[1]*u1)*v2 + (255*color[2]*u2 + 255*color[3]*u1)*v1); dst_color = g > 127 ? 1: 0; break; } } dst->putpixel(x, y, dst_color); u += du; } u = 0.0; v += dv; } break; } } }
/** * This routine does not modify the image to the human eye, but * internally tries to fixup all colors that are completelly * transparent (alpha = 0) with the average of its 4-neighbors. */ void image_fixup_transparent_colors(Image* image) { int x, y, u, v; switch (image->getPixelFormat()) { case IMAGE_RGB: { uint32_t c; int r, g, b, count; for (y=0; y<image->h; ++y) { for (x=0; x<image->w; ++x) { c = image_getpixel_fast<RgbTraits>(image, x, y); // if this is a completelly-transparent pixel... if (_rgba_geta(c) == 0) { count = 0; r = g = b = 0; for (v=y-1; v<=y+1; ++v) { for (u=x-1; u<=x+1; ++u) { if ((u >= 0) && (v >= 0) && (u < image->w) && (v < image->h)) { c = image_getpixel_fast<RgbTraits>(image, u, v); if (_rgba_geta(c) > 0) { r += _rgba_getr(c); g += _rgba_getg(c); b += _rgba_getb(c); ++count; } } } } if (count > 0) { r /= count; g /= count; b /= count; image_putpixel_fast<RgbTraits>(image, x, y, _rgba(r, g, b, 0)); } } } } break; } case IMAGE_GRAYSCALE: { uint16_t c; int k, count; for (y=0; y<image->h; ++y) { for (x=0; x<image->w; ++x) { c = image_getpixel_fast<GrayscaleTraits>(image, x, y); // if this is a completelly-transparent pixel... if (_graya_geta(c) == 0) { count = 0; k = 0; for (v=y-1; v<=y+1; ++v) { for (u=x-1; u<=x+1; ++u) { if ((u >= 0) && (v >= 0) && (u < image->w) && (v < image->h)) { c = image_getpixel_fast<GrayscaleTraits>(image, u, v); if (_graya_geta(c) > 0) { k += _graya_getv(c); ++count; } } } } if (count > 0) { k /= count; image_putpixel_fast<GrayscaleTraits>(image, x, y, _graya(k, 0)); } } } } break; } } }
void write_pixel(FILE* f, GrayscaleTraits::pixel_t c) { fputc(_graya_getv(c), f); fputc(_graya_geta(c), f); }
bool JpegFormat::onSave(FileOp* fop) { struct jpeg_compress_struct cinfo; struct error_mgr jerr; Image *image = fop->seq.image; FILE *file; JSAMPARRAY buffer; JDIMENSION buffer_height; SharedPtr<JpegOptions> jpeg_options = fop->seq.format_options; int c; // Open the file for write in it. file = fopen(fop->filename.c_str(), "wb"); if (!file) { fop_error(fop, "Error creating file.\n"); return false; } // Allocate and initialize JPEG compression object. jerr.fop = fop; cinfo.err = jpeg_std_error(&jerr.head); jpeg_create_compress(&cinfo); // SPECIFY data destination file. jpeg_stdio_dest(&cinfo, file); // SET parameters for compression. cinfo.image_width = image->w; cinfo.image_height = image->h; if (image->imgtype == IMAGE_GRAYSCALE) { cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; } else { cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; } jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, (int)MID(0, 100.0f * jpeg_options->quality, 100), true); cinfo.dct_method = JDCT_ISLOW; cinfo.smoothing_factor = 0; // START compressor. jpeg_start_compress(&cinfo, true); // CREATE the buffer. buffer_height = 1; buffer = (JSAMPARRAY)base_malloc(sizeof(JSAMPROW) * buffer_height); if (!buffer) { fop_error(fop, "Not enough memory for the buffer.\n"); jpeg_destroy_compress(&cinfo); fclose(file); return false; } for (c=0; c<(int)buffer_height; c++) { buffer[c] = (JSAMPROW)base_malloc(sizeof(JSAMPLE) * cinfo.image_width * cinfo.num_components); if (!buffer[c]) { fop_error(fop, "Not enough memory for buffer scanlines.\n"); for (c--; c>=0; c--) base_free(buffer[c]); base_free(buffer); jpeg_destroy_compress(&cinfo); fclose(file); return false; } } // Write each scan line. while (cinfo.next_scanline < cinfo.image_height) { // RGB if (image->imgtype == IMAGE_RGB) { uint32_t* src_address; uint8_t* dst_address; int x, y; for (y=0; y<(int)buffer_height; y++) { src_address = ((uint32_t**)image->line)[cinfo.next_scanline+y]; dst_address = ((uint8_t**)buffer)[y]; for (x=0; x<image->w; x++) { c = *(src_address++); *(dst_address++) = _rgba_getr(c); *(dst_address++) = _rgba_getg(c); *(dst_address++) = _rgba_getb(c); } } } // Grayscale. else { uint16_t* src_address; uint8_t* dst_address; int x, y; for (y=0; y<(int)buffer_height; y++) { src_address = ((uint16_t**)image->line)[cinfo.next_scanline+y]; dst_address = ((uint8_t**)buffer)[y]; for (x=0; x<image->w; x++) *(dst_address++) = _graya_getv(*(src_address++)); } } jpeg_write_scanlines(&cinfo, buffer, buffer_height); fop_progress(fop, (float)(cinfo.next_scanline+1) / (float)(cinfo.image_height)); } // Destroy all data. for (c=0; c<(int)buffer_height; c++) base_free(buffer[c]); base_free(buffer); // Finish compression. jpeg_finish_compress(&cinfo); // Release JPEG compression object. jpeg_destroy_compress(&cinfo); // We can close the output file. fclose(file); // All fine. return true; }
static void thumbnail_render(BITMAP* bmp, const Image* image, bool has_alpha, const Palette* palette) { register int c, x, y; int w, h, x1, y1; double sx, sy, scale; ASSERT(image != NULL); sx = (double)image->w / (double)bmp->w; sy = (double)image->h / (double)bmp->h; scale = MAX(sx, sy); w = image->w / scale; h = image->h / scale; w = MIN(bmp->w, w); h = MIN(bmp->h, h); x1 = bmp->w/2 - w/2; y1 = bmp->h/2 - h/2; x1 = MAX(0, x1); y1 = MAX(0, y1); /* with alpha blending */ if (has_alpha) { register int c2; rectgrid(bmp, 0, 0, bmp->w-1, bmp->h-1, bmp->w/4, bmp->h/4); switch (image->getPixelFormat()) { case IMAGE_RGB: for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); c2 = getpixel(bmp, x1+x, y1+y); c = _rgba_blend_normal(_rgba(getr(c2), getg(c2), getb(c2), 255), c, 255); putpixel(bmp, x1+x, y1+y, makecol(_rgba_getr(c), _rgba_getg(c), _rgba_getb(c))); } break; case IMAGE_GRAYSCALE: for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); c2 = getpixel(bmp, x1+x, y1+y); c = _graya_blend_normal(_graya(getr(c2), 255), c, 255); putpixel(bmp, x1+x, y1+y, makecol(_graya_getv(c), _graya_getv(c), _graya_getv(c))); } break; case IMAGE_INDEXED: { for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); if (c != 0) { ASSERT(c >= 0 && c < palette->size()); c = palette->getEntry(MID(0, c, palette->size()-1)); putpixel(bmp, x1+x, y1+y, makecol(_rgba_getr(c), _rgba_getg(c), _rgba_getb(c))); } } break; } } } /* without alpha blending */ else { clear_to_color(bmp, makecol(128, 128, 128)); switch (image->getPixelFormat()) { case IMAGE_RGB: for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); putpixel(bmp, x1+x, y1+y, makecol(_rgba_getr(c), _rgba_getg(c), _rgba_getb(c))); } break; case IMAGE_GRAYSCALE: for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); putpixel(bmp, x1+x, y1+y, makecol(_graya_getv(c), _graya_getv(c), _graya_getv(c))); } break; case IMAGE_INDEXED: { for (y=0; y<h; y++) for (x=0; x<w; x++) { c = image_getpixel(image, x*scale, y*scale); ASSERT(c >= 0 && c < palette->size()); c = palette->getEntry(MID(0, c, palette->size()-1)); putpixel(bmp, x1+x, y1+y, makecol(_rgba_getr(c), _rgba_getg(c), _rgba_getb(c))); } break; } } } }