1/*
2 * Copyright 2009, Christian Packmann.
3 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
4 * Copyright 2005-2014, Stephan A��mus <superstippi@gmx.de>.
5 * Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
6 * All rights reserved. Distributed under the terms of the MIT License.
7 */
8#ifndef DRAW_BITMAP_BILINEAR_H
9#define DRAW_BITMAP_BILINEAR_H
10
11#include "Painter.h"
12
13#include <typeinfo>
14
15
16// Prototypes for assembler routines
17extern "C" {
18	void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst,
19		void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR);
20}
21
22
23extern uint32 gSIMDFlags;
24
25
26namespace BitmapPainterPrivate {
27
28
29struct FilterInfo {
30	uint16 index;	// index into source bitmap row/column
31	uint16 weight;	// weight of the pixel at index [0..255]
32};
33
34
35struct FilterData {
36	FilterInfo* fWeightsX;
37	FilterInfo* fWeightsY;
38	uint32 fIndexOffsetX;
39	uint32 fIndexOffsetY;
40};
41
42
43template<class OptimizedVersion>
44struct DrawBitmapBilinearOptimized {
45	void Draw(PainterAggInterface& aggInterface, const BRect& destinationRect,
46		agg::rendering_buffer* bitmap, const FilterData& filterData)
47	{
48		fSource = bitmap;
49		fSourceBytesPerRow = bitmap->stride();
50		fDestination = NULL;
51		fDestinationBytesPerRow = aggInterface.fBuffer.stride();
52		fWeightsX = filterData.fWeightsX;
53		fWeightsY = filterData.fWeightsY;
54
55		const int32 left = (int32)destinationRect.left;
56		const int32 top = (int32)destinationRect.top;
57		const int32 right = (int32)destinationRect.right;
58		const int32 bottom = (int32)destinationRect.bottom;
59
60		renderer_base& baseRenderer = aggInterface.fBaseRenderer;
61
62		// iterate over clipping boxes
63		baseRenderer.first_clip_box();
64		do {
65			const int32 x1 = max_c(baseRenderer.xmin(), left);
66			const int32 x2 = min_c(baseRenderer.xmax(), right);
67			if (x1 > x2)
68				continue;
69
70			int32 y1 = max_c(baseRenderer.ymin(), top);
71			int32 y2 = min_c(baseRenderer.ymax(), bottom);
72			if (y1 > y2)
73				continue;
74
75			// buffer offset into destination
76			fDestination = aggInterface.fBuffer.row_ptr(y1) + x1 * 4;
77
78			// x and y are needed as indices into the weight arrays, so the
79			// offset into the target buffer needs to be compensated
80			const int32 xIndexL = x1 - left - filterData.fIndexOffsetX;
81			const int32 xIndexR = x2 - left - filterData.fIndexOffsetX;
82			y1 -= top + filterData.fIndexOffsetY;
83			y2 -= top + filterData.fIndexOffsetY;
84
85			//printf("x: %ld - %ld\n", xIndexL, xIndexR);
86			//printf("y: %ld - %ld\n", y1, y2);
87
88			static_cast<OptimizedVersion*>(this)->DrawToClipRect(
89				xIndexL, xIndexR, y1, y2);
90
91		} while (baseRenderer.next_clip_box());
92	}
93
94protected:
95	agg::rendering_buffer*	fSource;
96	uint32					fSourceBytesPerRow;
97	uint8*					fDestination;
98	uint32					fDestinationBytesPerRow;
99	FilterInfo*				fWeightsX;
100	FilterInfo*				fWeightsY;
101};
102
103
104struct ColorTypeRgb {
105	static void
106	Interpolate(uint32* t, const uint8* s, uint32 sourceBytesPerRow,
107		uint16 wLeft, uint16 wTop, uint16 wRight, uint16 wBottom)
108	{
109		// left and right of top row
110		t[0] = (s[0] * wLeft + s[4] * wRight) * wTop;
111		t[1] = (s[1] * wLeft + s[5] * wRight) * wTop;
112		t[2] = (s[2] * wLeft + s[6] * wRight) * wTop;
113
114		// left and right of bottom row
115		s += sourceBytesPerRow;
116		t[0] += (s[0] * wLeft + s[4] * wRight) * wBottom;
117		t[1] += (s[1] * wLeft + s[5] * wRight) * wBottom;
118		t[2] += (s[2] * wLeft + s[6] * wRight) * wBottom;
119
120		t[0] >>= 16;
121		t[1] >>= 16;
122		t[2] >>= 16;
123	}
124
125	static void
126	InterpolateLastColumn(uint32* t, const uint8* s, const uint8* sBottom,
127		uint16 wTop, uint16 wBottom)
128	{
129		t[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
130		t[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
131		t[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
132	}
133
134	static void
135	InterpolateLastRow(uint32* t, const uint8* s, uint16 wLeft,
136		uint16 wRight)
137	{
138		t[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
139		t[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
140		t[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
141	}
142};
143
144
145struct ColorTypeRgba {
146	static void
147	Interpolate(uint32* t, const uint8* s, uint32 sourceBytesPerRow,
148		uint16 wLeft, uint16 wTop, uint16 wRight, uint16 wBottom)
149	{
150		// left and right of top row
151		t[0] = (s[0] * wLeft + s[4] * wRight) * wTop;
152		t[1] = (s[1] * wLeft + s[5] * wRight) * wTop;
153		t[2] = (s[2] * wLeft + s[6] * wRight) * wTop;
154		t[3] = (s[3] * wLeft + s[7] * wRight) * wTop;
155
156		// left and right of bottom row
157		s += sourceBytesPerRow;
158
159		t[0] += (s[0] * wLeft + s[4] * wRight) * wBottom;
160		t[1] += (s[1] * wLeft + s[5] * wRight) * wBottom;
161		t[2] += (s[2] * wLeft + s[6] * wRight) * wBottom;
162		t[3] += (s[3] * wLeft + s[7] * wRight) * wBottom;
163
164		t[0] >>= 16;
165		t[1] >>= 16;
166		t[2] >>= 16;
167		t[3] >>= 16;
168	}
169
170	static void
171	InterpolateLastColumn(uint32* t, const uint8* s, const uint8* sBottom,
172		uint16 wTop, uint16 wBottom)
173	{
174		t[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
175		t[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
176		t[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
177		t[3] = (s[3] * wTop + sBottom[3] * wBottom) >> 8;
178	}
179
180	static void
181	InterpolateLastRow(uint32* t, const uint8* s, uint16 wLeft,
182		uint16 wRight)
183	{
184		t[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
185		t[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
186		t[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
187		t[3] = (s[3] * wLeft + s[7] * wRight) >> 8;
188	}
189};
190
191
192struct DrawModeCopy {
193	static void
194	Blend(uint8*& d, uint32* t)
195	{
196		d[0] = t[0];
197		d[1] = t[1];
198		d[2] = t[2];
199		d += 4;
200	}
201};
202
203
204struct DrawModeAlphaOverlay {
205	static void
206	Blend(uint8*& d, uint32* t)
207	{
208		uint8 t0 = t[0];
209		uint8 t1 = t[1];
210		uint8 t2 = t[2];
211		uint8 t3 = t[3];
212
213		if (t3 == 255) {
214			d[0] = t0;
215			d[1] = t1;
216			d[2] = t2;
217		} else {
218			d[0] = ((t0 - d[0]) * t3 + (d[0] << 8)) >> 8;
219			d[1] = ((t1 - d[1]) * t3 + (d[1] << 8)) >> 8;
220			d[2] = ((t2 - d[2]) * t3 + (d[2] << 8)) >> 8;
221		}
222
223		d += 4;
224	}
225};
226
227
228template<class ColorType, class DrawMode>
229struct BilinearDefault :
230	DrawBitmapBilinearOptimized<BilinearDefault<ColorType, DrawMode> > {
231
232	void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2)
233	{
234		// In this mode we anticipate many pixels wich need filtering,
235		// there are no special cases for direct hit pixels except for
236		// the last column/row and the right/bottom corner pixel.
237
238		// The last column/row handling does not need to be performed
239		// for all clipping rects!
240		int32 yMax = y2;
241		if (this->fWeightsY[yMax].weight == 255)
242			yMax--;
243		int32 xIndexMax = xIndexR;
244		if (this->fWeightsX[xIndexMax].weight == 255)
245			xIndexMax--;
246
247		for (; y1 <= yMax; y1++) {
248			// cache the weight of the top and bottom row
249			const uint16 wTop = this->fWeightsY[y1].weight;
250			const uint16 wBottom = 255 - this->fWeightsY[y1].weight;
251
252			// buffer offset into source (top row)
253			const uint8* src = this->fSource->row_ptr(
254				this->fWeightsY[y1].index);
255
256			// buffer handle for destination to be incremented per
257			// pixel
258			uint8* d = this->fDestination;
259
260			for (int32 x = xIndexL; x <= xIndexMax; x++) {
261				const uint8* s = src + this->fWeightsX[x].index;
262
263				// calculate the weighted sum of all four
264				// interpolated pixels
265				const uint16 wLeft = this->fWeightsX[x].weight;
266				const uint16 wRight = 255 - wLeft;
267
268				uint32 t[4];
269
270				if (this->fSource->height() > 1) {
271					ColorType::Interpolate(&t[0], s, this->fSourceBytesPerRow,
272						wLeft, wTop, wRight, wBottom);
273				} else {
274					ColorType::InterpolateLastRow(&t[0], s,  wLeft, wRight);
275				}
276				DrawMode::Blend(d, &t[0]);
277			}
278			// last column of pixels if necessary
279			if (xIndexMax < xIndexR && this->fSource->height() > 1) {
280				const uint8* s = src + this->fWeightsX[xIndexR].index;
281				const uint8* sBottom = s + this->fSourceBytesPerRow;
282
283				uint32 t[4];
284				ColorType::InterpolateLastColumn(&t[0], s, sBottom, wTop,
285					wBottom);
286				DrawMode::Blend(d, &t[0]);
287			}
288
289			this->fDestination += this->fDestinationBytesPerRow;
290		}
291
292		// last row of pixels if necessary
293		// buffer offset into source (bottom row)
294		const uint8* src
295			= this->fSource->row_ptr(this->fWeightsY[y2].index);
296		// buffer handle for destination to be incremented per pixel
297		uint8* d = this->fDestination;
298
299		if (yMax < y2) {
300			for (int32 x = xIndexL; x <= xIndexMax; x++) {
301				const uint8* s = src + this->fWeightsX[x].index;
302				const uint16 wLeft = this->fWeightsX[x].weight;
303				const uint16 wRight = 255 - wLeft;
304				uint32 t[4];
305				ColorType::InterpolateLastRow(&t[0], s, wLeft, wRight);
306				DrawMode::Blend(d, &t[0]);
307			}
308		}
309
310		// pixel in bottom right corner if necessary
311		if (yMax < y2 && xIndexMax < xIndexR) {
312			const uint8* s = src + this->fWeightsX[xIndexR].index;
313			*(uint32*)d = *(uint32*)s;
314		}
315	}
316};
317
318
319struct BilinearLowFilterRatio :
320	DrawBitmapBilinearOptimized<BilinearLowFilterRatio> {
321	void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2)
322	{
323		// In this mode, we anticipate to hit many destination pixels
324		// that map directly to a source pixel, we have more branches
325		// in the inner loop but save time because of the special
326		// cases. If there are too few direct hit pixels, the branches
327		// only waste time.
328
329		for (; y1 <= y2; y1++) {
330			// cache the weight of the top and bottom row
331			const uint16 wTop = fWeightsY[y1].weight;
332			const uint16 wBottom = 255 - fWeightsY[y1].weight;
333
334			// buffer offset into source (top row)
335			const uint8* src = fSource->row_ptr(fWeightsY[y1].index);
336			// buffer handle for destination to be incremented per
337			// pixel
338			uint8* d = fDestination;
339
340			if (wTop == 255) {
341				for (int32 x = xIndexL; x <= xIndexR; x++) {
342					const uint8* s = src + fWeightsX[x].index;
343					// This case is important to prevent out
344					// of bounds access at bottom edge of the source
345					// bitmap. If the scale is low and integer, it will
346					// also help the speed.
347					if (fWeightsX[x].weight == 255) {
348						// As above, but to prevent out of bounds
349						// on the right edge.
350						*(uint32*)d = *(uint32*)s;
351					} else {
352						// Only the left and right pixels are
353						// interpolated, since the top row has 100%
354						// weight.
355						const uint16 wLeft = fWeightsX[x].weight;
356						const uint16 wRight = 255 - wLeft;
357						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
358						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
359						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
360					}
361					d += 4;
362				}
363			} else {
364				for (int32 x = xIndexL; x <= xIndexR; x++) {
365					const uint8* s = src + fWeightsX[x].index;
366					if (fWeightsX[x].weight == 255) {
367						// Prevent out of bounds access on the right
368						// edge or simply speed up.
369						const uint8* sBottom = s + fSourceBytesPerRow;
370						d[0] = (s[0] * wTop + sBottom[0] * wBottom)
371							>> 8;
372						d[1] = (s[1] * wTop + sBottom[1] * wBottom)
373							>> 8;
374						d[2] = (s[2] * wTop + sBottom[2] * wBottom)
375							>> 8;
376					} else {
377						// calculate the weighted sum of all four
378						// interpolated pixels
379						const uint16 wLeft = fWeightsX[x].weight;
380						const uint16 wRight = 255 - wLeft;
381						// left and right of top row
382						uint32 t0 = (s[0] * wLeft + s[4] * wRight)
383							* wTop;
384						uint32 t1 = (s[1] * wLeft + s[5] * wRight)
385							* wTop;
386						uint32 t2 = (s[2] * wLeft + s[6] * wRight)
387							* wTop;
388
389						// left and right of bottom row
390						s += fSourceBytesPerRow;
391						t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
392						t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
393						t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
394
395						d[0] = t0 >> 16;
396						d[1] = t1 >> 16;
397						d[2] = t2 >> 16;
398					}
399					d += 4;
400				}
401			}
402			fDestination += fDestinationBytesPerRow;
403		}
404	}
405};
406
407
408#ifdef __i386__
409
410struct BilinearSimd : DrawBitmapBilinearOptimized<BilinearSimd> {
411	void DrawToClipRect(int32 xIndexL, int32 xIndexR, int32 y1, int32 y2)
412	{
413		// Basically the same as the "standard" mode, but we use SIMD
414		// routines for the processing of the single display lines.
415
416		// The last column/row handling does not need to be performed
417		// for all clipping rects!
418		int32 yMax = y2;
419		if (fWeightsY[yMax].weight == 255)
420			yMax--;
421		int32 xIndexMax = xIndexR;
422		if (fWeightsX[xIndexMax].weight == 255)
423			xIndexMax--;
424
425		for (; y1 <= yMax; y1++) {
426			// cache the weight of the top and bottom row
427			const uint16 wTop = fWeightsY[y1].weight;
428			const uint16 wBottom = 255 - fWeightsY[y1].weight;
429
430			// buffer offset into source (top row)
431			const uint8* src = fSource->row_ptr(fWeightsY[y1].index);
432			// buffer handle for destination to be incremented per
433			// pixel
434			uint8* d = fDestination;
435			bilinear_scale_xloop_mmxsse(src, fDestination, fWeightsX, xIndexL,
436				xIndexMax, wTop, fSourceBytesPerRow);
437			// increase pointer by processed pixels
438			d += (xIndexMax - xIndexL + 1) * 4;
439
440			// last column of pixels if necessary
441			if (xIndexMax < xIndexR) {
442				const uint8* s = src + fWeightsX[xIndexR].index;
443				const uint8* sBottom = s + fSourceBytesPerRow;
444				d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
445				d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
446				d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
447			}
448
449			fDestination += fDestinationBytesPerRow;
450		}
451
452		// last row of pixels if necessary
453		// buffer offset into source (bottom row)
454		const uint8* src = fSource->row_ptr(fWeightsY[y2].index);
455		// buffer handle for destination to be incremented per pixel
456		uint8* d = fDestination;
457
458		if (yMax < y2) {
459			for (int32 x = xIndexL; x <= xIndexMax; x++) {
460				const uint8* s = src + fWeightsX[x].index;
461				const uint16 wLeft = fWeightsX[x].weight;
462				const uint16 wRight = 255 - wLeft;
463				d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
464				d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
465				d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
466				d += 4;
467			}
468		}
469
470		// pixel in bottom right corner if necessary
471		if (yMax < y2 && xIndexMax < xIndexR) {
472			const uint8* s = src + fWeightsX[xIndexR].index;
473			*(uint32*)d = *(uint32*)s;
474		}
475	}
476};
477
478#endif	// __i386__
479
480
481template<class ColorType, class DrawMode>
482struct DrawBitmapBilinear {
483	void
484	Draw(const Painter* painter, PainterAggInterface& aggInterface,
485		agg::rendering_buffer& bitmap, BPoint offset,
486		double scaleX, double scaleY, BRect destinationRect)
487	{
488		//bigtime_t now = system_time();
489		uint32 dstWidth = destinationRect.IntegerWidth() + 1;
490		uint32 dstHeight = destinationRect.IntegerHeight() + 1;
491		uint32 srcWidth = bitmap.width();
492		uint32 srcHeight = bitmap.height();
493
494		// Do not calculate more filter weights than necessary and also
495		// keep the stack based allocations reasonably sized
496		const BRegion& clippingRegion = *painter->ClippingRegion();
497		if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth)
498			dstWidth = clippingRegion.Frame().IntegerWidth() + 1;
499		if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight)
500			dstHeight = clippingRegion.Frame().IntegerHeight() + 1;
501
502		// When calculating less filter weights than specified by
503		// destinationRect, we need to compensate the offset.
504		FilterData filterData;
505		filterData.fIndexOffsetX = 0;
506		filterData.fIndexOffsetY = 0;
507		if (clippingRegion.Frame().left > destinationRect.left) {
508			filterData.fIndexOffsetX = (int32)(clippingRegion.Frame().left
509				- destinationRect.left);
510		}
511		if (clippingRegion.Frame().top > destinationRect.top) {
512			filterData.fIndexOffsetY = (int32)(clippingRegion.Frame().top
513				- destinationRect.top);
514		}
515
516//#define FILTER_INFOS_ON_HEAP
517#ifdef FILTER_INFOS_ON_HEAP
518		filterData.fWeightsX = new (nothrow) FilterInfo[dstWidth];
519		filterData.fWeightsY = new (nothrow) FilterInfo[dstHeight];
520		if (filterData.fWeightsX == NULL || filterData.fWeightsY == NULL) {
521			delete[] filterData.fWeightsX;
522			delete[] filterData.fWeightsY;
523			return;
524		}
525#else
526		// stack based saves about 200��s on 1.85 GHz Core 2 Duo
527		// should not pose a problem with stack overflows
528		// (needs around 12Kb for 1920x1200)
529		FilterInfo xWeights[dstWidth];
530		FilterInfo yWeights[dstHeight];
531		filterData.fWeightsX = &xWeights[0];
532		filterData.fWeightsY = &yWeights[0];
533#endif
534
535		// Extract the cropping information for the source bitmap,
536		// If only a part of the source bitmap is to be drawn with scale,
537		// the offset will be different from the destinationRect left top
538		// corner.
539		const int32 xBitmapShift = (int32)(destinationRect.left - offset.x);
540		const int32 yBitmapShift = (int32)(destinationRect.top - offset.y);
541
542		for (uint32 i = 0; i < dstWidth; i++) {
543			// fractional index into source
544			// NOTE: It is very important to calculate the fractional index
545			// into the source pixel grid like this to prevent out of bounds
546			// access! It will result in the rightmost pixel of the destination
547			// to access the rightmost pixel of the source with a weighting
548			// of 255. This in turn will trigger an optimization in the loop
549			// that also prevents out of bounds access.
550			float index = (i + filterData.fIndexOffsetX) * (srcWidth - 1)
551				/ (srcWidth * scaleX - 1);
552			// round down to get the left pixel
553			filterData.fWeightsX[i].index = (uint16)index;
554			filterData.fWeightsX[i].weight =
555				255 - (uint16)((index - filterData.fWeightsX[i].index) * 255);
556			// handle cropped source bitmap
557			filterData.fWeightsX[i].index += xBitmapShift;
558			// precompute index for 32 bit pixels
559			filterData.fWeightsX[i].index *= 4;
560		}
561
562		for (uint32 i = 0; i < dstHeight; i++) {
563			// fractional index into source
564			// NOTE: It is very important to calculate the fractional index
565			// into the source pixel grid like this to prevent out of bounds
566			// access! It will result in the bottommost pixel of the
567			// destination to access the bottommost pixel of the source with a
568			// weighting of 255. This in turn will trigger an optimization in
569			// the loop that also prevents out of bounds access.
570			float index = (i + filterData.fIndexOffsetY) * (srcHeight - 1)
571				/ (srcHeight * scaleY - 1);
572			// round down to get the top pixel
573			filterData.fWeightsY[i].index = (uint16)index;
574			filterData.fWeightsY[i].weight =
575				255 - (uint16)((index - filterData.fWeightsY[i].index) * 255);
576			// handle cropped source bitmap
577			filterData.fWeightsY[i].index += yBitmapShift;
578		}
579		//printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n",
580		//	xWeights[0].index, xWeights[0].weight,
581		//	xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight,
582		//	xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight,
583		//	dstWidth);
584		//printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n",
585		//	yWeights[0].index, yWeights[0].weight,
586		//	yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight,
587		//	yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight,
588		//	dstHeight);
589
590		// Figure out which version of the code we want to use...
591		enum {
592			kOptimizeForLowFilterRatio = 0,
593			kUseDefaultVersion,
594			kUseSIMDVersion
595		};
596
597		int codeSelect = kUseDefaultVersion;
598
599		if (typeid(ColorType) == typeid(ColorTypeRgb)
600			&& typeid(DrawMode) == typeid(DrawModeCopy)) {
601			uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE;
602			if ((gSIMDFlags & neededSIMDFlags) == neededSIMDFlags)
603				codeSelect = kUseSIMDVersion;
604			else {
605				if (scaleX == scaleY && (scaleX == 1.5 || scaleX == 2.0
606					|| scaleX == 2.5 || scaleX == 3.0)) {
607					codeSelect = kOptimizeForLowFilterRatio;
608				}
609			}
610		}
611
612		switch (codeSelect) {
613			case kUseDefaultVersion:
614			{
615				BilinearDefault<ColorType, DrawMode> bilinearPainter;
616				bilinearPainter.Draw(aggInterface, destinationRect, &bitmap,
617					filterData);
618				break;
619			}
620
621			case kOptimizeForLowFilterRatio:
622			{
623				BilinearLowFilterRatio bilinearPainter;
624				bilinearPainter.Draw(aggInterface, destinationRect,
625					&bitmap, filterData);
626				break;
627			}
628
629#ifdef __i386__
630			case kUseSIMDVersion:
631			{
632				BilinearSimd bilinearPainter;
633				bilinearPainter.Draw(aggInterface, destinationRect, &bitmap,
634					filterData);
635				break;
636			}
637#endif	// __i386__
638		}
639
640#ifdef FILTER_INFOS_ON_HEAP
641		delete[] filterData.fWeightsX;
642		delete[] filterData.fWeightsY;
643#endif
644		//printf("draw bitmap %.5fx%.5f: %lld\n", scaleX, scaleY,
645		//	system_time() - now);
646	}
647};
648
649
650} // namespace BitmapPainterPrivate
651
652
653#endif // DRAW_BITMAP_BILINEAR_H
654