IconEditorIO.mesa
Copyright Ó 1987, 1992 by Xerox Corporation. All rights reserved.
Ken Pier, May 18, 1991 2:55 pm PDT
Mike Spreitzer July 29, 1986 11:29:17 am PDT
Russ Atkinson (RRA) March 9, 1987 5:22:52 pm PST
Tim Diebert: October 1, 1987 2:08:33 pm PDT
Bloomenthal, April 11, 1990 12:23:26 pm PDT
Willie-s, June 5, 1992 11:24 am PDT
DIRECTORY
Basics USING [IsBound],
FS USING [StreamOpen, Error],
Imager USING [Box],
IconEditorDefs,
IconRegistry USING [IsRegistered, InvalidateCache],
IO USING [Close, Error, Flush, GetLength, STREAM, UnsafeGetBlock, UnsafePutBlock],
MessageWindow USING [Append, Blink],
Real USING [Fix],
Rope USING [ROPE],
RuntimeError USING [BoundsFault];
IconEditorIO:
CEDAR
PROGRAM
IMPORTS Basics, FS, IO, IconRegistry, MessageWindow, Real, RuntimeError
EXPORTS IconEditorDefs ~ {
CouldntLoadIcons: PUBLIC SIGNAL = CODE;
CouldntSaveIcons: PUBLIC SIGNAL = CODE;
ViewerNoGood: PUBLIC SIGNAL = CODE;
PointsMarked:
PUBLIC
PROC
[handle: IconEditorDefs.IconHandle, type: IconEditorDefs.MarkType]
RETURNS [
BOOL ¬
TRUE] ~ {
IF (handle.mark1.x = 0)
AND (handle.mark1.y = 0)
AND (handle.mark2.x = 0)
AND (handle.mark2.y = 0)
THEN {
SELECT type
FROM
rect => MessageWindow.Append["Hold down yellow mouse button and mark a rectangular area first...", TRUE];
line => MessageWindow.Append["Hold down yellow mouse button and control key and mark a line first...", TRUE];
ENDCASE => ERROR;
MessageWindow.Blink[];
RETURN[FALSE]
};
};
SaveBitMap:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle] ~ {
handle.savedBitMap ¬ handle.icons[LOOPHOLE[handle.currentIC]].bits;
};
LayoutBoard:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, bounds: Imager.Box] ~ {
iconsOnDisplay: INT16 ¬ MIN[handle.numberOfIcons, IconEditorDefs.maxIconsOnDisplay];
handle.numIconsPerRow ¬ Real.Fix[bounds.xmax/(IconEditorDefs.iconW+IconEditorDefs.distX)];
handle.numIconsPerCol ¬ Real.Fix[bounds.ymax/(IconEditorDefs.iconH+IconEditorDefs.distY)];
IF handle.numIconsPerRow*handle.numIconsPerCol < iconsOnDisplay
THEN {
MessageWindow.Append["Icon Editor viewer is too small to display current Icon menu.", TRUE];
SIGNAL ViewerNoGood
};
handle.possibleX ¬ Real.Fix[bounds.xmax - ((iconsOnDisplay + handle.numIconsPerCol - 1) /
handle.numIconsPerCol) * (IconEditorDefs.iconW + IconEditorDefs.distX)];
handle.possibleY ¬ Real.Fix[bounds.ymax - ((iconsOnDisplay + handle.numIconsPerRow-1) /
handle.numIconsPerRow) * (IconEditorDefs.iconH+IconEditorDefs.distY)];
IF handle.possibleX < IconEditorDefs.minimumUnit * IconEditorDefs.iconW
AND handle.possibleY < IconEditorDefs.minimumUnit * IconEditorDefs.iconH
THEN {
MessageWindow.Append["Icon Editor viewer is too small for a decent drawing board.", TRUE];
SIGNAL ViewerNoGood
};
IF handle.possibleX <= handle.possibleY
THEN {
use a vertical layout, preferred if square
handle.boardSize ¬ MIN[bounds.xmax, handle.possibleY];
handle.numIconsPerCol ¬ (iconsOnDisplay + handle.numIconsPerRow - 1) /
handle.numIconsPerRow;
handle.icX ¬ 0;
handle.icY ¬ Real.Fix[handle.boardSize] + 1
}
ELSE {
use a horizontal layout
handle.boardSize ¬ MIN[bounds.ymax, handle.possibleX];
handle.numIconsPerRow ¬ (iconsOnDisplay + handle.numIconsPerCol - 1) /
handle.numIconsPerCol;
handle.icX ¬ Real.Fix[handle.boardSize] + 1;
handle.icY ¬ 0
};
handle.unit ¬ Real.Fix[handle.boardSize / (IconEditorDefs.intHBound + 1)];
handle.boardSize ¬ handle.unit * (IconEditorDefs.intHBound + 1);
};
GetNewFlavor:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle]
RETURNS [newFlavor: IconEditorDefs.IconFlavor] ~ {
newFlavor ¬ handle.nextFlavor;
handle.nextFlavor ¬ SUCC[newFlavor];
IF
LOOPHOLE[handle.nextFlavor,
INT16] >= IconEditorDefs.maxStorage
THEN {
MessageWindow.Append[message: "Sorry- you have exceeded the current limit on number of icons.",
clearFirst: TRUE];
MessageWindow.Blink[];
ERROR
};
};
LoadIcons:
PUBLIC
PROC[handle: IconEditorDefs.IconHandle, filename: Rope.
ROPE]
RETURNS [nRead:
INT16 ¬ 0, iconFile:
IO.
STREAM ¬
NIL] =
TRUSTED {
ENABLE IO.Error => GOTO Punt;
badNews: BOOL ¬ FALSE;
newFlavor: IconEditorDefs.IconFlavor;
handle.nextFlavor ¬ FIRST[IconEditorDefs.IconFlavor]; -- Initialize this since our icons start w/ the 1st flavor
iconFile ¬
FS.StreamOpen[fileName: filename !
FS.Error => {
MessageWindow.Append[filename, TRUE];
MessageWindow.Append[": ", FALSE];
MessageWindow.Append[error.explanation, FALSE];
MessageWindow.Blink[]; badNews ¬ TRUE; CONTINUE
}];
IF badNews THEN RETURN;
FOR i: INTEGER IN [0..IconEditorDefs.maxStorage) DO handle.icons[i] ¬ NIL; ENDLOOP;
nRead ¬ iconFile.GetLength[] / (<<Basics.bytesPerWord*>>SIZE[IconEditorDefs.IconFileFormat]);
FOR n:
INT16
IN [0..nRead)
DO
newFlavor ¬ GetNewFlavor[handle];
IF handle.icons[LOOPHOLE[newFlavor]] = NIL THEN
handle.icons[LOOPHOLE[newFlavor]] ¬ NEW[IconEditorDefs.IconFileFormat];
[] ¬ iconFile.UnsafeGetBlock[block: [base: LOOPHOLE[handle.icons[LOOPHOLE[newFlavor]]], startIndex: 0, count: SIZE[IconEditorDefs.IconFileFormat]<<*Basics.bytesPerWord>>] ! RuntimeError.BoundsFault => GOTO Punt];
ENDLOOP;
iconFile.Close[]; -- now close things up
EXITS Punt => SIGNAL CouldntLoadIcons;
};
SaveIcons:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, filename: Rope.
ROPE, numberOfIcons:
INT16]
RETURNS [iconFile:
IO.
STREAM ¬
NIL] ~ {
badNews: BOOL ¬ FALSE;
iconFile ¬
FS.StreamOpen[fileName: filename, accessOptions: create, keep: 2 !
FS.Error => {
MessageWindow.Append[filename, TRUE];
MessageWindow.Append[": ", FALSE];
MessageWindow.Append[error.explanation, FALSE];
MessageWindow.Blink[]; badNews ¬ TRUE; CONTINUE
}];
IF badNews THEN RETURN;
FOR j:
INT16
IN [0..numberOfIcons)
DO
iconName: Rope.ROPE;
iconFile.UnsafePutBlock[block: [
LOOPHOLE[handle.icons[
LOOPHOLE[j]]], 0,
SIZE[IconEditorDefs.IconFileFormat]<<*Basics.bytesPerWord>>] ! RuntimeError.BoundsFault => GOTO Punt];
TRUSTED {IF Basics.IsBound[LOOPHOLE[IconRegistry.IsRegistered]] AND (iconName ¬ IconRegistry.IsRegistered[fileName: filename, index: j].name) # NIL THEN IconRegistry.InvalidateCache[iconName]}; -- w.t.
ENDLOOP;
iconFile.Flush[]; -- make sure we got everything
iconFile.Close[]; -- now close things up
EXITS Punt => SIGNAL CouldntSaveIcons;
};
NormalizeRectangle:
PUBLIC
PROC [corner1, corner2: IconEditorDefs.MarkPoint]
RETURNS [rect: IconEditorDefs.RectangleRec] ~ {
rect.x ¬ MIN[corner1.x, corner2.x];
rect.y ¬ MIN[corner1.y, corner2.y];
rect.w ¬ ABS[corner2.x-corner1.x] + 1;
rect.h ¬ ABS[corner2.y-corner1.y] + 1;
};
TransferEndPoints:
PUBLIC
PROC [endpoint1, endpoint2: IconEditorDefs.MarkPoint]
RETURNS [line: IconEditorDefs.LineRec] ~ {
line.x1 ¬ endpoint1.x; line.y1 ¬ endpoint1.y;
line.x2 ¬ endpoint2.x; line.y2 ¬ endpoint2.y;
};
ComputeEndPoints:
PUBLIC
PROC
[handle: IconEditorDefs.IconHandle, oldLine: IconEditorDefs.LineRec]
RETURNS [newLine: IconEditorDefs.LineRec] ~ {
center: INT16;
center ¬ Real.Fix[handle.unit/2]; -- used to center endpoints of line in grid square
newLine.x1 ¬ oldLine.x1*handle.unit; newLine.y1 ¬ (IconEditorDefs.intHBound- oldLine.y1)*handle.unit;
newLine.x2 ¬ oldLine.x2*handle.unit; newLine.y2 ¬ (IconEditorDefs.intHBound- oldLine.y2)*handle.unit;
IF newLine.x1 > newLine.x2
THEN {
newLine.x1 ¬ newLine.x1 + handle.unit - center;
newLine.x2 ¬ newLine.x2 + center
};
IF newLine.x2 > newLine.x1
THEN {
newLine.x2 ¬ newLine.x2 + handle.unit - center;
newLine.x1 ¬ newLine.x1 + center
};
IF newLine.x1 = newLine.x2
THEN {
newLine.x1 ¬ newLine.x2 ¬ newLine.x2 + center
};
IF newLine.y1 > newLine.y2
THEN {
newLine.y1 ¬ newLine.y1 + handle.unit - center;
newLine.y2 ¬ newLine.y2 + center
};
IF newLine.y2 > newLine.y1
THEN {
newLine.y2 ¬ newLine.y2 + handle.unit - center;
newLine.y1 ¬ newLine.y1 + center
};
IF newLine.y1 = newLine.y2
THEN {
newLine.y1 ¬ newLine.y2 ¬ newLine.y2 + center
};
};
ClearIcon:
PUBLIC
PROC
[handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor] ~ {
i,j: INT16;
iconToClear: IconEditorDefs.IconRef ¬ handle.icons[LOOPHOLE[flavor]];
FOR i
IN IconEditorDefs.IntW
DO
FOR j
IN IconEditorDefs.IntH
DO
iconToClear.bits[j].b[i] ¬ FALSE;
ENDLOOP;
ENDLOOP;
};
IconHandle: TYPE ~ IconEditorDefs.IconHandle;
IconFlavor: TYPE ~ IconEditorDefs.IconFlavor;
RectangleRec: TYPE ~ IconEditorDefs.RectangleRec;
LineRec:
TYPE ~ IconEditorDefs.LineRec;
FlipIcon:
PUBLIC
PROC [handle: IconHandle, flavor: IconFlavor, rect: RectangleRec] ~ {
bottom: INT16 ¬ rect.y+rect.h-1;
FOR i:
INT16
IN [rect.x..rect.x+rect.w)
DO
FOR j:
INT16
IN [0..rect.h/2)
DO
temp: BOOL ¬ handle.icons[LOOPHOLE[flavor]].bits[rect.y+j].b[i];
handle.icons[
LOOPHOLE[flavor]].bits[rect.y+j].b[i] ¬
handle.icons[LOOPHOLE[flavor]].bits[bottom-j].b[i];
handle.icons[LOOPHOLE[flavor]].bits[bottom-j].b[i] ¬ temp;
ENDLOOP;
ENDLOOP;
};
MirrorIcon:
PUBLIC
PROC [handle: IconHandle, flavor: IconFlavor, rect: RectangleRec] ~ {
farRight: INT16 ¬ rect.x+rect.w-1;
FOR j:
INT16
IN [rect.y..rect.y+rect.h-1]
DO
FOR i:
INT16
IN [0..rect.w/2)
DO
temp: BOOL ¬ handle.icons[LOOPHOLE[flavor]].bits[j].b[rect.x + i];
handle.icons[
LOOPHOLE[flavor]].bits[j].b[rect.x + i] ¬
handle.icons[LOOPHOLE[flavor]].bits[j].b[farRight - i];
handle.icons[LOOPHOLE[flavor]].bits[j].b[farRight - i] ¬ temp;
ENDLOOP;
ENDLOOP;
};
DrawLine:
PUBLIC
PROC [handle: IconHandle, line: LineRec, flavor: IconFlavor] ~ {
factor: INTEGER;
deltaX, deltaY, i, e, x, y: INT16;
deltaX ¬ ABS[line.x2 - line.x1];
deltaY ¬ ABS[line.y2 - line.y1];
IF ((line.x1<line.x2)
AND (line.y2<line.y1))
OR ((line.x2<line.x1)
AND (line.y1<line.y2))
THEN {
factor ¬ -1;
y ¬ MAX[line.y1, line.y2]
}
ELSE {
factor ¬ 1;
y ¬ MIN[line.y1, line.y2]
};
x ¬ MIN[line.x1, line.x2];
IF deltaX >= deltaY
THEN {
e ¬ 2*deltaY - deltaX;
FOR i
IN [0..deltaX]
DO
handle.icons[LOOPHOLE[flavor]].bits[y].b[x] ¬ TRUE;
IF e > 0
THEN {
IF (y >= 0) AND (y < IconEditorDefs.intHBound) THEN y ¬ y + factor; -- Just to be safe
e ¬ e + (2*deltaY - 2*deltaX)
}
ELSE e ¬ e + 2*deltaY;
IF x < IconEditorDefs.intWBound THEN x ¬ x + 1; -- Just to be safe
ENDLOOP;
}
ELSE {
e ¬ 2*deltaX - deltaY;
FOR i
IN [0..deltaY]
DO
handle.icons[LOOPHOLE[flavor]].bits[y].b[x] ¬ TRUE;
IF e > 0
THEN {
IF x < IconEditorDefs.intWBound THEN x ¬ x + 1; -- Just to be safe
e ¬ e + (2*deltaX - 2*deltaY)
}
ELSE e ¬ e + 2*deltaX;
IF (y >= 0) AND (y < IconEditorDefs.intHBound) THEN y ¬ y + factor; -- Just to be safe
ENDLOOP;
};
FreshMarks[handle];
};
FreshMarks:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle] ~ {
handle.mark1.x ¬ handle.mark1.y ¬ handle.mark2.x ¬ handle.mark2.y ¬ 0;
handle.functionNotApplied ¬ FALSE;
};
WhiteLabel:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor] ~ {
handle.icons[LOOPHOLE[flavor]].invertLabel ¬ TRUE;
};
BlackLabel:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor] ~ {
handle.icons[LOOPHOLE[flavor]].invertLabel ¬ FALSE;
};
SetLabel:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec]~ {
i: IconEditorDefs.IconRef ¬ handle.icons[LOOPHOLE[flavor]];
i.label ¬ TRUE;
handle.labelRect.x ¬ i.lx ¬ rect.x;
i.ly ¬ IconEditorDefs.iconH - 1 - (rect.y + rect.h);
handle.labelRect.y ¬ rect.y;
handle.labelRect.w ¬ i.lw ¬ rect.w;
handle.labelRect.h ¬ i.lh ¬ rect.h;
FreshMarks[handle];
};
UnSetLabel:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor]~ {
i: IconEditorDefs.IconRef ¬ handle.icons[LOOPHOLE[flavor]];
i.label ¬ FALSE;
i.lx ¬ 0; i.ly ¬ 0;
i.lw ¬ 0; i.lh ¬ 0;
};
SetBlack:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec] ~ {
i, j: INT16;
FOR i
IN [rect.x..rect.x+rect.w-1]
DO
FOR j
IN [rect.y..rect.y+rect.h-1]
DO
handle.icons[LOOPHOLE[flavor]].bits[j].b[i] ¬ TRUE;
ENDLOOP;
ENDLOOP;
FreshMarks[handle];
};
SetWhite:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec] ~ {
i, j: INT16;
iconToClear: IconEditorDefs.IconRef ¬ handle.icons[LOOPHOLE[flavor]];
FOR i
IN [rect.x..rect.x+rect.w-1]
DO
FOR j
IN [rect.y..rect.y+rect.h-1]
DO
iconToClear.bits[j].b[i] ¬ FALSE;
ENDLOOP;
ENDLOOP;
FreshMarks[handle];
};
SetDeskTopGray:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec, firstClearToWhite:
BOOL ¬
TRUE] ~ {
xmin0: INT16 = (rect.x/4)*4;
xmin2: INT16 = ((MAX[rect.x, 2] - 2)/4)*4 + 2;
phase: INT16 ¬ rect.y MOD 4;
x, y: INT16;
IF firstClearToWhite THEN SetWhite[handle: handle, flavor: flavor, rect: rect]; --must clear before setting grey bits
FOR y
IN [rect.y .. rect.y+rect.h)
DO
xmin: INT16 = IF phase < 2 THEN xmin0 ELSE xmin2;
FOR x ¬ xmin, x+4
WHILE x < rect.x+rect.w
DO
handle.icons[LOOPHOLE[flavor]].bits[y].b[x] ¬ TRUE;
ENDLOOP;
IF (phase ¬ phase + 1) = 4 THEN phase ¬ 0;
ENDLOOP;
FreshMarks[handle];
};
SetDarkGray:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec] ~ {
i, j: INT16;
indent: INT16 ¬ 0;
SetWhite[handle: handle, flavor: flavor, rect: rect]; --must clear before setting grey bits
FOR j
IN [rect.y..rect.y+rect.h)
DO
paint gray
FOR i ¬ (rect.x+indent), i+2
WHILE i < (rect.x+rect.w)
DO
handle.icons[LOOPHOLE[flavor]].bits[j].b[i] ¬ TRUE;
ENDLOOP;
next time do a differnt indentation
indent ¬ IF indent=0 THEN 1 ELSE 0;
ENDLOOP;
FreshMarks[handle];
};
Invert:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, rect: IconEditorDefs.RectangleRec] ~ {
i,j: INT16;
FOR i
IN [rect.x..rect.x+rect.w-1]
DO
FOR j
IN [rect.y..rect.y+rect.h-1]
DO
handle.icons[
LOOPHOLE[flavor]].bits[j].b[i] ¬
IF handle.icons[LOOPHOLE[flavor]].bits[j].b[i] THEN FALSE ELSE TRUE;
ENDLOOP;
ENDLOOP;
FreshMarks[handle];
};
ICRectangle: TYPE = RECORD [xmin, ymin, xmax, ymax: INT16];
GetRectangle:
PROC[handle: IconEditorDefs.IconHandle]
RETURNS[ICRectangle] ~ {
x1, y1, x2, y2: INT16;
x1 ¬ handle.mark1.x; y1 ¬ handle.mark1.y;
x2 ¬ handle.mark2.x; y2 ¬ handle.mark2.y;
IF x1=0 AND y1=0 AND x2=0 AND y2=0 THEN { x2 ¬ IconEditorDefs.intWBound; y2 ¬ IconEditorDefs.intHBound };
RETURN[[xmin: MIN[x1, x2], ymin: MIN[y1, y2], xmax: MAX[x1, x2], ymax: MAX[y1, y2]]];
};
MoveRight:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, slowShift:
BOOL] ~ {
numberToMove: INT16 ¬ IF slowShift THEN 1 ELSE 4;
r: ICRectangle = GetRectangle[handle];
FOR k:
INT16
IN [1..numberToMove]
DO
oldBits: IconEditorDefs.BitArray ¬ handle.icons[LOOPHOLE[flavor]].bits;
FOR i:
INT16
IN [r.xmin..r.xmax]
DO
FOR j:
INT16
IN [r.ymin..r.ymax]
DO
handle.icons[
LOOPHOLE[flavor]].bits[j].b[i] ¬
oldBits[j].b[IF i=r.xmin THEN r.xmax ELSE i-1];
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
MoveLeft:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, slowShift:
BOOL] ~ {
numberToMove: INT16 ¬ IF slowShift THEN 1 ELSE 4;
r: ICRectangle = GetRectangle[handle];
FOR k:
INT16
IN [1..numberToMove]
DO
oldBits: IconEditorDefs.BitArray ¬ handle.icons[LOOPHOLE[flavor]].bits;
FOR i:
INT16
IN [r.xmin..r.xmax]
DO
FOR j:
INT16
IN [r.ymin..r.ymax]
DO
handle.icons[
LOOPHOLE[flavor]].bits[j].b[i] ¬
oldBits[j].b[IF i=r.xmax THEN r.xmin ELSE i+1];
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
MoveUp:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, slowShift:
BOOL] ~ {
numberToMove: INT16 ¬ IF slowShift THEN 1 ELSE 4;
r: ICRectangle = GetRectangle[handle];
FOR k:
INT16
IN [1..numberToMove]
DO
oldBits: IconEditorDefs.BitArray ¬ handle.icons[LOOPHOLE[flavor]].bits;
FOR i:
INT16
IN [r.xmin..r.xmax]
DO
FOR j:
INT16
IN [r.ymin..r.ymax]
DO
handle.icons[
LOOPHOLE[flavor]].bits[j].b[i] ¬
oldBits[IF j=r.ymax THEN r.ymin ELSE j+1].b[i];
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
MoveDown:
PUBLIC
PROC [handle: IconEditorDefs.IconHandle, flavor: IconEditorDefs.IconFlavor, slowShift:
BOOL] ~ {
numberToMove: INT16 ¬ IF slowShift THEN 1 ELSE 4;
r: ICRectangle = GetRectangle[handle];
FOR k:
INT16
IN [1..numberToMove]
DO
oldBits: IconEditorDefs.BitArray ¬ handle.icons[LOOPHOLE[flavor]].bits;
FOR i:
INT16
IN [r.xmin..r.xmax]
DO
FOR j:
INT16
IN [r.ymin..r.ymax]
DO
handle.icons[
LOOPHOLE[flavor]].bits[j].b[i] ¬
oldBits[IF j=r.ymin THEN r.ymax ELSE j-1].b[i];
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
}.