aspose-free-consulting / projects

The starting point for Aspose free consulting projects
5 stars 5 forks source link

Not able to read the clipping path embedded in the tiff file as directed in https://docs.aspose.com/imaging/net/create-vector-path-in-tiff-format-and-export-it-to-psd-image/ #421

Open Eakshitha11 opened 1 month ago

Eakshitha11 commented 1 month ago

Hi Team

I am trying to work with the creating clipping path and embedding it into the tiff file using the dotnet support as given here- https://docs.aspose.com/imaging/net/create-vector-path-in-tiff-format-and-export-it-to-psd-image/.

I have a tiff image file with an object in between whose path is required and a grey background, and I read the path resource in block id 2000.

I am loading the path from PathPoints.txt file as shown in the link. As per my understanding, each line in the txt contains 3 relative coordinates in the order - Control point, Anchor point, Control point. Each of this point contains two components - vertical coordinate relative to height, horizontal coordinate relative to width. I have kept single space between each component and each point. Example of how my txt file looks with a test clipping path :

0.66225 0.06725 0.66225 0.06725 0.66225 0.06725 0.64075 0.06925 0.64075 0.06925 0.64075 0.06925 0.62675 0.07125 0.62675 0.07125 0.62675 0.07125 0.61125 0.072 0.61125 0.072 0.61125 0.072 0.59275 0.07075 0.59275 0.07075 0.59275 0.07075 0.577 0.0715 0.577 0.0715 0.577 0.0715

Issues:

  1. Here, above path has total 18 points. But when I try to embed the same into the tiff file and read the count of records in the tiff, it gives 19 instead of 18.

  2. Also after saving the tiff with clipping path embedded into it, the background color of the image gets changed from grey to bluish tone somehow.

  3. While I try to load the tiff file with embedded clipping path in photoshop, it errors out saying "Could not open 'SampleWithPaths.tif' because of a problem parsing the path data".

Sharing here the code that I have used to embed the path:

using Aspose.Imaging.FileFormats.Tiff;
using Aspose.Imaging.FileFormats.Tiff.PathResources;
using Aspose.Imaging;
using Aspose.Imaging.FileFormats.Core.VectorPaths;
using Aspose.Imaging.ImageOptions;

class Program
{
    static void Main()
    {
        using (var image = (TiffImage)Image.Load(@"D:\clipping_path\dotnet_code\Test_embedding\SampleNoPaths.tif"))
        {
            image.ActiveFrame.PathResources = new List<PathResource> { new PathResource
            {
                BlockId = 2000, // Block Id according to Photoshop specification
                Name = "Without Shadow", // Path name
                // Records = CreateRecords(0.2f, 0.2f, 0.8f, 0.2f, 0.8f, 0.8f, 0.2f, 0.8f) // Create path records using coordinates
                Records = CreateRecords(LoadPathPoints(@"D:\clipping_path\dotnet_code\Test_embedding\PathPoints.txt")) // Create path records using coordinates
            }};

            image.Save(@"D:\clipping_path\dotnet_code\Test_embedding\SampleWithPaths.tif");
            using (var img = Image.Load(@"D:\clipping_path\dotnet_code\Test_embedding\SampleWithPaths.tif"))
            {
                image.Save(@"D:\clipping_path\dotnet_code\Test_embedding\SampleWithPaths.psd", new PsdOptions());
            }
            Console.WriteLine("Hello, World!");
        }
    }

    // static void Main()
    // {
    //     // using (var image = (TiffImage)Image.Load(@"D:\clipping_path\dotnet_code\Test_embedding\image.tif"))
    //     using (var image = (TiffImage)Image.Load(@"D:\clipping_path\dotnet_code\Test_embedding\SampleWithPaths.tif"))
    //     {
    //         // Get path resources from TIFF image
    //         var pathResources = image.ActiveFrame.PathResources;

    //         if (pathResources != null)
    //         {
    //             foreach (PathResource path in pathResources)
    //             {
    //                 Console.WriteLine("pathresource: ", pathResources);
    //                 var x = pathResources.Take(3).ToList();
    //                 Console.WriteLine("x: ", x);

