windows-nt/Source/XPSP1/NT/shell/cpls/utc/worldmap.c
2020-09-26 16:20:57 +08:00

643 lines
13 KiB
C

/*++
Copyright (c) 1994-1998, Microsoft Corporation All rights reserved.
Module Name:
worldmap.c
Abstract:
This module implements the world map for the Date/Time applet.
Revision History:
--*/
//
// Include Files.
//
#include "timedate.h"
#include <commctrl.h>
#include "worldmap.h"
////////////////////////////////////////////////////////////////////////////
//
// ZeroCDC
//
////////////////////////////////////////////////////////////////////////////
static void ZeroCDC(
LPCDC cdc)
{
cdc->dc = NULL;
cdc->bitmap = cdc->defbitmap = NULL;
}
////////////////////////////////////////////////////////////////////////////
//
// CreateCDC
//
////////////////////////////////////////////////////////////////////////////
static BOOL CreateCDC(
LPCDC cdc,
HBITMAP bitmap)
{
cdc->dc = CreateCompatibleDC(NULL);
cdc->bitmap = cdc->defbitmap = NULL;
if (!bitmap)
{
return (FALSE);
}
if (!cdc->dc)
{
if (bitmap)
{
DeleteBitmap(bitmap);
}
return (FALSE);
}
if (bitmap)
{
SetLayout(cdc->dc, LAYOUT_BITMAPORIENTATIONPRESERVED); // to avoid mirroring on mirrored builds
cdc->defbitmap = SelectBitmap(cdc->dc, cdc->bitmap = bitmap);
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// DestroyCDC
//
////////////////////////////////////////////////////////////////////////////
static void DestroyCDC(
LPCDC cdc)
{
if (cdc->dc)
{
if (cdc->defbitmap)
{
SelectBitmap(cdc->dc, cdc->defbitmap);
}
if (cdc->bitmap)
{
DeleteBitmap(cdc->bitmap);
}
DeleteDC(cdc->dc);
cdc->dc = NULL;
}
cdc->bitmap = cdc->defbitmap = NULL;
}
////////////////////////////////////////////////////////////////////////////
//
// LoadWorldMap
//
////////////////////////////////////////////////////////////////////////////
BOOL LoadWorldMap(
LPWORLDMAP map,
HINSTANCE instance,
LPCTSTR resource)
{
HDC tempdc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
BOOL result = FALSE;
ZeroCDC(&map->original);
ZeroCDC(&map->prepared);
map->size.cx = map->size.cy = 0;
map->rotation = 0;
if (tempdc)
{
if (CreateCDC( &map->original,
LoadImage( instance,
resource,
IMAGE_BITMAP,
0,
0,
LR_CREATEDIBSECTION ) ))
{
DIBSECTION ds;
if (GetObject(map->original.bitmap, sizeof(DIBSECTION), &ds))
{
map->size.cx = ds.dsBm.bmWidth;
map->size.cy = ds.dsBm.bmHeight;
map->bits = (BYTE *)ds.dsBm.bmBits;
map->scanbytes = ds.dsBm.bmWidthBytes;
if (( (GetDeviceCaps(tempdc, BITSPIXEL) *
GetDeviceCaps(tempdc, PLANES)) > 4 ) ||
CreateCDC( &map->prepared,
CreateCompatibleBitmap( tempdc,
ds.dsBm.bmWidth,
ds.dsBm.bmHeight ) ))
{
RGBQUAD init = { 127, 0, 0, 0 };
RGBQUAD *color = map->dirty.colors;
int i = WORLDMAP_MAX_COLORS;
while (i--)
{
*color++ = init;
}
//
// Mark everything as dirty.
//
map->dirty.first = 0;
map->dirty.last = WORLDMAP_MAX_COLORS - 1;
map->dirty.spans = NULL;
map->dirty.freespans = NULL;
map->source = (map->prepared.dc)
? map->prepared.dc
: map->original.dc;
result = TRUE;
}
}
else
{
DestroyCDC(&map->original);
}
}
DeleteDC(tempdc);
}
return (result);
}
////////////////////////////////////////////////////////////////////////////
//
// FreeWorldMap
//
////////////////////////////////////////////////////////////////////////////
void FreeWorldMap(
LPWORLDMAP map)
{
DIRTYSPAN *span = map->dirty.spans;
while (span)
{
DIRTYSPAN *next = span->next;
LocalFree((HANDLE)span);
span = next;
}
span = map->dirty.freespans;
while (span)
{
DIRTYSPAN *next = span->next;
LocalFree((HANDLE)span);
span = next;
}
DestroyCDC(&map->original);
DestroyCDC(&map->prepared);
}
////////////////////////////////////////////////////////////////////////////
//
// SetWorldMapRotation
//
////////////////////////////////////////////////////////////////////////////
void SetWorldMapRotation(
LPWORLDMAP map,
int rotation)
{
rotation %= (int)map->size.cx;
if (rotation < 0)
{
rotation += (int)map->size.cx;
}
map->rotation = rotation;
}
////////////////////////////////////////////////////////////////////////////
//
// RotateWorldMap
//
////////////////////////////////////////////////////////////////////////////
void RotateWorldMap(
LPWORLDMAP map,
int delta)
{
SetWorldMapRotation(map, map->rotation + delta);
}
////////////////////////////////////////////////////////////////////////////
//
// WorldMapGetDisplayedLocation
//
////////////////////////////////////////////////////////////////////////////
int WorldMapGetDisplayedLocation(
LPWORLDMAP map,
int pos)
{
return ( (pos + map->rotation) % map->size.cx );
}
////////////////////////////////////////////////////////////////////////////
//
// EnumWorldMapDirtySpans
//
////////////////////////////////////////////////////////////////////////////
void EnumWorldMapDirtySpans(
LPWORLDMAP map,
ENUMSPANPROC proc,
LPARAM data,
BOOL rotate)
{
DIRTYSPAN *span = map->dirty.spans;
while (span)
{
if (rotate)
{
int left = (span->left + map->rotation) % map->size.cx;
int right = left + span->right - span->left;
if (right > map->size.cx)
{
proc(data, left, map->size.cx);
left = 0;
right -= map->size.cx;
}
proc(data, left, right);
}
else
{
proc(data, span->left, span->right);
}
span = span->next;
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetWorldMapColorIndex
//
////////////////////////////////////////////////////////////////////////////
int GetWorldMapColorIndex(
LPWORLDMAP map,
int x,
int y)
{
//
// Protect against faulting.
//
if ( !map->bits ||
(x < 0) || (x >= map->size.cx) ||
(y < 0) || (y >= map->size.cy) )
{
return (-1);
}
//
// Correct source X coordinate for map's virtual rotation.
//
x += 2 * (int)map->size.cx - map->rotation;
x %= (int)map->size.cx;
//
// Correct for dib origin.
//
y = (LONG)map->size.cy - 1 - y;
return ( map->bits[map->scanbytes * y + x] );
}
////////////////////////////////////////////////////////////////////////////
//
// NewSpan
//
////////////////////////////////////////////////////////////////////////////
DIRTYSPAN *NewSpan(
DIRTYSTUFF *dirty,
DIRTYSPAN *a,
DIRTYSPAN *b)
{
DIRTYSPAN *span = dirty->freespans;
if (span)
{
dirty->freespans = span->next;
}
else
{
if ((span = (DIRTYSPAN *)LocalAlloc(LPTR, sizeof(DIRTYSPAN))) == NULL)
{
return (NULL);
}
}
span->next = b;
if (a)
{
a->next = span;
}
else
{
dirty->spans = span;
}
return (span);
}
////////////////////////////////////////////////////////////////////////////
//
// DeleteSpan
//
////////////////////////////////////////////////////////////////////////////
void DeleteSpan(
DIRTYSTUFF *dirty,
DIRTYSPAN *a,
DIRTYSPAN *b)
{
if (a)
{
a->next = b->next;
}
else
{
dirty->spans = b->next;
}
b->next = dirty->freespans;
dirty->freespans = b;
}
////////////////////////////////////////////////////////////////////////////
//
// AddDirtySpan
//
////////////////////////////////////////////////////////////////////////////
void AddDirtySpan(
DIRTYSTUFF *dirty,
int left,
int cx)
{
int right = left + cx;
DIRTYSPAN *curr = dirty->spans;
DIRTYSPAN *temp = NULL;
DIRTYSPAN *span;
cx = left - 1;
while (curr && (cx > curr->right))
{
temp = curr;
curr = curr->next;
}
cx = right + 1;
if (curr && (cx >= curr->left))
{
if (left < curr->left)
{
curr->left = left;
}
span = temp = curr;
if (right > curr->right)
{
curr->right = right;
curr = curr->next;
while (curr && (cx >= curr->left))
{
span->right = curr->right;
DeleteSpan(dirty, temp, curr);
temp = temp->next;
curr = (temp ? temp->next : NULL);
}
}
}
else
{
if ((span = NewSpan(dirty, temp, curr)) == NULL)
{
if (!temp)
{
return;
}
span = temp;
left = span->left;
}
span->left = left;
span->right = right;
}
}
////////////////////////////////////////////////////////////////////////////
//
// ChangeWorldMapColor
//
////////////////////////////////////////////////////////////////////////////
void ChangeWorldMapColor(
LPWORLDMAP map,
int index,
const RGBQUAD *color,
int x,
int cx)
{
if ((index >= 0) && (index < WORLDMAP_MAX_COLORS))
{
//
// Store the new color.
//
map->dirty.colors[index] = *color;
//
// Update the dirty markers to include this entry.
//
if (index < map->dirty.first)
{
map->dirty.first = index;
}
if (index > map->dirty.last)
{
map->dirty.last = index;
}
}
while (((x + cx) > map->size.cx) && (x >= 0))
{
x -= map->size.cx;
}
if (x < 0)
{
AddDirtySpan(&map->dirty, map->size.cx + x, -x);
cx += x;
x = 0;
}
AddDirtySpan(&map->dirty, x, cx);
}
////////////////////////////////////////////////////////////////////////////
//
// CommitChanges
//
////////////////////////////////////////////////////////////////////////////
void CommitChanges(
LPWORLDMAP map)
{
if (map->dirty.last >= 0)
{
SetDIBColorTable( map->original.dc,
map->dirty.first,
1 + map->dirty.last - map->dirty.first,
map->dirty.colors + map->dirty.first );
//
// Reset the dirty markers.
//
map->dirty.first = WORLDMAP_MAX_COLORS;
map->dirty.last = -1;
}
while (map->dirty.spans)
{
DIRTYSPAN *span = map->dirty.spans;
if (map->prepared.dc)
{
BitBlt( map->prepared.dc,
span->left,
0,
span->right - span->left,
(int)map->size.cy,
map->original.dc,
span->left,
0,
SRCCOPY );
}
DeleteSpan(&map->dirty, NULL, span);
}
}
////////////////////////////////////////////////////////////////////////////
//
// DrawWorldMap
//
////////////////////////////////////////////////////////////////////////////
void DrawWorldMap(
HDC dc,
int xdst,
int ydst,
int cx,
int cy,
LPWORLDMAP map,
int xmap,
int ymap,
DWORD rop)
{
CommitChanges(map);
//
// Lop off extra Y stuff cause there's nothing there.
//
if ((ymap + cy) > (int)map->size.cy)
{
cy = (int)map->size.cy - ymap;
}
//
// Clip off extra X so we'll enter the case below only when we need to.
//
if (cx > (int)map->size.cx)
{
cx = (int)map->size.cx;
}
//
// Correct source X coordinate for map's virtual rotation.
//
xmap += 2 * (int)map->size.cx - map->rotation;
xmap %= (int)map->size.cx;
//
// See if the blt rect falls off the end of our flat little world.
//
if ((xmap + cx) > (int)map->size.cx)
{
//
// Compute the width of the first blt.
//
int firstcx = (int)map->size.cx - xmap;
//
// See bits. See bits blt.
//
BitBlt(dc, xdst, ydst, firstcx, cy, map->source, xmap, ymap, rop);
//
// Adjust the params so the second blt does the right wrapping.
//
xdst += firstcx;
cx -= firstcx;
xmap = 0;
}
//
// blt bits blt!
//
BitBlt(dc, xdst, ydst, cx, cy, map->source, xmap, ymap, rop);
}