protected void RedefineBuiltinCursorFromChar(int index, int chr) { // Cursor image in both Loom versions are based on images from charset. // This function is *only* supported for Loom! if (_game.GameId != Scumm.IO.GameId.Loom) { throw new NotSupportedException("RedefineBuiltinCursorFromChar is *only* supported for Loom!"); } if (index < 0 || index >= 4) { throw new ArgumentException("index"); } // const int oldID = _charset->getCurID(); var ptr = _cursorImages[index]; if (_game.Version == 3) { _charset.SetCurID(0); } else if (_game.Version >= 4) { _charset.SetCurID(1); } var s = new Surface(_charset.GetCharWidth(chr), _charset.GetFontHeight(), PixelFormat.Indexed8, false); var p = new PixelNavigator(s); Gdi.Fill(new PixelNavigator(s), 123, s.Width, s.Height); _charset.DrawChar(chr, s, 0, 0); Array.Clear(ptr, 0, ptr.Length); for (int h = 0; h < s.Height; h++) { for (int w = 0; w < s.Width; w++) { p.GoTo(w, h); if (p.Read() != 123) { ptr[h] |= (ushort)(1 << (15 - w)); } } } // _charset->setCurID(oldID); }
static byte[] Capture(VirtScreen screen, int x, int y, int w, int h) { var pixels = new byte[w * h]; var nav = new PixelNavigator(screen.Surfaces[0]); nav.GoTo(x, y); for (int height = 0; height < h; height++) { for (int width = 0; width < w; width++) { pixels[height * w + width] = nav.Read(); nav.OffsetX(1); } nav.Offset(-w, 1); } return(pixels); }
protected void GetPixel(int x, int y) { var vs = FindVirtScreen(y); if (vs == null || x > ScreenWidth - 1 || x < 0) { Push(-1); return; } var nav = new PixelNavigator(vs.Surfaces[0]); nav.GoTo(x, y - vs.TopLine); var pixel = nav.Read(); Push(pixel); }
static void DumpZPlanes(Room room, string name) { if (room.Image != null) { for (int i = 0; i < room.Image.ZPlanes.Count; i++) { var zplane = room.Image.ZPlanes[i]; var nStrips = zplane.StripOffsets.Count; var pixels = new byte[nStrips * room.Header.Height]; var pn = new PixelNavigator(pixels, nStrips, 1); using (var ms = new MemoryStream(zplane.Data)) { for (int nStrip = 0; nStrip < nStrips; nStrip++) { var offset = zplane.StripOffsets[nStrip]; if (offset.HasValue) { ms.Seek(offset.Value, SeekOrigin.Begin); pn.GoTo(nStrip, 0); DecompressMaskImg(pn, ms, room.Header.Height); } } } using (var bmpZ = new System.Drawing.Bitmap(nStrips * 8, room.Header.Height)) { for (int j = 0; j < room.Header.Height; j++) { for (int x = 0; x < nStrips; x++) { for (int b = 0; b < 8; b++) { bmpZ.SetPixel(x * 8 + b, j, (pixels[x + j * nStrips] & (0x80 >> b)) != 0 ? System.Drawing.Color.White : System.Drawing.Color.Black); } } } bmpZ.Save(name + "_z" + i + ".png"); } } } }
protected void TownsDrawStripToScreen(VirtScreen vs, int dstX, int dstY, int srcX, int srcY, int width, int height) { if (width <= 0 || height <= 0) { return; } int m = _textSurfaceMultiplier; var src1 = new PixelNavigator(vs.Surfaces[0]); src1.GoTo(vs.XStart + srcX, srcY); var src2 = new PixelNavigator(_textSurface); src2.GoTo(srcX * m, (srcY + vs.TopLine - ScreenTop) * m); var dst1 = new PixelNavigator(_townsScreen.GetLayerPixels(0, dstX, dstY).Value); var dst2 = new PixelNavigator(_townsScreen.GetLayerPixels(1, dstX * m, dstY * m).Value); int dp1 = _townsScreen.GetLayerPitch(0) - width * _townsScreen.GetLayerBpp(0); int dp2 = _townsScreen.GetLayerPitch(1) - width * m * _townsScreen.GetLayerBpp(1); int sp1 = vs.Pitch - (width * Surface.GetBytesPerPixel(vs.PixelFormat)); int sp2 = _textSurface.Pitch - width * m; if (vs == MainVirtScreen || Game.GameId == GameId.Indy3 || Game.GameId == GameId.Zak) { for (int h = 0; h < height; ++h) { if (Surface.GetBytesPerPixel(_gfxManager.PixelFormat) == 2) { for (int w = 0; w < width; ++w) { dst1.WriteUInt16(_16BitPalette[src1.Read()]); src1.OffsetX(1); dst1.OffsetX(1); } src1.OffsetX(sp1 / src1.BytesByPixel); dst1.OffsetX(dp1 / dst1.BytesByPixel); } else { for (int i = 0; i < width; i++) { dst1.Write(src1.Read()); dst1.OffsetX(1); src1.OffsetX(1); } } for (int sH = 0; sH < m; ++sH) { for (int i = 0; i < width * m; i++) { dst2.Write(src2.Read()); src2.OffsetX(1); dst2.OffsetX(1); } src2.OffsetX(_textSurface.Width - (width * m)); dst2.OffsetX(dst2.Width - (width * m)); } } } else { dst1 = new PixelNavigator(dst2); for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { var t = (src1.Read()) & 0x0f; src1.OffsetX(1); for (int i = 0; i < m; i++) { dst1.Write((byte)((t << 4) | t)); dst1.OffsetX(1); } } dst1 = new PixelNavigator(dst2); var src3 = new PixelNavigator(src2); if (m == 2) { dst2.OffsetY(1); src3.OffsetY(1); } for (int w = 0; w < width * m; ++w) { dst2.Write((byte)(src3.Read() | dst1.Read() & _townsLayer2Mask[src3.Read()])); dst2.OffsetX(1); dst1.Write((byte)(src2.Read() | dst1.Read() & _townsLayer2Mask[src2.Read()])); src2.OffsetX(1); src3.OffsetX(1); dst1.OffsetX(1); } src1.OffsetX(sp1 / src1.BytesByPixel); src2 = new PixelNavigator(src3); src2.OffsetX(sp2 / src2.BytesByPixel); dst1 = new PixelNavigator(dst2); dst1.OffsetX(dp2 / dst1.BytesByPixel); dst2.OffsetX(dp2 / dst2.BytesByPixel); } } _townsScreen.AddDirtyRect(dstX * m, dstY * m, width * m, height * m); }
protected void RestoreBackground(Rect rect, byte backColor = 0) { VirtScreen vs; if (rect.Top < 0) { rect.Top = 0; } if (rect.Left >= rect.Right || rect.Top >= rect.Bottom) { return; } if ((vs = FindVirtScreen(rect.Top)) == null) { return; } if (rect.Left > vs.Width) { return; } // Convert 'rect' to local (virtual screen) coordinates rect.Top -= vs.TopLine; rect.Bottom -= vs.TopLine; rect.Clip(vs.Width, vs.Height); int height = rect.Height; int width = rect.Width; if (_game.Platform == Platform.FMTowns && Game.GameId == GameId.Monkey1 && vs == VerbVirtScreen && rect.Bottom <= 154) { rect.Right = 319; } MarkRectAsDirty(vs, rect.Left, rect.Right, rect.Top, rect.Bottom, Gdi.UsageBitRestored); var screenBuf = new PixelNavigator(vs.Surfaces[0]); screenBuf.GoTo(vs.XStart + rect.Left, rect.Top); if (height == 0) { return; } if (vs.HasTwoBuffers && _currentRoom != 0 && IsLightOn()) { var back = new PixelNavigator(vs.Surfaces[1]); back.GoTo(vs.XStart + rect.Left, rect.Top); Gdi.Blit(screenBuf, back, width, height); if (vs == MainVirtScreen && _charset.HasMask) { if (_game.Platform == Platform.FMTowns) { var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left * _textSurfaceMultiplier, (rect.Top + vs.TopLine) * _textSurfaceMultiplier); Gdi.Fill(mask, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } else { var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left, rect.Top - ScreenTop); Gdi.Fill(mask, CharsetMaskTransparency, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } } } else { if (Game.Platform == Platform.FMTowns) { backColor |= (byte)(backColor << 4); var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left * _textSurfaceMultiplier, (rect.Top + vs.TopLine) * _textSurfaceMultiplier); Gdi.Fill(mask, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } if (Game.Features.HasFlag(GameFeatures.Is16BitColor)) { Gdi.Fill(screenBuf, _16BitPalette[backColor], width, height); } else { Gdi.Fill(screenBuf, backColor, width, height); } } }
/// <summary> /// Blit the specified rectangle from the given virtual screen to the display. /// Note: t and b are in *virtual screen* coordinates, while x is relative to /// the *real screen*. This is due to the way tdirty/vdirty work: they are /// arrays which map 'strips' (sections of the real screen) to dirty areas as /// specified by top/bottom coordinate in the virtual screen. /// </summary> /// <param name="vs"></param> /// <param name="x"></param> /// <param name="width"></param> /// <param name="top"></param> /// <param name="bottom"></param> void DrawStripToScreen(VirtScreen vs, int x, int width, int top, int bottom) { // Short-circuit if nothing has to be drawn if (bottom <= top || top >= vs.Height) { return; } // Perform some clipping if (width > vs.Width - x) { width = vs.Width - x; } if (top < ScreenTop) { top = ScreenTop; } if (bottom > ScreenTop + ScreenHeight) { bottom = ScreenTop + ScreenHeight; } // Convert the vertical coordinates to real screen coords int y = vs.TopLine + top - ScreenTop; int height = bottom - top; if (width <= 0 || height <= 0) { return; } byte[] src; if (Game.Version < 7) { if (Game.Platform == Platform.FMTowns) { TownsDrawStripToScreen(vs, x, y, x, top, width, height); return; } var srcNav = new PixelNavigator(vs.Surfaces[0]); srcNav.GoTo(vs.XStart + x, top); var compNav = new PixelNavigator(_composite); var txtNav = new PixelNavigator(_textSurface); int m = _textSurfaceMultiplier; txtNav.GoTo(x * m, y * m); var vsPitch = vs.Pitch - width * vs.BytesPerPixel; var textPitch = _textSurface.Pitch - width * m; for (int h = height * m; h > 0; --h) { for (int w = width * m; w > 0; w--) { var temp = txtNav.Read(); int mask = temp ^ CharsetMaskTransparency; mask = (((mask & 0x7f) + 0x7f) | mask) & 0x80; mask = ((mask >> 7) + 0x7f) ^ 0x80; var dst = ((temp ^ srcNav.Read()) & mask) ^ temp; compNav.Write((byte)dst); srcNav.OffsetX(1); txtNav.OffsetX(1); compNav.OffsetX(1); } srcNav.OffsetX(vsPitch); txtNav.OffsetX(textPitch); } src = _composite.Pixels; } else { src = new byte[width * height * vs.BytesPerPixel]; var srcNav = new PixelNavigator(vs.Surfaces[0]); srcNav.GoTo(vs.XStart + x, top); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { src[h * width + w] = srcNav.Read(); srcNav.OffsetX(1); } srcNav.Offset(-width, 1); } } // Finally blit the whole thing to the screen _gfxManager.CopyRectToScreen(src, width * vs.BytesPerPixel, x, y, width, height); }
protected void DrawBoxCore(int x, int y, int x2, int y2, int color) { VirtScreen vs; if ((vs = FindVirtScreen(y)) == null) { return; } if (x > x2) { ScummHelper.Swap(ref x, ref x2); } if (y > y2) { ScummHelper.Swap(ref y, ref y2); } x2++; y2++; // Adjust for the topline of the VirtScreen y -= vs.TopLine; y2 -= vs.TopLine; // Clip the coordinates if (x < 0) { x = 0; } else if (x >= vs.Width) { return; } if (x2 < 0) { return; } if (x2 > vs.Width) { x2 = vs.Width; } if (y < 0) { y = 0; } else if (y > vs.Height) { return; } if (y2 < 0) { return; } if (y2 > vs.Height) { y2 = vs.Height; } int width = x2 - x; int height = y2 - y; // This will happen in the Sam & Max intro - see bug #1039162 - where // it would trigger an assertion in blit(). if (width <= 0 || height <= 0) { return; } MarkRectAsDirty(vs, x, x2, y, y2); var backbuff = new PixelNavigator(vs.Surfaces[0]); backbuff.GoTo(vs.XStart + x, y); // A check for -1 might be wrong in all cases since o5_drawBox() in its current form // is definitely not capable of passing a parameter of -1 (color range is 0 - 255). // Just to make sure I don't break anything I restrict the code change to FM-Towns // version 5 games where this change is necessary to fix certain long standing bugs. if (color == -1 || (color >= 254 && Game.Platform == Platform.FMTowns && (Game.GameId == GameId.Monkey2 || Game.GameId == GameId.Indy4))) { if (_game.Platform == Platform.FMTowns) { if (color == 254) { TownsSetupPalCycleField(x, y, x2, y2); } } else { if (vs != MainVirtScreen) { Debug.WriteLine("can only copy bg to main window"); } var bgbuff = new PixelNavigator(vs.Surfaces[1]); bgbuff.GoTo(vs.XStart + x, y); Gdi.Blit(backbuff, bgbuff, width, height); if (_charset.HasMask) { var mask = new PixelNavigator(_textSurface); mask.GoToIgnoreBytesByPixel(x * _textSurfaceMultiplier, (y - ScreenTop) * _textSurfaceMultiplier); Gdi.Fill(mask, CharsetMaskTransparency, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } } } else { if (_game.Features.HasFlag(GameFeatures.Is16BitColor)) { Gdi.Fill(backbuff, _16BitPalette[color], width, height); } else { if (_game.Platform == Platform.FMTowns) { color = ((color & 0x0f) << 4) | (color & 0x0f); var mask = new PixelNavigator(_textSurface); mask.GoTo(x * _textSurfaceMultiplier, (y - ScreenTop + vs.TopLine) * _textSurfaceMultiplier); Gdi.Fill(mask, (byte)color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); if (Game.GameId == GameId.Monkey2 || Game.GameId == GameId.Indy4 || ((Game.GameId == GameId.Indy3 || Game.GameId == GameId.Zak) && vs != TextVirtScreen) || (_game.GameId == GameId.Loom && vs == MainVirtScreen)) { return; } } Gdi.Fill(backbuff, (byte)color, width, height); } } }
void DrawFlashlight() { int i, j, x, y; var vs = MainVirtScreen; // Remove the flash light first if it was previously drawn if (_flashlight.IsDrawn) { MarkRectAsDirty(MainVirtScreen, _flashlight.X, _flashlight.X + _flashlight.W, _flashlight.Y, _flashlight.Y + _flashlight.H, Gdi.UsageBitDirty); if (_flashlight.PixelNavigator.HasValue) { Gdi.Fill(_flashlight.PixelNavigator.Value, 0, _flashlight.W, _flashlight.H); } _flashlight.IsDrawn = false; } if (_flashlight.XStrips == 0 || _flashlight.YStrips == 0) { return; } // Calculate the area of the flashlight if (Game.GameId == GameId.Zak || Game.GameId == GameId.Maniac) { x = _mousePos.X + vs.XStart; y = _mousePos.Y - vs.TopLine; } else { var a = Actors[Variables[VariableEgo.Value]]; x = a.Position.X; y = a.Position.Y; } _flashlight.W = _flashlight.XStrips * 8; _flashlight.H = _flashlight.YStrips * 8; _flashlight.X = x - _flashlight.W / 2 - _screenStartStrip * 8; _flashlight.Y = y - _flashlight.H / 2; if (Game.GameId == GameId.Loom) { _flashlight.Y -= 12; } // Clip the flashlight at the borders if (_flashlight.X < 0) { _flashlight.X = 0; } else if (_flashlight.X + _flashlight.W > Gdi.NumStrips * 8) { _flashlight.X = Gdi.NumStrips * 8 - _flashlight.W; } if (_flashlight.Y < 0) { _flashlight.Y = 0; } else if (_flashlight.Y + _flashlight.H > vs.Height) { _flashlight.Y = vs.Height - _flashlight.H; } // Redraw any actors "under" the flashlight for (i = _flashlight.X / 8; i < (_flashlight.X + _flashlight.W) / 8; i++) { Debug.Assert(0 <= i && i < Gdi.NumStrips); Gdi.SetGfxUsageBit(_screenStartStrip + i, Gdi.UsageBitDirty); vs.TDirty[i] = 0; vs.BDirty[i] = vs.Height; } var pn = new PixelNavigator(vs.Surfaces[0]); pn.GoTo(_flashlight.X + vs.XStart, _flashlight.Y); pn = new PixelNavigator(pn); _flashlight.PixelNavigator = pn; var bgbak = new PixelNavigator(vs.Surfaces[1]); bgbak.GoTo(_flashlight.X + vs.XStart, _flashlight.Y); Gdi.Blit(pn, bgbak, _flashlight.W, _flashlight.H); // Round the corners. To do so, we simply hard-code a set of nicely // rounded corners. var corner_data = new [] { 8, 6, 4, 3, 2, 2, 1, 1 }; int minrow = 0; int maxcol = (_flashlight.W - 1); int maxrow = (_flashlight.H - 1); for (i = 0; i < 8; i++, minrow++, maxrow--) { int d = corner_data[i]; for (j = 0; j < d; j++) { if (vs.BytesPerPixel == 2) { pn.GoTo(j, minrow); pn.WriteUInt16(0); pn.GoTo(maxcol - j, minrow); pn.WriteUInt16(0); pn.GoTo(j, maxrow); pn.WriteUInt16(0); pn.GoTo(maxcol - j, maxrow); pn.WriteUInt16(0); } else { pn.GoTo(j, minrow); pn.Write(0); pn.GoTo(maxcol - j, minrow); pn.Write(0); pn.GoTo(j, maxrow); pn.Write(0); pn.GoTo(maxcol - j, maxrow); pn.Write(0); } } } _flashlight.IsDrawn = true; }
protected virtual void KernelGetFunctions() { var vs = MainVirtScreen; var args = GetStackList(30); switch (args[0]) { case 113: // WORKAROUND for bug #899249: The scripts used for screen savers // in Sam & Max use hard coded values for the maximum height and width. // This causes problems in rooms (ie. Credits) where their values are // lower, so we set result to zero if out of bounds. if (args[1] >= 0 && args[1] <= vs.Width && args[2] >= 0 && args[2] <= vs.Height) { var nav = new PixelNavigator(vs.Surfaces[0]); nav.GoTo(args[1], args[2]); var pixel = nav.Read(); Push(pixel); } else { Push(0); } break; case 115: Push(GetSpecialBox(new Point((short)args[1], (short)args[2]))); break; case 116: Push(CheckXYInBoxBounds(args[3], new Point((short)args[1], (short)args[2]))); break; case 206: Push(RemapPaletteColor(args[1], args[2], args[3], -1)); break; case 207: { var i = GetObjectIndex(args[1]); Debug.Assert(i != 0); Push(_objs[i].Position.X); } break; case 208: { var i = GetObjectIndex(args[1]); Debug.Assert(i != 0); Push(_objs[i].Position.Y); } break; case 209: { var i = GetObjectIndex(args[1]); Debug.Assert(i != 0); Push(_objs[i].Width); } break; case 210: { var i = GetObjectIndex(args[1]); Debug.Assert(i != 0); Push(_objs[i].Height); } break; case 211: /* * 13 = thrust * 336 = thrust * 328 = thrust * 27 = abort * 97 = left * 331 = left * 115 = right * 333 = right */ Push(GetKeyState(args[1])); break; case 212: { var a = Actors[args[1]]; // This is used by walk scripts Push(a.Frame); } break; case 213: { var slot = GetVerbSlot(args[1], 0); Push(Verbs[slot].CurRect.Left); } break; case 214: { var slot = GetVerbSlot(args[1], 0); Push(Verbs[slot].CurRect.Top); } break; case 215: if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) { Push(_extraBoxFlags[args[1]]); } else { Push((int)GetBoxFlags((byte)args[1])); } break; default: throw new NotSupportedException(string.Format("KernelGetFunctions: default case {0}", args[0])); } }