    //                 if (path.BlockId == 2000)
    //                 {
    //                     // Determine clipping path name
    //                     var clippingPathName = path.Name;
    //                     // Get clipping path
    //                     var clippingPath = path;
    //                     // Write record count to the console
    //                     Console.WriteLine("Clipping path record count: " + clippingPath.Records.Count);
    //                     Console.WriteLine("path name: " + clippingPathName);
    //                     Console.WriteLine("path: ", clippingPath.Records);
    //                 }
    //             }        
    //         }
    //     }
    // }

    private static float[] LoadPathPoints(string filePath)
    {
        return File.ReadAllText(filePath)
            .Split(new[] { " ", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
            .Select(float.Parse)
            .ToArray();
    }

    private static List<VectorPathRecord> CreateRecords(params float[] coordinates)
    {
        var records = CreateBezierRecords(coordinates); // Create Bezier records using coordinates

        records.Insert(0, new LengthRecord // LengthRecord required by Photoshop specification
        {
            IsOpen = false, // Lets create closed path
            RecordCount = (ushort)records.Count // Record count in the path
        });

        return records;
    }

    private static List<VectorPathRecord> CreateBezierRecords(float[] coordinates)
    {
        return CoordinatesToPoints(coordinates)
            .Select(CreateBezierRecord)
            .ToList();
    }

    private static IEnumerable<PointF> CoordinatesToPoints(float[] coordinates)
    {
        for (var index = 0; index < coordinates.Length; index += 2)
            yield return new PointF(coordinates[index], coordinates[index + 1]);
    }

    private static VectorPathRecord CreateBezierRecord(PointF point)
    {
        return new BezierKnotRecord { PathPoints = new[] { point, point, point } };
    }
}

Do let me know where I am going wrong. The coordinates estimated in the txt file are for testing. Let me know if my understanding in creating the path points in txt file is wrong, or if you can share me any test path points (since the points are relative, it should work, though it would not necessarily outline the object) that would work might also help me.

Aspose.Imaging Version : 24.6.0

Thanks and Regards

sam199008 commented 1 month ago

@Eakshitha11 , we will investigate issue and reply.

alex-p-k commented 1 month ago

@Eakshitha11 Hello! Thank you for your interest in our product and for your patience.

We have thoroughly investigated the issue you reported and would like to provide the following guidance. To resolve the problem, please update the usage of the RecordCount property in the CreateRecords method to the BezierKnotRecordsCount property. The corrected line of code should be:

BezierKnotRecordsCount = (ushort)records.Count // Record count in the path

Regarding your question about the record count, it is important to note that an additional "length" record is required. Therefore, the inclusion of this extra record is indeed the correct behavior.

We apologize for any inconvenience this may have caused and appreciate your understanding. The documentation will be updated shortly to reflect this change.

Thank you for bringing this to our attention, and please do not hesitate to reach out if you have any further questions or concerns.

Eakshitha11 commented 1 month ago

@alex-p-k Hello! Thank You for coming up with the response so quick. It is greatly appreciated.

The code update you sent has actually worked for me. Also, it now actually makes sense that the extra count is because of record length.

Thanks again for answering my queries so quick.

I do have an extended query to it -

  1. Currently I am able to access until the record count of the pre-existing clipping path in the tiff image. But is there a way where we can also access the record values that is each of the Bezier points? ( the commented out code is what I am using read and access clipping path from the tiff file with path embedded in it )

  2. regarding the PathPoints.txt ( if it is in the scope of discussion ) - Could you shed some light upon the points that are present in each line. I do understand that there are three points in each line : Control Point, Anchor Point , Control Point. In the Path points that I shared above, I actually took the coordinates lying on the edge of the object as the anchor point, and for now, assumed the control points merged with the anchor points ( all the three points are same in a record in the PathPoints I shared ). Hence I would get a clipping path of connected straight lines instead of curvatures adjusted according to the edges which should be the case if I were able to compute those control points.

Let me know, if so far my understanding is correct or not. Also, if it's possible for you to point out which Bezier Curve ( quadratic or cubic ) is used, how did we actually create each of these points - control point , anchor point ( manual or automated ) or if you could share some visual representation showing the anchor points, control points, or if you could share me some references that helps me create these Bezier points myself would be great.

Thanks and Regards Eakshitha K

alex-p-k commented 1 month ago

@Eakshitha11 Below is the code snippet that demonstrates how to modify specific points within a path resource:

PathResource activeFramePathResource = tiffImage.ActiveFrame.PathResources[0];
foreach (VectorPathRecord vectorPathRecord in activeFramePathResource.Records)
{
    if (vectorPathRecord is BezierKnotRecord bezierKnotRecord)
    {
        PointF point1 = bezierKnotRecord.PathPoints[0];
        PointF point2 = bezierKnotRecord.PathPoints[1];
        PointF point3 = bezierKnotRecord.PathPoints[2];
        float point2x = point2.X;
        float point2y = point2.Y;
        DoSomething(bezierKnotRecord);
    }
}
activeFramePathResource.Records.Clear();
activeFramePathResource.Records.Add(new LengthRecord() {BezierKnotRecordsCount = 3, IsClosed = true});
// The sample image's size is 2000x2000
PointF p1 = new PointF(500/2000f, 1500/2000f); // a way of getting relative coordinate
PointF p2_1 = new PointF(500/2000f, 400/2000f);
PointF p2_2 = new PointF(1000/2000f, 400/2000f);
PointF p2_3 = new PointF(1500/2000f, 400/2000f);
PointF p3 = new PointF(1500/2000f, 1500/2000f);
activeFramePathResource.Records.Add(new BezierKnotRecord { PathPoints = new[] { p1, p1, p1 } });
activeFramePathResource.Records.Add(new BezierKnotRecord { PathPoints = new[] { p2_1, p2_2, p2_3 } });
activeFramePathResource.Records.Add(new BezierKnotRecord { PathPoints = new[] { p3, p3, p3 } });
tiffImage.ActiveFrame.PathResources = new List<PathResource>() { activeFramePathResource };

Please note that you need to set a new PathResources value.

The curve is a quadratic Bezier curve. In Bezier knot records, the following three path points for:

I also attached an example of a saved image with the points mentioned in the code snippet, with anchor and control points labeled for better understanding.

Screenshot 2024-07-25 at 13 30 09

Regarding your question about PathPoints.txt and the initial sample code, the file contains a set of relative float XY coordinate pairs, which are subsequently used to create actual BezierKnotRecord objects. In the initial sample, BezierKnotRecord objects are created using individual points. However, you have the flexibility to create records similar to those in the current code snippet, which includes all three different points.

We hope this information is helpful. If you have any further questions or need additional assistance, please do not hesitate to contact us.

Eakshitha11 commented 1 month ago

Hi @alex-p-k, My Apologies for a delayed response, and thank you so much for such a detailed explanation. This indeed helped me understand the code and the nature of Bezier Curve that has been used here.

Also the visual representation cleared most of my confusion with respect to which is anchor and which being the control points, confirming my understanding of the same.

Now, I can easily get the anchor points that I believe that which lie on the edges of the object. But I am curious to know how about calculating the coordinates of these control point pairs like P2_1 and P2_3 for a given anchor point P2_2 and may be its adjacent anchor point is also known like P1 (if this is in the scope of discussion). Is there any given formulae that can be useful for the same. (Sorry, if I am sounding naive at this point, I am relatively new to the whole Bezier curve concepts ). Also, I tried looking up in the internet about them but it all seemed pretty confusing.

Thank You so much your help so far. Greatly Appreciated!! Regards Eakshitha K

sam199008 commented 1 month ago

@Eakshitha11 , we will investigate and answer you.

alex-p-k commented 1 month ago

@Eakshitha11 Could you please clarify what you mean by calculating control point coordinates? The formulas for calculating Bezier curves allow you to obtain intermediate coordinates on the curve, which is necessary for rendering, but Aspose.Imaging handles this automatically, so there's no need to do these calculations manually. As for choosing control points to achieve the desired curve shape, it's mainly a matter of adjusting points based on the resulting curves to fit your desired outcome. If you're talking about converting a set of simple points to a smoother curve or matching a specific smooth curve, that's a more complex topic. Please specify what you're interested in.

Eakshitha11 commented 1 month ago

@alex-p-k , Sure. By calculating control point coordinates, I mean I have the specific curve, I have the anchor points, but instead of manually finding out the control points by adjusting it attain desired curve shape, how can we automate finding out the control points? So yes, I am essentially talking about converting a set of simple points (that lie on this curve ) to a smoother bezier curve (that matches this curve). Let me know if you have any insights on this, any supporting blogs/articles that can be of help to reverse engineer the process.

Thanks and Regards Eakshitha K

alex-p-k commented 1 month ago

@Eakshitha11 I'm afraid this topic falls outside the scope of our free services related to explaining Aspose.Imaging product capabilities. You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.