public void Render(IBitmapSrc source, AffinePlan[] affinePlans) { VectorToolBox.GetFreeVxs(out var v1, out var v2); //BuildImageBoundsPath(source.Width, source.Height, affinePlans, v1); BuildOrgImgRectVxs(source.Width, source.Height, v1); //Affine destRectTransform = Affine.NewMatix(affinePlans); var destRectTransform = new AffineMat(); destRectTransform.BuildFromAffinePlans(affinePlans); //TODO: review reusable span generator an interpolator *** var spanInterpolator = new SpanInterpolatorLinear(); // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] _reuseableAffine.SetElements(destRectTransform.CreateInvert()); spanInterpolator.Transformer = _reuseableAffine;// var imgSpanGen = new ImgSpanGenRGBA_BilinearClip( source, Drawing.Color.Transparent, spanInterpolator); TransformToVxs(ref destRectTransform, v1, v2); Render(v2, imgSpanGen); // VectorToolBox.ReleaseVxs(ref v1, ref v2); }
public void Sharpen(IBitmapSrc img, int radius) { unsafe { TempMemPtr bufferPtr = img.GetBufferPtr(); int[] output = new int[bufferPtr.LengthInBytes / 4]; //TODO: review here again fixed(int *outputPtr = &output[0]) { byte *srcBuffer = (byte *)bufferPtr.Ptr; int * srcBuffer1 = (int *)srcBuffer; int * outputBuffer1 = (int *)outputPtr; int stride = img.Stride; int w = img.Width; int h = img.Height; MemHolder srcMemHolder = new MemHolder((IntPtr)srcBuffer1, bufferPtr.LengthInBytes / 4);// Surface srcSurface = new Surface(stride, w, h, srcMemHolder); // MemHolder destMemHolder = new MemHolder((IntPtr)outputPtr, bufferPtr.LengthInBytes / 4); Surface destSurface = new Surface(stride, w, h, destMemHolder); // SharpenRenderer shRenderer1 = new SharpenRenderer(); shRenderer1.Amount = radius; shRenderer1.Render(srcSurface, destSurface, new PixelFarm.Drawing.Rectangle[] { new PixelFarm.Drawing.Rectangle(0, 0, w, h) }, 0, 1); } bufferPtr.Release(); //ActualImage.SaveImgBufferToPngFile(output, img.Stride, img.Width + 1, img.Height + 1, "d:\\WImageTest\\test_1.png"); img.ReplaceBuffer(output); } }
public void Render(IBitmapSrc source, AffinePlan[] affinePlans) { using (VxsTemp.Borrow(out var v1, out var v2)) { BuildOrgImgRectVxs(source.Width, source.Height, v1); AffineMat destRectTransform; if (affinePlans == null) { destRectTransform = AffineMat.Iden; _reuseableAffine.SetElements(destRectTransform); } else { destRectTransform = new AffineMat(); destRectTransform.BuildFromAffinePlans(affinePlans); _reuseableAffine.SetElements(destRectTransform.CreateInvert()); } //TODO: review reusable span generator an interpolator *** //We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] _spanInterpolator.Transformer = _reuseableAffine; _currentImgSpanGen.BackgroundColor = Drawing.Color.Transparent; _currentImgSpanGen.SetInterpolator(_spanInterpolator); _currentImgSpanGen.SetSrcBitmap(source); TransformToVxs(ref destRectTransform, v1, v2); Render(v2, _currentImgSpanGen); _currentImgSpanGen.ReleaseSrcBitmap(); } }
public MemBitmap GetTransformedBitmap(IBitmapSrc bitmap) { _srcBmp = bitmap; if (bitmap == null) { return(null); } _srcBmp = bitmap; _srcH = bitmap.Height; _srcW = bitmap.Width; //------------------- if (_srcH == 0 || _srcW == 0) { return(null); } switch (this.Interpolation) { default: throw new NotSupportedException(); case InterpolationMode.None: return(GetTransformedBitmapNoInterpolation()); case InterpolationMode.Bilinear: return(GetTransformedBilinearInterpolation()); case InterpolationMode.Bicubic: return(GetTransformedBicubicInterpolation()); } }
public void Render(IBitmapSrc source, double destX, double destY, double srcX, double srcY, double srcW, double srcH) { //map some part of src img to destination _subBitmap.SetSrcBitmap(source, (int)srcX, (int)srcY, (int)srcW, (int)srcH); Render(_subBitmap, destX, destY); _subBitmap.Reset(); }
public unsafe static TempMemPtr FromBmp(IBitmapSrc actualBmp, out int *headPtr) { TempMemPtr ptr = actualBmp.GetBufferPtr(); headPtr = (int *)ptr.Ptr; return(ptr); }
public void SetSrcBitmap(IBitmapSrc src) { if (src.BitDepth != 32) { throw new NotSupportedException("The source is expected to be 32 bit."); } _bmpSrc = src; }
public ImgSpanGenRGBA_BilinearClip(IBitmapSrc src, Drawing.Color back_color, ISpanInterpolator inter) : base(inter) { m_bgcolor = back_color; _imgsrc = src; bytesBetweenPixelInclusive = _imgsrc.BytesBetweenPixelsInclusive; }
public void SetSrcBitmap(IBitmapSrc src, int x, int y, int w, int h) { _orgSrcW = src.Width; _src = src; _x = x; _y = y; _w = w; _h = h; }
public ImgSpanGenRGBA_NN_StepXBy1(IBitmapSrc src, ISpanInterpolator spanInterpolator) : base(spanInterpolator) { _bmpSrc = src; if (_bmpSrc.BitDepth != 32) { throw new NotSupportedException("The source is expected to be 32 bit."); } }
public SubBitmapBlender(IBitmapSrc image, PixelBlender32 blender, int distanceBetweenPixelsInclusive, int arrayOffset32, int bitsPerPixel) { this.OutputPixelBlender = blender; Attach(image, blender, distanceBetweenPixelsInclusive, arrayOffset32, bitsPerPixel); }
public override void CopyFrom(IBitmapSrc sourceImage, Q1Rect sourceImageRect, int destXOffset, int destYOffset) { Q1Rect destRect = sourceImageRect.CreateNewFromOffset(destXOffset, destYOffset); if (Q1Rect.IntersectRectangles(destRect, _clippingRect, out Q1Rect clippedSourceRect)) { // move it back relative to the source base.CopyFrom(sourceImage, clippedSourceRect.CreateNewFromOffset(-destXOffset, -destYOffset), destXOffset, destYOffset); } }
public void CopyFrom(IBitmapSrc srcimg, Q1Rect srcImgRect, int destXOffset, int destYOffset) { if (Q1Rect.IntersectRectangles(srcimg.GetBounds(), srcImgRect, out Q1Rect clipped_srcRect)) { Q1Rect dstImgRect = clipped_srcRect.CreateNewFromOffset(destXOffset, destYOffset); if (Q1Rect.IntersectRectangles(dstImgRect, GetBounds(), out Q1Rect clipped_dstRect)) { // we need to make sure the source is also clipped to the dest. So, we'll copy this back to source and offset it. clipped_srcRect = clipped_dstRect.CreateNewFromOffset(-destXOffset, -destYOffset); CopyFromNoClipping(srcimg, clipped_srcRect, destXOffset, destYOffset); } } }
public override void CopyFrom(IBitmapSrc sourceImage, RectInt sourceImageRect, int destXOffset, int destYOffset) { RectInt destRect = sourceImageRect; destRect.Offset(destXOffset, destYOffset); RectInt clippedSourceRect = new RectInt(); if (clippedSourceRect.IntersectRectangles(destRect, _clippingRect)) { // move it back relative to the source clippedSourceRect.Offset(-destXOffset, -destYOffset); base.CopyFrom(sourceImage, clippedSourceRect, destXOffset, destYOffset); } }
void Attach(IBitmapSrc sourceImage, PixelBlender32 outputPxBlender, int distanceBetweenPixelsInclusive, int arrayElemOffset, int bitsPerPixel) { _sourceImage = sourceImage; SetDimmensionAndFormat(sourceImage.Width, sourceImage.Height, sourceImage.Stride, bitsPerPixel, distanceBetweenPixelsInclusive); int srcOffset32 = sourceImage.GetBufferOffsetXY32(0, 0); SetBuffer(sourceImage.GetBufferPtr(), srcOffset32 + arrayElemOffset); this.OutputPixelBlender = outputPxBlender; }
public void CopyFrom(IBitmapSrc sourceImage, Q1Rect sourceImageRect, int destXOffset, int destYOffset) { Q1Rect sourceImageBounds = sourceImage.GetBounds(); Q1Rect clippedSourceImageRect = new Q1Rect(); if (clippedSourceImageRect.IntersectRectangles(sourceImageRect, sourceImageBounds)) { Q1Rect destImageRect = clippedSourceImageRect; destImageRect.Offset(destXOffset, destYOffset); Q1Rect destImageBounds = GetBounds(); Q1Rect clippedDestImageRect = new Q1Rect(); if (clippedDestImageRect.IntersectRectangles(destImageRect, destImageBounds)) { // we need to make sure the source is also clipped to the dest. So, we'll copy this back to source and offset it. clippedSourceImageRect = clippedDestImageRect; clippedSourceImageRect.Offset(-destXOffset, -destYOffset); CopyFromNoClipping(sourceImage, clippedSourceImageRect, destXOffset, destYOffset); } } }
public void Render(IBitmapSrc source, ICoordTransformer coordtx) { using (VxsTemp.Borrow(out var v1, out var v2)) { BuildOrgImgRectVxs( source.Width, source.Height, v1); //** //TODO: review reusable span generator an interpolator *** //We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] _spanInterpolator.Transformer = coordtx.CreateInvert(); _currentImgSpanGen.BackgroundColor = Color.Transparent; _currentImgSpanGen.SetInterpolator(_spanInterpolator); _currentImgSpanGen.SetSrcBitmap(source); TransformToVxs(coordtx, v1, v2); Render(v2, _currentImgSpanGen); _currentImgSpanGen.ReleaseSrcBitmap(); } }
internal unsafe static void NN_StepXBy1(IBitmapSrc bmpsrc, int srcIndex, Drawing.Color[] outputColors, int dstIndex, int len) { using (CpuBlit.TempMemPtr srcBufferPtr = bmpsrc.GetBufferPtr()) { int *pSource = (int *)srcBufferPtr.Ptr + srcIndex; do { int srcColor = *pSource; //separate each component //TODO: review here, color from source buffer //should be in 'pre-multiplied' format. //so it should be converted to 'straight' color by call something like ..'FromPreMult()' outputColors[dstIndex++] = Drawing.Color.FromArgb( (srcColor >> CO.A_SHIFT) & 0xff, //a (srcColor >> CO.R_SHIFT) & 0xff, //r (srcColor >> CO.G_SHIFT) & 0xff, //g (srcColor >> CO.B_SHIFT) & 0xff); //b pSource++; //move next } while (--len != 0); } }
public void Render(IBitmapSrc source, double destX, double destY) { int inScaleX = 1; int inScaleY = 1; int angleRadians = 0; // exit early if the dest and source bounds don't touch. // TODO: <BUG> make this do rotation and scalling RectInt sourceBounds = new RectInt((int)destX, (int)destY, (int)destX + source.Width, (int)destY + source.Height); //sourceBounds.Offset((int)destX, (int)destY); RectInt destBounds = _destBitmapBlender.GetBounds(); if (!RectInt.DoIntersect(sourceBounds, destBounds)) { //if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0) //{ // throw new NotImplementedException(); //} return; } double scaleX = inScaleX; double scaleY = inScaleY; ICoordTransformer graphicsTransform = this.CurrentTransformMatrix; if (!graphicsTransform.IsIdentity) { if (scaleX != 1 || scaleY != 1 || angleRadians != 0) { throw new NotImplementedException(); } graphicsTransform.Transform(ref destX, ref destY); } #if false // this is an optomization that eliminates the drawing of images that have their alpha set to all 0 (happens with generated images like explosions). MaxAlphaFrameProperty maxAlphaFrameProperty = MaxAlphaFrameProperty::GetMaxAlphaFrameProperty(source); if ((maxAlphaFrameProperty.GetMaxAlpha() * color.A_Byte) / 256 <= ALPHA_CHANNEL_BITS_DIVISOR) { m_OutFinalBlitBounds.SetRect(0, 0, 0, 0); } #endif bool isScale = (scaleX != 1 || scaleY != 1); bool isRotated = false; if (angleRadians != 0 && Math.Abs(angleRadians) >= (0.1 * MathHelper.Tau / 360)) { isRotated = true; } else { angleRadians = 0;//reset very small angle to 0 } //bool IsMipped = false; //double ox, oy; //source.GetOriginOffset(out ox, out oy); bool canUseMipMaps = isScale; if (scaleX > 0.5 || scaleY > 0.5) { canUseMipMaps = false; } bool needSourceResampling = isScale || isRotated;// || destX != (int)destX || destY != (int)destY; //VectorToolBox.GetFreeVxs(out VertexStore imgBoundsPath); // this is the fast drawing path if (needSourceResampling) { #if false // if the scalling is small enough the results can be improved by using mip maps if (CanUseMipMaps) { CMipMapFrameProperty *pMipMapFrameProperty = CMipMapFrameProperty::GetMipMapFrameProperty(source); double OldScaleX = scaleX; double OldScaleY = scaleY; const CFrameInterface *pMippedFrame = pMipMapFrameProperty.GetMipMapFrame(ref scaleX, ref scaleY); if (pMippedFrame != source) { IsMipped = true; source = pMippedFrame; sourceOriginOffsetX *= (OldScaleX / scaleX); sourceOriginOffsetY *= (OldScaleY / scaleY); } HotspotOffsetX *= (inScaleX / scaleX); HotspotOffsetY *= (inScaleY / scaleY); } #endif using (VxsTemp.Borrow(out var imgBoundsPath, out var v1)) { BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath); Affine destRectTransform = CreateAffine(destX, destY, _ox, _oy, scaleX, scaleY, angleRadians); //TODO: review reusable span generator an interpolator *** // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] //spanInterpolator.Transformer = destRectTransform.CreateInvert(); //var imgSpanGen = new ImgSpanGenRGBA_BilinearClip( // Drawing.Color.Black, // spanInterpolator); _spanInterpolator.Transformer = destRectTransform.CreateInvert(); _currentImgSpanGen.SetInterpolator(_spanInterpolator); _currentImgSpanGen.SetSrcBitmap(source); destRectTransform.TransformToVxs(imgBoundsPath, v1); Render(v1, _currentImgSpanGen); _currentImgSpanGen.ReleaseSrcBitmap(); } ////Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, //// destX, destY, ox, oy, scaleX, scaleY, angleRadians, imgBoundsPath); //Affine destRectTransform = CreateAffine(destX, destY, ox, oy, scaleX, scaleY, angleRadians); ////TODO: review reusable span generator an interpolator *** //var spanInterpolator = new SpanInterpolatorLinear(); //// We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] //spanInterpolator.Transformer = destRectTransform.CreateInvert(); //var imgSpanGen = new ImgSpanGenRGBA_BilinearClip( // source, // Drawing.Color.Black, // spanInterpolator); //VectorToolBox.GetFreeVxs(out VertexStore v1); //destRectTransform.TransformToVxs(imgBoundsPath, v1); //Render(v1, imgSpanGen); //VectorToolBox.ReleaseVxs(ref v1); #if false // this is some debug you can enable to visualize the dest bounding box LineFloat(BoundingRect.left, BoundingRect.top, BoundingRect.right, BoundingRect.top, WHITE); LineFloat(BoundingRect.right, BoundingRect.top, BoundingRect.right, BoundingRect.bottom, WHITE); LineFloat(BoundingRect.right, BoundingRect.bottom, BoundingRect.left, BoundingRect.bottom, WHITE); LineFloat(BoundingRect.left, BoundingRect.bottom, BoundingRect.left, BoundingRect.top, WHITE); #endif } else // TODO: this can be even faster if we do not use an intermediate buffer { //Affine destRectTransform = BuildImageBoundsPath( // source.Width, source.Height, // destX, destY, imgBoundsPath); using (VxsTemp.Borrow(out var imgBoundsPath, out var v1)) { BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath); //... Affine destRectTransform = CreateAffine(destX, destY); //TODO: review reusable span generator an interpolator *** // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] _spanInterpolator.Transformer = destRectTransform.CreateInvert(); //we generate image by this imagespan generator ImgSpanGen imgSpanGen = null; switch (source.BitDepth) { case 32: if (_imgSpanGenCustom != null) { //use custom span gen _imgSpanGenCustom.SetInterpolator(_spanInterpolator); _imgSpanGenCustom.SetSrcBitmap(source); imgSpanGen = _imgSpanGenCustom; } else { _imgSpanGenNNStepXBy1.SetInterpolator(_spanInterpolator); _imgSpanGenNNStepXBy1.SetSrcBitmap(source); imgSpanGen = _imgSpanGenNNStepXBy1; } break; //case 8: // imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator); // break; default: throw new NotImplementedException(); } destRectTransform.TransformToVxs(imgBoundsPath, v1); //... Render(v1, imgSpanGen); } // unchecked { _destImageChanged++; }; } }
// Create //-------------------------------------------------------------------- public void Create(IBitmapSrc src) { // we are going to create a dialated image for filtering // we add m_dilation pixels to every side of the image and then copy the image in the x // dirrection into each end so that we can sample into this image to get filtering on x repeating // if the original image look like this // // 123456 // // the new image would look like this // // 0000000000 // 0000000000 // 5612345612 // 0000000000 // 0000000000 m_height = (int)AggMath.uceil(src.Height); m_width = (int)AggMath.uceil(src.Width); m_width_hr = (int)AggMath.uround(src.Width * LineAA.SUBPIXEL_SCALE); m_half_height_hr = (int)AggMath.uround(src.Height * LineAA.SUBPIXEL_SCALE / 2); m_offset_y_hr = m_dilation_hr + m_half_height_hr - LineAA.SUBPIXEL_SCALE / 2; m_half_height_hr += LineAA.SUBPIXEL_SCALE / 2; int bufferWidth = m_width + m_dilation * 2; int bufferHeight = m_height + m_dilation * 2; int bytesPerPixel = src.BitDepth / 8; int newSizeInBytes = bufferWidth * bufferHeight * bytesPerPixel; if (m_DataSizeInBytes < newSizeInBytes) { m_DataSizeInBytes = newSizeInBytes; m_data = new int[m_DataSizeInBytes / 4]; } m_buf = new PixelProcessing.SubBitmapBlender(m_data, 0, bufferWidth, bufferHeight, bufferWidth * bytesPerPixel, src.BitDepth, bytesPerPixel); unsafe { CpuBlit.Imaging.TempMemPtr destMemPtr = m_buf.GetBufferPtr(); CpuBlit.Imaging.TempMemPtr srcMemPtr = src.GetBufferPtr(); int *destBuffer = (int *)destMemPtr.Ptr; int *srcBuffer = (int *)srcMemPtr.Ptr; // copy the image into the middle of the dest for (int y = 0; y < m_height; y++) { for (int x = 0; x < m_width; x++) { int sourceOffset = src.GetBufferOffsetXY32(x, y); int destOffset = m_buf.GetBufferOffsetXY32(m_dilation, y + m_dilation); destBuffer[destOffset] = srcBuffer[sourceOffset]; } } // copy the first two pixels form the end into the begining and from the begining into the end for (int y = 0; y < m_height; y++) { int s1Offset = src.GetBufferOffsetXY32(0, y); int d1Offset = m_buf.GetBufferOffsetXY32(m_dilation + m_width, y); int s2Offset = src.GetBufferOffsetXY32(m_width - m_dilation, y); int d2Offset = m_buf.GetBufferOffsetXY32(0, y); for (int x = 0; x < m_dilation; x++) { destBuffer[d1Offset++] = srcBuffer[s1Offset++]; destBuffer[d2Offset++] = srcBuffer[s2Offset++]; } } srcMemPtr.Release(); destMemPtr.Release(); } }
public void Fill(IBitmapSrc bufferToFillOn, int x, int y) { y -= imageHeight; unchecked // this way we can overflow the uint on negative and get a big number { if ((uint)x >= bufferToFillOn.Width || (uint)y >= bufferToFillOn.Height) { return; } } _destImgRW = bufferToFillOn; TempMemPtr destBufferPtr = bufferToFillOn.GetBufferPtr(); unsafe { imageWidth = bufferToFillOn.Width; imageHeight = bufferToFillOn.Height; //reset new buffer, clear mem? pixelsChecked = new bool[imageWidth * imageHeight]; int *destBuffer = (int *)destBufferPtr.Ptr; int startColorBufferOffset = bufferToFillOn.GetBufferOffsetXY32(x, y); int start_color = *(destBuffer + startColorBufferOffset); fillRule.SetStartColor(Drawing.Color.FromArgb( (start_color >> 16) & 0xff, (start_color >> 8) & 0xff, (start_color) & 0xff)); LinearFill(destBuffer, x, y); while (ranges.Count > 0) { Range range = ranges.Dequeue(); int downY = range.y - 1; int upY = range.y + 1; int downPixelOffset = (imageWidth * (range.y - 1)) + range.startX; int upPixelOffset = (imageWidth * (range.y + 1)) + range.startX; for (int rangeX = range.startX; rangeX <= range.endX; rangeX++) { if (range.y > 0) { if (!pixelsChecked[downPixelOffset]) { int bufferOffset = bufferToFillOn.GetBufferOffsetXY32(rangeX, downY); if (fillRule.CheckPixel(*(destBuffer + bufferOffset))) { LinearFill(destBuffer, rangeX, downY); } } } if (range.y < (imageHeight - 1)) { if (!pixelsChecked[upPixelOffset]) { int bufferOffset = bufferToFillOn.GetBufferOffsetXY32(rangeX, upY); if (fillRule.CheckPixel(*(destBuffer + bufferOffset))) { LinearFill(destBuffer, rangeX, upY); } } } upPixelOffset++; downPixelOffset++; } } } destBufferPtr.Release(); }
public void ReleaseSrcBitmap() { _bmpSrc = null; }
public void ReleaseSrcBitmap() { _imgsrc = null; }
public void SetSrcBitmap(IBitmapSrc src) { _imgsrc = src; bytesBetweenPixelInclusive = _imgsrc.BytesBetweenPixelsInclusive; }
internal void ReleaseSrcBitmap() { _bmpSrc = null; }
public SubBitmapBlender(IBitmapSrc image, PixelBlender32 blender) { Attach(image, blender, image.BytesBetweenPixelsInclusive, 0, image.BitDepth); }
// Create //-------------------------------------------------------------------- public void Create(IBitmapSrc src) { // we are going to create a dialated image for filtering // we add m_dilation pixels to every side of the image and then copy the image in the x // dirrection into each end so that we can sample into this image to get filtering on x repeating // if the original image look like this // // 123456 // // the new image would look like this // // 0000000000 // 0000000000 // 5612345612 // 0000000000 // 0000000000 _height = (int)AggMath.uceil(src.Height); _width = (int)AggMath.uceil(src.Width); _width_hr = (int)AggMath.uround(src.Width * LineAA.SUBPIXEL_SCALE); _half_height_hr = (int)AggMath.uround(src.Height * LineAA.SUBPIXEL_SCALE / 2); _offset_y_hr = _dilation_hr + _half_height_hr - LineAA.SUBPIXEL_SCALE / 2; _half_height_hr += LineAA.SUBPIXEL_SCALE / 2; int bufferWidth = _width + _dilation * 2; int bufferHeight = _height + _dilation * 2; int bytesPerPixel = src.BitDepth / 8; int newSizeInBytes = bufferWidth * bufferHeight * bytesPerPixel; if (_DataSizeInBytes < newSizeInBytes) { _DataSizeInBytes = newSizeInBytes; _data.Dispose(); IntPtr nativeBuff = System.Runtime.InteropServices.Marshal.AllocHGlobal(_DataSizeInBytes); _data = new TempMemPtr(nativeBuff, _DataSizeInBytes); } _buf = new PixelProcessing.SubBitmapBlender(_data, 0, bufferWidth, bufferHeight, bufferWidth * bytesPerPixel, src.BitDepth, bytesPerPixel); unsafe { using (TempMemPtr destMemPtr = _buf.GetBufferPtr()) using (TempMemPtr srcMemPtr = src.GetBufferPtr()) { int *destBuffer = (int *)destMemPtr.Ptr; int *srcBuffer = (int *)srcMemPtr.Ptr; // copy the image into the middle of the dest for (int y = 0; y < _height; y++) { for (int x = 0; x < _width; x++) { int sourceOffset = src.GetBufferOffsetXY32(x, y); int destOffset = _buf.GetBufferOffsetXY32(_dilation, y + _dilation); destBuffer[destOffset] = srcBuffer[sourceOffset]; } } // copy the first two pixels form the end into the begining and from the begining into the end for (int y = 0; y < _height; y++) { int s1Offset = src.GetBufferOffsetXY32(0, y); int d1Offset = _buf.GetBufferOffsetXY32(_dilation + _width, y); int s2Offset = src.GetBufferOffsetXY32(_width - _dilation, y); int d2Offset = _buf.GetBufferOffsetXY32(0, y); for (int x = 0; x < _dilation; x++) { destBuffer[d1Offset++] = srcBuffer[s1Offset++]; destBuffer[d2Offset++] = srcBuffer[s2Offset++]; } } } } }
public void Render(IBitmapSrc source, double destX, double destY, double angleRadians, double inScaleX, double inScaleY) { { // exit early if the dest and source bounds don't touch. // TODO: <BUG> make this do rotation and scalling RectInt sourceBounds = source.GetBounds(); RectInt destBounds = this.destImageReaderWriter.GetBounds(); sourceBounds.Offset((int)destX, (int)destY); if (!RectInt.DoIntersect(sourceBounds, destBounds)) { if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0) { throw new NotImplementedException(); } return; } } double scaleX = inScaleX; double scaleY = inScaleY; Affine graphicsTransform = this.CurrentTransformMatrix; if (!graphicsTransform.IsIdentity()) { if (scaleX != 1 || scaleY != 1 || angleRadians != 0) { throw new NotImplementedException(); } graphicsTransform.Transform(ref destX, ref destY); } #if false // this is an optomization that eliminates the drawing of images that have their alpha set to all 0 (happens with generated images like explosions). MaxAlphaFrameProperty maxAlphaFrameProperty = MaxAlphaFrameProperty::GetMaxAlphaFrameProperty(source); if ((maxAlphaFrameProperty.GetMaxAlpha() * color.A_Byte) / 256 <= ALPHA_CHANNEL_BITS_DIVISOR) { m_OutFinalBlitBounds.SetRect(0, 0, 0, 0); } #endif bool isScale = (scaleX != 1 || scaleY != 1); bool isRotated = true; if (Math.Abs(angleRadians) < (0.1 * MathHelper.Tau / 360)) { isRotated = false; angleRadians = 0; } //bool IsMipped = false; //double ox, oy; //source.GetOriginOffset(out ox, out oy); bool canUseMipMaps = isScale; if (scaleX > 0.5 || scaleY > 0.5) { canUseMipMaps = false; } bool renderRequriesSourceSampling = isScale || isRotated || destX != (int)destX || destY != (int)destY; VectorToolBox.GetFreeVxs(out VertexStore imgBoundsPath); // this is the fast drawing path if (renderRequriesSourceSampling) { // if the scalling is small enough the results can be improved by using mip maps //if(CanUseMipMaps) //{ // CMipMapFrameProperty* pMipMapFrameProperty = CMipMapFrameProperty::GetMipMapFrameProperty(source); // double OldScaleX = scaleX; // double OldScaleY = scaleY; // const CFrameInterface* pMippedFrame = pMipMapFrameProperty.GetMipMapFrame(ref scaleX, ref scaleY); // if(pMippedFrame != source) // { // IsMipped = true; // source = pMippedFrame; // sourceOriginOffsetX *= (OldScaleX / scaleX); // sourceOriginOffsetY *= (OldScaleY / scaleY); // } // HotspotOffsetX *= (inScaleX / scaleX); // HotspotOffsetY *= (inScaleY / scaleY); //} //Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, // destX, destY, ox, oy, scaleX, scaleY, angleRadians, imgBoundsPath); //1. BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath); //2. Affine destRectTransform = CreateAffine(destX, destY, ox, oy, scaleX, scaleY, angleRadians); //TODO: review reusable span generator an interpolator *** var interpolator = new SpanInterpolatorLinear(); // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] interpolator.Transformer = destRectTransform.CreateInvert(); var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(source, Drawing.Color.Black, interpolator); VectorToolBox.GetFreeVxs(out var v1); destRectTransform.TransformToVxs(imgBoundsPath, v1); Render(v1, imgSpanGen); VectorToolBox.ReleaseVxs(ref v1); // this is some debug you can enable to visualize the dest bounding box //LineFloat(BoundingRect.left, BoundingRect.top, BoundingRect.right, BoundingRect.top, WHITE); //LineFloat(BoundingRect.right, BoundingRect.top, BoundingRect.right, BoundingRect.bottom, WHITE); //LineFloat(BoundingRect.right, BoundingRect.bottom, BoundingRect.left, BoundingRect.bottom, WHITE); //LineFloat(BoundingRect.left, BoundingRect.bottom, BoundingRect.left, BoundingRect.top, WHITE); } else // TODO: this can be even faster if we do not use an intermediat buffer { //Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, destX, destY, imgBoundsPath); BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath); // var destRectTransform = new AffineMat(); destRectTransform.Translate(destX, destY); //TODO: review reusable span generator an interpolator *** var interpolator = new SpanInterpolatorLinear(); // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] _reuseableAffine.SetElements(destRectTransform.CreateInvert()); interpolator.Transformer = _reuseableAffine; ImgSpanGen imgSpanGen = null; switch (source.BitDepth) { case 32: imgSpanGen = new ImgSpanGenRGBA_NN_StepXBy1(source, interpolator); break; //case 24: // imgSpanGen = new ImgSpanGenRGB_NNStepXby1(source, interpolator); // break; //case 8: // imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator); // break; default: throw new NotImplementedException(); } VectorToolBox.GetFreeVxs(out var v1); TransformToVxs(ref destRectTransform, imgBoundsPath, v1); Render(v1, imgSpanGen); VectorToolBox.ReleaseVxs(ref v1); // unchecked { destImageChanged++; }; } VectorToolBox.ReleaseVxs(ref imgBoundsPath); }
public void Reset() { _src = null; }
/// <summary> /// create thumbnail img with super-sampling technique,(Expensive, High quality thumb) /// </summary> /// <param name="source"></param> /// <param name="dst"></param> public static MemBitmap CreateThumbnailWithSuperSamplingTechnique(this MemBitmap source, float scaleRatio) { // Paint.NET (MIT,from version 3.36.7, see=> https://github.com/rivy/OpenPDN //in this version new image MUST smaller than the original one *** if (scaleRatio >= 1 || scaleRatio < 0) { return(null); } //create new bitmap int newBmpW = (int)Math.Round(source.Width * scaleRatio); int newBmpH = (int)Math.Round(source.Height * scaleRatio); MemBitmap thumbBitmap = new MemBitmap(newBmpW, newBmpH); //*** IBitmapSrc source_1 = (IBitmapSrc)source; unsafe { Rectangle dstRoi2 = new Rectangle(0, 0, newBmpW, newBmpH); int dstWidth = dstRoi2.Width; int dstHeight = dstRoi2.Height; int srcH = source.Height; int srcW = source.Width; Imaging.TempMemPtr dstMemPtr = MemBitmap.GetBufferPtr(thumbBitmap); int dstStrideInt32 = newBmpW; for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY) { //from dst => find proper source (y) //double srcTop = (double)(dstY * srcH) / (double)dstHeight; double srcTop = (double)(dstY * srcH) / (double)dstHeight; double srcTopFloor = Math.Floor(srcTop); double srcTopWeight = 1 - (srcTop - srcTopFloor); int srcTopInt = (int)srcTopFloor; //double srcBottom = (double)((dstY + 1) * srcH) / (double)dstHeight; double srcBottom = (double)((dstY + 1) * srcH) / (double)dstHeight; double srcBottomFloor = Math.Floor(srcBottom - 0.00001); double srcBottomWeight = srcBottom - srcBottomFloor; int srcBottomInt = (int)srcBottomFloor; int *srcBuffer = (int *)(MemBitmap.GetBufferPtr(source)).Ptr; int srcStrideInt32 = source.Width; //*** int *dstAddr = (int *)dstMemPtr.Ptr + (dstStrideInt32 * dstY); //begin at for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX) { //from dst=> find proper source (x) double srcLeft = (double)(dstX * srcW) / (double)dstWidth; double srcLeftFloor = Math.Floor(srcLeft); double srcLeftWeight = 1 - (srcLeft - srcLeftFloor); int srcLeftInt = (int)srcLeftFloor; double srcRight = (double)((dstX + 1) * srcW) / (double)dstWidth; double srcRightFloor = Math.Floor(srcRight - 0.00001); double srcRightWeight = srcRight - srcRightFloor; int srcRightInt = (int)srcRightFloor; double blueSum = 0; double greenSum = 0; double redSum = 0; double alphaSum = 0; //now we know (left,top) of source that we want //then ask the pixel value from source at that pos //(1) left fractional edge { //PaintFx.ColorBgra* srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1); int *srcLeftColorAddr = srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { int srcColor = *srcLeftColorAddr; double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * srcLeftWeight; blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; //move to next row srcLeftColorAddr += srcStrideInt32; //srcLeftPtr = (ColorBgra*)((byte*)srcLeftPtr + source._stride); } } // { //(2) right fractional edge //ColorBgra* srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1); int *srcRightColorAddr = srcBuffer + source_1.GetBufferOffsetXY32(srcRightInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { int srcColor = *srcRightColorAddr; double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * srcRightWeight; blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; //srcRightPtr = (ColorBgra*)((byte*)srcRightPtr + source._stride); srcRightColorAddr += srcStrideInt32; //move to next row } } // { //(3) top fractional edge //ColorBgra* srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt); int *srcTopColorAddr = srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt + 1, srcTopInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { int srcColor = *srcTopColorAddr; double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * srcTopWeight; blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; //move to next column //++srcTopPtr; ++srcTopColorAddr; } } // { //(4) bottom fractional edge //ColorBgra* srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt); int *srcBottomColorAddr = srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt + 1, srcBottomInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { int srcColor = *srcBottomColorAddr; double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * srcBottomWeight; blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; //++srcBottomPtr; //move to next column //++srcTopPtr; ++srcBottomColorAddr; } } { //(5) center area for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { //ColorBgra* srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY); int *srcColorAddr = srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt + 1, srcY); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { int srcColor = *srcColorAddr; int a = ((srcColor >> CO.A_SHIFT) & 0xff); blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a; alphaSum += a; ++srcColorAddr; } } } //(6) four corner pixels { //6.1 //ColorBgra srcTL = source.GetPoint(srcLeftInt, srcTopInt); int srcColor = *(srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt, srcTopInt)); double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * (srcTopWeight * srcLeftWeight); blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; } { //6.2 //ColorBgra srcTR = source.GetPoint(srcRightInt, srcTopInt); //double srcTRA = srcTR.A; //blueSum += srcTR.B * (srcTopWeight * srcRightWeight) * srcTRA; //greenSum += srcTR.G * (srcTopWeight * srcRightWeight) * srcTRA; //redSum += srcTR.R * (srcTopWeight * srcRightWeight) * srcTRA; //alphaSum += srcTR.A * (srcTopWeight * srcRightWeight); int srcColor = *(srcBuffer + source_1.GetBufferOffsetXY32(srcRightInt, srcTopInt)); double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * (srcTopWeight * srcRightWeight); blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; } { //(6.3) int srcColor = *(srcBuffer + source_1.GetBufferOffsetXY32(srcLeftInt, srcBottomInt)); double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * (srcBottomWeight * srcLeftWeight); blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; //without a //ColorBgra srcBL = source.GetPoint(srcLeftInt, srcBottomInt); //double srcBLA = srcBL.A; //blueSum += srcBL.B * (srcBottomWeight * srcLeftWeight) * srcBLA; //greenSum += srcBL.G * (srcBottomWeight * srcLeftWeight) * srcBLA; //redSum += srcBL.R * (srcBottomWeight * srcLeftWeight) * srcBLA; //alphaSum += srcBL.A * (srcBottomWeight * srcLeftWeight); } { //(6.4) //ColorBgra srcBR = source.GetPoint(srcRightInt, srcBottomInt); //double srcBRA = srcBR.A; //blueSum += srcBR.B * (srcBottomWeight * srcRightWeight) * srcBRA; //greenSum += srcBR.G * (srcBottomWeight * srcRightWeight) * srcBRA; //redSum += srcBR.R * (srcBottomWeight * srcRightWeight) * srcBRA; //alphaSum += srcBR.A * (srcBottomWeight * srcRightWeight); int srcColor = *(srcBuffer + source_1.GetBufferOffsetXY32(srcRightInt, srcBottomInt)); double a_w = ((srcColor >> CO.A_SHIFT) & 0xff) * (srcBottomWeight * srcRightWeight); blueSum += ((srcColor >> CO.B_SHIFT) & 0xff) * a_w; greenSum += ((srcColor >> CO.G_SHIFT) & 0xff) * a_w; redSum += ((srcColor >> CO.R_SHIFT) & 0xff) * a_w; alphaSum += a_w; } double area = (srcRight - srcLeft) * (srcBottom - srcTop); double alpha = alphaSum / area; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; } // add 0.5 so that rounding goes in the direction we want it to blue += 0.5; green += 0.5; red += 0.5; alpha += 0.5; //*** //dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); //++dstPtr; *dstAddr = ((byte)alpha) << CO.A_SHIFT | ((byte)blue) << CO.B_SHIFT | ((byte)green) << CO.G_SHIFT | ((byte)red) << CO.R_SHIFT; //(uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstAddr; } } } return(thumbBitmap); }