magic/windows/windTrans.c

375 lines
12 KiB
C
Raw Normal View History

/* windTransform.c -
*
* Routines to transform from screen coords to surface coords and the
* other way.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
2020-05-25 21:46:59 +02:00
static char rcsid[] __attribute__ ((unused)) = "$Header$";
#endif /* not lint */
#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"
/*
* ----------------------------------------------------------------------------
* WindScreenToSurface --
*
* Convert a rectangle in screen coordinates to surface coords.
*
* Results:
* None.
*
* Side effects:
* Returns a rectangle that is big enough to fully contain everything
* that is displayed in the screen rectangle.
* ----------------------------------------------------------------------------
*/
void
WindScreenToSurface(w, screen, surface)
MagWindow *w; /* Window in whose coordinates screen is
* is defined.
*/
Rect *screen; /* A rectangle in screen coordinates */
Rect *surface; /* A pointer to a rectangle to be filled
* in with a rectangle in surface coords that
* is big enough to contain everything
* displayed within the screen rectangle.
*/
{
Rect r;
WindPointToSurface(w, &(screen->r_ll), (Point *) NULL, surface);
WindPointToSurface(w, &(screen->r_ur), (Point *) NULL, &r);
surface->r_ur = r.r_ur;
}
/*
* ----------------------------------------------------------------------------
* WindPointToSurface --
*
* Take a point in screen coordinates and find the nearest point
* in surface coordinates. Also return a box in surface coords
* that completely surrounds the pixel.
*
* Results:
* None.
*
* Side effects:
* The parameters surfaceBox and surfacePoint are modified, if
* they are non-NULL. The box is always at least 1x1, but it
* will be larger if the scale is so coarse that a single screen
* coordinate is more than one surface coordinate. In this case
* the box is large enough to include every surface coordinate
* that can map to the given pixel (this feature is essential
* if WindScreenToSurface is to use this routine).
*
* ----------------------------------------------------------------------------
*/
void
WindPointToSurface(w, screenPoint, surfacePoint, surfaceBox)
MagWindow *w; /* Window in whose coordinate system the
* transformation is to be done.
*/
Point *screenPoint; /* The point in screen coordinates. */
Point *surfacePoint;/* Filled in with the nearest surface coordinate.
* Nothing is filled in if the pointer is NULL.
*/
Rect *surfaceBox; /* Filled in with a box in surface coordinates that
* surrounds the point. It is not filled in if this
* is a NULL pointer.
*/
{
int tmp, adjust, unitsPerPixel;
/* Do the inverse transformation twice, once with rounding for
* the point and once with truncation for the box.
*/
unitsPerPixel = SUBPIXEL/w->w_scale;
if (surfaceBox != NULL)
{
tmp = (SUBPIXEL*screenPoint->p_x) - w->w_origin.p_x;
if (tmp < 0) tmp -= w->w_scale-1;
surfaceBox->r_xbot = w->w_surfaceArea.r_xbot + tmp/w->w_scale;
surfaceBox->r_xtop = surfaceBox->r_xbot + unitsPerPixel + 1;
tmp = (SUBPIXEL*screenPoint->p_y) - w->w_origin.p_y;
if (tmp < 0) tmp -= w->w_scale-1;
surfaceBox->r_ybot = w->w_surfaceArea.r_ybot + tmp/w->w_scale;
surfaceBox->r_ytop = surfaceBox->r_ybot + unitsPerPixel + 1;
}
if (surfacePoint != NULL)
{
adjust = w->w_scale/2;
tmp = (SUBPIXEL*screenPoint->p_x) - w->w_origin.p_x;
if (tmp >= 0) tmp += adjust;
else tmp -= adjust;
surfacePoint->p_x = w->w_surfaceArea.r_xbot + tmp/w->w_scale;
tmp = (SUBPIXEL*screenPoint->p_y) - w->w_origin.p_y;
if (tmp >= 0) tmp += adjust;
else tmp -= adjust;
surfacePoint->p_y = w->w_surfaceArea.r_ybot + tmp/w->w_scale;
}
}
/*
* ----------------------------------------------------------------------------
*
* WindSurfaceToScreen --
*
* This procedure transforms a rectangle in surface coordinates
* to a rectangle in the screen coordinates of a particular window.
*
* Results:
* None.
*
* Side effects:
* The rectangle pointed to by screen is filled in with the
* coordinates (in pixels) of the screen area corresponding
* to surface. A pixel is included in screen if it is
* touched in any way by surface. If any of the edges of
* screen is outside w->w_surfaceArea, the corresponding
* edge of the result will also be outside w->w_screenArea.
*
* Design Note:
* This procedure is trickier than it looks, for a couple of
* reasons having to do with numerical accuracy. The
* transformation is done in units of 1/SUBPIXEL pixel (fix-point
* arithmetic), in order to avoid accumulated round-off error
* when there are fractional pixels per surface unit. Furthermore,
* it's important that the transformation to screen units NOT be
* done with a procedure like GeoTransRect. The reason is that
* GeoTransRect does the multiplication before the addition or
* subtraction, which can result in arithmeic overflow. We use
* a different form for the transform here, in terms of an origin
* and a scale, with the translation done FIRST and multiplication
* later. This, combined with the clipping to w->w_surfaceArea,
* guarantees numerical accuracy as long as surface coordinates
* fit into an integer and SUBPIXEL*screenSize fits into an integer.
*
* ----------------------------------------------------------------------------
*/
void
WindSurfaceToScreen(w, surface, screen)
MagWindow *w; /* Window in whose coordinate system the
* transform is to be done.
*/
Rect *surface; /* Rectangle in surface coordinates of w. */
Rect *screen; /* Rectangle filled in with screen coordinates
* (in w) of surface.
*/
{
int tmp;
/* Do the four coordinates one at a time. */
/* The apparently redundant clipping (also done later by the box */
/* drawing routine) is necessary for two reasons: 1) it prevents */
/* having to do a multiply and shift function for any box extending */
/* outside the viewport, and more critically 2) it prevents integer */
/* overflow when a tile extends very far out of the viewport. */
tmp = surface->r_xbot;
if (tmp > w->w_surfaceArea.r_xtop)
screen->r_xbot = w->w_screenArea.r_xtop + 1;
else
{
tmp -= w->w_surfaceArea.r_xbot;
if (tmp < 0)
screen->r_xbot = w->w_screenArea.r_xbot - 1;
else screen->r_xbot = (w->w_origin.p_x + (tmp * w->w_scale)) >> SUBPIXELBITS;
}
tmp = surface->r_ybot;
if (tmp > w->w_surfaceArea.r_ytop)
screen->r_ybot = w->w_screenArea.r_ytop + 1;
else
{
tmp -= w->w_surfaceArea.r_ybot;
if (tmp < 0)
screen->r_ybot = w->w_screenArea.r_ybot - 1;
else screen->r_ybot = (w->w_origin.p_y + (tmp * w->w_scale)) >> SUBPIXELBITS;
}
tmp = surface->r_xtop;
if (tmp > w->w_surfaceArea.r_xtop)
screen->r_xtop = w->w_screenArea.r_xtop + 1;
else
{
tmp -= w->w_surfaceArea.r_xbot;
if (tmp < 0)
screen->r_xtop = w->w_screenArea.r_xbot - 1;
else screen->r_xtop = (w->w_origin.p_x + (tmp * w->w_scale)) >> SUBPIXELBITS;
}
tmp = surface->r_ytop;
if (tmp > w->w_surfaceArea.r_ytop)
screen->r_ytop = w->w_screenArea.r_ytop + 1;
else
{
tmp -= w->w_surfaceArea.r_ybot;
if (tmp < 0)
screen->r_ytop = w->w_screenArea.r_ybot - 1;
else screen->r_ytop = (w->w_origin.p_y + (tmp * w->w_scale)) >> SUBPIXELBITS;
}
}
/*
* ----------------------------------------------------------------------------
*
* WindSurfaceToScreenNoClip --
*
* This procedure is like WindSurfaceToScreen except that it
* does not clip to the window boundary. This is necessary for
* non-Manhattan tiles to make sure they are not clipped.
* However, this routine is also useful for Manhattan geometry
* when, for example, determining the transformation of a unit
* coordinate.
*
* We convert to type double long because at close-in scales, large
* values of w_scale can cause integer overflow on 32-bit integers.
*
* Results:
* None.
*
* Side effects:
* Pointer argument "screen" is filled with the transformed
* coordinates.
*
* ----------------------------------------------------------------------------
*/
void
WindSurfaceToScreenNoClip(w, surface, screen)
MagWindow *w;
Rect *surface;
Rect *screen;
{
dlong tmp, dval;
tmp = (dlong)(surface->r_xbot - w->w_surfaceArea.r_xbot);
dval = (dlong)w->w_origin.p_x + (tmp * (dlong)w->w_scale);
screen->r_xbot = (int)(dval >> SUBPIXELBITS);
tmp = surface->r_ybot - w->w_surfaceArea.r_ybot;
dval = (dlong)w->w_origin.p_y + (tmp * (dlong)w->w_scale);
screen->r_ybot = (int)(dval >> SUBPIXELBITS);
tmp = surface->r_xtop - w->w_surfaceArea.r_xbot;
dval = (dlong)w->w_origin.p_x + (tmp * (dlong)w->w_scale);
screen->r_xtop = (int)(dval >> SUBPIXELBITS);
tmp = surface->r_ytop - w->w_surfaceArea.r_ybot;
dval = (dlong)w->w_origin.p_y + (tmp * (dlong)w->w_scale);
screen->r_ytop = (int)(dval >> SUBPIXELBITS);
}
/*
* ----------------------------------------------------------------------------
*
* WindPointToScreen --
*
* This is just like WindSurfaceToScreen, except it transforms a
* point instead of a rectangle.
*
* Results:
* None.
*
* Side effects:
* Screen is modified to contain screen coordinates. They may be
* clipped just as in WindSurfaceToScreen.
*
* ----------------------------------------------------------------------------
*/
void
WindPointToScreen(w, surface, screen)
MagWindow *w; /* Window in whose coordinate system the
* transform is to be done.
*/
Point *surface; /* Point in surface coordinates of w. */
Point *screen; /* Point filled in with screen coordinates
* (in w) of surface.
*/
{
int tmp;
/* Do the coordinates one at a time. */
tmp = surface->p_x;
if (tmp > w->w_surfaceArea.r_xtop)
tmp = w->w_surfaceArea.r_xtop;
tmp -= w->w_surfaceArea.r_xbot;
if (tmp < 0) tmp = 0;
screen->p_x = (w->w_origin.p_x + (tmp*w->w_scale)) >> SUBPIXELBITS;
tmp = surface->p_y;
if (tmp > w->w_surfaceArea.r_ytop)
tmp = w->w_surfaceArea.r_ytop;
tmp -= w->w_surfaceArea.r_ybot;
if (tmp < 0) tmp = 0;
screen->p_y = (w->w_origin.p_y + (tmp*w->w_scale)) >> SUBPIXELBITS;
}
/*
* ----------------------------------------------------------------------------
*
* windScreenToFrame --
*
* Transform a point from screen coordinates to frame coordinates.
*
* Results:
* None.
*
* Side effects:
* Frame is filled in with the frame coordinates corresponding to
* the screen coordinates.
*
* ----------------------------------------------------------------------------
*/
void
windScreenToFrame(w, screen, frame)
MagWindow *w; /* Window in whose coordinate system the
* transform is to be done.
*/
Point *screen; /* Point in screen coordinates of w. */
Point *frame; /* Point filled in with frame coordinates.
*/
{
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
frame->p_x = screen->p_x + w->w_frameArea.r_xbot;
frame->p_y = screen->p_y + w->w_frameArea.r_ybot;
break;
default:
/* WIND_MAGIC_WINDOWS */
*frame = *screen;
}
}