int MainRoutine(int xmoveCur, int ymoveCur) { int i, skip = 0; byte drawFlag = 1; bool use_scaling; byte startScaleIndexX; int ex1, ex2; var rect = new Rect(); int step; var v1 = new Codec1(); const int ScaletableSize = 128; bool newAmiCost = (_vm.Game.Version == 5) && (_vm.Game.Platform == Platform.Amiga); v1.Scaletable = smallCostumeScaleTable; if (_loaded.NumColors == 32) { v1.Mask = 7; v1.Shr = 3; } else { v1.Mask = 15; v1.Shr = 4; } _loaded.CostumeReader.BaseStream.Seek(_srcptr, System.IO.SeekOrigin.Begin); switch (_loaded.Format) { case 0x60: case 0x61: // This format is used e.g. in the Sam&Max intro ex1 = _loaded.CostumeReader.ReadByte(); ex2 = _loaded.CostumeReader.ReadByte(); _srcptr += 2; if (ex1 != 0xFF || ex2 != 0xFF) { _loaded.CostumeReader.BaseStream.Seek(_loaded.FrameOffsets + ex1 * 2, System.IO.SeekOrigin.Begin); ex1 = _loaded.CostumeReader.ReadUInt16(); _loaded.CostumeReader.BaseStream.Seek(_loaded.BasePtr + ex1 + ex2 * 2, System.IO.SeekOrigin.Begin); _srcptr = _loaded.BasePtr + _loaded.CostumeReader.ReadUInt16() + 14; } break; } use_scaling = (ScaleX != 0xFF) || (ScaleY != 0xFF); v1.X = ActorX; v1.Y = ActorY; if (use_scaling) { /* Scale direction */ v1.ScaleXStep = -1; if (xmoveCur < 0) { xmoveCur = -xmoveCur; v1.ScaleXStep = 1; } // It's possible that the scale indexes will overflow and wrap // around to zero, so it's important that we use the same // method of accessing it both when calculating the size of the // scaled costume, and when drawing it. See bug #1519667. if (_mirror) { /* Adjust X position */ startScaleIndexX = _scaleIndexX = (byte)(ScaletableSize - xmoveCur); for (i = 0; i < xmoveCur; i++) { if (v1.Scaletable[_scaleIndexX++] < ScaleX) v1.X -= v1.ScaleXStep; } rect.Left = rect.Right = v1.X; _scaleIndexX = startScaleIndexX; for (i = 0; i < _width; i++) { if (rect.Right < 0) { skip++; startScaleIndexX = _scaleIndexX; } if (v1.Scaletable[_scaleIndexX++] < ScaleX) rect.Right++; } } else { /* No mirror */ /* Adjust X position */ startScaleIndexX = _scaleIndexX = (byte)(xmoveCur + ScaletableSize); for (i = 0; i < xmoveCur; i++) { if (v1.Scaletable[_scaleIndexX--] < ScaleX) v1.X += v1.ScaleXStep; } rect.Left = rect.Right = v1.X; _scaleIndexX = startScaleIndexX; for (i = 0; i < _width; i++) { if (rect.Left >= _w) { startScaleIndexX = _scaleIndexX; skip++; } if (v1.Scaletable[_scaleIndexX--] < ScaleX) rect.Left--; } } _scaleIndexX = startScaleIndexX; if (skip != 0) skip--; step = -1; if (ymoveCur < 0) { ymoveCur = -ymoveCur; step = 1; } _scaleIndexY = (byte)(ScaletableSize - ymoveCur); for (i = 0; i < ymoveCur; i++) { if (v1.Scaletable[_scaleIndexY++] < ScaleY) v1.Y -= step; } rect.Top = rect.Bottom = v1.Y; _scaleIndexY = (byte)(ScaletableSize - ymoveCur); for (i = 0; i < _height; i++) { if (v1.Scaletable[_scaleIndexY++] < ScaleY) rect.Bottom++; } _scaleIndexY = (byte)(ScaletableSize - ymoveCur); } else { if (!_mirror) xmoveCur = -xmoveCur; v1.X += xmoveCur; v1.Y += ymoveCur; if (_mirror) { rect.Left = v1.X; rect.Right = v1.X + _width; } else { rect.Left = v1.X - _width; rect.Right = v1.X; } rect.Top = v1.Y; rect.Bottom = rect.Top + _height; } v1.SkipWidth = _width; v1.ScaleXStep = _mirror ? 1 : -1; if (_vm.Game.Version == 1) // V1 games uses 8 x 8 pixels for actors _vm.MarkRectAsDirty(_vm.MainVirtScreen, rect.Left, rect.Right + 8, rect.Top, rect.Bottom, ActorID); else _vm.MarkRectAsDirty(_vm.MainVirtScreen, rect.Left, rect.Right + 1, rect.Top, rect.Bottom, ActorID); if (rect.Top >= _h || rect.Bottom <= 0) return 0; if (rect.Left >= _w || rect.Right <= 0) return 0; v1.RepLen = 0; if (_mirror) { if (!use_scaling) skip = -v1.X; if (skip > 0) { if (!newAmiCost && _loaded.Format != 0x57) { v1.SkipWidth -= skip; Codec1IgnorePakCols(v1, skip); v1.X = 0; } } else { skip = rect.Right - _w; if (skip <= 0) { drawFlag = 2; } else { v1.SkipWidth -= skip; } } } else { if (!use_scaling) skip = rect.Right - _w; if (skip > 0) { if (!newAmiCost && _loaded.Format != 0x57) { v1.SkipWidth -= skip; Codec1IgnorePakCols(v1, skip); v1.X = _w - 1; } } else { // V1 games uses 8 x 8 pixels for actors if (_loaded.Format == 0x57) skip = -8 - rect.Left; else skip = -1 - rect.Left; if (skip <= 0) drawFlag = 2; else v1.SkipWidth -= skip; } } if (v1.SkipWidth <= 0) return 0; if (rect.Left < 0) rect.Left = 0; if (rect.Top < 0) rect.Top = 0; if (rect.Top > _h) rect.Top = _h; if (rect.Bottom > _h) rect.Bottom = _h; if (DrawTop > rect.Top) DrawTop = rect.Top; if (DrawBottom < rect.Bottom) DrawBottom = rect.Bottom; if (_height + rect.Top >= 256) { return 2; } _pixelsNavigator = new PixelNavigator(startNav); _pixelsNavigator.Offset(v1.X, v1.Y); v1.DestPtr = _pixelsNavigator; v1.MaskPtr = _vm.GetMaskBuffer(0, v1.Y, ZBuffer); if (_loaded.Format == 0x57) { // The v1 costume renderer needs the actor number, which is // the same thing as the costume renderer's _actorID. ProcC64(v1, ActorID); } else if (newAmiCost) { Proc3Amiga(v1); } else { Proc3(v1); } return drawFlag; }
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); } }
internal void MarkRectAsDirty(VirtScreen vs, Rect r, int dirtybit = 0) { MarkRectAsDirty(vs, r.Left, r.Right, r.Top, r.Bottom, dirtybit); }
void TownsSetupPalCycleField(int x1, int y1, int x2, int y2) { if (_numCyclRects >= 10) return; _cyclRects[_numCyclRects] = new Rect(x1, y1, x2, y2); _numCyclRects++; TownsPaletteFlags |= 1; }
public void AddDirtyRect(int x, int y, int w, int h) { if (w <= 0 || h <= 0 || _numDirtyRects > DIRTY_RECTS_MAX) return; if (_numDirtyRects == DIRTY_RECTS_MAX) { // full redraw _dirtyRects.Clear(); _dirtyRects.Add(new Rect(_width - 1, _height - 1)); _numDirtyRects++; return; } int x2 = x + w - 1; int y2 = y + h - 1; Debug.Assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height); bool skip = false; for (int i = 0; i < _dirtyRects.Count; i++) { var r = _dirtyRects[i]; // Try to merge new rect with an existing rect (only once, since trying to merge // more than one overlapping rect would be causing more overhead than doing any good). if (x > r.Left && x < r.Right && y > r.Top && y < r.Bottom) { x = r.Left; y = r.Top; skip = true; } if (x2 > r.Left && x2 < r.Right && y > r.Top && y < r.Bottom) { x2 = r.Right; y = r.Top; skip = true; } if (x2 > r.Left && x2 < r.Right && y2 > r.Top && y2 < r.Bottom) { x2 = r.Right; y2 = r.Bottom; skip = true; } if (x > r.Left && x < r.Right && y2 > r.Top && y2 < r.Bottom) { x = r.Left; y2 = r.Bottom; skip = true; } if (skip) { _dirtyRects[i] = new Rect(x, y, x2, y2); break; } } if (!skip) { _dirtyRects.Add(new Rect(x, y, x2, y2)); _numDirtyRects++; } }
void MarkRectAsDirty(Rect rect) { rect.Left -= _vm.MainVirtScreen.XStart & 7; rect.Right -= _vm.MainVirtScreen.XStart & 7; _vm.MarkRectAsDirty(_vm.MainVirtScreen, rect, ActorID); }
public void SetFocusRectangle(Rect rect) { // TODO: _system.SetFocusRectangle(rect); //_system.SetFocusRectangle(rect); }
public static void Fill(Surface surface, Rect r, int color) { r = new Rect(r.Left, r.Top, r.Right, r.Bottom); r.Clip(surface.Width, surface.Height); if (!r.IsValid) return; int width = r.Width; int lineLen = width; int height = r.Height; bool useMemset = true; var bpp = Surface.GetBytesPerPixel(surface.PixelFormat); if (bpp == 2) { lineLen *= 2; if ((ushort)color != ((color & 0xff) | (color & 0xff) << 8)) useMemset = false; } else if (bpp == 4) { useMemset = false; } else if (bpp != 1) { throw new InvalidOperationException("Surface::fillRect: bytesPerPixel must be 1, 2, or 4"); } if (useMemset) { var pn = new PixelNavigator(surface); pn.GoTo(r.Left, r.Top); for (int i = 0; i < height; i++) { pn.Set((byte)color, lineLen); pn.OffsetY(1); } } else { if (bpp == 2) { var pn = new PixelNavigator(surface); pn.GoTo(r.Left, r.Top); for (int i = 0; i < height; i++) { pn.Set((ushort)color, lineLen); pn.OffsetX(surface.Width); } } else { var pn = new PixelNavigator(surface); pn.GoTo(r.Left, r.Top); for (int i = 0; i < height; i++) { pn.Set((uint)color, lineLen); pn.OffsetX(surface.Width / 2); } } } }