1 #include "Draw.h"
2
3 namespace Upp {
4
5 enum
6 {
7 MAXAA = 4,
8
9 MAP_COUNT = 0,
10 MAP_SEGMENT = 1,
11 MAP_BLOCK = 2,
12 MAP_STEP = 3,
13 MAP_DATA = 4,
14 };
15
16 enum { LOG2_STRETCH_CURVE = 10, COUNT_STRETCH_CURVE = 1 << LOG2_STRETCH_CURVE };
17
GetStretchCurve()18 static const byte *GetStretchCurve()
19 {
20 static byte cache[COUNT_STRETCH_CURVE];
21 ONCELOCK {
22 for(int i = 0; i < COUNT_STRETCH_CURVE; i++)
23 {
24 enum { HALF = COUNT_STRETCH_CURVE >> 1 };
25 double a = (i <= HALF ? i / double(HALF) : (COUNT_STRETCH_CURVE - i) / double(HALF));
26 double o = pow(a, 0.85);
27 cache[i] = minmax<int>((int)((i <= HALF ? o : 2 - o) * 128), 0, 255);
28 }
29 }
30 return cache;
31 }
32
AAGetMap(int & dmin,int & dmax,int dclipmin,int dclipmax,int smin,int smax,int sclipmin,int sclipmax,int times,int avail)33 Vector<dword> AAGetMap(int& dmin, int& dmax, int dclipmin, int dclipmax,
34 int smin, int smax, int sclipmin, int sclipmax, int times, int avail)
35 {
36 Vector<dword> map;
37 if(dmax == dmin || smax == smin)
38 return map;
39 if(dmax < dmin)
40 {
41 Swap(dmin, dmax);
42 Swap(smin, smax);
43 }
44 int dw = dmax - dmin, sw = smax - smin, spos;
45 if(sw > 0)
46 {
47 int x0 = dmin;
48 if(smin < sclipmin)
49 x0 += iscalefloor(sclipmin - smin, dw, sw);
50 if(x0 < dclipmin)
51 x0 = dclipmin;
52 spos = smin * dw + (x0 - dmin) * sw;
53 dmin = x0;
54 if(smax > sclipmax)
55 dmax -= iscalefloor(smax - sclipmax, dw, sw);
56 if(dmax > dclipmax)
57 dmax = dclipmax;
58 }
59 else
60 {
61 int x0 = dmin;
62 if(smin > sclipmax)
63 x0 += iscalefloor(sclipmax - smin, dw, sw);
64 if(x0 < dclipmin)
65 x0 = dclipmin;
66 spos = smin * dw + (x0 - dmin) * sw;
67 dmin = x0;
68 if(smax < sclipmin)
69 dmax -= iscalefloor(smax - sclipmin, dw, sw);
70 Swap(smin, smax);
71 }
72 int count = min(dclipmax, dmax) - dmin;
73 if(smin < sclipmin)
74 smin = sclipmin;
75 if(smax > sclipmax)
76 smax = sclipmax;
77 if(smax <= smin || count <= 0)
78 return map;
79 int span = min(tabs(sw) % dw ? idivceil(tabs(sw), dw) + 1 : tabs(sw) / dw, smax - smin);
80 bool bigseg = (span >= MAXAA);
81 int segment = (bigseg ? MAXAA : span);
82 int segstep = span / segment;
83 map.SetCount(4 + count * (bigseg ? 1 : 1 + segment));
84 map[MAP_COUNT] = dword(count);
85 map[MAP_SEGMENT] = dword(segment);
86 map[MAP_BLOCK] = 1 + (bigseg ? 0 : segment);
87 map[MAP_STEP] = (span / segment) * times;
88 dword *out = map.Begin() + MAP_DATA;
89 int sendoffset = (smax - (segment - 1) * segstep - 1) * times;
90 int last = 0;
91
92 if(smax - smin == 1)
93 {
94 ASSERT(segment == 1);
95 dword dval = dword(smin * times);
96 *out++ = dval;
97 *out++ = avail;
98 while(--count > 0)
99 {
100 *out++ = 0;
101 *out++ = avail;
102 }
103 }
104 else if(tabs(sw) >= dw)
105 { // size reduction
106 int sbegin = smin * dw, send = smax * dw, aw = tabs(sw);
107 for(spos += min(sw, 0); --count >= 0; spos += sw)
108 {
109 int pb = max(spos, sbegin), pe = min(spos + aw, send);
110 int total = pe - pb, left = avail;
111 int start = idivfloor(pb, dw), end = idivceil(pe, dw) - 1, rem = pb % dw;
112 // DUMP(start);
113 // DUMP(end);
114 if(pb >= send)
115 {
116 last += *out++ = sendoffset - last;
117 if(!bigseg)
118 {
119 int i = segment - 1;
120 while(--i >= 0)
121 *out++ = 0;
122 *out++ = left;
123 }
124 }
125 else if(end <= start)
126 { // 1 source pixel only
127 // ASSERT(!bigseg);
128 int scomp = minmax(start + segment - smax, 0, start - smin);
129 last += *out++ = (start - scomp) * times - last;
130 if(!bigseg)
131 {
132 int i = scomp;
133 while(--i >= 0)
134 *out++ = 0;
135 *out++ = dword(left);
136 i = segment - scomp - 1;
137 while(--i >= 0)
138 *out++ = 0;
139 }
140 }
141 else
142 {
143 int delta = (dw - rem) * left / total;
144 if(!delta)
145 start++;
146 int scomp = minmax(start + span - smax, 0, start - smin);
147 last += *out++ = (start - scomp) * times - last;
148 if(!bigseg)
149 {
150 int i = scomp;
151 while(--i >= 0)
152 *out++ = 0;
153 i = segment - scomp;
154 if(delta)
155 {
156 *out++ = delta;
157 left -= delta;
158 total -= dw - rem;
159 i--;
160 }
161 while(++start < end)
162 {
163 ASSERT(i > 0);
164 delta = dw * left / total;
165 *out++ = delta;
166 left -= delta;
167 total -= dw;
168 --i;
169 }
170 if(left > 0)
171 {
172 ASSERT(i > 0);
173 *out++ = left;
174 --i;
175 }
176 while(--i >= 0)
177 *out++ = 0;
178 }
179 }
180 // LOG("-> " << map[rec] << " + " << map[rec + 1]);
181 }
182 }
183 else
184 { // size inflation
185 static const byte *curve = GetStretchCurve();
186 ASSERT(segment == 2 && !bigseg);
187 int sbegin = smin * dw, send = (smax - 1) * dw;
188 for(spos += (sw - dw) >> 1; --count >= 0; spos += sw)
189 {
190 if(spos <= sbegin)
191 {
192 last += out[0] = smin * times - last;
193 out[1] = avail;
194 out[2] = 0;
195 }
196 else if(spos >= send)
197 {
198 last += out[0] = sendoffset - last;
199 out[1] = 0;
200 out[2] = avail;
201 }
202 else
203 {
204 int pos = spos / dw;
205 int rel = spos % dw;
206 last += out[0] = pos * times - last;
207 out[1] = avail - (out[2] = curve[rel * COUNT_STRETCH_CURVE / dw]);
208 }
209 out += 3;
210 }
211 }
212
213 #ifdef _DEBUG
214 ASSERT(out == map.End());
215 int offs = 0, step = map[MAP_BLOCK], segspan = (map[MAP_SEGMENT] - 1) * map[MAP_STEP] + 1;
216 for(int t = 0; t < (int)map[MAP_COUNT]; t++)
217 {
218 offs += map[MAP_DATA + t * step];
219 ASSERT(offs >= times * smin && offs + segspan <= times * smax);
220 }
221 #endif
222
223 return map;
224 }
225
BltAAMapRGBA1(dword * dest,const RGBA * s,const dword * map)226 static void BltAAMapRGBA1(dword *dest, const RGBA *s, const dword *map)
227 {
228 int count = map[MAP_COUNT];
229 map += 4;
230 while(count--) {
231 s += map[0];
232 dest[0] = s->b << 8;
233 dest[1] = s->g << 8;
234 dest[2] = s->r << 8;
235 dest[3] = s->a << 8;
236 map += 2;
237 dest += 4;
238 }
239 }
240
BltAAMapRGBA2(dword * dest,const RGBA * s,const dword * map)241 static void BltAAMapRGBA2(dword *dest, const RGBA *s, const dword *map)
242 {
243 int count = map[MAP_COUNT];
244 map += 4;
245 while(count--) {
246 s += map[0];
247 dest[0] = s[0].b * map[1] + s[1].b * map[2];
248 dest[1] = s[0].g * map[1] + s[1].g * map[2];
249 dest[2] = s[0].r * map[1] + s[1].r * map[2];
250 dest[3] = s[0].a * map[1] + s[1].a * map[2];
251 map += 3;
252 dest += 4;
253 }
254 }
255
256
BltAAMapRGBA3(dword * dest,const RGBA * s,const dword * map)257 static void BltAAMapRGBA3(dword *dest, const RGBA *s, const dword *map)
258 {
259 int count = map[MAP_COUNT];
260 map += 4;
261 while(count--) {
262 s += map[0];
263 dest[0] = s[0].b * map[1] + s[1].b * map[2] + s[2].b * map[3];
264 dest[1] = s[0].g * map[1] + s[1].g * map[2] + s[2].g * map[3];
265 dest[2] = s[0].r * map[1] + s[1].r * map[2] + s[2].r * map[3];
266 dest[3] = s[0].a * map[1] + s[1].a * map[2] + s[2].a * map[3];
267 map += 4;
268 dest += 4;
269 }
270 }
271
BltAAMapRGBA4(dword * dest,const RGBA * s,const dword * map)272 static void BltAAMapRGBA4(dword *dest, const RGBA *s, const dword *map)
273 {
274 int step = map[MAP_STEP];
275 int count = map[MAP_COUNT];
276 map += 4;
277 while(count--) {
278 s += map[0];
279 dest[0] = (s[0].b + s[step].b + s[2 * step].b + s[3 * step].b) << 6;
280 dest[1] = (s[0].g + s[step].g + s[2 * step].g + s[3 * step].g) << 6;
281 dest[2] = (s[0].r + s[step].r + s[2 * step].r + s[3 * step].r) << 6;
282 dest[3] = (s[0].a + s[step].a + s[2 * step].a + s[3 * step].a) << 6;
283 map += 1;
284 dest += 4;
285 }
286 }
287
BltAASet2Fix(RGBA * dest,const dword * src1,dword w1,const dword * src2,dword w2,int count)288 void BltAASet2Fix(RGBA *dest, const dword *src1, dword w1, const dword *src2, dword w2, int count)
289 {
290 while(count--) {
291 dest->b = byte((src1[0] * w1 + src2[0] * w2) >> 16);
292 dest->g = byte((src1[1] * w1 + src2[1] * w2) >> 16);
293 dest->r = byte((src1[2] * w1 + src2[2] * w2) >> 16);
294 dest->a = byte((src1[3] * w1 + src2[3] * w2) >> 16);
295 dest++;
296 src1 += 4;
297 src2 += 4;
298 }
299 }
300
BltAASet3Fix(RGBA * dest,const dword * src1,dword w1,const dword * src2,dword w2,const dword * src3,dword w3,dword count)301 void BltAASet3Fix(RGBA *dest,
302 const dword *src1, dword w1, const dword *src2, dword w2, const dword *src3, dword w3,
303 dword count)
304 {
305 while(count--) {
306 dest->b = byte((src1[0] * w1 + src2[0] * w2 + src3[0] * w3) >> 16);
307 dest->g = byte((src1[1] * w1 + src2[1] * w2 + src3[1] * w3) >> 16);
308 dest->r = byte((src1[2] * w1 + src2[2] * w2 + src3[2] * w3) >> 16);
309 dest->a = byte((src1[3] * w1 + src2[3] * w2 + src3[3] * w3) >> 16);
310 dest++;
311 src1 += 4;
312 src2 += 4;
313 src3 += 4;
314 }
315 }
316
BltAASet4Fix(RGBA * dest,const dword * src1,const dword * src2,const dword * src3,const dword * src4,int count)317 void BltAASet4Fix(RGBA *dest, const dword *src1, const dword *src2,
318 const dword *src3, const dword *src4, int count)
319 {
320 while(count--) {
321 dest->b = byte((src1[0] + src2[0] + src3[0] + src4[0]) >> 10);
322 dest->g = byte((src1[1] + src2[1] + src3[1] + src4[1]) >> 10);
323 dest->r = byte((src1[2] + src2[2] + src3[2] + src4[2]) >> 10);
324 dest->a = byte((src1[3] + src2[3] + src3[3] + src4[3]) >> 10);
325 dest++;
326 src1 += 4;
327 src2 += 4;
328 src3 += 4;
329 src4 += 4;
330 }
331 }
332
BltAAFix2(RGBA * dest,const dword * src,int count)333 void BltAAFix2(RGBA *dest, const dword *src, int count)
334 {
335 #ifdef CPU_LITTLE_ENDIAN
336 const byte *s = (byte *)src + 1;
337 #else
338 const byte *s = (byte *)src + 2;
339 #endif
340 while(count--) {
341 dest->b = s[0];
342 dest->g = s[4];
343 dest->r = s[8];
344 dest->a = s[12];
345 dest++;
346 s += 16;
347 }
348 }
349
Create(Size _tsz,Raster & _src,const Rect & src_rc)350 void RescaleImage::Create(Size _tsz, Raster& _src, const Rect& src_rc)
351 {
352 y = -1;
353 src = &_src;
354 tsz = _tsz;
355 if(tsz.cx == 0 || tsz.cy == 0)
356 return;
357
358 size = src->GetSize();
359
360 Rect dr = tsz;
361 horz = AAGetMap(dr.left, dr.right, dr.left, dr.right,
362 src_rc.left, src_rc.right, 0, size.cx, 1, 0x100);
363 if(horz.IsEmpty())
364 return;
365
366 vert = AAGetMap(dr.top, dr.bottom, dr.top, dr.bottom,
367 src_rc.top, src_rc.bottom, 0, size.cy, 1, 0x100);
368 if(vert.IsEmpty())
369 return;
370
371 switch(horz[MAP_SEGMENT]) {
372 case 1: row_proc = BltAAMapRGBA1; break;
373 case 2: row_proc = BltAAMapRGBA2; break;
374 case 3: row_proc = BltAAMapRGBA3; break;
375 case 4: row_proc = BltAAMapRGBA4; break;
376 default: NEVER(); return;
377 }
378
379 cx4 = 4 * tsz.cx;
380 count = vert[MAP_COUNT];
381 segment = vert[MAP_SEGMENT];
382 entry = vert[MAP_BLOCK];
383 step = vert[MAP_STEP];
384 segspan = (segment - 1) * step + 1;
385 bigseg = (segment == MAXAA);
386 row_buffers.Alloc(cx4 * segment);
387 first = vert[4];
388 full = 0;
389 offsets = vert.GetIter(4);
390 offset = 0;
391 y = 0;
392 cii = 0;
393 cache[0].ii = cache[1].ii = cache[2].ii = cache[3].ii = -1;
394 }
395
GetLine(int ii)396 const RGBA *RescaleImage::GetLine(int ii)
397 {
398 if(cache[0].ii == ii)
399 return cache[0].line;
400 if(cache[1].ii == ii)
401 return cache[1].line;
402 if(cache[2].ii == ii)
403 return cache[2].line;
404 if(cache[3].ii == ii)
405 return cache[3].line;
406 cache[cii].line = (*src)[ii];
407 cache[cii].ii = ii;
408 const RGBA *l = cache[cii].line;
409 cii = (cii + 1) % 4;
410 return l;
411 }
412
Get(RGBA * tgt)413 void RescaleImage::Get(RGBA *tgt)
414 {
415 if(y < 0 || offsets >= vert.End()) {
416 memset(tgt, 0, sizeof(RGBA) * tsz.cx);
417 return;
418 }
419 offset += *offsets++;
420 ASSERT(offset >= 0 && offset + segspan <= size.cy);
421 if(bigseg) {
422 row_proc(&row_buffers[0 * cx4], GetLine(offset + 0 * step), horz);
423 row_proc(&row_buffers[1 * cx4], GetLine(offset + 1 * step), horz);
424 row_proc(&row_buffers[2 * cx4], GetLine(offset + 2 * step), horz);
425 row_proc(&row_buffers[3 * cx4], GetLine(offset + 3 * step), horz);
426 BltAASet4Fix(tgt, &row_buffers[0 * cx4], &row_buffers[1 * cx4],
427 &row_buffers[2 * cx4], &row_buffers[3 * cx4], tsz.cx);
428 }
429 else {
430 int endoff = offset + segment;
431 for(int next = first + full; next < endoff; next++) {
432 if(full >= segment)
433 first++;
434 else
435 full++;
436 row_proc(&row_buffers[next % segment * cx4], GetLine(next), horz);
437 }
438 while(first > offset) {
439 if(full < segment)
440 full++;
441 --first;
442 row_proc(&row_buffers[first % segment * cx4], GetLine(first), horz);
443 }
444 ASSERT(offset >= first && endoff <= first + full);
445 switch(segment) {
446 case 1:
447 BltAAFix2(tgt, &row_buffers[offset % segment * cx4], tsz.cx);
448 offsets++;
449 break;
450 case 2:
451 if(offsets[0] == 0)
452 BltAAFix2(tgt, &row_buffers[(offset + 1) % segment * cx4], tsz.cx);
453 else
454 if(offsets[1] == 0)
455 BltAAFix2(tgt, &row_buffers[offset % segment * cx4], tsz.cx);
456 else
457 BltAASet2Fix(tgt, &row_buffers[(offset + 0) % segment * cx4], offsets[0],
458 &row_buffers[(offset + 1) % segment * cx4], offsets[1],
459 tsz.cx);
460 offsets += 2;
461 break;
462 case 3:
463 BltAASet3Fix(tgt,
464 &row_buffers[(offset + 0) % segment * cx4], offsets[0],
465 &row_buffers[(offset + 1) % segment * cx4], offsets[1],
466 &row_buffers[(offset + 2) % segment * cx4], offsets[2], tsz.cx);
467 offsets += 3;
468 break;
469 default:
470 NEVER();
471 break;
472 }
473 }
474 }
475
Rescale(RasterEncoder & tgt,Size tsz,Raster & src,const Rect & src_rc,Gate<int,int> progress)476 bool Rescale(RasterEncoder& tgt, Size tsz, Raster& src, const Rect& src_rc,
477 Gate<int, int> progress)
478 {
479 tgt.Create(tsz, src);
480 RescaleImage rs;
481 rs.Create(tsz, src, src_rc);
482 for(int i = 0; i < tsz.cy; i++) {
483 if(progress(i, tsz.cy))
484 return false;
485 rs.Get(tgt);
486 tgt.WriteLine();
487 }
488 return true;
489 }
490
Rescale(const Image & src,Size sz,const Rect & src_rc,Gate<int,int> progress)491 Image Rescale(const Image& src, Size sz, const Rect& src_rc, Gate<int, int> progress)
492 {
493 if(src.GetSize() == sz && src_rc == sz)
494 return src;
495 ImageRaster isrc(src);
496 ImageEncoder tgt;
497 Rescale(tgt, sz, isrc, src_rc);
498 return tgt;
499 }
500
Rescale(const Image & src,Size sz,Gate<int,int> progress)501 Image Rescale(const Image& src, Size sz, Gate<int, int> progress)
502 {
503 return Rescale(src, sz, src.GetSize(), progress);
504 }
505
Rescale(const Image & src,int cx,int cy,Gate<int,int> progress)506 Image Rescale(const Image& src, int cx, int cy, Gate<int, int> progress)
507 {
508 return Rescale(src, Size(cx, cy), progress);
509 }
510
511 }
512