1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * This library is free software: you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 3 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <https://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <glib-object.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 
25 #include "gimpcolortypes.h"
26 
27 #include "gimpbilinear.h"
28 
29 
30 /**
31  * SECTION: gimpbilinear
32  * @title: GimpBilinear
33  * @short_description: Utility functions for bilinear interpolation.
34  *
35  * Utility functions for bilinear interpolation.
36  **/
37 
38 
39 gdouble
gimp_bilinear(gdouble x,gdouble y,gdouble * values)40 gimp_bilinear (gdouble  x,
41                gdouble  y,
42                gdouble *values)
43 {
44   gdouble m0, m1;
45 
46   g_return_val_if_fail (values != NULL, 0.0);
47 
48   x = fmod (x, 1.0);
49   y = fmod (y, 1.0);
50 
51   if (x < 0.0)
52     x += 1.0;
53   if (y < 0.0)
54     y += 1.0;
55 
56   m0 = (1.0 - x) * values[0] + x * values[1];
57   m1 = (1.0 - x) * values[2] + x * values[3];
58 
59   return (1.0 - y) * m0 + y * m1;
60 }
61 
62 guchar
gimp_bilinear_8(gdouble x,gdouble y,guchar * values)63 gimp_bilinear_8 (gdouble x,
64                  gdouble y,
65                  guchar *values)
66 {
67   gdouble m0, m1;
68 
69   g_return_val_if_fail (values != NULL, 0);
70 
71   x = fmod (x, 1.0);
72   y = fmod (y, 1.0);
73 
74   if (x < 0.0)
75     x += 1.0;
76   if (y < 0.0)
77     y += 1.0;
78 
79   m0 = (1.0 - x) * values[0] + x * values[1];
80   m1 = (1.0 - x) * values[2] + x * values[3];
81 
82   return (guchar) ((1.0 - y) * m0 + y * m1);
83 }
84 
85 guint16
gimp_bilinear_16(gdouble x,gdouble y,guint16 * values)86 gimp_bilinear_16 (gdouble  x,
87                   gdouble  y,
88                   guint16 *values)
89 {
90   gdouble m0, m1;
91 
92   g_return_val_if_fail (values != NULL, 0);
93 
94   x = fmod (x, 1.0);
95   y = fmod (y, 1.0);
96 
97   if (x < 0.0)
98     x += 1.0;
99   if (y < 0.0)
100     y += 1.0;
101 
102   m0 = (1.0 - x) * values[0] + x * values[1];
103   m1 = (1.0 - x) * values[2] + x * values[3];
104 
105   return (guint16) ((1.0 - y) * m0 + y * m1);
106 }
107 
108 guint32
gimp_bilinear_32(gdouble x,gdouble y,guint32 * values)109 gimp_bilinear_32 (gdouble  x,
110                   gdouble  y,
111                   guint32 *values)
112 {
113   gdouble m0, m1;
114 
115   g_return_val_if_fail (values != NULL, 0);
116 
117   x = fmod (x, 1.0);
118   y = fmod (y, 1.0);
119 
120   if (x < 0.0)
121     x += 1.0;
122   if (y < 0.0)
123     y += 1.0;
124 
125   m0 = (1.0 - x) * values[0] + x * values[1];
126   m1 = (1.0 - x) * values[2] + x * values[3];
127 
128   return (guint32) ((1.0 - y) * m0 + y * m1);
129 }
130 
131 GimpRGB
gimp_bilinear_rgb(gdouble x,gdouble y,GimpRGB * values)132 gimp_bilinear_rgb (gdouble  x,
133                    gdouble  y,
134                    GimpRGB *values)
135 {
136   gdouble m0, m1;
137   gdouble ix, iy;
138   GimpRGB v = { 0, };
139 
140   g_return_val_if_fail (values != NULL, v);
141 
142   x = fmod(x, 1.0);
143   y = fmod(y, 1.0);
144 
145   if (x < 0)
146     x += 1.0;
147   if (y < 0)
148     y += 1.0;
149 
150   ix = 1.0 - x;
151   iy = 1.0 - y;
152 
153   /* Red */
154 
155   m0 = ix * values[0].r + x * values[1].r;
156   m1 = ix * values[2].r + x * values[3].r;
157 
158   v.r = iy * m0 + y * m1;
159 
160   /* Green */
161 
162   m0 = ix * values[0].g + x * values[1].g;
163   m1 = ix * values[2].g + x * values[3].g;
164 
165   v.g = iy * m0 + y * m1;
166 
167   /* Blue */
168 
169   m0 = ix * values[0].b + x * values[1].b;
170   m1 = ix * values[2].b + x * values[3].b;
171 
172   v.b = iy * m0 + y * m1;
173 
174   return v;
175 }
176 
177 GimpRGB
gimp_bilinear_rgba(gdouble x,gdouble y,GimpRGB * values)178 gimp_bilinear_rgba (gdouble  x,
179                     gdouble  y,
180                     GimpRGB *values)
181 {
182   gdouble m0, m1;
183   gdouble ix, iy;
184   gdouble a0, a1, a2, a3, alpha;
185   GimpRGB v = { 0, };
186 
187   g_return_val_if_fail (values != NULL, v);
188 
189   x = fmod (x, 1.0);
190   y = fmod (y, 1.0);
191 
192   if (x < 0)
193     x += 1.0;
194   if (y < 0)
195     y += 1.0;
196 
197   ix = 1.0 - x;
198   iy = 1.0 - y;
199 
200   a0 = values[0].a;
201   a1 = values[1].a;
202   a2 = values[2].a;
203   a3 = values[3].a;
204 
205   /* Alpha */
206 
207   m0 = ix * a0 + x * a1;
208   m1 = ix * a2 + x * a3;
209 
210   alpha = v.a = iy * m0 + y * m1;
211 
212   if (alpha > 0)
213     {
214       /* Red */
215 
216       m0 = ix * a0 * values[0].r + x * a1 * values[1].r;
217       m1 = ix * a2 * values[2].r + x * a3 * values[3].r;
218 
219       v.r = (iy * m0 + y * m1)/alpha;
220 
221       /* Green */
222 
223       m0 = ix * a0 * values[0].g + x * a1 * values[1].g;
224       m1 = ix * a2 * values[2].g + x * a3 * values[3].g;
225 
226       v.g = (iy * m0 + y * m1)/alpha;
227 
228       /* Blue */
229 
230       m0 = ix * a0 * values[0].b + x * a1 * values[1].b;
231       m1 = ix * a2 * values[2].b + x * a3 * values[3].b;
232 
233       v.b = (iy * m0 + y * m1)/alpha;
234     }
235 
236   return v;
237 }
238 
239 /**
240  * gimp_bilinear_pixels_8:
241  * @dest: Pixel, where interpolation result is to be stored.
242  * @x: x-coordinate (0.0 to 1.0).
243  * @y: y-coordinate (0.0 to 1.0).
244  * @bpp: Bytes per pixel.  @dest and each @values item is an array of
245  *    @bpp bytes.
246  * @has_alpha: %TRUE if the last channel is an alpha channel.
247  * @values: Array of four pointers to pixels.
248  *
249  * Computes bilinear interpolation of four pixels.
250  *
251  * When @has_alpha is %FALSE, it's identical to gimp_bilinear_8() on
252  * each channel separately.  When @has_alpha is %TRUE, it handles
253  * alpha channel correctly.
254  *
255  * The pixels in @values correspond to corner x, y coordinates in the
256  * following order: [0,0], [1,0], [0,1], [1,1].
257  **/
258 void
gimp_bilinear_pixels_8(guchar * dest,gdouble x,gdouble y,guint bpp,gboolean has_alpha,guchar ** values)259 gimp_bilinear_pixels_8 (guchar    *dest,
260                         gdouble    x,
261                         gdouble    y,
262                         guint      bpp,
263                         gboolean   has_alpha,
264                         guchar   **values)
265 {
266   guint i;
267 
268   g_return_if_fail (dest != NULL);
269   g_return_if_fail (values != NULL);
270 
271   x = fmod (x, 1.0);
272   y = fmod (y, 1.0);
273 
274   if (x < 0.0)
275     x += 1.0;
276   if (y < 0.0)
277     y += 1.0;
278 
279   if (has_alpha)
280     {
281       guint   ai     = bpp - 1;
282       gdouble alpha0 = values[0][ai];
283       gdouble alpha1 = values[1][ai];
284       gdouble alpha2 = values[2][ai];
285       gdouble alpha3 = values[3][ai];
286       gdouble alpha  = ((1.0 - y) * ((1.0 - x) * alpha0 + x * alpha1)
287                         + y * ((1.0 - x) * alpha2 + x * alpha3));
288 
289       dest[ai] = (guchar) alpha;
290       if (dest[ai])
291         {
292           for (i = 0; i < ai; i++)
293             {
294               gdouble m0 = ((1.0 - x) * values[0][i] * alpha0
295                             + x * values[1][i] * alpha1);
296               gdouble m1 = ((1.0 - x) * values[2][i] * alpha2
297                             + x * values[3][i] * alpha3);
298 
299               dest[i] = (guchar) (((1.0 - y) * m0 + y * m1) / alpha);
300             }
301         }
302     }
303   else
304     {
305       for (i = 0; i < bpp; i++)
306         {
307           gdouble m0 = (1.0 - x) * values[0][i] + x * values[1][i];
308           gdouble m1 = (1.0 - x) * values[2][i] + x * values[3][i];
309 
310           dest[i] = (guchar) ((1.0 - y) * m0 + y * m1);
311         }
312     }
313 }
314