2 Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "imagefilters.h"
20 #include "util/numeric.h"
23 /* Fill in RGB values for transparent pixels, to correct for odd colors
24 * appearing at borders when blending. This is because many PNG optimizers
25 * like to discard RGB values of transparent pixels, but when blending then
26 * with non-transparent neighbors, their RGB values will shpw up nonetheless.
28 * This function modifies the original image in-place.
30 * Parameter "threshold" is the alpha level below which pixels are considered
31 * transparent. Should be 127 for 3d where alpha is threshold, but 0 for
32 * 2d where alpha is blended.
34 void imageCleanTransparent(video::IImage *src, u32 threshold)
36 core::dimension2d<u32> dim = src->getDimension();
38 // Walk each pixel looking for fully transparent ones.
39 // Note: loop y around x for better cache locality.
40 for (u32 ctry = 0; ctry < dim.Height; ctry++)
41 for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) {
43 // Ignore opaque pixels.
44 irr::video::SColor c = src->getPixel(ctrx, ctry);
45 if (c.getAlpha() > threshold)
48 // Sample size and total weighted r, g, b values.
49 u32 ss = 0, sr = 0, sg = 0, sb = 0;
51 // Walk each neighbor pixel (clipped to image bounds).
52 for (u32 sy = (ctry < 1) ? 0 : (ctry - 1);
53 sy <= (ctry + 1) && sy < dim.Height; sy++)
54 for (u32 sx = (ctrx < 1) ? 0 : (ctrx - 1);
55 sx <= (ctrx + 1) && sx < dim.Width; sx++) {
57 // Ignore transparent pixels.
58 irr::video::SColor d = src->getPixel(sx, sy);
59 if (d.getAlpha() <= threshold)
62 // Add RGB values weighted by alpha.
66 sg += a * d.getGreen();
67 sb += a * d.getBlue();
70 // If we found any neighbor RGB data, set pixel to average
76 src->setPixel(ctrx, ctry, c);
81 /* Scale a region of an image into another image, using nearest-neighbor with
82 * anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
83 * to prevent non-integer scaling ratio artifacts. Note that this may cause
84 * some blending at the edges where pixels don't line up perfectly, but this
85 * filter is designed to produce the most accurate results for both upscaling
88 void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest)
90 double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;
94 // Cache rectsngle boundaries.
95 double sox = srcrect.UpperLeftCorner.X * 1.0;
96 double soy = srcrect.UpperLeftCorner.Y * 1.0;
97 double sw = srcrect.getWidth() * 1.0;
98 double sh = srcrect.getHeight() * 1.0;
100 // Walk each destination image pixel.
101 // Note: loop y around x for better cache locality.
102 core::dimension2d<u32> dim = dest->getDimension();
103 for (dy = 0; dy < dim.Height; dy++)
104 for (dx = 0; dx < dim.Width; dx++) {
106 // Calculate floating-point source rectangle bounds.
107 // Do some basic clipping, and for mirrored/flipped rects,
108 // make sure min/max are in the right order.
109 minsx = sox + (dx * sw / dim.Width);
110 minsx = rangelim(minsx, 0, sw);
111 maxsx = minsx + sw / dim.Width;
112 maxsx = rangelim(maxsx, 0, sw);
114 SWAP(double, minsx, maxsx);
115 minsy = soy + (dy * sh / dim.Height);
116 minsy = rangelim(minsy, 0, sh);
117 maxsy = minsy + sh / dim.Height;
118 maxsy = rangelim(maxsy, 0, sh);
120 SWAP(double, minsy, maxsy);
122 // Total area, and integral of r, g, b values over that area,
123 // initialized to zero, to be summed up in next loops.
130 // Loop over the integral pixel positions described by those bounds.
131 for (sy = floor(minsy); sy < maxsy; sy++)
132 for (sx = floor(minsx); sx < maxsx; sx++) {
134 // Calculate width, height, then area of dest pixel
135 // that's covered by this source pixel.
139 if (maxsx < (sx + 1))
140 pw += maxsx - sx - 1;
144 if (maxsy < (sy + 1))
145 ph += maxsy - sy - 1;
148 // Get source pixel and add it to totals, weighted
149 // by covered area and alpha.
150 pxl = src->getPixel((u32)sx, (u32)sy);
152 ra += pa * pxl.getRed();
153 ga += pa * pxl.getGreen();
154 ba += pa * pxl.getBlue();
155 aa += pa * pxl.getAlpha();
158 // Set the destination image pixel to the average color.
160 pxl.setRed(ra / area + 0.5);
161 pxl.setGreen(ga / area + 0.5);
162 pxl.setBlue(ba / area + 0.5);
163 pxl.setAlpha(aa / area + 0.5);
170 dest->setPixel(dx, dy, pxl);