xref: /haiku/src/servers/app/drawing/Painter/bitmap_painter/DrawBitmapNearestNeighbor.h (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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_NEAREST_NEIGHBOR_H
9 #define DRAW_BITMAP_NEAREST_NEIGHBOR_H
10 
11 #include "Painter.h"
12 
13 
14 struct DrawBitmapNearestNeighborCopy {
15 	static void
16 	Draw(const Painter* painter, PainterAggInterface& aggInterface,
17 		agg::rendering_buffer& bitmap, BPoint offset,
18 		double scaleX, double scaleY, BRect destinationRect)
19 	{
20 		//bigtime_t now = system_time();
21 		uint32 dstWidth = destinationRect.IntegerWidth() + 1;
22 		uint32 dstHeight = destinationRect.IntegerHeight() + 1;
23 		uint32 srcWidth = bitmap.width();
24 		uint32 srcHeight = bitmap.height();
25 
26 		// Do not calculate more filter weights than necessary and also
27 		// keep the stack based allocations reasonably sized
28 		const BRegion& clippingRegion = *painter->ClippingRegion();
29 		if (clippingRegion.Frame().IntegerWidth() + 1 < (int32)dstWidth)
30 			dstWidth = clippingRegion.Frame().IntegerWidth() + 1;
31 		if (clippingRegion.Frame().IntegerHeight() + 1 < (int32)dstHeight)
32 			dstHeight = clippingRegion.Frame().IntegerHeight() + 1;
33 
34 		// When calculating less filter weights than specified by
35 		// destinationRect, we need to compensate the offset.
36 		uint32 filterWeightXIndexOffset = 0;
37 		uint32 filterWeightYIndexOffset = 0;
38 		if (clippingRegion.Frame().left > destinationRect.left) {
39 			filterWeightXIndexOffset = (int32)(clippingRegion.Frame().left
40 				- destinationRect.left);
41 		}
42 		if (clippingRegion.Frame().top > destinationRect.top) {
43 			filterWeightYIndexOffset = (int32)(clippingRegion.Frame().top
44 				- destinationRect.top);
45 		}
46 
47 		// should not pose a problem with stack overflows
48 		// (needs around 6Kb for 1920x1200)
49 		uint16 xIndices[dstWidth];
50 		uint16 yIndices[dstHeight];
51 
52 		// Extract the cropping information for the source bitmap,
53 		// If only a part of the source bitmap is to be drawn with scale,
54 		// the offset will be different from the destinationRect left top
55 		// corner.
56 		const int32 xBitmapShift = (int32)(destinationRect.left - offset.x);
57 		const int32 yBitmapShift = (int32)(destinationRect.top - offset.y);
58 
59 		for (uint32 i = 0; i < dstWidth; i++) {
60 			// index into source
61 			uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth
62 				/ (srcWidth * scaleX));
63 			// round down to get the left pixel
64 			xIndices[i] = index;
65 			// handle cropped source bitmap
66 			xIndices[i] += xBitmapShift;
67 			// precompute index for 32 bit pixels
68 			xIndices[i] *= 4;
69 		}
70 
71 		for (uint32 i = 0; i < dstHeight; i++) {
72 			// index into source
73 			uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight
74 				/ (srcHeight * scaleY));
75 			// round down to get the top pixel
76 			yIndices[i] = index;
77 			// handle cropped source bitmap
78 			yIndices[i] += yBitmapShift;
79 		}
80 		//printf("X: %d ... %d, %d (%ld or %f)\n",
81 		//	xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1],
82 		//	dstWidth, srcWidth * scaleX);
83 		//printf("Y: %d ... %d, %d (%ld or %f)\n",
84 		//	yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1],
85 		//	dstHeight, srcHeight * scaleY);
86 
87 		const int32 left = (int32)destinationRect.left;
88 		const int32 top = (int32)destinationRect.top;
89 		const int32 right = (int32)destinationRect.right;
90 		const int32 bottom = (int32)destinationRect.bottom;
91 
92 		const uint32 dstBPR = aggInterface.fBuffer.stride();
93 
94 		renderer_base& baseRenderer = aggInterface.fBaseRenderer;
95 
96 		// iterate over clipping boxes
97 		baseRenderer.first_clip_box();
98 		do {
99 			const int32 x1 = max_c(baseRenderer.xmin(), left);
100 			const int32 x2 = min_c(baseRenderer.xmax(), right);
101 			if (x1 > x2)
102 				continue;
103 
104 			int32 y1 = max_c(baseRenderer.ymin(), top);
105 			int32 y2 = min_c(baseRenderer.ymax(), bottom);
106 			if (y1 > y2)
107 				continue;
108 
109 			// buffer offset into destination
110 			uint8* dst = aggInterface.fBuffer.row_ptr(y1) + x1 * 4;
111 
112 			// x and y are needed as indeces into the wheight arrays, so the
113 			// offset into the target buffer needs to be compensated
114 			const int32 xIndexL = x1 - left - filterWeightXIndexOffset;
115 			const int32 xIndexR = x2 - left - filterWeightXIndexOffset;
116 			y1 -= top + filterWeightYIndexOffset;
117 			y2 -= top + filterWeightYIndexOffset;
118 
119 		//printf("x: %ld - %ld\n", xIndexL, xIndexR);
120 		//printf("y: %ld - %ld\n", y1, y2);
121 
122 			for (; y1 <= y2; y1++) {
123 				// buffer offset into source (top row)
124 				const uint8* src = bitmap.row_ptr(yIndices[y1]);
125 				// buffer handle for destination to be incremented per pixel
126 				uint32* d = (uint32*)dst;
127 
128 				for (int32 x = xIndexL; x <= xIndexR; x++) {
129 					*d = *(uint32*)(src + xIndices[x]);
130 					d++;
131 				}
132 				dst += dstBPR;
133 			}
134 		} while (baseRenderer.next_clip_box());
135 
136 		//printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale,
137 		//	system_time() - now);
138 	}
139 };
140 
141 
142 
143 #endif // DRAW_BITMAP_NEAREST_NEIGHBOR_H
144