chalk

kis_scale_visitor.cpp
1/*
2 * Copyright (c) 2004, 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
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 General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18#include <tqdatetime.h>
19
20#include <kdebug.h>
21#include <tdelocale.h>
22
23#include "kis_paint_device.h"
24#include "kis_scale_visitor.h"
25#include "kis_filter_strategy.h"
26
27
28void KisScaleWorker::run()
29{
30 double fwidth = m_filterStrategy->support();
31
32 TQRect rect = m_dev -> exactBounds();
33 TQ_INT32 width = rect.width();
34 TQ_INT32 height = rect.height();
35 m_pixelSize=m_dev -> pixelSize();
36
37 // compute size of target image
38 if ( m_sx == 1.0F && m_sy == 1.0F ) {
39 return;
40 }
41 TQ_INT32 targetW = TQABS( tqRound( m_sx * width ) );
42 TQ_INT32 targetH = TQABS( tqRound( m_sy * height ) );
43
44 TQ_UINT8* newData = new TQ_UINT8[targetW * targetH * m_pixelSize ];
45 TQ_CHECK_PTR(newData);
46
47 double* weight = new double[ m_pixelSize ]; /* filter calculation variables */
48
49 TQ_UINT8* pel = new TQ_UINT8[ m_pixelSize ];
50 TQ_CHECK_PTR(pel);
51
52 TQ_UINT8 *pel2 = new TQ_UINT8[ m_pixelSize ];
53 TQ_CHECK_PTR(pel2);
54
55 bool* bPelDelta = new bool[ m_pixelSize ];
56 ContribList *contribX;
57 ContribList contribY;
58 const TQ_INT32 BLACK_PIXEL=0;
59 const TQ_INT32 WHITE_PIXEL=255;
60
61
62 // create intermediate row to hold vertical dst row zoom
63 TQ_UINT8 * tmp = new TQ_UINT8[ width * m_pixelSize ];
64 TQ_CHECK_PTR(tmp);
65
66 //create array of pointers to intermediate rows
67 TQ_UINT8 **tmpRows = new TQ_UINT8*[ height ];
68
69 //create array of pointers to intermediate rows that are actually used simultaneously and allocate memory for the rows
70 TQ_UINT8 **tmpRowsMem;
71 if(m_sy < 1.0)
72 {
73 tmpRowsMem = new TQ_UINT8*[ (int)(fwidth / m_sy * 2 + 1) ];
74 for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
75 {
76 tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ];
77 TQ_CHECK_PTR(tmpRowsMem[i]);
78 }
79 }
80 else
81 {
82 tmpRowsMem = new TQ_UINT8*[ (int)(fwidth * 2 + 1) ];
83 for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
84 {
85 tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ];
86 TQ_CHECK_PTR(tmpRowsMem[i]);
87 }
88 }
89
90 // build x weights
91 contribX = new ContribList[ targetW ];
92 for(int x = 0; x < targetW; x++)
93 {
94 calcContrib(&contribX[x], m_sx, fwidth, width, m_filterStrategy, x);
95 }
96
97 TQTime starttime = TQTime::currentTime ();
98
99 for(int y = 0; y < targetH; y++)
100 {
101 // build y weights
102 calcContrib(&contribY, m_sy, fwidth, height, m_filterStrategy, y);
103
104 //copy pixel data to temporary arrays
105 for(int srcpos = 0; srcpos < contribY.n; srcpos++)
106 {
107 if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height))
108 {
109 tmpRows[contribY.p[srcpos].m_pixel] = new TQ_UINT8[ width * m_pixelSize ];
110 //tmpRows[ contribY.p[srcpos].m_pixel ] = tmpRowsMem[ srcpos ];
111 m_dev ->readBytes(tmpRows[contribY.p[srcpos].m_pixel], 0, contribY.p[srcpos].m_pixel, width, 1);
112 }
113 }
114
115 /* Apply vert filter to make dst row in tmp. */
116 for(int x = 0; x < width; x++)
117 {
118 for(int channel = 0; channel < m_pixelSize; channel++){
119 weight[channel] = 0.0;
120 bPelDelta[channel] = false;
121 pel[channel]=tmpRows[contribY.p[0].m_pixel][ x * m_pixelSize + channel ];
122 }
123 for(int srcpos = 0; srcpos < contribY.n; srcpos++)
124 {
125 if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)){
126 for(int channel = 0; channel < m_pixelSize; channel++)
127 {
128 pel2[channel]=tmpRows[contribY.p[srcpos].m_pixel][ x * m_pixelSize + channel ];
129 if(pel2[channel] != pel[channel]) bPelDelta[channel] = true;
130 weight[channel] += pel2[channel] * contribY.p[srcpos].m_weight;
131 }
132 }
133 }
134
135 for(int channel = 0; channel < m_pixelSize; channel++){
136 weight[channel] = bPelDelta[channel] ? static_cast<int>(tqRound(weight[channel])) : pel[channel];
137 tmp[ x * m_pixelSize + channel ] = static_cast<TQ_UINT8>(CLAMP(weight[channel], BLACK_PIXEL, WHITE_PIXEL));
138 }
139 } /* next row in temp column */
140 delete[] contribY.p;
141
142 for(int x = 0; x < targetW; x++)
143 {
144 for(int channel = 0; channel < m_pixelSize; channel++){
145 weight[channel] = 0.0;
146 bPelDelta[channel] = false;
147 pel[channel] = tmp[ contribX[x].p[0].m_pixel * m_pixelSize + channel ];
148 }
149 for(int srcpos = 0; srcpos < contribX[x].n; srcpos++)
150 {
151 for(int channel = 0; channel < m_pixelSize; channel++){
152 pel2[channel] = tmp[ contribX[x].p[srcpos].m_pixel * m_pixelSize + channel ];
153 if(pel2[channel] != pel[channel])
154 bPelDelta[channel] = true;
155 weight[channel] += pel2[channel] * contribX[x].p[srcpos].m_weight;
156 }
157 }
158 for(int channel = 0; channel < m_pixelSize; channel++){
159 weight[channel] = bPelDelta[channel] ? static_cast<int>(tqRound(weight[channel])) : pel[channel];
160 int currentPos = (y*targetW+x) * m_pixelSize; // try to be at least a little efficient
161 if (weight[channel]<0)
162 newData[currentPos + channel] = 0;
163 else if (weight[channel]>255)
164 newData[currentPos + channel] = 255;
165 else
166 newData[currentPos + channel] = (uchar)weight[channel];
167 }
168 } /* next dst row */
169 } /* next dst column */
170
171 // XXX: I'm thinking that we should be able to cancel earlier, in the look.
172 if(!isCanceled()){
173 m_dev -> writeBytes( newData, 0, 0, targetW, targetH);
174 m_dev -> crop(0, 0, targetW, targetH);
175 }
176
177 /* free the memory allocated for horizontal filter weights */
178 for(int x = 0; x < targetW; x++)
179 delete[] contribX[x].p;
180 delete[] contribX;
181
182 delete[] newData;
183 delete[] pel;
184 delete[] pel2;
185 delete[] tmp;
186 delete[] weight;
187 delete[] bPelDelta;
188
189 if(m_sy < 1.0)
190 {
191 for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
192 {
193 delete[] tmpRowsMem[i];
194 }
195 }
196 else
197 {
198 for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
199 {
200 delete[] tmpRowsMem[i];
201 }
202 }
203
204 TQTime stoptime = TQTime::currentTime ();
205 return;
206}
207
208int KisScaleWorker::calcContrib(ContribList *contrib, double scale, double fwidth, int srcwidth, KisFilterStrategy* filterStrategy, TQ_INT32 i)
209{
210 //ContribList* contribX: receiver of contrib info
211 //double m_sx: horizontal zooming scale
212 //double fwidth: Filter sampling width
213 //int dstwidth: Target bitmap width
214 //int srcwidth: Source bitmap width
215 //double (*filterf)(double): Filter proc
216 //int i: Pixel column in source bitmap being processed
217
218 double width;
219 double fscale;
220 double center, begin, end;
221 double weight;
222 TQ_INT32 k, n;
223
224 if(scale < 1.0)
225 {
226 //Shrinking image
227 width = fwidth / scale;
228 fscale = 1.0 / scale;
229
230 contrib->n = 0;
231 contrib->p = new Contrib[ (int)(width * 2 + 1) ];
232
233 center = (double) i / scale;
234 begin = ceil(center - width);
235 end = floor(center + width);
236 for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
237 {
238 weight = center - (double) srcpos;
239 weight = filterStrategy->valueAt(weight / fscale) / fscale;
240 if(srcpos < 0)
241 n = -srcpos;
242 else if(srcpos >= srcwidth)
243 n = (srcwidth - srcpos) + srcwidth - 1;
244 else
245 n = srcpos;
246
247 k = contrib->n++;
248 contrib->p[k].m_pixel = n;
249 contrib->p[k].m_weight = weight;
250 }
251 }
252 else
253 {
254 // Expanding image
255 contrib->n = 0;
256 contrib->p = new Contrib[ (int)(fwidth * 2 + 1) ];
257
258 center = (double) i / scale;
259 begin = ceil(center - fwidth);
260 end = floor(center + fwidth);
261
262 for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
263 {
264 weight = center - (double) srcpos;
265 weight = filterStrategy->valueAt(weight);
266 if(srcpos < 0) {
267 n = -srcpos;
268 } else if(srcpos >= srcwidth) {
269 n = (srcwidth - srcpos) + srcwidth - 1;
270 } else {
271 n = srcpos;
272 }
273 k = contrib->n++;
274 contrib->p[k].m_pixel = n;
275 contrib->p[k].m_weight = weight;
276 }
277 }
278 return 0;
279} /* calc_x_contrib */