Playing with screen rotation (landscape and portrait mode)

After reading this post about screen rotation I wanted to play a bit further with that idea. I have a ThinkPad X1 that I like to use in tent mode (upside down), so an option to rotate the screen would be welcome.

I added a setting to the Screen preferences to set screen rotation:
image

I also updated HWInterface::_CopyToFront to handle all four rotations (Landscape, Landscape (flipped), Portrait and Portrait (flipped)).

At this point I’m stuck trying to connect the two pieces together. How can I update the currently hard coded screenRotation value in HWInterface::_CopyToFront with whatever setting is used in Screen preferences?

void
HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y,
	int32 right, int32 bottom) const
{
	RenderingBuffer* frontBuffer = FrontBuffer();

	uint8* dst = (uint8*)frontBuffer->Bits();
	uint32 dstBPR = frontBuffer->BytesPerRow();

	// Todo: Get this value from Screen preferences
	int32 screenRotation = 3; // 0 - none, 1 - 90 degrees, 2 - 180 degrees, 3 - 270 degrees
	
	// transfer, handle colorspace conversion
	switch (frontBuffer->ColorSpace()) {

		case B_RGB32:
		case B_RGBA32:
		{
			int32 src_width = right - x + 1;
			int32 src_height = bottom - y + 1;
			int32 dst_width = frontBuffer->Width();
			int32 dst_height = frontBuffer->Height();

			if (screenRotation == 0) {
				int32 bytes = (right - x + 1) * 4;

				if (bytes > 0) {
					// offset to left top pixel in dest buffer
					dst += y * dstBPR + x * 4;
					// copy
					for (; y <= bottom; y++) {
						// bytes is guaranteed to be multiple of 4
						memcpy(dst, src, bytes);
						dst += dstBPR;
						src += srcBPR;
					}
				}
			} else if (screenRotation == 1 || screenRotation == 3) {
				for (int32 ysrc = 0; ysrc < src_height; ysrc++) {
					for (int32 xsrc = 0; xsrc < src_width; xsrc++) {
						int32 ydst = (screenRotation == 1) ? x + xsrc : dst_height - (x + xsrc); // 90 or -90 degrees rotation
						int32 xdst = (screenRotation == 1) ? dst_width - (y + ysrc) : y + ysrc; // 90 or -90 degrees rotation
						if (ydst < 0 || xdst < 0 || ydst >= dst_height || xdst >= dst_width) continue;

						uint8* psrc = src + ysrc * srcBPR + xsrc * 4;
						uint8* pdst = dst + ydst * dstBPR + xdst * 4;
						memcpy(pdst, psrc, 4);
					}
				}
			} else if (screenRotation == 2) {
				for (int32 ysrc = 0; ysrc < src_height; ysrc++) {
					for (int32 xsrc = 0; xsrc < src_width; xsrc++) {
						int32 ydst = dst_height - (ysrc + y);
						int32 xdst = dst_width - (xsrc + x);
						if (ydst < 0 || xdst < 0 || ydst >= dst_height || xdst >= dst_width) continue;

						uint8* psrc = src + ysrc * srcBPR + xsrc * 4;
						uint8* pdst = dst + ydst * dstBPR + xdst * 4;
						memcpy(pdst, psrc, 4);
					}
				}
			}
			break;
		}
2 Likes

Probably in the same way as the resolution, and color space is put there.
Basically, you’ll have to follow BScreen to where it ends up setting up the HWInterface.

1 Like

It may be worth noting that this will break color subpixel antialiasing on fonts.

Instead of applying the transform in HWInterface, the right place to do it is on all drawing commands being posted to the screen, to avoid that problem. (Or, disable color subpixel antialiasing when screen rotation is enabled, which may make the most sense, as otherwise fonts drawn to bitmaps may get messed up…)

3 Likes