Skip to content

Commit 42d1971

Browse files
committed
Initial support for uncompressed TVPaint DEEP images
1 parent c07c550 commit 42d1971

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed

Diff for: ImageFormats/DeepReader.cs

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
using MechanikaDesign.ImageFormats;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Drawing;
5+
using System.IO;
6+
using System.Text;
7+
8+
9+
/*
10+
11+
Decoder for IFF (FORM-based) images.
12+
13+
Copyright 2013-2023 by Warren Galyen
14+
https://www.mechanikadesign.com
15+
16+
Licensed under the Apache License, Version 2.0 (the "License");
17+
you may not use this file except in compliance with the License.
18+
You may obtain a copy of the License at
19+
20+
http://www.apache.org/licenses/LICENSE-2.0
21+
22+
Unless required by applicable law or agreed to in writing, software
23+
distributed under the License is distributed on an "AS IS" BASIS,
24+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25+
See the License for the specific language governing permissions and
26+
limitations under the License.
27+
28+
*/
29+
30+
31+
namespace Mechanika.ImageFormats
32+
{
33+
/// <summary>
34+
/// Handles reading TVPaint DEEP images.
35+
/// </summary>
36+
public static class DeepReader
37+
{
38+
/// <summary>
39+
/// Reads an DEEP image from a file.
40+
/// </summary>
41+
/// <param name="fileName">Name of the file to read.</param>
42+
/// <returns>Bitmap that contains the image that was read.</returns>
43+
public static Bitmap Load(string fileName)
44+
{
45+
using (var f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
46+
{
47+
return Load(f);
48+
}
49+
}
50+
51+
public static Bitmap Load(Stream stream)
52+
{
53+
int imgWidth = -1;
54+
int imgHeight = -1;
55+
56+
int numElements = 0;
57+
int[] elementTypes = new int[16];
58+
int[] elementSizes = new int[16];
59+
60+
int compressionType = 0;
61+
int[] tvdcTable = new int[16];
62+
byte[] bodyChunk = null;
63+
64+
BinaryReader reader = new BinaryReader(stream);
65+
66+
byte[] tempBytes = new byte[65536];
67+
68+
stream.Read(tempBytes, 0, 4);
69+
if (Encoding.ASCII.GetString(tempBytes, 0, 4) != "FORM") { throw new ApplicationException("This is not a valid DEEP file."); }
70+
71+
uint chunkSize = Util.BigEndian(reader.ReadUInt32());
72+
73+
stream.Read(tempBytes, 0, 4);
74+
string fileType = Encoding.ASCII.GetString(tempBytes, 0, 4);
75+
if (fileType != "DEEP") { throw new ApplicationException("This is not a valid DEEP file."); }
76+
77+
byte[] palette = null;
78+
var rowPalette = new List<byte[]>();
79+
80+
while (stream.Position < stream.Length)
81+
{
82+
stream.Read(tempBytes, 0, 4);
83+
string chunkName = Encoding.ASCII.GetString(tempBytes, 0, 4);
84+
chunkSize = Util.BigEndian(reader.ReadUInt32());
85+
86+
if (chunkName == "DBOD")
87+
{
88+
bodyChunk = new byte[chunkSize];
89+
stream.Read(bodyChunk, 0, (int)chunkSize);
90+
break;
91+
}
92+
93+
94+
if (chunkSize <= tempBytes.Length)
95+
{
96+
stream.Read(tempBytes, 0, (int)chunkSize);
97+
}
98+
else
99+
{
100+
stream.Seek(chunkSize, SeekOrigin.Current);
101+
}
102+
103+
104+
if (chunkName == "DGBL")
105+
{
106+
if (imgWidth <= 0) imgWidth = Util.BigEndian(BitConverter.ToUInt16(tempBytes, 0));
107+
if (imgHeight <= 0) imgHeight = Util.BigEndian(BitConverter.ToUInt16(tempBytes, 2));
108+
compressionType = Util.BigEndian(BitConverter.ToUInt16(tempBytes, 4));
109+
// last 2 bytes are xAspect, yAspect.
110+
}
111+
else if (chunkName == "DLOC")
112+
{
113+
imgWidth = Util.BigEndian(BitConverter.ToUInt16(tempBytes, 0));
114+
imgHeight = Util.BigEndian(BitConverter.ToUInt16(tempBytes, 2));
115+
// last 4 bytes are xLocation, yLocation.
116+
}
117+
else if (chunkName == "DPEL")
118+
{
119+
numElements = (int)Util.BigEndian(BitConverter.ToUInt32(tempBytes, 0));
120+
int ptr = 4;
121+
for (int i = 0; i < numElements; i++)
122+
{
123+
elementTypes[i] = Util.BigEndian(BitConverter.ToUInt16(tempBytes, ptr)); ptr += 2;
124+
elementSizes[i] = Util.BigEndian(BitConverter.ToUInt16(tempBytes, ptr)); ptr += 2;
125+
}
126+
}
127+
else if (chunkName == "TVDC" && chunkSize >= 32)
128+
{
129+
for (int i = 0; i < 16; i++)
130+
{
131+
tvdcTable[i] = (short)Util.BigEndian(BitConverter.ToUInt16(tempBytes, i * 2));
132+
}
133+
}
134+
}
135+
136+
if (imgWidth == -1 || imgHeight == -1)
137+
{
138+
throw new ApplicationException("Invalid format of DEEP file.");
139+
}
140+
141+
byte[] bmpData = new byte[(imgWidth + 1) * 4 * imgHeight];
142+
143+
try
144+
{
145+
if (compressionType == 0)
146+
{
147+
int ptr = 0;
148+
for (int y = 0; y < imgHeight; y++)
149+
{
150+
for (int x = 0; x < imgWidth; x++)
151+
{
152+
if (numElements == 3)
153+
{
154+
bmpData[4 * (y * imgWidth + x) + 2] = bodyChunk[ptr++];
155+
bmpData[4 * (y * imgWidth + x) + 1] = bodyChunk[ptr++];
156+
bmpData[4 * (y * imgWidth + x)] = bodyChunk[ptr++];
157+
bmpData[4 * (y * imgWidth + x) + 3] = 0xFF;
158+
}
159+
else if (numElements == 4)
160+
{
161+
bmpData[4 * (y * imgWidth + x) + 2] = bodyChunk[ptr++];
162+
bmpData[4 * (y * imgWidth + x) + 1] = bodyChunk[ptr++];
163+
bmpData[4 * (y * imgWidth + x)] = bodyChunk[ptr++];
164+
bmpData[4 * (y * imgWidth + x) + 3] = bodyChunk[ptr++];
165+
}
166+
}
167+
}
168+
}
169+
else if (compressionType == 5)
170+
{
171+
int scanLineSize = imgWidth;
172+
173+
byte[] uncompressed = new byte[bmpData.Length * 2];
174+
175+
int pos = 0;
176+
177+
for (int y = 0; y < imgHeight; y++)
178+
{
179+
int d;
180+
int v = 0;
181+
182+
for (int i = 0; i < scanLineSize; i++)
183+
{
184+
d = bodyChunk[pos >> 1];
185+
if ((pos++ & 0x1) != 0) d &= 0xF;
186+
else d >>= 4;
187+
v += tvdcTable[d];
188+
uncompressed[i] = (byte)v;
189+
if (tvdcTable[d] == 0)
190+
{
191+
d = bodyChunk[pos >> 1];
192+
if ((pos++ & 0x1) != 0) d &= 0xf;
193+
else d >>= 4;
194+
while (d-- > 0) uncompressed[++i] = (byte)v;
195+
}
196+
}
197+
198+
pos = (pos + 1) / 2;
199+
200+
int xx = 0;
201+
for (int x = 0; x < imgWidth; x++)
202+
{
203+
bmpData[4 * (y * imgWidth + x)] = uncompressed[xx];
204+
bmpData[4 * (y * imgWidth + x) + 1] = uncompressed[xx];
205+
bmpData[4 * (y * imgWidth + x) + 2] = uncompressed[xx];
206+
bmpData[4 * (y * imgWidth + x) + 3] = 0xFF;
207+
xx++;
208+
}
209+
}
210+
}
211+
}
212+
catch (Exception e)
213+
{
214+
// return a partial image in case of unexpected EOF
215+
Util.log("Error while processing DEEP file: " + e.Message);
216+
}
217+
218+
var bmp = new Bitmap(imgWidth, imgHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
219+
System.Drawing.Imaging.BitmapData bmpBits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
220+
System.Runtime.InteropServices.Marshal.Copy(bmpData, 0, bmpBits.Scan0, imgWidth * 4 * imgHeight);
221+
bmp.UnlockBits(bmpBits);
222+
return bmp;
223+
}
224+
225+
226+
}
227+
}

Diff for: ImageFormats/ImageFormats.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
</ItemGroup>
4949
<ItemGroup>
5050
<Compile Include="DdsReader.cs" />
51+
<Compile Include="DeepReader.cs" />
5152
<Compile Include="FitsReader.cs" />
5253
<Compile Include="FormMain.cs">
5354
<SubType>Form</SubType>

Diff for: ImageFormats/Picture.cs

+5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public static Bitmap Load(Stream stream)
104104
{
105105
bmp = IlbmReader.Load(stream);
106106
}
107+
else if ((header[0] == 'F') && (header[1] == 'O') && (header[2] == 'R') && (header[3] == 'M')
108+
&& (header[8] == 'D') && (header[9] == 'E') && (header[10] == 'E') && (header[11] == 'P'))
109+
{
110+
bmp = DeepReader.Load(stream);
111+
}
107112
else if ((header[0] == 'S') && (header[1] >= 'I') && (header[2] >= 'M') && (header[3] >= 'P'))
108113
{
109114
bmp = FitsReader.Load(stream);

0 commit comments

Comments
 (0)