1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // pyramid.cpp
18 
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include "Pyramid.h"
23 
24 // We allocate the entire pyramid into one contiguous storage. This makes
25 // cleanup easier than fragmented stuff. In addition, we added a "pitch"
26 // field, so pointer manipulation is much simpler when it would be faster.
allocatePyramidPacked(real levels,real width,real height,real border)27 PyramidShort *PyramidShort::allocatePyramidPacked(real levels,
28         real width, real height, real border)
29 {
30     real border2 = (real) (border << 1);
31     int lines, size = calcStorage(width, height, border2, levels, &lines);
32 
33     PyramidShort *img = (PyramidShort *) calloc(sizeof(PyramidShort) * levels
34             + sizeof(short *) * lines +
35             + sizeof(short) * size, 1);
36 
37     if (img) {
38         PyramidShort *curr, *last;
39         ImageTypeShort *y = (ImageTypeShort *) &img[levels];
40         ImageTypeShort position = (ImageTypeShort) &y[lines];
41         for (last = (curr = img) + levels; curr < last; curr++) {
42             curr->width = width;
43             curr->height = height;
44             curr->border = border;
45             curr->pitch = (real) (width + border2);
46             curr->ptr = y + border;
47 
48             // Assign row pointers
49             for (int j = height + border2; j--; y++, position += curr->pitch) {
50                 *y = position + border;
51             }
52 
53             width >>= 1;
54             height >>= 1;
55         }
56     }
57 
58     return img;
59 }
60 
61 // Allocate an image of type short
allocateImage(real width,real height,real border)62 PyramidShort *PyramidShort::allocateImage(real width, real height, real border)
63 {
64     real border2 = (real) (border << 1);
65     PyramidShort *img = (PyramidShort *)
66         calloc(sizeof(PyramidShort) + sizeof(short *) * (height + border2) +
67                 sizeof(short) * (width + border2) * (height + border2), 1);
68 
69     if (img) {
70         short **y = (short **) &img[1];
71         short *position = (short *) &y[height + border2];
72         img->width = width;
73         img->height = height;
74         img->border = border;
75         img->pitch = (real) (width + border2);
76         img->ptr = y + border;
77         position += border; // Move position down to origin of real image
78 
79         // Assign row pointers
80         for (int j = height + border2; j--; y++, position += img->pitch) {
81             *y = position;
82         }
83     }
84 
85     return img;
86 }
87 
88 // Free the images
freeImage(PyramidShort * image)89 void PyramidShort::freeImage(PyramidShort *image)
90 {
91     if (image != NULL)
92         free(image);
93 }
94 
95 // Calculate amount of storage needed taking into account the borders, etc.
calcStorage(real width,real height,real border2,int levels,int * lines)96 unsigned int PyramidShort::calcStorage(real width, real height, real border2,   int levels, int *lines)
97 {
98     int size;
99 
100     *lines = size = 0;
101 
102     while(levels--) {
103         size += (width + border2) * (height + border2);
104         *lines += height + border2;
105         width >>= 1;
106         height >>= 1;
107     }
108 
109     return size;
110 }
111 
BorderSpread(PyramidShort * pyr,int left,int right,int top,int bot)112 void PyramidShort::BorderSpread(PyramidShort *pyr, int left, int right,
113         int top, int bot)
114 {
115     int off, off2, height, h, w;
116     ImageTypeShort base;
117 
118     if (left || right) {
119         off = pyr->border - left;
120         off2 = pyr->width + off + pyr->border - right - 1;
121         h = pyr->border - top;
122         height = pyr->height + (h << 1);
123         base = pyr->ptr[-h] - off;
124 
125         // spread in X
126         for (h = height; h--; base += pyr->pitch) {
127             for (w = left; w--;)
128                 base[-1 - w] = base[0];
129             for (w = right; w--;)
130                 base[off2 + w + 1] = base[off2];
131         }
132     }
133 
134     if (top || bot) {
135         // spread in Y
136         base = pyr->ptr[top - pyr->border] - pyr->border;
137         for (h = top; h--; base -= pyr->pitch) {
138             memcpy(base - pyr->pitch, base, pyr->pitch * sizeof(short));
139         }
140 
141         base = pyr->ptr[pyr->height + pyr->border - bot] - pyr->border;
142         for (h = bot; h--; base += pyr->pitch) {
143             memcpy(base, base - pyr->pitch, pyr->pitch * sizeof(short));
144         }
145     }
146 }
147 
BorderExpandOdd(PyramidShort * in,PyramidShort * out,PyramidShort * scr,int mode)148 void PyramidShort::BorderExpandOdd(PyramidShort *in, PyramidShort *out, PyramidShort *scr,
149         int mode)
150 {
151     int i,j;
152     int off = in->border / 2;
153 
154     // Vertical Filter
155     for (j = -off; j < in->height + off; j++) {
156         int j2 = j * 2;
157         for (i = -scr->border; i < scr->width + scr->border; i++) {
158             scr->ptr[j2][i] = (short)
159                 ((6 * in->ptr[j][i] + (in->ptr[j-1][i] + in->ptr[j+1][i]) + 4) >> 3);
160             scr->ptr[j2+1][i] = (short)((in->ptr[j][i] + in->ptr[j+1][i] + 1) >> 1);
161         }
162     }
163 
164     BorderSpread(scr, 0, 0, 3, 3);
165 
166     // Horizontal Filter
167     for (i = -off; i < scr->width + off; i++) {
168         int i2 = i * 2;
169         for (j = -out->border; j < out->height + out->border; j++) {
170             out->ptr[j][i2] = (short) (out->ptr[j][i2] +
171                     (mode * ((6 * scr->ptr[j][i] +
172                               scr->ptr[j][i-1] + scr->ptr[j][i+1] + 4) >> 3)));
173             out->ptr[j][i2+1] = (short) (out->ptr[j][i2+1] +
174                     (mode * ((scr->ptr[j][i] + scr->ptr[j][i+1] + 1) >> 1)));
175         }
176     }
177 
178 }
179 
BorderExpand(PyramidShort * pyr,int nlev,int mode)180 int PyramidShort::BorderExpand(PyramidShort *pyr, int nlev, int mode)
181 {
182     PyramidShort *tpyr = pyr + nlev - 1;
183     PyramidShort *scr = allocateImage(pyr[1].width, pyr[0].height, pyr->border);
184     if (scr == NULL) return 0;
185 
186     if (mode > 0) {
187         // Expand and add (reconstruct from Laplacian)
188         for (; tpyr > pyr; tpyr--) {
189             scr->width = tpyr[0].width;
190             scr->height = tpyr[-1].height;
191             BorderExpandOdd(tpyr, tpyr - 1, scr, 1);
192         }
193     }
194     else if (mode < 0) {
195         // Expand and subtract (build Laplacian)
196         while ((pyr++) < tpyr) {
197             scr->width = pyr[0].width;
198             scr->height = pyr[-1].height;
199             BorderExpandOdd(pyr, pyr - 1, scr, -1);
200         }
201     }
202 
203     freeImage(scr);
204     return 1;
205 }
206 
BorderReduceOdd(PyramidShort * in,PyramidShort * out,PyramidShort * scr)207 void PyramidShort::BorderReduceOdd(PyramidShort *in, PyramidShort *out, PyramidShort *scr)
208 {
209     ImageTypeShortBase *s, *ns, *ls, *p, *np;
210 
211     int off = scr->border - 2;
212     s = scr->ptr[-scr->border] - (off >> 1);
213     ns = s + scr->pitch;
214     ls = scr->ptr[scr->height + scr->border - 1] + scr->pitch - (off >> 1);
215     int width = scr->width + scr->border;
216     p = in->ptr[-scr->border] - off;
217     np = p + in->pitch;
218 
219     // treat it as if the whole thing were the image
220     for (; s < ls; s = ns, ns += scr->pitch, p = np, np += in->pitch) {
221         for (int w = width; w--; s++, p += 2) {
222             *s = (short)((((int) p[-2]) + ((int) p[2]) + 8 +    // 1
223                         ((((int) p[-1]) + ((int) p[1])) << 2) + // 4
224                         ((int) *p) * 6) >> 4);          // 6
225         }
226     }
227 
228     BorderSpread(scr, 5, 4 + ((in->width ^ 1) & 1), 0, 0); //
229 
230     s = out->ptr[-(off >> 1)] - out->border;
231     ns = s + out->pitch;
232     ls = s + out->pitch * (out->height + off);
233     p = scr->ptr[-off] - out->border;
234     int pitch = scr->pitch;
235     int pitch2 = pitch << 1;
236     np = p + pitch2;
237     for (; s < ls; s = ns, ns += out->pitch, p = np, np += pitch2) {
238         for (int w = out->pitch; w--; s++, p++) {
239             *s = (short)((((int) p[-pitch2]) + ((int) p[pitch2]) + 8 + // 1
240                         ((((int) p[-pitch]) + ((int) p[pitch])) << 2) + // 4
241                         ((int) *p) * 6) >> 4);              // 6
242         }
243     }
244     BorderSpread(out, 0, 0, 5, 5);
245 
246 }
247 
BorderReduce(PyramidShort * pyr,int nlev)248 int PyramidShort::BorderReduce(PyramidShort *pyr, int nlev)
249 {
250     PyramidShort *scr = allocateImage(pyr[1].width, pyr[0].height, pyr->border);
251     if (scr == NULL)
252         return 0;
253 
254     BorderSpread(pyr, pyr->border, pyr->border, pyr->border, pyr->border);
255     while (--nlev) {
256         BorderReduceOdd(pyr, pyr + 1, scr);
257         pyr++;
258         scr->width = pyr[1].width;
259         scr->height = pyr[0].height;
260     }
261 
262     freeImage(scr);
263     return 1;
264 }
265