/// <summary> /// Create a surface to cache Pdu. /// </summary> /// <param name="sid">This is used to indicate surface id.</param> /// <param name="key">This is used to indicate a key to associate with the bitmap cache entry.</param> /// <param name="slot">This is used to indicate the index of the bitmap cache entry in which /// the source bitmap data is stored.</param> /// <param name="rect">This is used to indicate rectangle that bounds the source bitmap.</param> public RDPGFX_SURFACE_TO_CACHE CreateSurfaceToCachePdu(ushort sid, ulong key, ushort slot, RDPGFX_RECT16 rect) { RDPGFX_SURFACE_TO_CACHE surfaceToCache = new RDPGFX_SURFACE_TO_CACHE(sid, key, slot, rect); return(surfaceToCache); }
/// <summary> /// Verify SUT Display /// </summary> /// <param name="usingRemoteFX">Whether the output image is using RemoteFX codec</param> /// <param name="rect">The Rectangle on the image to be compared</param> /// <param name="callStackIndex">Call stack index from the test method, which help to get test method name in the function</param> private void VerifySUTDisplay(bool usingRemoteFX, RDPGFX_RECT16 rect, int callStackIndex = 1) { Rectangle compareRect = new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); base.VerifySUTDisplay(usingRemoteFX, compareRect, callStackIndex + 1); }
public void RDPEGFX_SurfaceToSurface_PositiveTest_DestRectsOverlapped() { uint fid; // Init for capability exchange RDPEGFX_CapabilityExchange(); // Create & output source surface, then fill it with green color RDPGFX_RECT16 surfRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.surfPos, RdpegfxTestUtility.surfWidth, RdpegfxTestUtility.surfHeight); Surface surf = this.rdpegfxAdapter.CreateAndOutputSurface(surfRect, PixelFormat.PIXEL_FORMAT_ARGB_8888); this.TestSite.Assert.IsNotNull(surf, "Surface {0} is created", surf.Id); RDPGFX_RECT16 fillSurfRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.imgPos, RdpegfxTestUtility.surfWidth, RdpegfxTestUtility.surfHeight); RDPGFX_RECT16[] fillRects = { fillSurfRect }; // Relative to surface fid = this.rdpegfxAdapter.SolidFillSurface(surf, RdpegfxTestUtility.fillColorGreen, fillRects); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface is filled with solid color in frame: {0}", fid); this.rdpegfxAdapter.ExpectFrameAck(fid); // Create & output destination surface, then fill it with blue color RDPGFX_RECT16 surfRect2 = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.surfPos2, RdpegfxTestUtility.surfWidth2, RdpegfxTestUtility.surfHeight2); Surface surf2 = this.rdpegfxAdapter.CreateAndOutputSurface(surfRect2, PixelFormat.PIXEL_FORMAT_ARGB_8888); this.TestSite.Assert.IsNotNull(surf, "Surface {0} is created", surf2.Id); RDPGFX_RECT16 fillSurfRect2 = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.imgPos, RdpegfxTestUtility.surfWidth2, RdpegfxTestUtility.surfHeight2); RDPGFX_RECT16[] fillRects2 = { fillSurfRect2 }; fid = this.rdpegfxAdapter.SolidFillSurface(surf2, RdpegfxTestUtility.fillColorBlue, fillRects2); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface is filled with solid color in frame {0}, dest rects are overlapped each other", fid); this.rdpegfxAdapter.ExpectFrameAck(fid); // Generate destPts array List <RDPGFX_POINT16> destPtsList = new List <RDPGFX_POINT16>(); // First point int x = 0; int y = 0; destPtsList.Add(new RDPGFX_POINT16((ushort)x, (ushort)y)); // Second Point, the rect ovelap partial of the first one x = RdpegfxTestUtility.copySrcRect.Width / 2; y = RdpegfxTestUtility.copySrcRect.Height / 2; destPtsList.Add(new RDPGFX_POINT16((ushort)x, (ushort)y)); fid = this.rdpegfxAdapter.InterSurfaceCopy(surf, RdpegfxTestUtility.copySrcRect, RdpegfxTestUtility.fillColorRed, surf2, destPtsList.ToArray()); this.TestSite.Log.Add(LogEntryKind.Debug, "Send RDPGFX_SURFACE_TO_SURFACE_PDU message in frame: {0}", fid); this.rdpegfxAdapter.ExpectFrameAck(fid); this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(false, new RDPGFX_RECT16[] { surfRect, surfRect2 }); // Delete the surfaces this.rdpegfxAdapter.DeleteSurface(surf.Id); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface {0} is deleted", surf.Id); this.rdpegfxAdapter.DeleteSurface(surf2.Id); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface {0} is deleted", surf2.Id); }
/// <summary> /// Create a wire to surface Pdu1. /// </summary> /// <param name="sId">This is used to indicate the target surface id.</param> /// <param name="cId">This is used to indicate the codecId.</param> /// <param name="pixFormat">This is used to indicate the pixel format to fill target surface.</param> /// <param name="bmRect">This is used to indicate border of bitmap on target surface.</param> /// <param name="bmLen">This is used to indicate the length of bitmap data.</param> /// <param name="bmData">This is used to indicate the bitmap data encoded by cId codec.</param> public RDPGFX_WIRE_TO_SURFACE_PDU_1 CreateWireToSurfacePdu1(ushort sId, CodecType cId, PixelFormat pixFormat, RDPGFX_RECT16 bmRect, byte[] bmData) { RDPGFX_WIRE_TO_SURFACE_PDU_1 wireToSurf1 = new RDPGFX_WIRE_TO_SURFACE_PDU_1(sId, cId, pixFormat, bmRect, bmData); return(wireToSurf1); }
public void RDPEGFX_WireToSurface_PositiveTest_ImageBorderOverlapSurface() { this.TestSite.Log.Add(LogEntryKind.Comment, "Do capability exchange."); // Init for capability exchange RDPEGFX_CapabilityExchange(); this.TestSite.Log.Add(LogEntryKind.Comment, "Create a surface and fill it with green color."); // Create & output a surface RDPGFX_RECT16 surfRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.surfPos, RdpegfxTestUtility.surfWidth, RdpegfxTestUtility.surfHeight); Surface surf = this.rdpegfxAdapter.CreateAndOutputSurface(surfRect, PixelFormat.PIXEL_FORMAT_XRGB_8888); this.TestSite.Assert.IsNotNull(surf, "Surface {0} is created", surf.Id); // Send solid fill request to client to fill surface with green color RDPGFX_RECT16 fillSurfRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.imgPos, RdpegfxTestUtility.surfWidth, RdpegfxTestUtility.surfHeight); RDPGFX_RECT16[] fillRects = { fillSurfRect }; // Relative to surface uint fid = this.rdpegfxAdapter.SolidFillSurface(surf, RdpegfxTestUtility.fillColorGreen, fillRects); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface is filled with solid color in frame: {0}.", fid); this.rdpegfxAdapter.ExpectFrameAck(fid); Image bgImage; Image compImage = RdpegfxTestUtility.captureFromImage(image_64X64, RdpegfxTestUtility.imgPos, RdpegfxTestUtility.smallWidth, RdpegfxTestUtility.smallHeight, out bgImage); byte compFlag = (byte)PACKET_COMPR_FLAG.PACKET_COMPR_TYPE_RDP8; // Top-right corner this.TestSite.Log.Add(LogEntryKind.Comment, "The bitmap is sent to the client by frame {0}. whose top and right borders are overlapped with surface.", fid); int y = 0; int x = RdpegfxTestUtility.surfWidth - RdpegfxTestUtility.smallWidth; fid = this.rdpegfxAdapter.SendUncompressedImage(compImage, (ushort)x, (ushort)y, surf.Id, PixelFormat.PIXEL_FORMAT_XRGB_8888, compFlag, RdpegfxTestUtility.segmentPartSize); this.rdpegfxAdapter.ExpectFrameAck(fid); this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(false, surfRect); // Bottom-right corner this.TestSite.Log.Add(LogEntryKind.Comment, "The bitmap is sent to the client by frame {0}. whose bottom and right borders are overlapped with surface", fid); y = RdpegfxTestUtility.surfHeight - RdpegfxTestUtility.smallHeight; x = RdpegfxTestUtility.surfWidth - RdpegfxTestUtility.smallWidth; fid = this.rdpegfxAdapter.SendUncompressedImage(compImage, (ushort)x, (ushort)y, surf.Id, PixelFormat.PIXEL_FORMAT_XRGB_8888, compFlag, RdpegfxTestUtility.segmentPartSize); this.rdpegfxAdapter.ExpectFrameAck(fid); this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(false, surfRect); // Bottom-left corner this.TestSite.Log.Add(LogEntryKind.Comment, "The bitmap is sent to the client by frame {0}. whose bottom and left borders are overlapped with surface", fid); y = RdpegfxTestUtility.surfHeight - RdpegfxTestUtility.smallHeight; x = 0; fid = this.rdpegfxAdapter.SendUncompressedImage(compImage, (ushort)x, (ushort)y, surf.Id, PixelFormat.PIXEL_FORMAT_XRGB_8888, compFlag, RdpegfxTestUtility.segmentPartSize); this.rdpegfxAdapter.ExpectFrameAck(fid); this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(false, surfRect); // Delete the surface this.rdpegfxAdapter.DeleteSurface(surf.Id); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface {0} is deleted", surf.Id); }
/// <summary> /// Create a surface to surface Pdu. /// </summary> /// <param name="srcSID">This is used to indicate source surface id.</param> /// <param name="destSID">This is used to indicate destination surface id.</param> /// <param name="srcRect">This is used to indicate source rectangle bitmap area to be copied.</param> /// <param name="destPoints">This is used to specify destination points of source rectangle bitmap to be copied. </param> public RDPGFX_SURFACE_TO_SURFACE CreateSurfaceToSurfacePdu(ushort srcSID, ushort destSID, RDPGFX_RECT16 srcRect, RDPGFX_POINT16[] destPoints) { RDPGFX_SURFACE_TO_SURFACE surfToSurf = new RDPGFX_SURFACE_TO_SURFACE(srcSID, destSID, srcRect); if (destPoints != null) { for (int i = 0; i < destPoints.Length; i++) { surfToSurf.AddDestPosition(destPoints[i]); } } return(surfToSurf); }
/// <summary> /// Common function to send H264 data to the client /// </summary> /// <param name="h264DataFile">XML file of H264 data</param> /// <param name="isAVC444">Whether need RDP client support AVC444/AVC444v2</param> private void SendH264CodecStream(string h264DataFile, bool isAVC444) { //Load H264 data RdpegfxH264TestDatas h264TestData = GetH264TestData(h264DataFile); // Init for capability exchange this.TestSite.Log.Add(LogEntryKind.Comment, "Do capability exchange."); RDPEGFX_CapabilityExchange(); if (isAVC444) { this.TestSite.Assume.IsTrue(this.isH264AVC444Supported, "This test case requires RDP client to support AVC444/AVC444v2."); } else { this.TestSite.Assume.IsTrue(this.isH264AVC420Supported, "To test H264 codec, client must indicate support for H264 codec in RDPGFX_CAPS_ADVERTISE_PDU"); } this.TestSite.Log.Add(LogEntryKind.Comment, "Create a surface and fill it with green color."); // Create & output a surface RDPGFX_POINT16 surfPos = new RDPGFX_POINT16((ushort)h264TestData.SurfaceInfo.outputOriginX, (ushort)h264TestData.SurfaceInfo.outputOriginY); RDPGFX_RECT16 surfRect = RdpegfxTestUtility.ConvertToRect(surfPos, h264TestData.SurfaceInfo.width, h264TestData.SurfaceInfo.height); RDPGFX_RECT16 compareRect = RdpegfxTestUtility.ConvertToRect(surfPos, h264TestData.SurfaceInfo.width, h264TestData.SurfaceInfo.height); if (isWindowsImplementation && compareRect.top < 32 && compareRect.bottom > 32) { // Ignore the field of RDP client connection bar compareRect.top = 32; } Surface surf = this.rdpegfxAdapter.CreateAndOutputSurface(surfRect, (PixelFormat)h264TestData.SurfaceInfo.pixelFormat); this.TestSite.Assert.IsNotNull(surf, "Surface {0} is created", surf.Id); // Send solid fill request to client to fill surface with green color RDPGFX_RECT16[] fillRects = { new RDPGFX_RECT16(h264TestData.TestDataList[0].DestRect.left, h264TestData.TestDataList[0].DestRect.top, h264TestData.TestDataList[0].DestRect.right, h264TestData.TestDataList[0].DestRect.bottom) }; // Relative to surface uint fid = this.rdpegfxAdapter.SolidFillSurface(surf, RdpegfxTestUtility.fillColorGreen, fillRects); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface is filled with solid color in frame: {0}", fid); this.rdpegfxAdapter.ExpectFrameAck(fid); // Send H264 codec data foreach (TestData data in h264TestData.TestDataList) { ushort codecId = data.codecId; PixelFormat pixFormat = (PixelFormat)data.pixelFormat; RDPGFX_RECT16 bmRect = new RDPGFX_RECT16(); bmRect.left = data.DestRect.left; bmRect.top = data.DestRect.top; bmRect.right = data.DestRect.right; bmRect.bottom = data.DestRect.bottom; if (codecId == (ushort)CodecType.RDPGFX_CODECID_AVC420 && data.AVC420BitmapStream != null) { this.TestSite.Log.Add(LogEntryKind.Comment, "Sending H264 AVC420 Encoded Bitmap Data Messages to client."); fid = this.rdpegfxAdapter.SendImageWithH264AVC420Codec(surf.Id, pixFormat, bmRect, data.AVC420BitmapStream.To_RFX_AVC420_BITMAP_STREAM(), data.GetBaseImage()); // Test case pass if frame acknowledge is received. this.rdpegfxAdapter.ExpectFrameAck(fid); } else if (codecId == (ushort)CodecType.RDPGFX_CODECID_AVC444 && data.AVC444BitmapStream != null) { this.TestSite.Log.Add(LogEntryKind.Comment, "Sending H264 AVC444 Encoded Bitmap Data Messages to client."); fid = this.rdpegfxAdapter.SendImageWithH264AVC444Codec(surf.Id, pixFormat, bmRect, CodecType.RDPGFX_CODECID_AVC444, data.AVC444BitmapStream.To_RFX_AVC444_BITMAP_STREAM(), data.GetBaseImage()); // Test case pass if frame acknowledge is received. this.rdpegfxAdapter.ExpectFrameAck(fid); } else if (codecId == (ushort)CodecType.RDPGFX_CODECID_AVC444v2 && data.AVC444v2BitmapStream != null) { this.TestSite.Log.Add(LogEntryKind.Comment, "Sending H264 AVC444v2 Encoded Bitmap Data Messages to client."); fid = this.rdpegfxAdapter.SendImageWithH264AVC444Codec(surf.Id, pixFormat, bmRect, CodecType.RDPGFX_CODECID_AVC444v2, data.AVC444v2BitmapStream.To_RFX_AVC444V2_BITMAP_STREAM(), data.GetBaseImage()); // Test case pass if frame acknowledge is received. this.rdpegfxAdapter.ExpectFrameAck(fid); } else { Site.Assert.Fail("Test data doesn't contain proper H264 encoded data corresponding to codec ID."); } this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(true, compareRect, 2); } // Delete the surface this.rdpegfxAdapter.DeleteSurface(surf.Id); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface {0} is deleted", surf.Id); }
public void RDPEGFX_SurfaceToScreen_PositiveTest_MultiSurfaceOverlap() { uint fid; this.TestSite.Log.Add(LogEntryKind.Comment, "Do capability exchange."); // Init for capability exchange RDPEGFX_CapabilityExchange(); RDPGFX_RECT16 verifyRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.surfPos, RdpegfxTestUtility.surfWidth, RdpegfxTestUtility.surfHeight); // Init Data for test RDPGFX_POINT16[] positions = new RDPGFX_POINT16[4]; positions[0] = new RDPGFX_POINT16(RdpegfxTestUtility.surfPos.x, RdpegfxTestUtility.surfPos.y); positions[1] = new RDPGFX_POINT16((ushort)(RdpegfxTestUtility.surfPos.x + RdpegfxTestUtility.surfWidth / 2), RdpegfxTestUtility.surfPos.y); positions[2] = new RDPGFX_POINT16(RdpegfxTestUtility.surfPos.x, (ushort)(RdpegfxTestUtility.surfPos.y + RdpegfxTestUtility.surfHeight / 2)); positions[3] = new RDPGFX_POINT16(RdpegfxTestUtility.surfPos.x, RdpegfxTestUtility.surfPos.y); ushort[] widths = new ushort[4]; widths[0] = RdpegfxTestUtility.surfWidth; widths[1] = (ushort)(RdpegfxTestUtility.surfWidth / 2); widths[2] = RdpegfxTestUtility.surfWidth; widths[3] = (ushort)(RdpegfxTestUtility.surfWidth / 2); ushort[] heights = new ushort[4]; heights[0] = (ushort)(RdpegfxTestUtility.surfHeight / 2); heights[1] = RdpegfxTestUtility.surfHeight; heights[2] = (ushort)(RdpegfxTestUtility.surfHeight / 2); heights[3] = RdpegfxTestUtility.surfHeight; Color[] colors = new Color[4]; colors[0] = Color.Green; colors[1] = Color.Blue; colors[2] = Color.Red; colors[3] = Color.Yellow; Surface[] surfaces = new Surface[4]; // Test the overlap for (int i = 0; i < positions.Length; i++) { this.TestSite.Log.Add(LogEntryKind.Comment, "Create a surface and fill it with color: {0}.", colors[i]); // Create & output a surface RDPGFX_RECT16 surfRect = RdpegfxTestUtility.ConvertToRect(positions[i], widths[i], heights[i]); surfaces[i] = this.rdpegfxAdapter.CreateAndOutputSurface(surfRect, PixelFormat.PIXEL_FORMAT_XRGB_8888); this.TestSite.Assert.IsNotNull(surfaces[i], "Surface {0} is created", surfaces[i].Id); // Send solid fill request to client to fill surface with green color RDPGFX_RECT16 fillSurfRect = RdpegfxTestUtility.ConvertToRect(RdpegfxTestUtility.imgPos, widths[i], heights[i]); RDPGFX_RECT16[] fillRects = { fillSurfRect }; // Relative to surface fid = this.rdpegfxAdapter.SolidFillSurface(surfaces[i], RdpegfxTestUtility.ToRdpgfx_Color32(colors[i]), fillRects); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface is filled with solid color in frame: {0}", fid); // Expect the client to send a frame acknowledge pdu // If the server receives the message, it indicates that the client has been successfully decoded the logical frame of graphics commands this.rdpegfxAdapter.ExpectFrameAck(fid); this.TestSite.Log.Add(LogEntryKind.Comment, "Verify output on SUT Display if the verifySUTDisplay entry in PTF config is true."); this.VerifySUTDisplay(false, verifyRect); } // Delete the surface for (int i = 0; i < surfaces.Length; i++) { if (surfaces[i] != null) { this.rdpegfxAdapter.DeleteSurface(surfaces[i].Id); this.TestSite.Log.Add(LogEntryKind.Debug, "Surface {0} is deleted", surfaces[i].Id); } } }
/// <summary> /// Copy bitmap data from a source surface to the bitmap cache /// </summary> /// <param name="surfaceId"></param> /// <param name="cacheKey"></param> /// <param name="cacheSlot"></param> /// <param name="rectSrc"></param> public void SurfaceToCache(ushort surfaceId, ulong cacheKey, ushort cacheSlot, RDPGFX_RECT16 rectSrc) { if (surfaceDic.ContainsKey(surfaceId)) { Surface sur = surfaceDic[surfaceId]; CacheItem cacheItem = new CacheItem(cacheKey, cacheSlot, sur.Image, rectSrc); bitmapCache[cacheSlot] = cacheItem; } }
/// <summary> /// Render Clear codec image /// </summary> /// <param name="surfaceId"></param> /// <param name="pixFormat"></param> /// <param name="ccFlag"></param> /// <param name="graphIdx"></param> /// <param name="bmRect"></param> /// <param name="residualBmp"></param> /// <param name="bands"></param> /// <param name="subcodecs"></param> public void RenderClearCodecImage(ushort surfaceId, PixelFormat pixFormat, byte ccFlag, ushort graphIdx, RDPGFX_RECT16 bmRect, Image residualBmp, Dictionary <RDPGFX_POINT16, Bitmap> bands, Dictionary <RDPGFX_POINT16, BMP_INFO> subcodecs) { Image paint = new Bitmap(bmRect.right - bmRect.left, bmRect.bottom - bmRect.top); Graphics paintG = Graphics.FromImage(paint); if (ccFlag != 0 && ((ccFlag & ClearCodec_BitmapStream.CLEARCODEC_FLAG_GLYPH_INDEX) != 0) && ((ccFlag & ClearCodec_BitmapStream.CLEARCODEC_FLAG_GLYPH_HIT) != 0) && this.clearCodecGlyphStorage.ContainsKey(graphIdx)) { Image srcImage = this.clearCodecGlyphStorage[graphIdx]; Rectangle srcRect = new Rectangle(0, 0, srcImage.Width, srcImage.Height); Rectangle destRect = new Rectangle(0, 0, bmRect.right - bmRect.left, bmRect.bottom - bmRect.top); paintG.DrawImage(srcImage, destRect, srcRect, GraphicsUnit.Pixel); } else { // Draw the first layer: residualData if (residualBmp != null && residualBmp.Width <= paint.Width && residualBmp.Height <= residualBmp.Height) { paintG.DrawImage(residualBmp, 0, 0); } // Draw the second layer: bandsData if (bands != null) { foreach (RDPGFX_POINT16 pos in bands.Keys) { Bitmap image = bands[pos]; if (pos.x + image.Width <= paint.Width && pos.y + image.Height <= paint.Height) { paintG.DrawImage(image, pos.x, pos.y); } } } // Draw the third layer: subcodecData if (subcodecs != null) { foreach (RDPGFX_POINT16 pos in subcodecs.Keys) { Bitmap image = subcodecs[pos].bmp; if (pos.x + image.Width <= paint.Width && pos.y + image.Height <= paint.Height) { paintG.DrawImage(image, pos.x, pos.y); } } } paintG.Dispose(); } // Draw the image to the surface if (this.surfaceDic.ContainsKey(surfaceId)) { Surface sur = surfaceDic[surfaceId]; sur.DrawImage(paint, bmRect.left, bmRect.top); } // Save the image to Glyph Storage if (ccFlag != 0 && ((ccFlag & ClearCodec_BitmapStream.CLEARCODEC_FLAG_GLYPH_INDEX) != 0) && ((ccFlag & ClearCodec_BitmapStream.CLEARCODEC_FLAG_GLYPH_HIT) == 0)) { if (this.clearCodecGlyphStorage.ContainsKey(graphIdx)) { this.clearCodecGlyphStorage.Remove(graphIdx); } this.clearCodecGlyphStorage.Add(graphIdx, paint); } }