public static void AddPlanarCache(WicProcessingContext ctx) { if (!(ctx.Source.WicSource is IWICPlanarBitmapSourceTransform trans)) { throw new NotSupportedException("Transform chain doesn't support planar mode. Only JPEG Decoder, Rotator, Scaler, and PixelFormatConverter are allowed"); } double rat = ctx.Settings.HybridScaleRatio.Clamp(1d, 8d); uint width = (uint)Math.Ceiling(ctx.Source.Width / rat); uint height = (uint)Math.Ceiling(ctx.Source.Height / rat); var opt = ctx.DecoderFrame.ExifOrientation.ToWicTransformOptions(); var desc = new WICBitmapPlaneDescription[2]; if (!trans.DoesSupportTransform(ref width, ref height, opt, WICPlanarOptions.WICPlanarOptionsDefault, planarPixelFormats, desc, 2)) { throw new NotSupportedException("Requested planar transform not supported"); } var cacheSource = ctx.AddDispose(new WicPlanarCache(trans, desc[0], desc[1], ctx.Settings.Crop, opt, width, height, rat)); ctx.PlanarChromaSource = cacheSource.GetPlane(WicPlane.Chroma); ctx.PlanarLumaSource = cacheSource.GetPlane(WicPlane.Luma); ctx.Source = ctx.PlanarChromaSource; ctx.Source = ctx.PlanarLumaSource; }
public WicFrameReader(WicDecoder dec, WicProcessingContext ctx) { Frame = AddRef(dec.Decoder.GetFrame((uint)ctx.Settings.FrameIndex)); Source = Frame; Context = ctx; if (ctx.ContainerFormat == Consts.GUID_ContainerFormatRaw && ctx.Settings.FrameIndex == 0) { try { Source = AddRef(dec.Decoder.GetPreview()); } catch { } } ctx.PixelFormat = Source.GetPixelFormat(); Source.GetSize(out ctx.Width, out ctx.Height); var ptrans = Source as IWICPlanarBitmapSourceTransform; if (ptrans != null) { uint pw = ctx.Width, ph = ctx.Height; var pdesc = new WICBitmapPlaneDescription[2]; var pfmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; ctx.SupportsPlanar = ptrans.DoesSupportTransform(ref pw, ref ph, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsPreserveSubsampling, pfmts, pdesc, 2); ctx.IsSubsampled = pdesc[0].Width != pdesc[1].Width || pdesc[0].Height != pdesc[1].Height; } }
public WicPlanarCacheSource(IWICPlanarBitmapSourceTransform source, WICBitmapPlaneDescription descY, WICBitmapPlaneDescription descC, WICRect crop, WICBitmapTransformOptions transformOptions, uint width, uint height, double ratio) { // TODO fractional ratio support? subsampleRatioX = Math.Ceiling((double)descY.Width / descC.Width); subsampleRatioY = Math.Ceiling((double)descY.Height / descC.Height); var scrop = new WICRect(); scrop.X = (int)Math.Floor(crop.X / ratio); scrop.Y = (int)Math.Floor(crop.Y / ratio); scrop.Width = Math.Min((int)Math.Ceiling(crop.Width / ratio), (int)descY.Width); scrop.Height = Math.Min((int)Math.Ceiling(crop.Height / ratio), (int)descY.Height); if (subsampleRatioX > 1d) { descC.Width = Math.Min((uint)Math.Ceiling(scrop.Width / subsampleRatioX), descC.Width); descY.Width = (uint)Math.Min(descC.Width * subsampleRatioX, scrop.Width); if (scrop.X % subsampleRatioX > 0) { scrop.X = (int)(scrop.X / subsampleRatioX) * (int)subsampleRatioX; } } else { descC.Width = descY.Width = (uint)scrop.Width; } if (subsampleRatioY > 1d) { descC.Height = Math.Min((uint)Math.Ceiling(scrop.Height / subsampleRatioY), descC.Height); descY.Height = (uint)Math.Min(descC.Height * subsampleRatioY, scrop.Height); if (scrop.Y % subsampleRatioY > 0) { scrop.Y = (int)(scrop.Y / subsampleRatioY) * (int)subsampleRatioY; } } else { descC.Height = descY.Height = (uint)scrop.Height; } sourceTransform = source; sourceTransformOptions = transformOptions; planeDescriptionY = descY; planeDescriptionC = descC; scaledCrop = scrop; scaledWidth = width; scaledHeight = height; strideY = (uint)scrop.Width + 3u & ~3u; buffHeightY = 16u; strideC = (uint)(Math.Ceiling(scrop.Width / subsampleRatioX)) * 2u + 3u & ~3u; buffHeightC = (uint)(buffHeightY / subsampleRatioY); sourceY = new WicPlanarSource(this, WicPlane.Luma, descY); sourceC = new WicPlanarSource(this, WicPlane.Chroma, descC); }
public PlanarPixelSource(WicPlanarCache cache, WicPlane plane, WICBitmapPlaneDescription planeDesc) { Width = planeDesc.Width; Height = planeDesc.Height; Format = PixelFormat.Cache[planeDesc.Format]; cacheSource = cache; cachePlane = plane; }
public WicPlanarSource(WicPlanarCacheSource cache, WicPlane plane, WICBitmapPlaneDescription planeDesc) { Width = planeDesc.Width; Height = planeDesc.Height; Format = planeDesc.Format; cacheSource = cache; cachePlane = plane; }
public WicFrameReader(WicDecoder dec, WicProcessingContext ctx) : base(ctx) { Frame = AddRef(dec.Decoder.GetFrame((uint)ctx.Settings.FrameIndex)); Source = Frame; if (ctx.ContainerFormat == Consts.GUID_ContainerFormatRaw && ctx.Settings.FrameIndex == 0 && dec.Decoder.TryGetPreview(out var preview)) { Source = AddRef(preview); } ctx.PixelFormat = PixelFormat.Cache[Source.GetPixelFormat()]; Source.GetSize(out ctx.Width, out ctx.Height); Source.GetResolution(out ctx.DpiX, out ctx.DpiY); if (Source is IWICPlanarBitmapSourceTransform ptrans) { uint pw = ctx.Width, ph = ctx.Height; var pdesc = new WICBitmapPlaneDescription[2]; var pfmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; ctx.SupportsPlanar = ptrans.DoesSupportTransform(ref pw, ref ph, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsDefault, pfmts, pdesc, 2); } }
public WicPlanarCache(WicTransform prev) : base(prev) { if (!(prev.Source is IWICPlanarBitmapSourceTransform)) { throw new NotSupportedException("Transform chain doesn't support planar mode. Only JPEG Decoder, Rotator, Scaler, and ColorSpaceConverter are allowed"); } var trans = (IWICPlanarBitmapSourceTransform)prev.Source; double rat = Context.Settings.HybridScaleRatio.Clamp(1d, 8d); Context.Width = (uint)Math.Ceiling(Context.Width / rat); Context.Height = (uint)Math.Ceiling(Context.Height / rat); // Although generally the last scan has the least significant bit(s) of the luma plane and skipping it could be very beneficial performance-wise, there is no guarantee of scan order. Might be able to do something with more decoder support. //var prog = Frame as IWICProgressiveLevelControl; //if (prog != null) //{ // uint levels = prog.GetLevelCount(); // uint level = (uint)Math.Ceiling(levels / rat) + (Context.Settings.HybridMode == HybridScaleMode.FavorQuality || Context.Settings.HybridMode == HybridScaleMode.Off ? (uint)Math.Ceiling(levels / 8d) : 0u); // prog.SetCurrentLevel(Math.Min(level, levels - (Context.Settings.ScaleRatio >= 2d && levels > 7u ? 2u : 1u))); //} var fmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; var desc = new WICBitmapPlaneDescription[2]; if (!trans.DoesSupportTransform(ref Context.Width, ref Context.Height, Context.TransformOptions, WICPlanarOptions.WICPlanarOptionsDefault, fmts, desc, 2)) { throw new NotSupportedException("Requested planar transform not supported"); } var crop = new WICRect { X = Context.Settings.Crop.X, Y = Context.Settings.Crop.Y, Width = Context.Settings.Crop.Width, Height = Context.Settings.Crop.Height }; cacheSource = new WicPlanarCacheSource(trans, desc[0], desc[1], crop, Context.TransformOptions, Context.Width, Context.Height, rat, Context.NeedsCache); SourceY = cacheSource.GetPlane(WicPlane.Luma); SourceCbCr = cacheSource.GetPlane(WicPlane.Chroma); }
public WicPlanarCache(WicTransform prev) : base(prev) { Contract.Requires <NotSupportedException>(prev.Source is IWICPlanarBitmapSourceTransform, "Transform chain doesn't support planar mode. Only JPEG Decoder, Rotator, Scaler, and ColorSpaceConverter are allowed"); var trans = (IWICPlanarBitmapSourceTransform)prev.Source; double rat = Context.Settings.HybridScaleRatio.Clamp(1d, 8d); Context.Width = (uint)Math.Ceiling(Context.Width / rat); Context.Height = (uint)Math.Ceiling(Context.Height / rat); var prog = Frame as IWICProgressiveLevelControl; if (prog != null) // TODO needs work { uint levels = prog.GetLevelCount(); uint level = (uint)Math.Ceiling(levels / rat) + (Context.Settings.HybridMode == HybridScaleMode.FavorQuality || Context.Settings.HybridMode == HybridScaleMode.Off ? (uint)Math.Ceiling(levels / 8d) : 0u); prog.SetCurrentLevel(Math.Min(level, levels - (Context.Settings.ScaleRatio >= 2d && levels > 7u ? 2u : 1u))); } var fmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; var desc = new WICBitmapPlaneDescription[2]; if (!trans.DoesSupportTransform(ref Context.Width, ref Context.Height, Context.TransformOptions, WICPlanarOptions.WICPlanarOptionsPreserveSubsampling, fmts, desc, 2)) { throw new NotSupportedException("Planar Transform not supported"); } var crop = new WICRect() { X = Context.Settings.Crop.X, Y = Context.Settings.Crop.Y, Width = Context.Settings.Crop.Width, Height = Context.Settings.Crop.Height }; var source = new WicPlanarCacheSource(trans, desc[0], desc[1], crop, Context.TransformOptions, Context.Width, Context.Height, rat); SourceY = source.GetPlane(WicPlane.Luma); SourceCbCr = source.GetPlane(WicPlane.Chroma); }
public WicFrameReader(WicProcessingContext ctx, bool planar = false) { ctx.DecoderFrame = this; if (ctx.Decoder.Decoder is null) { DpiX = DpiY = 96d; return; } var source = default(IWICBitmapSource); source = Frame = ctx.AddRef(ctx.Decoder.Decoder.GetFrame((uint)ctx.Settings.FrameIndex)); if (ctx.Decoder.WicContainerFormat == Consts.GUID_ContainerFormatRaw && ctx.Settings.FrameIndex == 0 && ctx.Decoder.Decoder.TryGetPreview(out var preview)) { source = ctx.AddRef(preview); } source.GetResolution(out double dpix, out double dpiy); DpiX = dpix; DpiY = dpiy; if (PixelFormat.Cache[source.GetPixelFormat()].NumericRepresentation == PixelNumericRepresentation.Indexed) { var pal = ctx.AddRef(Wic.Factory.CreatePalette()); source.CopyPalette(pal); var newFormat = Consts.GUID_WICPixelFormat24bppBGR; if (pal.HasAlpha()) { newFormat = Consts.GUID_WICPixelFormat32bppBGRA; } else if (pal.IsGrayscale() || pal.IsBlackWhite()) { newFormat = Consts.GUID_WICPixelFormat8bppGray; } var conv = ctx.AddRef(Wic.Factory.CreateFormatConverter()); conv.Initialize(source, newFormat, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); source = conv; } if (source is IWICBitmapSourceTransform trans) { uint pw = 1, ph = 1; source.GetSize(out uint ow, out uint oh); trans.GetClosestSize(ref pw, ref ph); SupportsNativeScale = pw < ow || ph < oh; SupportsNativeTransform = trans.DoesSupportTransform(WICBitmapTransformOptions.WICBitmapTransformRotate270); } if (planar && source is IWICPlanarBitmapSourceTransform ptrans) { uint pw = 1, ph = 1; var pdesc = new WICBitmapPlaneDescription[2]; var pfmts = new[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; SupportsPlanarPipeline = ptrans.DoesSupportTransform(ref pw, ref ph, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsDefault, pfmts, pdesc, 2); } bool preserveNative = SupportsPlanarPipeline || SupportsNativeTransform || (SupportsNativeScale && ctx.Settings.HybridScaleRatio > 1d); ctx.Source = source.AsPixelSource(nameof(IWICBitmapFrameDecode), !preserveNative); }
public static void AddMetadataReader(WicProcessingContext ctx, bool basicOnly = false) { if (ctx.DecoderFrame.Frame is null) { return; } if (ctx.DecoderFrame.Frame.TryGetMetadataQueryReader(out var metareader)) { ctx.AddRef(metareader); // Exif orientation var pvorient = default(PropVariant); if (ctx.Settings.OrientationMode != OrientationMode.Ignore && metareader.TryGetMetadataByName("System.Photo.Orientation", out pvorient)) { #pragma warning disable 0618 // VarEnum is obsolete if (ctx.Settings.OrientationMode == OrientationMode.Normalize && pvorient.UnmanagedType == VarEnum.VT_UI2) { ctx.DecoderFrame.ExifOrientation = (Orientation)Math.Min(Math.Max((ushort)Orientation.Normal, (ushort)pvorient.Value), (ushort)Orientation.Rotate270); } #pragma warning restore 0618 var opt = ctx.DecoderFrame.ExifOrientation.ToWicTransformOptions(); if (ctx.DecoderFrame.SupportsPlanarPipeline && opt != WICBitmapTransformOptions.WICBitmapTransformRotate0 && ctx.DecoderFrame.Frame is IWICPlanarBitmapSourceTransform ptrans) { uint pw = 1, ph = 1; var desc = new WICBitmapPlaneDescription[2]; ctx.DecoderFrame.SupportsPlanarPipeline = ptrans.DoesSupportTransform(ref pw, ref ph, opt, WICPlanarOptions.WICPlanarOptionsDefault, planarPixelFormats, desc, 2); } } if (basicOnly) { return; } // other requested properties var propdic = new Dictionary <string, PropVariant>(); foreach (string prop in ctx.Settings.MetadataNames ?? Enumerable.Empty <string>()) { if (metareader.TryGetMetadataByName(prop, out var pvar) && pvar.Value != null) { propdic[prop] = pvar; } } if (ctx.Settings.OrientationMode == OrientationMode.Preserve && pvorient != null) { propdic["System.Photo.Orientation"] = pvorient; } ctx.DecoderFrame.Metadata = propdic; } if (basicOnly) { return; } // ICC profiles //http://ninedegreesbelow.com/photography/embedded-color-space-information.html uint ccc = ctx.DecoderFrame.Frame.GetColorContextCount(); var fmt = ctx.Source.Format; var profiles = new IWICColorContext[ccc]; var profile = default(IWICColorContext); if (ccc > 0) { for (int i = 0; i < ccc; i++) { profiles[i] = ctx.AddRef(Wic.Factory.CreateColorContext()); } ctx.DecoderFrame.Frame.GetColorContexts(ccc, profiles); } foreach (var cc in profiles) { var cct = cc.GetType(); if (cct == WICColorContextType.WICColorContextProfile) { int ccs = (int)cc.GetProfileBytes(0, null); // don't try to read giant profiles. 4MiB is more than enough if ((uint)ccs > (1024 * 1024 * 4)) { continue; } using (var ccb = MemoryPool <byte> .Shared.Rent(ccs)) { MemoryMarshal.TryGetArray(ccb.Memory.Slice(0, ccs), out ArraySegment <byte> cca); cc.GetProfileBytes((uint)cca.Count, cca.Array); var cpi = ColorProfile.Cache.GetOrAdd(cca); // match only color profiles that match our intended use. if we have a standard sRGB profile, don't save it; we don't need to convert if (cpi.IsValid && ( (cpi.DataColorSpace == ColorProfile.ProfileColorSpace.Rgb && (fmt.ColorRepresentation == PixelColorRepresentation.Bgr || fmt.ColorRepresentation == PixelColorRepresentation.Rgb) && !cpi.IsSrgb) || (cpi.DataColorSpace == ColorProfile.ProfileColorSpace.Grey && fmt.ColorRepresentation == PixelColorRepresentation.Grey && !cpi.IsSrgbCurve) || (cpi.DataColorSpace == ColorProfile.ProfileColorSpace.Cmyk && fmt.ColorRepresentation == PixelColorRepresentation.Cmyk) )) { profile = cc; if (cpi.IsRgbMatrix || cpi.IsGreyTrc) { ctx.SourceColorProfile = cpi; } break; } } } else if (cct == WICColorContextType.WICColorContextExifColorSpace && cc.GetExifColorSpace() == ExifColorSpace.AdobeRGB) { profile = cc; break; } } var defaultColorContext = fmt.ColorRepresentation == PixelColorRepresentation.Grey ? Wic.GreyContext.Value : Wic.SrgbContext.Value; ctx.SourceColorContext = profile ?? (fmt.ColorRepresentation == PixelColorRepresentation.Cmyk ? Wic.CmykContext.Value : null); ctx.DestColorContext = ctx.Settings.ColorProfileMode <= ColorProfileMode.NormalizeAndEmbed || ctx.SourceColorContext is null ? defaultColorContext : ctx.SourceColorContext; var defaultColorProfile = fmt.ColorRepresentation == PixelColorRepresentation.Grey ? ColorProfile.sGrey : ColorProfile.sRGB; ctx.SourceColorProfile = ctx.SourceColorProfile ?? defaultColorProfile; ctx.DestColorProfile = ctx.Settings.ColorProfileMode <= ColorProfileMode.NormalizeAndEmbed ? defaultColorProfile : ctx.SourceColorProfile; }
public WicPlanarCache(IWICPlanarBitmapSourceTransform source, WICBitmapPlaneDescription descY, WICBitmapPlaneDescription descC, WICRect crop, WICBitmapTransformOptions transformOptions, uint width, uint height, double ratio) { subsampleRatioX = Math.Ceiling((double)descY.Width / descC.Width); subsampleRatioY = Math.Ceiling((double)descY.Height / descC.Height); var scrop = new WICRect { X = (int)Math.Floor(crop.X / ratio), Y = (int)Math.Floor(crop.Y / ratio), Width = Math.Min((int)Math.Ceiling(crop.Width / ratio), (int)descY.Width), Height = Math.Min((int)Math.Ceiling(crop.Height / ratio), (int)descY.Height) }; if (subsampleRatioX > 1d) { if (scrop.X % subsampleRatioX > double.Epsilon) { scrop.X = (int)(scrop.X / subsampleRatioX) * (int)subsampleRatioX; } if (scrop.Width % subsampleRatioX > double.Epsilon) { scrop.Width = (int)Math.Min(Math.Ceiling(scrop.Width / subsampleRatioX) * (int)subsampleRatioX, descY.Width - scrop.X); } descC.Width = Math.Min((uint)Math.Ceiling(scrop.Width / subsampleRatioX), descC.Width); descY.Width = (uint)Math.Min(descC.Width * subsampleRatioX, scrop.Width); } else { descC.Width = descY.Width = (uint)scrop.Width; } if (subsampleRatioY > 1d) { if (scrop.Y % subsampleRatioY > double.Epsilon) { scrop.Y = (int)(scrop.Y / subsampleRatioY) * (int)subsampleRatioY; } if (scrop.Height % subsampleRatioY > double.Epsilon) { scrop.Height = (int)Math.Min(Math.Ceiling(scrop.Height / subsampleRatioY) * (int)subsampleRatioY, descY.Height - scrop.Y); } descC.Height = Math.Min((uint)Math.Ceiling(scrop.Height / subsampleRatioY), descC.Height); descY.Height = (uint)Math.Min(descC.Height * subsampleRatioY, scrop.Height); } else { descC.Height = descY.Height = (uint)scrop.Height; } sourceTransform = source; sourceTransformOptions = transformOptions; scaledCrop = scrop; scaledWidth = width; scaledHeight = height; strideY = scrop.Width + 3 & ~3; strideC = (int)Math.Ceiling(scrop.Width / subsampleRatioX) * 2 + 3 & ~3; buffHeightY = Math.Min(scrop.Height, transformOptions.RequiresCache() ? scrop.Height : 16); buffHeightC = (int)Math.Ceiling(buffHeightY / subsampleRatioY); sourceY = new PlanarPixelSource(this, WicPlane.Luma, descY); sourceC = new PlanarPixelSource(this, WicPlane.Chroma, descC); }
public static void AddMetadataReader(WicProcessingContext ctx, bool basicOnly = false) { if (ctx.DecoderFrame.Frame == null) { return; } if (ctx.DecoderFrame.Frame.TryGetMetadataQueryReader(out var metareader)) { ctx.AddRef(metareader); // Exif orientation if (metareader.TryGetMetadataByName("System.Photo.Orientation", out var pv)) { #pragma warning disable 0618 // VarEnum is obsolete if (pv.UnmanagedType == VarEnum.VT_UI2) { ctx.DecoderFrame.ExifOrientation = (Orientation)Math.Min(Math.Max((ushort)Orientation.Normal, (ushort)pv.Value), (ushort)Orientation.Rotate270); } #pragma warning restore 0618 var opt = ctx.DecoderFrame.ExifOrientation.ToWicTransformOptions(); if (ctx.DecoderFrame.SupportsPlanarPipeline && opt != WICBitmapTransformOptions.WICBitmapTransformRotate0 && ctx.DecoderFrame.Frame is IWICPlanarBitmapSourceTransform ptrans) { uint pw = 1, ph = 1; var pdesc = new WICBitmapPlaneDescription[2]; var pfmts = new[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; ctx.DecoderFrame.SupportsPlanarPipeline = ptrans.DoesSupportTransform(ref pw, ref ph, opt, WICPlanarOptions.WICPlanarOptionsDefault, pfmts, pdesc, 2); } } if (basicOnly) { return; } // other requested properties var propdic = new Dictionary <string, PropVariant>(); foreach (string prop in ctx.Settings.MetadataNames ?? Enumerable.Empty <string>()) { if (metareader.TryGetMetadataByName(prop, out var pvar) && pvar.Value != null) { propdic[prop] = pvar; } } ctx.DecoderFrame.Metadata = propdic; } if (basicOnly) { return; } // ICC profiles //http://ninedegreesbelow.com/photography/embedded-color-space-information.html uint ccc = ctx.DecoderFrame.Frame.GetColorContextCount(); var profiles = new IWICColorContext[ccc]; var profile = default(IWICColorContext); if (ccc > 0) { for (int i = 0; i < ccc; i++) { profiles[i] = ctx.AddRef(Wic.Factory.CreateColorContext()); } ctx.DecoderFrame.Frame.GetColorContexts(ccc, profiles); } foreach (var cc in profiles) { var cct = cc.GetType(); if (cct == WICColorContextType.WICColorContextProfile) { uint ccs = cc.GetProfileBytes(0, null); var ccb = ArrayPool <byte> .Shared.Rent((int)ccs); cc.GetProfileBytes(ccs, ccb); var cp = new ColorProfileInfo(new ArraySegment <byte>(ccb, 0, (int)ccs)); ArrayPool <byte> .Shared.Return(ccb); // match only color profiles that match our intended use. if we have a standard sRGB profile, don't save it; we don't need to convert if (cp.IsValid && ((cp.IsDisplayRgb && !cp.IsStandardSrgb) || (cp.IsCmyk && ctx.Source.Format.ColorRepresentation == PixelColorRepresentation.Cmyk) /* || (Context.IsGreyscale && cp.DataColorSpace == "GRAY") */)) { profile = cc; break; } } else if (cct == WICColorContextType.WICColorContextExifColorSpace && cc.GetExifColorSpace() == ExifColorSpace.AdobeRGB) { profile = cc; break; } } ctx.SourceColorContext = profile ?? (ctx.Source.Format.ColorRepresentation == PixelColorRepresentation.Cmyk ? cmykProfile.Value : null); ctx.DestColorContext = srgbProfile.Value; }
public WicPlanarCache(IWICPlanarBitmapSourceTransform source, WICBitmapPlaneDescription descY, WICBitmapPlaneDescription descC, in Rectangle crop, WICBitmapTransformOptions transformOptions, uint width, uint height, double ratio)
public WicPlanarCacheSource(IWICPlanarBitmapSourceTransform source, WICBitmapPlaneDescription descY, WICBitmapPlaneDescription descC, WICRect crop, WICBitmapTransformOptions transformOptions, uint width, uint height, double ratio, bool cacheFull) { this.cacheFull = cacheFull; // TODO fractional ratio support? subsampleRatioX = Math.Ceiling((double)descY.Width / descC.Width); subsampleRatioY = Math.Ceiling((double)descY.Height / descC.Height); var scrop = new WICRect { X = (int)Math.Floor(crop.X / ratio), Y = (int)Math.Floor(crop.Y / ratio), Width = Math.Min((int)Math.Ceiling(crop.Width / ratio), (int)descY.Width), Height = Math.Min((int)Math.Ceiling(crop.Height / ratio), (int)descY.Height) }; if (subsampleRatioX > 1d) { if (scrop.X % subsampleRatioX > 0d) { scrop.X = (int)(scrop.X / subsampleRatioX) * (int)subsampleRatioX; } if (scrop.Width % subsampleRatioX > 0d) { scrop.Width = (int)Math.Min(Math.Ceiling(scrop.Width / subsampleRatioX) * (int)subsampleRatioX, descY.Width); } descC.Width = Math.Min((uint)Math.Ceiling(scrop.Width / subsampleRatioX), descC.Width); descY.Width = (uint)Math.Min(descC.Width * subsampleRatioX, scrop.Width); } else { descC.Width = descY.Width = (uint)scrop.Width; } if (subsampleRatioY > 1d) { if (scrop.Y % subsampleRatioY > 0d) { scrop.Y = (int)(scrop.Y / subsampleRatioY) * (int)subsampleRatioY; } if (scrop.Height % subsampleRatioY > 0d) { scrop.Height = (int)Math.Min(Math.Ceiling(scrop.Height / subsampleRatioY) * (int)subsampleRatioY, descY.Height); } descC.Height = Math.Min((uint)Math.Ceiling(scrop.Height / subsampleRatioY), descC.Height); descY.Height = (uint)Math.Min(descC.Height * subsampleRatioY, scrop.Height); } else { descC.Height = descY.Height = (uint)scrop.Height; } sourceTransform = source; sourceTransformOptions = transformOptions; scaledCrop = scrop; scaledWidth = width; scaledHeight = height; strideY = scrop.Width + 3 & ~3; buffHeightY = Math.Min(scrop.Height, cacheFull ? scrop.Height : 16); strideC = (int)Math.Ceiling(scrop.Width / subsampleRatioX) * 2 + 3 & ~3; buffHeightC = (int)Math.Ceiling(buffHeightY / subsampleRatioY); sourceY = new WicPlanarSource(this, WicPlane.Luma, descY); sourceC = new WicPlanarSource(this, WicPlane.Chroma, descC); }