public int DrawCostume(VirtScreen vs, int numStrips, Actor actor, bool drawToBackBuf) { var pixelsNavigator = new PixelNavigator(vs.Surfaces[drawToBackBuf ? 1 : 0]); pixelsNavigator.OffsetX(vs.XStart); ActorX += (vs.XStart & 7); pixelsNavigator.OffsetX(-(vs.XStart & 7)); startNav = new PixelNavigator(pixelsNavigator); if (_vm.Game.IsOldBundle) { _xmove = -72; _ymove = -100; } else { _xmove = _ymove = 0; } int result = 0; for (int i = 0; i < 16; i++) { result |= DrawLimb(actor, i); } return(result); }
public void DrawBitmap(ImageData img, VirtScreen vs, int x, int y, int width, int height, int stripnr, int numstrip, int roomWidth, DrawBitmaps flags) { // Check whether lights are turned on or not var lightsOn = _vm.IsLightOn(); DrawBitmap(img, vs, new Point(x, y), width, height, stripnr, numstrip, roomWidth, flags, lightsOn); }
protected override void PrepareDrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip) { if (_objectMode) { var imgData = (ImageData1)img; objectMap = imgData.ObjectMap; } }
protected CharsetRenderer(ScummEngine vm) { Top = 0; Left = 0; StartLeft = 0; Right = 0; Color = 0; Center = false; HasMask = false; TextScreen = vm.MainVirtScreen; BlitAlso = false; FirstChar = false; DisableOffsX = false; Vm = vm; CurId = -1; }
void UpdateDirtyScreen(VirtScreen vs) { // Do nothing for unused virtual screens if (vs.Height == 0) return; int i; int w = 8; int start = 0; for (i = 0; i < Gdi.NumStrips; i++) { if (vs.BDirty[i] != 0) { int top = vs.TDirty[i]; int bottom = vs.BDirty[i]; vs.TDirty[i] = vs.Height; vs.BDirty[i] = 0; if (i != (Gdi.NumStrips - 1) && vs.BDirty[i + 1] == bottom && vs.TDirty[i + 1] == top) { // Simple optimizations: if two or more neighboring strips // form one bigger rectangle, coalesce them. w += 8; continue; } DrawStripToScreen(vs, start * 8, w, top, bottom); w = 8; } start = i + 1; } }
void DumpRoomObjectImages(Room room, ObjectData obj, Gdi gdi) { var text = new ScummText(obj.Name); var sb = new StringBuilder(); sb.Append("Object #" + obj.Number).Append(" "); var decoder = new TextDecoder(sb); text.Decode(decoder); sb.AppendFormat(" size: {0}x{1}", obj.Width, obj.Height); Console.WriteLine(sb); var j = 0; foreach (var img in obj.Images) { // try // { var screen = new VirtScreen(0, obj.Width, obj.Height, PixelFormat.Indexed8, 2); if (img.IsBomp) { var bdd = new BompDrawData(); bdd.Src = img.Data; bdd.Dst = new PixelNavigator(screen.Surfaces[0]); bdd.X = 0; bdd.Y = 0; bdd.Width = obj.Width; bdd.Height = obj.Height; bdd.ScaleX = 255; bdd.ScaleY = 255; bdd.DrawBomp(); } else { gdi.DrawBitmap(img, screen, new Point(0, 0), obj.Width, obj.Height & 0xFFF8, 0, obj.Width / 8, room.Header.Width, DrawBitmaps.None, true); } using (var bmp = ToBitmap(room, screen)) { bmp.Save("obj_" + obj.Number + "_" + (++j) + ".png"); } // } // catch (Exception e) // { // Console.ForegroundColor = ConsoleColor.Red; // Console.WriteLine(e); // Console.ResetColor(); // Console.ReadLine(); // } } }
void DumpRoomImage(Room room, Gdi gdi) { var name = room.Name ?? "room_" + room.Number; var screen = new VirtScreen(0, room.Header.Width, room.Header.Height, PixelFormat.Indexed8, 2); var numStrips = room.Header.Width / 8; gdi.NumStrips = numStrips; gdi.IsZBufferEnabled = false; if (room.Header.Height > 0) { gdi.RoomChanged(room); gdi.DrawBitmap(room.Image, screen, new Point(), room.Header.Width, room.Header.Height, 0, numStrips, room.Header.Width, 0, true); using (var bmpRoom = ToBitmap(room, screen)) { bmpRoom.Save(name + ".png"); } } DumpZPlanes(room, name); }
internal void StartScene(byte room, Actor a = null, int objectNr = 0) { StopTalk(); FadeOut(_switchRoomEffect2); _newEffect = _switchRoomEffect; if (CurrentScript != 0xFF) { if (_slots[CurrentScript].Where == WhereIsObject.Room || _slots[CurrentScript].Where == WhereIsObject.FLObject) { //nukeArrays(CurrentScript); CurrentScript = 0xFF; } else if (_slots[CurrentScript].Where == WhereIsObject.Local) { //if (slots[CurrentScript].cutsceneOverride && _game.version >= 5) // error("Script %d stopped with active cutscene/override in exit", slots[CurrentScript].number); //nukeArrays(CurrentScript); CurrentScript = 0xFF; } } if (VariableNewRoom.HasValue) _variables[VariableNewRoom.Value] = room; RunExitScript(); KillScriptsAndResources(); if (_game.Version >= 4) { StopCycle(0); } for (var i = 1; i < Actors.Length; i++) { Actors[i].Hide(); } if (Game.Version >= 7) { // Set the shadow palette(s) to all black. This fixes // bug #795940, and actually makes some sense (after all, // shadows tend to be rather black, don't they? ;-) Array.Clear(_shadowPalette, 0, _shadowPalette.Length); } else { for (var i = 0; i < 256; i++) { Gdi.RoomPalette[i] = (byte)i; if (_shadowPalette != null) _shadowPalette[i] = (byte)i; } if (Game.Version < 5) { SetDirtyColors(0, 255); } } Variables[VariableRoom.Value] = room; _fullRedraw = true; _currentRoom = room; if (room >= 0x80 && Game.Version < 7) _roomResource = _resourceMapper[room & 0x7F]; else _roomResource = room; if (VariableRoomResource.HasValue) Variables[VariableRoomResource.Value] = _roomResource; if (room != 0) ResourceManager.LoadRoom(room); if (room != 0 && _game.Version == 5 && room == _roomResource) Variables[VariableRoomFlag.Value] = 1; ClearRoomObjects(); if (_currentRoom == 0) { if (roomData != null) { _ignoreEntryExitScript = true; roomData.ExitScript.Data = new byte[0]; //roomData.Objects.Clear(); } return; } roomData = _resManager.GetRoom(_roomResource); _ignoreEntryExitScript = false; if (roomData.HasPalette) { SetCurrentPalette(0); } Gdi.NumZBuffer = GetNumZBuffers(); Gdi.TransparentColor = roomData.TransparentColor; ResetRoomSubBlocks(); ResetRoomObjects(); _drawingObjects.Clear(); if (Game.Version >= 7) { // Resize main virtual screen in V7 games. This is necessary // because in V7, rooms may be higher than one screen, so we have // to accomodate for that. _mainVirtScreen = new VirtScreen(MainVirtScreen.TopLine, ScreenWidth, roomData.Header.Height - MainVirtScreen.TopLine, MainVirtScreen.PixelFormat, 2, true); } Gdi.SetMaskHeight(roomData.Header.Height); if (VariableRoomWidth.HasValue && VariableRoomHeight.HasValue) { Variables[VariableRoomWidth.Value] = roomData.Header.Width; Variables[VariableRoomHeight.Value] = roomData.Header.Height; } if (VariableCameraMinX.HasValue) { _variables[VariableCameraMinX.Value] = ScreenWidth / 2; } if (VariableCameraMaxX.HasValue) { _variables[VariableCameraMaxX.Value] = roomData.Header.Width - (ScreenWidth / 2); } if (Game.Version >= 7) { Variables[VariableCameraMinY.Value] = ScreenHeight / 2; Variables[VariableCameraMaxY.Value] = roomData.Header.Height - (ScreenHeight / 2); SetCameraAt(new Point((ScreenWidth / 2), (ScreenHeight / 2))); } else { _camera.Mode = CameraMode.Normal; _camera.CurrentPosition.X = _camera.DestinationPosition.X = (ScreenWidth / 2); _camera.CurrentPosition.Y = _camera.DestinationPosition.Y = (ScreenHeight / 2); } if (_roomResource == 0) return; Gdi.ClearGfxUsageBits(); if (_game.Version >= 5 && a != null) { var where = GetWhereIsObject(objectNr); if (where != WhereIsObject.Room && where != WhereIsObject.FLObject) throw new NotSupportedException(string.Format("StartScene: Object {0} is not in room {1}", objectNr, _currentRoom)); Point pos; int dir; GetObjectXYPos(objectNr, out pos, out dir); a.PutActor(pos, _currentRoom); a.SetDirection(dir + 180); a.StopActorMoving(); if (Game.GameId == Scumm.IO.GameId.SamNMax) { Camera.CurrentPosition.X = Camera.DestinationPosition.X = a.Position.X; SetCameraAt(a.Position); } } ShowActors(); EgoPositioned = false; TownsResetPalCycleFields(); RunEntryScript(); if (Game.Version >= 1 && Game.Version <= 2) { RunScript(5, false, false, new int[0]); } else if ((Game.Version >= 5) && (Game.Version <= 6)) { if (a != null && !EgoPositioned) { var pos = GetObjectXYPos(objectNr); a.PutActor(pos, _currentRoom); a.Moving = 0; } } else if (_game.Version >= 7) { if (Camera.ActorToFollow != 0) { a = Actors[Camera.ActorToFollow]; SetCameraAt(a.Position); } } _doEffect = true; // Hint the backend about the virtual keyboard during copy protection screens if (_game.GameId == GameId.Monkey2) { if (room == 108) _inputManager.ShowVirtualKeyboard(); else _inputManager.HideVirtualKeyboard(); } else if (_game.GameId == GameId.Monkey1 && _game.Variant == "ega") { // this is my estimation that the room code is 90 (untested) if (room == 90) _inputManager.ShowVirtualKeyboard(); else _inputManager.HideVirtualKeyboard(); } }
void PrintCharIntern(byte[] fontPtr, int charPos, int origWidth, int origHeight, int width, int height, VirtScreen vs, bool ignoreCharsetMask) { int drawTop = Top - vs.TopLine; PixelNavigator?back = null; PixelNavigator dstPtr; Surface dstSurface; if ((ignoreCharsetMask || !vs.HasTwoBuffers)) { dstSurface = vs.Surfaces[0]; dstPtr = new PixelNavigator(vs.Surfaces[0]); dstPtr.GoTo(vs.XStart + Left, drawTop); } else { dstSurface = Vm.TextSurface; dstPtr = new PixelNavigator(dstSurface); dstPtr.GoTo(Left, Top - Vm.ScreenTop); } if (BlitAlso && vs.HasTwoBuffers) { back = dstPtr; dstSurface = vs.Surfaces[0]; dstPtr = new PixelNavigator(dstSurface); dstPtr.GoTo(vs.XStart + Left, drawTop); } if (!ignoreCharsetMask && vs.HasTwoBuffers) { drawTop = Top - Vm.ScreenTop; } DrawBitsN(dstSurface, dstPtr, fontPtr, charPos, fontPtr[_fontPos], drawTop, origWidth, origHeight); if (BlitAlso && vs.HasTwoBuffers) { // FIXME: Revisiting this code, I think the _blitAlso mode is likely broken // right now -- we are copying stuff from "dstPtr" to "back", but "dstPtr" really // only conatains charset data... // One way to fix this: don't copy etc.; rather simply render the char twice, // once to each of the two buffers. That should hypothetically yield // identical results, though I didn't try it and right now I don't know // any spots where I can test this... if (!ignoreCharsetMask) { throw new NotSupportedException("This might be broken -- please report where you encountered this to Fingolfin"); } // Perform some clipping int w = Math.Min(width, dstSurface.Width - Left); int h = Math.Min(height, dstSurface.Height - drawTop); if (Left < 0) { w += Left; back.Value.OffsetX(-Left); dstPtr.OffsetX(-Left); } if (drawTop < 0) { h += drawTop; back.Value.OffsetY(-drawTop); dstPtr.OffsetY(-drawTop); } // Blit the image data if (w > 0) { while (h-- > 0) { for (int i = 0; i < w; i++) { back.Value.Write(dstPtr.Read()); back.Value.OffsetX(1); dstPtr.OffsetX(1); } back.Value.Offset(-w, 1); dstPtr.Offset(-w, 1); } } } }
public int DrawCostume(VirtScreen vs, int numStrips, Actor actor, bool drawToBackBuf) { var pixelsNavigator = new PixelNavigator(vs.Surfaces[drawToBackBuf ? 1 : 0]); pixelsNavigator.OffsetX(vs.XStart); ActorX += (vs.XStart & 7); _w = vs.Width; _h = vs.Height; pixelsNavigator.OffsetX(-(vs.XStart & 7)); startNav = new PixelNavigator(pixelsNavigator); if (_vm.Game.Version <= 1) { _xmove = 0; _ymove = 0; } else if (_vm.Game.IsOldBundle) { _xmove = -72; _ymove = -100; } else { _xmove = _ymove = 0; } int result = 0; for (int i = 0; i < 16; i++) result |= DrawLimb(actor, i); return result; }
protected override void PrepareDrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip) { var ptr = img.Data; // // Since V3, all graphics data was encoded in strips, which is very efficient // for redrawing only parts of the screen. However, V2 is different: here // the whole graphics are encoded as one big chunk. That makes it rather // difficult to draw only parts of a room/object. We handle the V2 graphics // differently from all other (newer) graphic formats for this reason. // var table = (_objectMode ? null : _roomStrips); int left = (stripnr * 8); int right = left + (numstrip * 8); PixelNavigator navDst; var srcOffset = 0; byte color, data = 0; int run; bool dither = false; byte[] dither_table = new byte[128]; var ditherOffset = 0; int theX, theY, maxX; var surface = vs.HasTwoBuffers ? vs.Surfaces[1] : vs.Surfaces[0]; navDst = new PixelNavigator(surface); navDst.GoTo(p.X * 8, p.Y); var mask_ptr = GetMaskBuffer(p.X, p.Y, 1); if (table != null) { run = table.run[stripnr]; color = (byte)table.color[stripnr]; srcOffset = table.offsets[stripnr]; theX = left; maxX = right; } else { run = 1; color = 0; srcOffset = 0; theX = 0; maxX = width; } // Decode and draw the image data. Debug.Assert(height <= 128); for (; theX < maxX; theX++) { ditherOffset = 0; for (theY = 0; theY < height; theY++) { if (--run == 0) { data = ptr[srcOffset++]; if ((data & 0x80) != 0) { run = data & 0x7f; dither = true; } else { run = data >> 4; dither = false; } color = RoomPalette[data & 0x0f]; if (run == 0) { run = ptr[srcOffset++]; } } if (!dither) { dither_table[ditherOffset] = color; } if (left <= theX && theX < right) { navDst.Write(dither_table[ditherOffset++]); navDst.OffsetY(1); } } if (left <= theX && theX < right) { navDst.Offset(1, -height); } } // Draw mask (zplane) data theY = 0; if (table != null) { srcOffset = table.zoffsets[stripnr]; run = table.zrun[stripnr]; theX = left; } else { run = ptr[srcOffset++]; theX = 0; } while (theX < right) { byte runFlag = (byte)(run & 0x80); if (runFlag != 0) { run &= 0x7f; data = ptr[srcOffset++]; } do { if (runFlag == 0) data = ptr[srcOffset++]; if (left <= theX) { mask_ptr.Write(data); mask_ptr.OffsetY(1); } theY++; if (theY >= height) { if (left <= theX) { mask_ptr.Offset(1, -height); } theY = 0; theX += 8; if (theX >= right) break; } } while ((--run) != 0); run = ptr[srcOffset++]; } }
static bool VirtScreenContains(VirtScreen vs, int y) { return (y >= vs.TopLine && y < vs.TopLine + vs.Height); }
internal void MarkRectAsDirty(VirtScreen vs, Rect r, int dirtybit = 0) { MarkRectAsDirty(vs, r.Left, r.Right, r.Top, r.Bottom, dirtybit); }
/// <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 virtual void PrepareDrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip) { }
public void DrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip, int roomWidth, DrawBitmaps flags, bool isLightOn) { var x = p.X; var y = p.Y; if ((_vm.TownsPaletteFlags & 2) != 0) { int cx = (x - _vm.ScreenStartStrip) << 3; Gdi.Fill(_vm.TextSurface, new Rect(cx * _vm.TextSurfaceMultiplier, y * _vm.TextSurfaceMultiplier, (cx + width - 1) * _vm.TextSurfaceMultiplier, (y + height - 1) * _vm.TextSurfaceMultiplier), 0); } _objectMode = flags.HasFlag(DrawBitmaps.ObjectMode); PrepareDrawBitmap(img, vs, p, width, height, stripnr, numstrip); int sx = x - vs.XStart / 8; if (sx < 0) { numstrip -= -sx; x += -sx; stripnr += -sx; sx = 0; } // Compute the number of strips we have to iterate over. // TODO/FIXME: The computation of its initial value looks very fishy. // It was added as a kind of hack to fix some corner cases, but it compares // the room width to the virtual screen width; but the former should always // be bigger than the latter (except for MM NES, maybe)... strange int limit = Math.Max(roomWidth, vs.Width) / 8 - x; if (limit > numstrip) limit = numstrip; if (limit > NumStrips - sx) limit = NumStrips - sx; for (int k = 0; k < limit; ++k, ++stripnr, ++sx, ++x) { if (y < vs.TDirty[sx]) vs.TDirty[sx] = y; if (y + height > vs.BDirty[sx]) vs.BDirty[sx] = y + height; // In the case of a double buffered virtual screen, we draw to // the backbuffer, otherwise to the primary surface memory. var surface = vs.HasTwoBuffers ? vs.Surfaces[1] : vs.Surfaces[0]; var navDst = new PixelNavigator(surface); navDst.GoTo(x * 8, y); bool transpStrip; using (var smapReader = new BinaryReader(new MemoryStream(img.Data))) { transpStrip = DrawStrip(navDst, width, height, stripnr, smapReader); } // COMI and HE games only uses flag value if (game.Version == 8) transpStrip = true; if (vs.HasTwoBuffers) { var navFrontBuf = new PixelNavigator(vs.Surfaces[0]); navFrontBuf.GoTo(x * 8, y); if (isLightOn) Copy8Col(navFrontBuf, navDst, height); else Clear8Col(navFrontBuf, height); } DecodeMask(x, y, width, height, stripnr, img.ZPlanes, transpStrip, flags); } }
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); }
public void DrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip, int roomWidth, DrawBitmaps flags, bool isLightOn) { var x = p.X; var y = p.Y; if ((_vm.TownsPaletteFlags & 2) != 0) { int cx = (x - _vm.ScreenStartStrip) << 3; Gdi.Fill(_vm.TextSurface, new Rect(cx * _vm.TextSurfaceMultiplier, y * _vm.TextSurfaceMultiplier, (cx + width - 1) * _vm.TextSurfaceMultiplier, (y + height - 1) * _vm.TextSurfaceMultiplier), 0); } _objectMode = flags.HasFlag(DrawBitmaps.ObjectMode); PrepareDrawBitmap(img, vs, p, width, height, stripnr, numstrip); int sx = x - vs.XStart / 8; if (sx < 0) { numstrip -= -sx; x += -sx; stripnr += -sx; sx = 0; } // Compute the number of strips we have to iterate over. // TODO/FIXME: The computation of its initial value looks very fishy. // It was added as a kind of hack to fix some corner cases, but it compares // the room width to the virtual screen width; but the former should always // be bigger than the latter (except for MM NES, maybe)... strange int limit = Math.Max(roomWidth, vs.Width) / 8 - x; if (limit > numstrip) { limit = numstrip; } if (limit > NumStrips - sx) { limit = NumStrips - sx; } for (int k = 0; k < limit; ++k, ++stripnr, ++sx, ++x) { if (y < vs.TDirty[sx]) { vs.TDirty[sx] = y; } if (y + height > vs.BDirty[sx]) { vs.BDirty[sx] = y + height; } // In the case of a double buffered virtual screen, we draw to // the backbuffer, otherwise to the primary surface memory. var surface = vs.HasTwoBuffers ? vs.Surfaces[1] : vs.Surfaces[0]; var navDst = new PixelNavigator(surface); navDst.GoTo(x * 8, y); bool transpStrip; using (var smapReader = new BinaryReader(new MemoryStream(img.Data))) { transpStrip = DrawStrip(navDst, width, height, stripnr, smapReader); } // COMI and HE games only uses flag value if (game.Version == 8) { transpStrip = true; } if (vs.HasTwoBuffers) { var navFrontBuf = new PixelNavigator(vs.Surfaces[0]); navFrontBuf.GoTo(x * 8, y); if (isLightOn) { Copy8Col(navFrontBuf, navDst, height); } else { Clear8Col(navFrontBuf, height); } } DecodeMask(x, y, width, height, stripnr, img.ZPlanes, transpStrip, flags); } }
internal void MarkRectAsDirty(VirtScreen vs, int left, int right, int top, int bottom, int dirtybit = 0) { int lp, rp; if (left > right || top > bottom) return; if (top > vs.Height || bottom < 0) return; if (top < 0) top = 0; if (bottom > vs.Height) bottom = vs.Height; if (vs == MainVirtScreen && dirtybit != 0) { lp = left / 8 + _screenStartStrip; if (lp < 0) lp = 0; rp = (right + vs.XStart) / 8; if (_game.Version >= 7) { if (rp > 409) rp = 409; } else { if (rp >= 200) rp = 200; } for (; lp <= rp; lp++) Gdi.SetGfxUsageBit(lp, dirtybit); } // The following code used to be in the separate method setVirtscreenDirty lp = left / 8; rp = right / 8; if ((lp >= Gdi.NumStrips) || (rp < 0)) return; if (lp < 0) lp = 0; if (rp >= Gdi.NumStrips) rp = Gdi.NumStrips - 1; while (lp <= rp) { if (top < vs.TDirty[lp]) vs.TDirty[lp] = top; if (bottom > vs.BDirty[lp]) vs.BDirty[lp] = bottom; lp++; } }
public override void PrintChar(int chr, bool ignoreCharsetMask) { Rect shadow; Debug.Assert(_current != null); if (chr == '@') { return; } shadow.Left = Left; shadow.Top = Top; if (FirstChar) { Str.Left = (shadow.Left >= 0) ? shadow.Left : 0; Str.Top = (shadow.Top >= 0) ? shadow.Top : 0; Str.Right = Str.Left; Str.Bottom = Str.Top; FirstChar = false; } int width = _current.GetCharWidth((char)chr); int height = _current.GetCharHeight((char)chr); // bool is2byte = chr >= 256 && _vm._useCJKMode; // if (is2byte) // width = _vm._2byteWidth; shadow.Right = Left + width; shadow.Bottom = Top + height; Surface s; if (!ignoreCharsetMask) { HasMask = true; TextScreen = Vm.MainVirtScreen; } int offsetX = 0; int drawTop = Top; if (ignoreCharsetMask) { VirtScreen vs = Vm.MainVirtScreen; s = vs.Surfaces[0]; offsetX = vs.XStart; } else { s = Vm.TextSurface; drawTop -= Vm.ScreenTop; } var dst = new PixelNavigator(s); dst.GoTo(Left + offsetX, drawTop); // if (chr >= 256 && _vm._useCJKMode) // _current.draw2byte(s, chr, _left, drawTop, _color); // else _current.DrawChar(dst, (char)chr, Left, drawTop, Color); Vm.MarkRectAsDirty(Vm.MainVirtScreen, shadow); if (Str.Left > Left) { Str.Left = Left; } // Original keeps glyph width and character dimensions separately // if ((_vm._language == Common::ZH_TWN || _vm._language == Common::KO_KOR) && is2byte) // width++; Left += width; if (Str.Right < shadow.Right) { Str.Right = shadow.Right; } if (Str.Bottom < shadow.Bottom) { Str.Bottom = shadow.Bottom; } }
protected void InitScreens(int b, int h) { if (_townsScreen != null) { if (_townsClearLayerFlag == 0 && MainVirtScreen != null && ((h - b) != MainVirtScreen.Height)) _townsScreen.ClearLayer(0); if (Game.GameId != GameId.Monkey1) { Gdi.Fill(TextSurface, new Rect(0, 0, _textSurface.Width * _textSurfaceMultiplier, _textSurface.Height * _textSurfaceMultiplier), 0); _townsScreen.ClearLayer(1); } } var format = Game.Features.HasFlag(GameFeatures.Is16BitColor) ? PixelFormat.Rgb16 : PixelFormat.Indexed8; _mainVirtScreen = new VirtScreen(b, ScreenWidth, h - b, format, 2, true); _textVirtScreen = new VirtScreen(0, ScreenWidth, b, format, 1); _verbVirtScreen = new VirtScreen(h, ScreenWidth, ScreenHeight - h, format, 1); // Since the size of screen 3 is fixed, there is no need to reallocate // it if its size changed. // Not sure what it is good for, though. I think it may have been used // in pre-V7 for the games messages (like 'Pause', Yes/No dialogs, // version display, etc.). I don't know about V7, maybe the same is the // case there. If so, we could probably just remove it completely. if (_game.Version >= 7) { _unkVirtScreen = new VirtScreen((ScreenHeight / 2) - 10, ScreenWidth, 13, format, 1); } else { _unkVirtScreen = new VirtScreen(80, ScreenWidth, 13, format, 1); } _screenB = b; _screenH = h; Gdi.Init(); }
protected override void PrepareDrawBitmap(ImageData img, VirtScreen vs, Point p, int width, int height, int stripnr, int numstrip) { var ptr = img.Data; // // Since V3, all graphics data was encoded in strips, which is very efficient // for redrawing only parts of the screen. However, V2 is different: here // the whole graphics are encoded as one big chunk. That makes it rather // difficult to draw only parts of a room/object. We handle the V2 graphics // differently from all other (newer) graphic formats for this reason. // var table = (_objectMode ? null : _roomStrips); int left = (stripnr * 8); int right = left + (numstrip * 8); PixelNavigator navDst; var srcOffset = 0; byte color, data = 0; int run; bool dither = false; byte[] dither_table = new byte[128]; var ditherOffset = 0; int theX, theY, maxX; var surface = vs.HasTwoBuffers ? vs.Surfaces[1] : vs.Surfaces[0]; navDst = new PixelNavigator(surface); navDst.GoTo(p.X * 8, p.Y); var mask_ptr = GetMaskBuffer(p.X, p.Y, 1); if (table != null) { run = table.run[stripnr]; color = (byte)table.color[stripnr]; srcOffset = table.offsets[stripnr]; theX = left; maxX = right; } else { run = 1; color = 0; srcOffset = 0; theX = 0; maxX = width; } // Decode and draw the image data. Debug.Assert(height <= 128); for (; theX < maxX; theX++) { ditherOffset = 0; for (theY = 0; theY < height; theY++) { if (--run == 0) { data = ptr[srcOffset++]; if ((data & 0x80) != 0) { run = data & 0x7f; dither = true; } else { run = data >> 4; dither = false; } color = RoomPalette[data & 0x0f]; if (run == 0) { run = ptr[srcOffset++]; } } if (!dither) { dither_table[ditherOffset] = color; } if (left <= theX && theX < right) { navDst.Write(dither_table[ditherOffset++]); navDst.OffsetY(1); } } if (left <= theX && theX < right) { navDst.Offset(1, -height); } } // Draw mask (zplane) data theY = 0; if (table != null) { srcOffset = table.zoffsets[stripnr]; run = table.zrun[stripnr]; theX = left; } else { run = ptr[srcOffset++]; theX = 0; } while (theX < right) { byte runFlag = (byte)(run & 0x80); if (runFlag != 0) { run &= 0x7f; data = ptr[srcOffset++]; } do { if (runFlag == 0) { data = ptr[srcOffset++]; } if (left <= theX) { mask_ptr.Write(data); mask_ptr.OffsetY(1); } theY++; if (theY >= height) { if (left <= theX) { mask_ptr.Offset(1, -height); } theY = 0; theX += 8; if (theX >= right) { break; } } } while ((--run) != 0); run = ptr[srcOffset++]; } }
System.Drawing.Color ToColor(Room room, VirtScreen screen, int x, int y) { var index = screen.Surfaces[0].Pixels[y * screen.Width + x]; var c = Game.Features.HasFlag(GameFeatures.SixteenColors) ? Palette.Cga.Colors[index] : room.Palette.Colors[index]; return System.Drawing.Color.FromArgb(c.R, c.G, c.B); }
void PrintCharIntern(byte[] fontPtr, int charPos, int origWidth, int origHeight, int width, int height, VirtScreen vs, bool ignoreCharsetMask) { int drawTop = Top - vs.TopLine; PixelNavigator? back = null; PixelNavigator dstPtr; Surface dstSurface; if ((ignoreCharsetMask || !vs.HasTwoBuffers)) { dstSurface = vs.Surfaces[0]; dstPtr = new PixelNavigator(vs.Surfaces[0]); dstPtr.GoTo(vs.XStart + Left, drawTop); } else { dstSurface = Vm.TextSurface; dstPtr = new PixelNavigator(dstSurface); dstPtr.GoTo(Left, Top - Vm.ScreenTop); } if (BlitAlso && vs.HasTwoBuffers) { back = dstPtr; dstSurface = vs.Surfaces[0]; dstPtr = new PixelNavigator(dstSurface); dstPtr.GoTo(vs.XStart + Left, drawTop); } if (!ignoreCharsetMask && vs.HasTwoBuffers) { drawTop = Top - Vm.ScreenTop; } DrawBitsN(dstSurface, dstPtr, fontPtr, charPos, fontPtr[_fontPos], drawTop, origWidth, origHeight); if (BlitAlso && vs.HasTwoBuffers) { // FIXME: Revisiting this code, I think the _blitAlso mode is likely broken // right now -- we are copying stuff from "dstPtr" to "back", but "dstPtr" really // only conatains charset data... // One way to fix this: don't copy etc.; rather simply render the char twice, // once to each of the two buffers. That should hypothetically yield // identical results, though I didn't try it and right now I don't know // any spots where I can test this... if (!ignoreCharsetMask) throw new NotSupportedException("This might be broken -- please report where you encountered this to Fingolfin"); // Perform some clipping int w = Math.Min(width, dstSurface.Width - Left); int h = Math.Min(height, dstSurface.Height - drawTop); if (Left < 0) { w += Left; back.Value.OffsetX(-Left); dstPtr.OffsetX(-Left); } if (drawTop < 0) { h += drawTop; back.Value.OffsetY(-drawTop); dstPtr.OffsetY(-drawTop); } // Blit the image data if (w > 0) { while (h-- > 0) { for (int i = 0; i < w; i++) { back.Value.Write(dstPtr.Read()); back.Value.OffsetX(1); dstPtr.OffsetX(1); } back.Value.Offset(-w, 1); dstPtr.Offset(-w, 1); } } } }
System.Drawing.Color ToColor(byte[] pixels, Palette palette, VirtScreen screen, int x, int y) { var index = pixels[y * screen.Width + x]; var c = palette.Colors[index]; return System.Drawing.Color.FromArgb(c.R, c.G, c.B); }
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; }
System.Drawing.Bitmap ToBitmap(Room room, VirtScreen screen) { var bmp = new System.Drawing.Bitmap(screen.Width, screen.Height); // DitherCGA(screen.Surfaces[0].Pixels, screen.Surfaces[0].Pitch, 0, 0, screen.Width, screen.Height); var palette = Game.Features.HasFlag(GameFeatures.SixteenColors) ? Palette.Ega : room.Palette; for (int x = 0; x < screen.Width; x++) { for (int y = 0; y < screen.Height; y++) { bmp.SetPixel(x, y, ToColor(screen.Surfaces[0].Pixels, palette, screen, x, y)); } } return bmp; }