///<summary> /// Copies a box from another PixelBuffer to a region of the /// this PixelBuffer. ///</summary> ///<param name="src">Source/dest pixel buffer</param> ///<param name="srcBox">Image.BasicBox describing the source region in this buffer</param> ///<param name="dstBox">Image.BasicBox describing the destination region in this buffer</param> ///<remarks> /// The source and destination regions dimensions don't have to match, in which /// case scaling is done. This scaling is generally done using a bilinear filter in hardware, /// but it is faster to pass the source image in the right dimensions. /// Only call this function when both buffers are unlocked. ///</remarks> public virtual void Blit(HardwarePixelBuffer src, BasicBox srcBox, BasicBox dstBox) { if (IsLocked || src.IsLocked) { throw new Exception("Source and destination buffer may not be locked! In HardwarePixelBuffer.Blit"); } if (src == this) { throw new Exception("Source must not be the same object, in HardwarePixelBuffer.Blit"); } PixelBox srclock = src.Lock(srcBox, BufferLocking.ReadOnly); BufferLocking method = BufferLocking.Normal; if (dstBox.Left == 0 && dstBox.Top == 0 && dstBox.Front == 0 && dstBox.Right == width && dstBox.Bottom == height && dstBox.Back == depth) { // Entire buffer -- we can discard the previous contents method = BufferLocking.Discard; } PixelBox dstlock = Lock(dstBox, method); if (dstlock.Width != srclock.Width || dstlock.Height != srclock.Height || dstlock.Depth != srclock.Depth) { // Scaling desired throw new Exception("Image scaling not yet implemented; in HardwarePixelBuffer.Blit"); } // Image.Scale(srclock, dstlock); else { // No scaling needed PixelUtil.BulkPixelConversion(srclock, dstlock); } Unlock(); src.Unlock(); }
///<summary> /// @copydoc HardwarePixelBuffer.BlitToMemory ///</summary> public override void BlitToMemory(BasicBox srcBox, PixelBox dst) { // Decide on pixel format of temp surface PixelFormat tmpFormat = format; if (D3DHelper.ConvertEnum(dst.Format) == D3D.Format.Unknown) { tmpFormat = dst.Format; } if (surface != null) { Debug.Assert(srcBox.Depth == 1 && dst.Depth == 1); // Create temp texture D3D.Texture tmp = new D3D.Texture(device, dst.Width, dst.Height, 1, // 1 mip level ie topmost, generate no mipmaps 0, D3DHelper.ConvertEnum(tmpFormat), Pool.Scratch); D3D.Surface subSurface = tmp.GetSurfaceLevel(0); // Copy texture to this temp surface Rectangle destRect, srcRect; srcRect = ToD3DRectangle(srcBox); destRect = ToD3DRectangleExtent(dst); SurfaceLoader.FromSurface(subSurface, destRect, surface, srcRect, Filter.None, 0); // Lock temp surface and copy it to memory int pitch; // Filled in by D3D GraphicsStream data = subSurface.LockRectangle(D3D.LockFlags.ReadOnly, out pitch); // Copy it PixelBox locked = new PixelBox(dst.Width, dst.Height, dst.Depth, tmpFormat); FromD3DLock(locked, pitch, data); PixelUtil.BulkPixelConversion(locked, dst); subSurface.UnlockRectangle(); // Release temporary surface and texture subSurface.Dispose(); tmp.Dispose(); } else { // Create temp texture D3D.VolumeTexture tmp = new D3D.VolumeTexture(device, dst.Width, dst.Height, dst.Depth, 0, D3D.Usage.None, D3DHelper.ConvertEnum(tmpFormat), Pool.Scratch); D3D.Volume subVolume = tmp.GetVolumeLevel(0); // Volume D3D.Box ddestBox = ToD3DBoxExtent(dst); D3D.Box dsrcBox = ToD3DBox(srcBox); VolumeLoader.FromVolume(subVolume, ddestBox, volume, dsrcBox, Filter.None, 0); // Lock temp surface and copy it to memory D3D.LockedBox lbox; // Filled in by D3D GraphicsStream data = subVolume.LockBox(LockFlags.ReadOnly, out lbox); // Copy it PixelBox locked = new PixelBox(dst.Width, dst.Height, dst.Depth, tmpFormat); FromD3DLock(locked, lbox, data); PixelUtil.BulkPixelConversion(locked, dst); subVolume.UnlockBox(); // Release temporary surface and texture subVolume.Dispose(); tmp.Dispose(); } }
protected virtual void LoadImages(List <Image> images) { Debug.Assert(images.Count >= 1); if (isLoaded) { log.InfoFormat("Unloading image: {0}", name); Unload(); } srcWidth = width = images[0].Width; srcHeight = height = images[0].Height; srcDepth = depth = images[0].Depth; if (hasAlpha && images[0].Format == PixelFormat.L8) { format = PixelFormat.A8; srcBpp = 8; } else { this.Format = images[0].Format; } if (finalBpp == 16) { switch (format) { case PixelFormat.R8G8B8: case PixelFormat.X8R8G8B8: format = PixelFormat.R5G6B5; break; case PixelFormat.B8G8R8: case PixelFormat.X8B8G8R8: format = PixelFormat.B5G6R5; break; case PixelFormat.A8R8G8B8: case PixelFormat.R8G8B8A8: case PixelFormat.A8B8G8R8: case PixelFormat.B8G8R8A8: format = PixelFormat.A4R4G4B4; break; default: // use the original format break; } } // The custom mipmaps in the image have priority over everything int imageMips = images[0].NumMipMaps; if (imageMips > 0) { numMipmaps = imageMips; usage &= ~TextureUsage.AutoMipMap; } // Create the texture CreateInternalResources(); // Check if we're loading one image with multiple faces // or a vector of images representing the faces int faces; bool multiImage; // Load from multiple images? if (images.Count > 1) { faces = images.Count; multiImage = true; } else { faces = images[0].NumFaces; multiImage = false; } // Check wether number of faces in images exceeds number of faces // in this texture. If so, clamp it. if (faces > this.NumFaces) { faces = this.NumFaces; } // Say what we're doing log.InfoFormat("Texture: {0}: Loading {1} faces({2},{3}x{4}x{5})", name, faces, PixelUtil.GetFormatName(images[0].Format), images[0].Width, images[0].Height, images[0].Depth); #if NOT // crazy ogre logging if (!(mMipmapsHardwareGenerated && mNumMipmaps == 0)) { str << mNumMipmaps; } if (mUsage & TU_AUTOMIPMAP) { if (mMipmapsHardwareGenerated) { str << " hardware"; } str << " generated mipmaps"; } else { str << " custom mipmaps"; } if (multiImage) { str << " from multiple Images."; } else { str << " from Image."; } // Scoped { // Print data about first destination surface HardwarePixelBufferSharedPtr buf = getBuffer(0, 0); str << " Internal format is " << PixelUtil::getFormatName(buf->getFormat()) << "," << buf->getWidth() << "x" << buf->getHeight() << "x" << buf->getDepth() << "."; } LogManager::getSingleton().logMessage( LML_NORMAL, str.str()); #endif // Main loading loop // imageMips == 0 if the image has no custom mipmaps, otherwise contains the number of custom mips for (int mip = 0; mip <= imageMips; ++mip) { for (int i = 0; i < faces; ++i) { PixelBox src; if (multiImage) { // Load from multiple images src = images[i].GetPixelBox(0, mip); } else { // Load from faces of images[0] src = images[0].GetPixelBox(i, mip); if (hasAlpha && src.Format == PixelFormat.L8) { src.Format = PixelFormat.A8; } } if (gamma != 1.0f) { // Apply gamma correction // Do not overwrite original image but do gamma correction in temporary buffer IntPtr buffer = Marshal.AllocHGlobal(PixelUtil.GetMemorySize(src.Width, src.Height, src.Depth, src.Format)); try { PixelBox corrected = new PixelBox(src.Width, src.Height, src.Depth, src.Format, buffer); PixelUtil.BulkPixelConversion(src, corrected); Image.ApplyGamma(corrected.Data, gamma, corrected.ConsecutiveSize, PixelUtil.GetNumElemBits(src.Format)); // Destination: entire texture. BlitFromMemory does the scaling to // a power of two for us when needed GetBuffer(i, mip).BlitFromMemory(corrected); } finally { Marshal.FreeHGlobal(buffer); } } else { // Destination: entire texture. BlitFromMemory does the scaling to // a power of two for us when needed GetBuffer(i, mip).BlitFromMemory(src); } } } // Update size (the final size, not including temp space) size = this.NumFaces * PixelUtil.GetMemorySize(width, height, depth, format); isLoaded = true; }
protected void BlitFromMemoryImpl(PixelBox src, BasicBox dstBox) { // TODO: This currently does way too many copies. We copy // from src to a converted buffer (if needed), then from // converted to a byte array, then into the temporary surface, // and finally from the temporary surface to the real surface. PixelBox converted = src; IntPtr bufPtr = IntPtr.Zero; GCHandle bufGCHandle = new GCHandle(); // convert to pixelbuffer's native format if necessary if (D3DHelper.ConvertEnum(src.Format) == D3D.Format.Unknown) { int bufSize = PixelUtil.GetMemorySize(src.Width, src.Height, src.Depth, format); byte[] newBuffer = new byte[bufSize]; bufGCHandle = GCHandle.Alloc(newBuffer, GCHandleType.Pinned); bufPtr = bufGCHandle.AddrOfPinnedObject(); converted = new PixelBox(src.Width, src.Height, src.Depth, format, bufPtr); PixelUtil.BulkPixelConversion(src, converted); } // int formatBytes = PixelUtil.GetNumElemBytes(converted.Format); Surface tmpSurface = device.CreateOffscreenPlainSurface(converted.Width, converted.Height, D3DHelper.ConvertEnum(converted.Format), Pool.Scratch); int pitch; // Ideally I would be using the Array mechanism here, but that doesn't seem to work GraphicsStream buf = tmpSurface.LockRectangle(LockFlags.NoSystemLock, out pitch); buf.Position = 0; unsafe { int bufSize = PixelUtil.GetMemorySize(converted.Width, converted.Height, converted.Depth, converted.Format); byte * srcPtr = (byte *)converted.Data.ToPointer(); byte[] ugh = new byte[bufSize]; for (int i = 0; i < bufSize; ++i) { ugh[i] = srcPtr[i]; } buf.Write(ugh); } tmpSurface.UnlockRectangle(); buf.Dispose(); //ImageInformation imageInfo = new ImageInformation(); //imageInfo.Format = D3DHelper.ConvertEnum(converted.Format); //imageInfo.Width = converted.Width; //imageInfo.Height = converted.Height; //imageInfo.Depth = converted.Depth; if (surface != null) { // I'm trying to write to surface using the data in converted Rectangle srcRect = ToD3DRectangleExtent(converted); Rectangle destRect = ToD3DRectangle(dstBox); SurfaceLoader.FromSurface(surface, destRect, tmpSurface, srcRect, Filter.None, 0); } else { D3D.Box srcBox = ToD3DBoxExtent(converted); D3D.Box destBox = ToD3DBox(dstBox); Debug.Assert(false, "Volume textures not yet supported"); // VolumeLoader.FromStream(volume, destBox, converted.Data, converted.RowPitch * converted.SlicePitch * formatBytes, srcBox, Filter.None, 0); VolumeLoader.FromStream(volume, destBox, buf, srcBox, Filter.None, 0); } tmpSurface.Dispose(); // If we allocated a buffer for the temporary conversion, free it here // If I used bufPtr to store my temporary data while I converted // it, I need to free it here. This invalidates converted. // My data has already been copied to tmpSurface and then to the // real surface. if (bufGCHandle.IsAllocated) { bufGCHandle.Free(); } if (doMipmapGen) { GenMipmaps(); } }