/// <summary> /// Imports the given <paramref name="rawBytes"/> as rgb image. /// </summary> /// <param name="rawBytes">The raw bytes to use for the image.</param> /// <param name="imageSize">Size of the resulting image.</param> /// <param name="fill">Pixel value used as a fill when no raw bytes are left.</param> /// <param name="rgbMode">Read-in mode of the rgb bytes.</param> /// <returns>Imported rgb image.</returns> public static Image ImportAsRGB(IEnumerable <byte> rawBytes, Size2D imageSize, byte fill, RGBMode rgbMode) { if (rawBytes == null) { throw new ArgumentNullException(nameof(rawBytes)); } var queue = new Queue <byte>(rawBytes); var img = new Image(imageSize, 3); if (rgbMode == RGBMode.RGBRGB) { ImportRGBAsRGBRGB(queue, img, fill); } else { ImportRGBAsRRGGBB(queue, img, fill); } return(img); }
/// <summary> /// Generates a distance texture from the alpha channel of a source texture. /// </summary> /// <param name="source"> /// Source texture. Alpha values of 1 are considered inside, values of 0 are considered outside, and any other values are considered /// to be on the edge. Must be readable. /// </param> /// <param name="destination"> /// Destination texture. Must be the same size as the source texture. Must be readable. /// The texture change does not get applied automatically, you need to do that yourself. /// </param> /// <param name="maxInside"> /// Maximum pixel distance measured inside the edge, resulting in an alpha value of 1. /// If set to or below 0, everything inside will have an alpha value of 1. /// </param> /// <param name="maxOutside"> /// Maximum pixel distance measured outside the edge, resulting in an alpha value of 0. /// If set to or below 0, everything outside will have an alpha value of 0. /// </param> /// <param name="postProcessDistance"> /// Pixel distance from the edge within which pixels will be post-processed using the edge gradient. /// </param> /// <param name="rgbMode"> /// How to fill the destination texture's RGB channels. /// </param> public static void Generate(Texture2D source, Texture2D destination, float maxInside, float maxOutside, float postProcessDistance, RGBMode rgbMode) { if (source.height != destination.height || source.width != destination.width) { Debug.LogError("Source and destination textures must be the same size."); return; } string sourcePath = AssetDatabase.GetAssetPath(source); TextureImporter importer = AssetImporter.GetAtPath(sourcePath) as TextureImporter; if (importer != null && !importer.isReadable) { AssetDatabase.StartAssetEditing(); importer.textureType = TextureImporterType.Default; importer.isReadable = true; AssetDatabase.StopAssetEditing(); AssetDatabase.ImportAsset(sourcePath); } try{ source.GetPixel(0, 0); } catch { Debug.LogError("Source texture is not read/write enabled."); return; } try{ destination.GetPixel(0, 0); } catch { Debug.LogError("Destination texture is not read/write enabled."); return; } width = source.width; height = source.height; pixels = new Pixel[width, height]; int x, y; float scale; Color c = rgbMode == RGBMode.White ? Color.white : Color.black; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y] = new Pixel(); } } if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = 1f - source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxInside; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = Mathf.Clamp01(pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } if (maxOutside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixels[x, y].alpha = source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if (postProcessDistance > 0f) { PostProcess(postProcessDistance); } scale = 1f / maxOutside; if (maxInside > 0f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = 0.5f + (destination.GetPixel(x, y).a - Mathf.Clamp01(pixels[x, y].distance * scale)) * 0.5f; destination.SetPixel(x, y, c); } } } else { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c.a = Mathf.Clamp01(1f - pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } } if (rgbMode == RGBMode.Distance) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = destination.GetPixel(x, y); c.r = c.a; c.g = c.a; c.b = c.a; destination.SetPixel(x, y, c); } } } else if (rgbMode == RGBMode.Source) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { c = source.GetPixel(x, y); c.a = destination.GetPixel(x, y).a; destination.SetPixel(x, y, c); } } } pixels = null; }
/// <summary> /// Generates a distance texture from the alpha channel of a source texture. /// </summary> /// <param name="source"> /// Source texture. Alpha values of 1 are considered inside, values of 0 are considered outside, and any other values are considered /// to be on the edge. Must be readable. /// </param> /// <param name="destination"> /// Destination texture. Must be the same size as the source texture. Must be readable. /// The texture change does not get applied automatically, you need to do that yourself. /// </param> /// <param name="maxInside"> /// Maximum pixel distance measured inside the edge, resulting in an alpha value of 1. /// If set to or below 0, everything inside will have an alpha value of 1. /// </param> /// <param name="maxOutside"> /// Maximum pixel distance measured outside the edge, resulting in an alpha value of 0. /// If set to or below 0, everything outside will have an alpha value of 0. /// </param> /// <param name="postProcessDistance"> /// Pixel distance from the edge within which pixels will be post-processed using the edge gradient. /// </param> /// <param name="rgbMode"> /// How to fill the destination texture's RGB channels. /// </param> public static void Generate (Texture2D source, Texture2D destination, float maxInside, float maxOutside, float postProcessDistance, RGBMode rgbMode) { if(source.height != destination.height || source.width != destination.width){ Debug.LogError("Source and destination textures must be the same size."); return; } try{ source.GetPixel(0, 0); } catch{ Debug.LogError("Source texture is not read/write enabled."); return; } try{ destination.GetPixel(0, 0); } catch{ Debug.LogError("Destination texture is not read/write enabled."); return; } width = source.width; height = source.height; pixels = new Pixel[width, height]; int x, y; float scale; Color c = rgbMode == RGBMode.White ? Color.white : Color.black; for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ pixels[x, y] = new Pixel(); } } if(maxInside > 0f){ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ pixels[x, y].alpha = 1f - source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if(postProcessDistance > 0f){ PostProcess(postProcessDistance); } scale = 1f / maxInside; for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ c.a = Mathf.Clamp01(pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } if(maxOutside > 0f){ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ pixels[x, y].alpha = source.GetPixel(x, y).a; } } ComputeEdgeGradients(); GenerateDistanceTransform(); if(postProcessDistance > 0f){ PostProcess(postProcessDistance); } scale = 1f / maxOutside; if(maxInside > 0f){ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ c.a = 0.5f + (destination.GetPixel(x, y).a - Mathf.Clamp01(pixels[x, y].distance * scale)) * 0.5f; destination.SetPixel(x, y, c); } } } else{ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ c.a = Mathf.Clamp01(1f - pixels[x, y].distance * scale); destination.SetPixel(x, y, c); } } } } if(rgbMode == RGBMode.Distance){ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ c = destination.GetPixel(x, y); c.r = c.a; c.g = c.a; c.b = c.a; destination.SetPixel(x, y, c); } } } else if(rgbMode == RGBMode.Source){ for(y = 0; y < height; y++){ for(x = 0; x < width; x++){ c = source.GetPixel(x, y); c.a = destination.GetPixel(x, y).a; destination.SetPixel(x, y, c); } } } pixels = null; }