joonsrenderer 1.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +53 -0
- data/.mvn/extensions.xml +8 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +6 -0
- data/LICENSE +674 -0
- data/README.md +2 -0
- data/Rakefile +43 -0
- data/docs/.gitignore +6 -0
- data/docs/_config.yml +20 -0
- data/docs/_includes/footer.html +38 -0
- data/docs/_includes/head.html +15 -0
- data/docs/_includes/header.html +27 -0
- data/docs/_includes/icon-github.html +1 -0
- data/docs/_includes/icon-github.svg +1 -0
- data/docs/_includes/icon-twitter.html +1 -0
- data/docs/_includes/icon-twitter.svg +1 -0
- data/docs/_layouts/default.html +20 -0
- data/docs/_layouts/page.html +14 -0
- data/docs/_layouts/post.html +15 -0
- data/docs/_posts/2017-01-08-animated_ray_tracing.md +72 -0
- data/docs/_posts/2017-01-08-welcome.md +78 -0
- data/docs/_sass/_base.scss +206 -0
- data/docs/_sass/_layout.scss +242 -0
- data/docs/_sass/_syntax-highlighting.scss +71 -0
- data/docs/about.md +12 -0
- data/docs/assets/Animation.ogv +0 -0
- data/docs/assets/Animation.png +0 -0
- data/docs/assets/basic.png +0 -0
- data/docs/assets/basic_traced.png +0 -0
- data/docs/css/main.scss +38 -0
- data/docs/favicon.ico +0 -0
- data/docs/feed.xml +30 -0
- data/docs/index.html +38 -0
- data/joonsrenderer.gemspec +23 -0
- data/lib/joonsrenderer.rb +12 -0
- data/lib/joonsrenderer/version.rb +3 -0
- data/pom.rb +75 -0
- data/pom.xml +163 -0
- data/src/main/java/SunflowGUI.java +1354 -0
- data/src/main/java/joons/JRFiller.java +79 -0
- data/src/main/java/joons/JRImagePanel.java +141 -0
- data/src/main/java/joons/JRRecorder.java +183 -0
- data/src/main/java/joons/JRStatics.java +199 -0
- data/src/main/java/joons/JoonsRenderer.java +837 -0
- data/src/main/java/org/sunflow/AsciiFileSunflowAPI.java +98 -0
- data/src/main/java/org/sunflow/Benchmark.java +313 -0
- data/src/main/java/org/sunflow/BinaryFileSunflowAPI.java +228 -0
- data/src/main/java/org/sunflow/FileSunflowAPI.java +354 -0
- data/src/main/java/org/sunflow/PluginRegistry.java +322 -0
- data/src/main/java/org/sunflow/RealtimeBenchmark.java +125 -0
- data/src/main/java/org/sunflow/RenderObjectMap.java +344 -0
- data/src/main/java/org/sunflow/SunflowAPI.java +762 -0
- data/src/main/java/org/sunflow/SunflowAPIInterface.java +277 -0
- data/src/main/java/org/sunflow/core/AccelerationStructure.java +20 -0
- data/src/main/java/org/sunflow/core/AccelerationStructureFactory.java +36 -0
- data/src/main/java/org/sunflow/core/BucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/Camera.java +125 -0
- data/src/main/java/org/sunflow/core/CameraLens.java +29 -0
- data/src/main/java/org/sunflow/core/CausticPhotonMapInterface.java +15 -0
- data/src/main/java/org/sunflow/core/Display.java +78 -0
- data/src/main/java/org/sunflow/core/Filter.java +27 -0
- data/src/main/java/org/sunflow/core/GIEngine.java +42 -0
- data/src/main/java/org/sunflow/core/Geometry.java +157 -0
- data/src/main/java/org/sunflow/core/GlobalPhotonMapInterface.java +21 -0
- data/src/main/java/org/sunflow/core/ImageSampler.java +26 -0
- data/src/main/java/org/sunflow/core/Instance.java +224 -0
- data/src/main/java/org/sunflow/core/InstanceList.java +83 -0
- data/src/main/java/org/sunflow/core/IntersectionState.java +120 -0
- data/src/main/java/org/sunflow/core/LightSample.java +104 -0
- data/src/main/java/org/sunflow/core/LightServer.java +382 -0
- data/src/main/java/org/sunflow/core/LightSource.java +67 -0
- data/src/main/java/org/sunflow/core/Modifier.java +16 -0
- data/src/main/java/org/sunflow/core/Options.java +20 -0
- data/src/main/java/org/sunflow/core/ParameterList.java +758 -0
- data/src/main/java/org/sunflow/core/PhotonStore.java +62 -0
- data/src/main/java/org/sunflow/core/PrimitiveList.java +70 -0
- data/src/main/java/org/sunflow/core/Ray.java +219 -0
- data/src/main/java/org/sunflow/core/RenderObject.java +25 -0
- data/src/main/java/org/sunflow/core/Scene.java +377 -0
- data/src/main/java/org/sunflow/core/SceneParser.java +58 -0
- data/src/main/java/org/sunflow/core/Shader.java +30 -0
- data/src/main/java/org/sunflow/core/ShadingCache.java +84 -0
- data/src/main/java/org/sunflow/core/ShadingState.java +939 -0
- data/src/main/java/org/sunflow/core/Statistics.java +85 -0
- data/src/main/java/org/sunflow/core/Tesselatable.java +36 -0
- data/src/main/java/org/sunflow/core/Texture.java +128 -0
- data/src/main/java/org/sunflow/core/TextureCache.java +48 -0
- data/src/main/java/org/sunflow/core/accel/BoundingIntervalHierarchy.java +652 -0
- data/src/main/java/org/sunflow/core/accel/KDTree.java +833 -0
- data/src/main/java/org/sunflow/core/accel/NullAccelerator.java +30 -0
- data/src/main/java/org/sunflow/core/accel/UniformGrid.java +329 -0
- data/src/main/java/org/sunflow/core/bucket/BucketOrderFactory.java +26 -0
- data/src/main/java/org/sunflow/core/bucket/ColumnBucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/bucket/DiagonalBucketOrder.java +28 -0
- data/src/main/java/org/sunflow/core/bucket/HilbertBucketOrder.java +65 -0
- data/src/main/java/org/sunflow/core/bucket/InvertedBucketOrder.java +28 -0
- data/src/main/java/org/sunflow/core/bucket/RandomBucketOrder.java +49 -0
- data/src/main/java/org/sunflow/core/bucket/RowBucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/bucket/SpiralBucketOrder.java +43 -0
- data/src/main/java/org/sunflow/core/camera/FisheyeLens.java +25 -0
- data/src/main/java/org/sunflow/core/camera/PinholeLens.java +43 -0
- data/src/main/java/org/sunflow/core/camera/SphericalLens.java +22 -0
- data/src/main/java/org/sunflow/core/camera/ThinLens.java +107 -0
- data/src/main/java/org/sunflow/core/display/FastDisplay.java +119 -0
- data/src/main/java/org/sunflow/core/display/FileDisplay.java +83 -0
- data/src/main/java/org/sunflow/core/display/FrameDisplay.java +97 -0
- data/src/main/java/org/sunflow/core/display/ImgPipeDisplay.java +109 -0
- data/src/main/java/org/sunflow/core/filter/BlackmanHarrisFilter.java +28 -0
- data/src/main/java/org/sunflow/core/filter/BoxFilter.java +16 -0
- data/src/main/java/org/sunflow/core/filter/CatmullRomFilter.java +29 -0
- data/src/main/java/org/sunflow/core/filter/CubicBSpline.java +32 -0
- data/src/main/java/org/sunflow/core/filter/GaussianFilter.java +24 -0
- data/src/main/java/org/sunflow/core/filter/LanczosFilter.java +30 -0
- data/src/main/java/org/sunflow/core/filter/MitchellFilter.java +28 -0
- data/src/main/java/org/sunflow/core/filter/SincFilter.java +25 -0
- data/src/main/java/org/sunflow/core/filter/TriangleFilter.java +16 -0
- data/src/main/java/org/sunflow/core/gi/AmbientOcclusionGIEngine.java +57 -0
- data/src/main/java/org/sunflow/core/gi/FakeGIEngine.java +48 -0
- data/src/main/java/org/sunflow/core/gi/InstantGI.java +194 -0
- data/src/main/java/org/sunflow/core/gi/IrradianceCacheGIEngine.java +268 -0
- data/src/main/java/org/sunflow/core/gi/PathTracingGIEngine.java +65 -0
- data/src/main/java/org/sunflow/core/light/DirectionalSpotlight.java +103 -0
- data/src/main/java/org/sunflow/core/light/ImageBasedLight.java +303 -0
- data/src/main/java/org/sunflow/core/light/PointLight.java +72 -0
- data/src/main/java/org/sunflow/core/light/SphereLight.java +166 -0
- data/src/main/java/org/sunflow/core/light/SunSkyLight.java +362 -0
- data/src/main/java/org/sunflow/core/light/TriangleMeshLight.java +296 -0
- data/src/main/java/org/sunflow/core/modifiers/BumpMappingModifier.java +37 -0
- data/src/main/java/org/sunflow/core/modifiers/NormalMapModifier.java +34 -0
- data/src/main/java/org/sunflow/core/modifiers/PerlinModifier.java +80 -0
- data/src/main/java/org/sunflow/core/parser/Keyword.java +39 -0
- data/src/main/java/org/sunflow/core/parser/RA2Parser.java +107 -0
- data/src/main/java/org/sunflow/core/parser/RA3Parser.java +68 -0
- data/src/main/java/org/sunflow/core/parser/SCAbstractParser.java +299 -0
- data/src/main/java/org/sunflow/core/parser/SCAsciiParser.java +251 -0
- data/src/main/java/org/sunflow/core/parser/SCBinaryParser.java +156 -0
- data/src/main/java/org/sunflow/core/parser/SCParser.java +1403 -0
- data/src/main/java/org/sunflow/core/parser/ShaveRibParser.java +174 -0
- data/src/main/java/org/sunflow/core/parser/TriParser.java +79 -0
- data/src/main/java/org/sunflow/core/photonmap/CausticPhotonMap.java +429 -0
- data/src/main/java/org/sunflow/core/photonmap/GlobalPhotonMap.java +530 -0
- data/src/main/java/org/sunflow/core/photonmap/GridPhotonMap.java +308 -0
- data/src/main/java/org/sunflow/core/primitive/Background.java +55 -0
- data/src/main/java/org/sunflow/core/primitive/BanchoffSurface.java +100 -0
- data/src/main/java/org/sunflow/core/primitive/Box.java +210 -0
- data/src/main/java/org/sunflow/core/primitive/CornellBox.java +476 -0
- data/src/main/java/org/sunflow/core/primitive/CubeGrid.java +318 -0
- data/src/main/java/org/sunflow/core/primitive/Cylinder.java +104 -0
- data/src/main/java/org/sunflow/core/primitive/Hair.java +275 -0
- data/src/main/java/org/sunflow/core/primitive/JuliaFractal.java +266 -0
- data/src/main/java/org/sunflow/core/primitive/ParticleSurface.java +114 -0
- data/src/main/java/org/sunflow/core/primitive/Plane.java +163 -0
- data/src/main/java/org/sunflow/core/primitive/QuadMesh.java +413 -0
- data/src/main/java/org/sunflow/core/primitive/Sphere.java +101 -0
- data/src/main/java/org/sunflow/core/primitive/SphereFlake.java +234 -0
- data/src/main/java/org/sunflow/core/primitive/Torus.java +145 -0
- data/src/main/java/org/sunflow/core/primitive/TriangleMesh.java +849 -0
- data/src/main/java/org/sunflow/core/renderer/BucketRenderer.java +491 -0
- data/src/main/java/org/sunflow/core/renderer/MultipassRenderer.java +237 -0
- data/src/main/java/org/sunflow/core/renderer/ProgressiveRenderer.java +171 -0
- data/src/main/java/org/sunflow/core/renderer/SimpleRenderer.java +106 -0
- data/src/main/java/org/sunflow/core/shader/AmbientOcclusionShader.java +53 -0
- data/src/main/java/org/sunflow/core/shader/AnisotropicWardShader.java +216 -0
- data/src/main/java/org/sunflow/core/shader/ConstantShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/DiffuseShader.java +65 -0
- data/src/main/java/org/sunflow/core/shader/GlassShader.java +147 -0
- data/src/main/java/org/sunflow/core/shader/IDShader.java +27 -0
- data/src/main/java/org/sunflow/core/shader/MirrorShader.java +68 -0
- data/src/main/java/org/sunflow/core/shader/NormalShader.java +32 -0
- data/src/main/java/org/sunflow/core/shader/PhongShader.java +89 -0
- data/src/main/java/org/sunflow/core/shader/PrimIDShader.java +30 -0
- data/src/main/java/org/sunflow/core/shader/QuickGrayShader.java +63 -0
- data/src/main/java/org/sunflow/core/shader/ShinyDiffuseShader.java +98 -0
- data/src/main/java/org/sunflow/core/shader/SimpleShader.java +24 -0
- data/src/main/java/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedDiffuseShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedPhongShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedShinyDiffuseShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedWardShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/UVShader.java +27 -0
- data/src/main/java/org/sunflow/core/shader/UberShader.java +149 -0
- data/src/main/java/org/sunflow/core/shader/ViewCausticsShader.java +33 -0
- data/src/main/java/org/sunflow/core/shader/ViewGlobalPhotonsShader.java +25 -0
- data/src/main/java/org/sunflow/core/shader/ViewIrradianceShader.java +25 -0
- data/src/main/java/org/sunflow/core/shader/WireframeShader.java +83 -0
- data/src/main/java/org/sunflow/core/tesselatable/BezierMesh.java +254 -0
- data/src/main/java/org/sunflow/core/tesselatable/FileMesh.java +251 -0
- data/src/main/java/org/sunflow/core/tesselatable/Gumbo.java +1147 -0
- data/src/main/java/org/sunflow/core/tesselatable/Teapot.java +237 -0
- data/src/main/java/org/sunflow/image/Bitmap.java +15 -0
- data/src/main/java/org/sunflow/image/BitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/BitmapWriter.java +79 -0
- data/src/main/java/org/sunflow/image/BlackbodySpectrum.java +16 -0
- data/src/main/java/org/sunflow/image/ChromaticitySpectrum.java +55 -0
- data/src/main/java/org/sunflow/image/Color.java +374 -0
- data/src/main/java/org/sunflow/image/ColorEncoder.java +94 -0
- data/src/main/java/org/sunflow/image/ColorFactory.java +122 -0
- data/src/main/java/org/sunflow/image/ConstantSpectralCurve.java +21 -0
- data/src/main/java/org/sunflow/image/IrregularSpectralCurve.java +57 -0
- data/src/main/java/org/sunflow/image/RGBSpace.java +207 -0
- data/src/main/java/org/sunflow/image/RegularSpectralCurve.java +30 -0
- data/src/main/java/org/sunflow/image/SpectralCurve.java +118 -0
- data/src/main/java/org/sunflow/image/XYZColor.java +50 -0
- data/src/main/java/org/sunflow/image/formats/BitmapBlack.java +27 -0
- data/src/main/java/org/sunflow/image/formats/BitmapG8.java +36 -0
- data/src/main/java/org/sunflow/image/formats/BitmapGA8.java +30 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGB8.java +40 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGBA8.java +40 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGBE.java +60 -0
- data/src/main/java/org/sunflow/image/formats/BitmapXYZ.java +38 -0
- data/src/main/java/org/sunflow/image/formats/GenericBitmap.java +73 -0
- data/src/main/java/org/sunflow/image/readers/BMPBitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/readers/HDRBitmapReader.java +155 -0
- data/src/main/java/org/sunflow/image/readers/IGIBitmapReader.java +104 -0
- data/src/main/java/org/sunflow/image/readers/JPGBitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/readers/PNGBitmapReader.java +40 -0
- data/src/main/java/org/sunflow/image/readers/TGABitmapReader.java +141 -0
- data/src/main/java/org/sunflow/image/writers/EXRBitmapWriter.java +395 -0
- data/src/main/java/org/sunflow/image/writers/HDRBitmapWriter.java +54 -0
- data/src/main/java/org/sunflow/image/writers/IGIBitmapWriter.java +75 -0
- data/src/main/java/org/sunflow/image/writers/PNGBitmapWriter.java +39 -0
- data/src/main/java/org/sunflow/image/writers/TGABitmapWriter.java +63 -0
- data/src/main/java/org/sunflow/math/BoundingBox.java +340 -0
- data/src/main/java/org/sunflow/math/MathUtils.java +159 -0
- data/src/main/java/org/sunflow/math/Matrix4.java +573 -0
- data/src/main/java/org/sunflow/math/MovingMatrix4.java +119 -0
- data/src/main/java/org/sunflow/math/OrthoNormalBasis.java +110 -0
- data/src/main/java/org/sunflow/math/PerlinScalar.java +331 -0
- data/src/main/java/org/sunflow/math/PerlinVector.java +132 -0
- data/src/main/java/org/sunflow/math/Point2.java +36 -0
- data/src/main/java/org/sunflow/math/Point3.java +133 -0
- data/src/main/java/org/sunflow/math/QMC.java +209 -0
- data/src/main/java/org/sunflow/math/Solvers.java +142 -0
- data/src/main/java/org/sunflow/math/Vector3.java +197 -0
- data/src/main/java/org/sunflow/system/BenchmarkFramework.java +73 -0
- data/src/main/java/org/sunflow/system/BenchmarkTest.java +17 -0
- data/src/main/java/org/sunflow/system/ByteUtil.java +119 -0
- data/src/main/java/org/sunflow/system/FileUtils.java +27 -0
- data/src/main/java/org/sunflow/system/ImagePanel.java +282 -0
- data/src/main/java/org/sunflow/system/Memory.java +18 -0
- data/src/main/java/org/sunflow/system/Parser.java +162 -0
- data/src/main/java/org/sunflow/system/Plugins.java +142 -0
- data/src/main/java/org/sunflow/system/RenderGlobalsPanel.java +209 -0
- data/src/main/java/org/sunflow/system/SearchPath.java +67 -0
- data/src/main/java/org/sunflow/system/Timer.java +53 -0
- data/src/main/java/org/sunflow/system/UI.java +112 -0
- data/src/main/java/org/sunflow/system/UserInterface.java +46 -0
- data/src/main/java/org/sunflow/system/ui/ConsoleInterface.java +48 -0
- data/src/main/java/org/sunflow/system/ui/SilentInterface.java +28 -0
- data/src/main/java/org/sunflow/util/FastHashMap.java +220 -0
- data/src/main/java/org/sunflow/util/FloatArray.java +77 -0
- data/src/main/java/org/sunflow/util/IntArray.java +77 -0
- data/src/test/java/a_maintest.java +129 -0
- metadata +300 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package org.sunflow.image.readers;
|
|
2
|
+
|
|
3
|
+
import java.io.BufferedInputStream;
|
|
4
|
+
import java.io.FileInputStream;
|
|
5
|
+
import java.io.IOException;
|
|
6
|
+
import java.io.InputStream;
|
|
7
|
+
|
|
8
|
+
import org.sunflow.image.Bitmap;
|
|
9
|
+
import org.sunflow.image.BitmapReader;
|
|
10
|
+
import org.sunflow.image.Color;
|
|
11
|
+
import org.sunflow.image.formats.BitmapG8;
|
|
12
|
+
import org.sunflow.image.formats.BitmapRGB8;
|
|
13
|
+
import org.sunflow.image.formats.BitmapRGBA8;
|
|
14
|
+
|
|
15
|
+
public class TGABitmapReader implements BitmapReader {
|
|
16
|
+
|
|
17
|
+
private static final int[] CHANNEL_INDEX = {2, 1, 0, 3};
|
|
18
|
+
|
|
19
|
+
public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException {
|
|
20
|
+
InputStream f = new BufferedInputStream(new FileInputStream(filename));
|
|
21
|
+
byte[] read = new byte[4];
|
|
22
|
+
|
|
23
|
+
// read header
|
|
24
|
+
int idsize = f.read();
|
|
25
|
+
int cmaptype = f.read(); // cmap byte (unsupported)
|
|
26
|
+
if (cmaptype != 0) {
|
|
27
|
+
throw new BitmapFormatException(String.format("Colormapping (type: %d) is unsupported", cmaptype));
|
|
28
|
+
}
|
|
29
|
+
int datatype = f.read();
|
|
30
|
+
|
|
31
|
+
// colormap info (5 bytes ignored)
|
|
32
|
+
f.read();
|
|
33
|
+
f.read();
|
|
34
|
+
f.read();
|
|
35
|
+
f.read();
|
|
36
|
+
f.read();
|
|
37
|
+
|
|
38
|
+
f.read(); // xstart, 16 bits (ignored)
|
|
39
|
+
f.read();
|
|
40
|
+
f.read(); // ystart, 16 bits (ignored)
|
|
41
|
+
f.read();
|
|
42
|
+
|
|
43
|
+
// read resolution
|
|
44
|
+
int width = f.read();
|
|
45
|
+
width |= f.read() << 8;
|
|
46
|
+
int height = f.read();
|
|
47
|
+
height |= f.read() << 8;
|
|
48
|
+
|
|
49
|
+
int bits = f.read();
|
|
50
|
+
int bpp = bits / 8;
|
|
51
|
+
|
|
52
|
+
int imgdscr = f.read();
|
|
53
|
+
|
|
54
|
+
// skip image ID if present
|
|
55
|
+
if (idsize != 0) {
|
|
56
|
+
f.skip(idsize);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// allocate byte buffer to hold the image
|
|
60
|
+
byte[] pixels = new byte[width * height * bpp];
|
|
61
|
+
if (datatype == 2 || datatype == 3) {
|
|
62
|
+
if (bpp != 1 && bpp != 3 && bpp != 4) {
|
|
63
|
+
throw new BitmapFormatException(String.format("Invalid bit depth in uncompressed TGA: %d", bits));
|
|
64
|
+
}
|
|
65
|
+
// uncompressed image
|
|
66
|
+
for (int ptr = 0; ptr < pixels.length; ptr += bpp) {
|
|
67
|
+
// read bytes
|
|
68
|
+
f.read(read, 0, bpp);
|
|
69
|
+
for (int i = 0; i < bpp; i++) {
|
|
70
|
+
pixels[ptr + CHANNEL_INDEX[i]] = read[i];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else if (datatype == 10) {
|
|
74
|
+
if (bpp != 3 && bpp != 4) {
|
|
75
|
+
throw new BitmapFormatException(String.format("Invalid bit depth in run-length encoded TGA: %d", bits));
|
|
76
|
+
}
|
|
77
|
+
// RLE encoded image
|
|
78
|
+
for (int ptr = 0; ptr < pixels.length;) {
|
|
79
|
+
int rle = f.read();
|
|
80
|
+
int num = 1 + (rle & 0x7F);
|
|
81
|
+
if ((rle & 0x80) != 0) {
|
|
82
|
+
// rle packet - decode length and copy pixel
|
|
83
|
+
f.read(read, 0, bpp);
|
|
84
|
+
for (int j = 0; j < num; j++) {
|
|
85
|
+
for (int i = 0; i < bpp; i++) {
|
|
86
|
+
pixels[ptr + CHANNEL_INDEX[i]] = read[i];
|
|
87
|
+
}
|
|
88
|
+
ptr += bpp;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// raw packet - decode length and read pixels
|
|
92
|
+
for (int j = 0; j < num; j++) {
|
|
93
|
+
f.read(read, 0, bpp);
|
|
94
|
+
for (int i = 0; i < bpp; i++) {
|
|
95
|
+
pixels[ptr + CHANNEL_INDEX[i]] = read[i];
|
|
96
|
+
}
|
|
97
|
+
ptr += bpp;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
throw new BitmapFormatException(String.format("Unsupported TGA image type: %d", datatype));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!isLinear) {
|
|
106
|
+
// apply reverse correction
|
|
107
|
+
for (int ptr = 0; ptr < pixels.length; ptr += bpp) {
|
|
108
|
+
for (int i = 0; i < 3 && i < bpp; i++) {
|
|
109
|
+
pixels[ptr + i] = Color.NATIVE_SPACE.rgbToLinear(pixels[ptr + i]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// should image be flipped in Y?
|
|
115
|
+
if ((imgdscr & 32) == 32) {
|
|
116
|
+
for (int y = 0, pix_ptr = 0; y < (height / 2); y++) {
|
|
117
|
+
int bot_ptr = bpp * (height - y - 1) * width;
|
|
118
|
+
for (int x = 0; x < width; x++) {
|
|
119
|
+
for (int i = 0; i < bpp; i++) {
|
|
120
|
+
byte t = pixels[pix_ptr + i];
|
|
121
|
+
pixels[pix_ptr + i] = pixels[bot_ptr + i];
|
|
122
|
+
pixels[bot_ptr + i] = t;
|
|
123
|
+
}
|
|
124
|
+
pix_ptr += bpp;
|
|
125
|
+
bot_ptr += bpp;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
f.close();
|
|
131
|
+
switch (bpp) {
|
|
132
|
+
case 1:
|
|
133
|
+
return new BitmapG8(width, height, pixels);
|
|
134
|
+
case 3:
|
|
135
|
+
return new BitmapRGB8(width, height, pixels);
|
|
136
|
+
case 4:
|
|
137
|
+
return new BitmapRGBA8(width, height, pixels);
|
|
138
|
+
}
|
|
139
|
+
throw new BitmapFormatException("Inconsistent code in TGA reader");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
package org.sunflow.image.writers;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.io.RandomAccessFile;
|
|
5
|
+
import java.util.Arrays;
|
|
6
|
+
import java.util.zip.Deflater;
|
|
7
|
+
|
|
8
|
+
import org.sunflow.image.BitmapWriter;
|
|
9
|
+
import org.sunflow.image.Color;
|
|
10
|
+
import org.sunflow.system.ByteUtil;
|
|
11
|
+
import org.sunflow.system.UI;
|
|
12
|
+
import org.sunflow.system.UI.Module;
|
|
13
|
+
|
|
14
|
+
public class EXRBitmapWriter implements BitmapWriter {
|
|
15
|
+
|
|
16
|
+
private static final byte HALF = 1;
|
|
17
|
+
private static final byte FLOAT = 2;
|
|
18
|
+
private static final int HALF_SIZE = 2;
|
|
19
|
+
private static final int FLOAT_SIZE = 4;
|
|
20
|
+
private final static int OE_MAGIC = 20000630;
|
|
21
|
+
private final static int OE_EXR_VERSION = 2;
|
|
22
|
+
private final static int OE_TILED_FLAG = 0x00000200;
|
|
23
|
+
private static final int NO_COMPRESSION = 0;
|
|
24
|
+
private static final int RLE_COMPRESSION = 1;
|
|
25
|
+
private static final int ZIP_COMPRESSION = 3;
|
|
26
|
+
private static final int RLE_MIN_RUN = 3;
|
|
27
|
+
private static final int RLE_MAX_RUN = 127;
|
|
28
|
+
private String filename;
|
|
29
|
+
private RandomAccessFile file;
|
|
30
|
+
private long[][] tileOffsets;
|
|
31
|
+
private long tileOffsetsPosition;
|
|
32
|
+
private int tilesX;
|
|
33
|
+
private int tilesY;
|
|
34
|
+
private int tileSize;
|
|
35
|
+
private int compression;
|
|
36
|
+
private byte channelType;
|
|
37
|
+
private int channelSize;
|
|
38
|
+
private byte[] tmpbuf;
|
|
39
|
+
private byte[] comprbuf;
|
|
40
|
+
final String COMPRESSION = "compression";
|
|
41
|
+
|
|
42
|
+
public EXRBitmapWriter() {
|
|
43
|
+
// default settings
|
|
44
|
+
configure(COMPRESSION, "zip");
|
|
45
|
+
configure("channeltype", "half");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Override
|
|
49
|
+
public final void configure(final String option, String value) {
|
|
50
|
+
if (option.equals(COMPRESSION)) {
|
|
51
|
+
if (value.equals("none")) {
|
|
52
|
+
compression = NO_COMPRESSION;
|
|
53
|
+
} else if (value.equals("rle")) {
|
|
54
|
+
compression = RLE_COMPRESSION;
|
|
55
|
+
} else if (value.equals("zip")) {
|
|
56
|
+
compression = ZIP_COMPRESSION;
|
|
57
|
+
} else {
|
|
58
|
+
UI.printWarning(Module.IMG, "EXR - Compression type was not recognized - defaulting to zip");
|
|
59
|
+
compression = ZIP_COMPRESSION;
|
|
60
|
+
}
|
|
61
|
+
} else if (option.equals("channeltype")) {
|
|
62
|
+
if (value.equals("float")) {
|
|
63
|
+
channelType = FLOAT;
|
|
64
|
+
channelSize = FLOAT_SIZE;
|
|
65
|
+
} else if (value.equals("half")) {
|
|
66
|
+
channelType = HALF;
|
|
67
|
+
channelSize = HALF_SIZE;
|
|
68
|
+
} else {
|
|
69
|
+
UI.printWarning(Module.DISP, "EXR - Channel type was not recognized - defaulting to float");
|
|
70
|
+
channelType = FLOAT;
|
|
71
|
+
channelSize = FLOAT_SIZE;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Override
|
|
77
|
+
public void openFile(String filename) throws IOException {
|
|
78
|
+
this.filename = filename == null ? "output.exr" : filename;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Override
|
|
82
|
+
public void writeHeader(int width, int height, int tileSize) throws IOException, UnsupportedOperationException {
|
|
83
|
+
file = new RandomAccessFile(filename, "rw");
|
|
84
|
+
file.setLength(0);
|
|
85
|
+
if (tileSize <= 0) {
|
|
86
|
+
throw new UnsupportedOperationException("Can't use OpenEXR bitmap writer without buckets.");
|
|
87
|
+
}
|
|
88
|
+
writeRGBAHeader(width, height, tileSize);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Override
|
|
92
|
+
public void writeTile(int x, int y, int w, int h, Color[] color, float[] alpha) throws IOException {
|
|
93
|
+
int tx = x / tileSize;
|
|
94
|
+
int ty = y / tileSize;
|
|
95
|
+
writeEXRTile(tx, ty, w, h, color, alpha);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
public void closeFile() throws IOException {
|
|
100
|
+
writeTileOffsets();
|
|
101
|
+
file.close();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private void writeRGBAHeader(int w, int h, int tileSize) throws IOException {
|
|
105
|
+
byte[] chanOut = {0, channelType, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
|
|
106
|
+
0, 0, 0};
|
|
107
|
+
|
|
108
|
+
file.write(ByteUtil.get4Bytes(OE_MAGIC));
|
|
109
|
+
|
|
110
|
+
file.write(ByteUtil.get4Bytes(OE_EXR_VERSION | OE_TILED_FLAG));
|
|
111
|
+
|
|
112
|
+
file.write("channels".getBytes());
|
|
113
|
+
file.write(0);
|
|
114
|
+
file.write("chlist".getBytes());
|
|
115
|
+
file.write(0);
|
|
116
|
+
file.write(ByteUtil.get4Bytes(73));
|
|
117
|
+
file.write("R".getBytes());
|
|
118
|
+
file.write(chanOut);
|
|
119
|
+
file.write("G".getBytes());
|
|
120
|
+
file.write(chanOut);
|
|
121
|
+
file.write("B".getBytes());
|
|
122
|
+
file.write(chanOut);
|
|
123
|
+
file.write("A".getBytes());
|
|
124
|
+
file.write(chanOut);
|
|
125
|
+
file.write(0);
|
|
126
|
+
|
|
127
|
+
// compression
|
|
128
|
+
file.write(COMPRESSION.getBytes());
|
|
129
|
+
file.write(0);
|
|
130
|
+
file.write(COMPRESSION.getBytes());
|
|
131
|
+
file.write(0);
|
|
132
|
+
file.write(1);
|
|
133
|
+
file.write(ByteUtil.get4BytesInv(compression));
|
|
134
|
+
|
|
135
|
+
// datawindow =~ image size
|
|
136
|
+
file.write("dataWindow".getBytes());
|
|
137
|
+
file.write(0);
|
|
138
|
+
file.write("box2i".getBytes());
|
|
139
|
+
file.write(0);
|
|
140
|
+
file.write(ByteUtil.get4Bytes(0x10));
|
|
141
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
142
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
143
|
+
file.write(ByteUtil.get4Bytes(w - 1));
|
|
144
|
+
file.write(ByteUtil.get4Bytes(h - 1));
|
|
145
|
+
|
|
146
|
+
// dispwindow -> look at openexr.com for more info
|
|
147
|
+
file.write("displayWindow".getBytes());
|
|
148
|
+
file.write(0);
|
|
149
|
+
file.write("box2i".getBytes());
|
|
150
|
+
file.write(0);
|
|
151
|
+
file.write(ByteUtil.get4Bytes(0x10));
|
|
152
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
153
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
154
|
+
file.write(ByteUtil.get4Bytes(w - 1));
|
|
155
|
+
file.write(ByteUtil.get4Bytes(h - 1));
|
|
156
|
+
|
|
157
|
+
/*
|
|
158
|
+
* lines in increasing y order = 0 decreasing would be 1
|
|
159
|
+
*/
|
|
160
|
+
file.write("lineOrder".getBytes());
|
|
161
|
+
file.write(0);
|
|
162
|
+
file.write("lineOrder".getBytes());
|
|
163
|
+
file.write(0);
|
|
164
|
+
file.write(1);
|
|
165
|
+
file.write(ByteUtil.get4BytesInv(2));
|
|
166
|
+
|
|
167
|
+
file.write("pixelAspectRatio".getBytes());
|
|
168
|
+
file.write(0);
|
|
169
|
+
file.write("float".getBytes());
|
|
170
|
+
file.write(0);
|
|
171
|
+
file.write(ByteUtil.get4Bytes(4));
|
|
172
|
+
file.write(ByteUtil.get4Bytes(Float.floatToIntBits(1)));
|
|
173
|
+
|
|
174
|
+
// meaningless to a flat (2D) image
|
|
175
|
+
file.write("screenWindowCenter".getBytes());
|
|
176
|
+
file.write(0);
|
|
177
|
+
file.write("v2f".getBytes());
|
|
178
|
+
file.write(0);
|
|
179
|
+
file.write(ByteUtil.get4Bytes(8));
|
|
180
|
+
file.write(ByteUtil.get4Bytes(Float.floatToIntBits(0)));
|
|
181
|
+
file.write(ByteUtil.get4Bytes(Float.floatToIntBits(0)));
|
|
182
|
+
|
|
183
|
+
// meaningless to a flat (2D) image
|
|
184
|
+
file.write("screenWindowWidth".getBytes());
|
|
185
|
+
file.write(0);
|
|
186
|
+
file.write("float".getBytes());
|
|
187
|
+
file.write(0);
|
|
188
|
+
file.write(ByteUtil.get4Bytes(4));
|
|
189
|
+
file.write(ByteUtil.get4Bytes(Float.floatToIntBits(1)));
|
|
190
|
+
|
|
191
|
+
this.tileSize = tileSize;
|
|
192
|
+
|
|
193
|
+
tilesX = ((w + tileSize - 1) / tileSize);
|
|
194
|
+
tilesY = ((h + tileSize - 1) / tileSize);
|
|
195
|
+
|
|
196
|
+
/*
|
|
197
|
+
* twice the space for the compressing buffer, as for ex. the compressor
|
|
198
|
+
* can actually increase the size of the data :) If that happens though,
|
|
199
|
+
* it is not saved into the file, but discarded
|
|
200
|
+
*/
|
|
201
|
+
tmpbuf = new byte[tileSize * tileSize * channelSize * 4];
|
|
202
|
+
comprbuf = new byte[tileSize * tileSize * channelSize * 4 * 2];
|
|
203
|
+
|
|
204
|
+
tileOffsets = new long[tilesX][tilesY];
|
|
205
|
+
|
|
206
|
+
file.write("tiles".getBytes());
|
|
207
|
+
file.write(0);
|
|
208
|
+
file.write("tiledesc".getBytes());
|
|
209
|
+
file.write(0);
|
|
210
|
+
file.write(ByteUtil.get4Bytes(9));
|
|
211
|
+
|
|
212
|
+
file.write(ByteUtil.get4Bytes(tileSize));
|
|
213
|
+
file.write(ByteUtil.get4Bytes(tileSize));
|
|
214
|
+
|
|
215
|
+
// ONE_LEVEL tiles, ROUNDING_MODE = not important
|
|
216
|
+
file.write(0);
|
|
217
|
+
|
|
218
|
+
// an attribute with a name of 0 to end the list
|
|
219
|
+
file.write(0);
|
|
220
|
+
|
|
221
|
+
// save a pointer to where the tileOffsets are stored and write dummy
|
|
222
|
+
// fillers for now
|
|
223
|
+
tileOffsetsPosition = file.getFilePointer();
|
|
224
|
+
writeTileOffsets();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private void writeTileOffsets() throws IOException {
|
|
228
|
+
file.seek(tileOffsetsPosition);
|
|
229
|
+
for (int ty = 0; ty < tilesY; ty++) {
|
|
230
|
+
for (int tx = 0; tx < tilesX; tx++) {
|
|
231
|
+
file.write(ByteUtil.get8Bytes(tileOffsets[tx][ty]));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private synchronized void writeEXRTile(int tileX, int tileY, int w, int h, Color[] tile, float[] alpha) throws IOException {
|
|
237
|
+
byte[] rgb;
|
|
238
|
+
|
|
239
|
+
// setting comprSize to max integer so without compression things
|
|
240
|
+
// don't go awry
|
|
241
|
+
int pixptr = 0, writeSize = 0, comprSize = Integer.MAX_VALUE;
|
|
242
|
+
int tileRangeX = (tileSize < w) ? tileSize : w;
|
|
243
|
+
int tileRangeY = (tileSize < h) ? tileSize : h;
|
|
244
|
+
int channelBase = tileRangeX * channelSize;
|
|
245
|
+
|
|
246
|
+
// lets see if the alignment matches, you can comment this out if
|
|
247
|
+
// need be
|
|
248
|
+
if ((tileSize != tileRangeX) && (tileX == 0)) {
|
|
249
|
+
System.out.print(" bad X alignment ");
|
|
250
|
+
}
|
|
251
|
+
if ((tileSize != tileRangeY) && (tileY == 0)) {
|
|
252
|
+
System.out.print(" bad Y alignment ");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
tileOffsets[tileX][tileY] = file.getFilePointer();
|
|
256
|
+
|
|
257
|
+
// the tile header: tile's x&y coordinate, levels x&y coordinate and
|
|
258
|
+
// tilesize
|
|
259
|
+
file.write(ByteUtil.get4Bytes(tileX));
|
|
260
|
+
file.write(ByteUtil.get4Bytes(tileY));
|
|
261
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
262
|
+
file.write(ByteUtil.get4Bytes(0));
|
|
263
|
+
|
|
264
|
+
// just in case
|
|
265
|
+
Arrays.fill(tmpbuf, (byte) 0);
|
|
266
|
+
|
|
267
|
+
for (int ty = 0; ty < tileRangeY; ty++) {
|
|
268
|
+
for (int tx = 0; tx < tileRangeX; tx++) {
|
|
269
|
+
float[] rgbf = tile[tx + ty * tileRangeX].getRGB();
|
|
270
|
+
if (channelType == FLOAT) {
|
|
271
|
+
rgb = ByteUtil.get4Bytes(Float.floatToRawIntBits(alpha[tx + ty * tileRangeX]));
|
|
272
|
+
tmpbuf[pixptr + 0] = rgb[0];
|
|
273
|
+
tmpbuf[pixptr + 1] = rgb[1];
|
|
274
|
+
tmpbuf[pixptr + 2] = rgb[2];
|
|
275
|
+
tmpbuf[pixptr + 3] = rgb[3];
|
|
276
|
+
} else if (channelType == HALF) {
|
|
277
|
+
rgb = ByteUtil.get2Bytes(ByteUtil.floatToHalf(alpha[tx + ty * tileRangeX]));
|
|
278
|
+
tmpbuf[pixptr + 0] = rgb[0];
|
|
279
|
+
tmpbuf[pixptr + 1] = rgb[1];
|
|
280
|
+
}
|
|
281
|
+
for (int component = 1; component <= 3; component++) {
|
|
282
|
+
if (channelType == FLOAT) {
|
|
283
|
+
rgb = ByteUtil.get4Bytes(Float.floatToRawIntBits(rgbf[3 - component]));
|
|
284
|
+
tmpbuf[(channelBase * component) + pixptr + 0] = rgb[0];
|
|
285
|
+
tmpbuf[(channelBase * component) + pixptr + 1] = rgb[1];
|
|
286
|
+
tmpbuf[(channelBase * component) + pixptr + 2] = rgb[2];
|
|
287
|
+
tmpbuf[(channelBase * component) + pixptr + 3] = rgb[3];
|
|
288
|
+
} else if (channelType == HALF) {
|
|
289
|
+
rgb = ByteUtil.get2Bytes(ByteUtil.floatToHalf(rgbf[3 - component]));
|
|
290
|
+
tmpbuf[(channelBase * component) + pixptr + 0] = rgb[0];
|
|
291
|
+
tmpbuf[(channelBase * component) + pixptr + 1] = rgb[1];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
pixptr += channelSize;
|
|
295
|
+
}
|
|
296
|
+
pixptr += (tileRangeX * channelSize * 3);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
writeSize = tileRangeX * tileRangeY * channelSize * 4;
|
|
300
|
+
|
|
301
|
+
if (compression != NO_COMPRESSION) {
|
|
302
|
+
comprSize = compress(compression, tmpbuf, writeSize, comprbuf);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// lastly, write the size of the tile and the tile itself
|
|
306
|
+
// (compressed or not)
|
|
307
|
+
if (comprSize < writeSize) {
|
|
308
|
+
file.write(ByteUtil.get4Bytes(comprSize));
|
|
309
|
+
file.write(comprbuf, 0, comprSize);
|
|
310
|
+
} else {
|
|
311
|
+
file.write(ByteUtil.get4Bytes(writeSize));
|
|
312
|
+
file.write(tmpbuf, 0, writeSize);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private static int compress(int tp, byte[] in, int inSize, byte[] out) {
|
|
317
|
+
if (inSize == 0) {
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
int t1 = 0, t2 = (inSize + 1) / 2;
|
|
322
|
+
int inPtr = 0, ret;
|
|
323
|
+
byte[] tmp = new byte[inSize];
|
|
324
|
+
|
|
325
|
+
// zip and rle treat the data first, in the same way so I'm not
|
|
326
|
+
// repeating the code
|
|
327
|
+
if ((tp == ZIP_COMPRESSION) || (tp == RLE_COMPRESSION)) {
|
|
328
|
+
// reorder the pixel data ~ straight from ImfZipCompressor.cpp :)
|
|
329
|
+
while (true) {
|
|
330
|
+
if (inPtr < inSize) {
|
|
331
|
+
tmp[t1++] = in[inPtr++];
|
|
332
|
+
} else {
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (inPtr < inSize) {
|
|
337
|
+
tmp[t2++] = in[inPtr++];
|
|
338
|
+
} else {
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Predictor ~ straight from ImfZipCompressor.cpp :)
|
|
344
|
+
t1 = 1;
|
|
345
|
+
int p = tmp[t1 - 1];
|
|
346
|
+
while (t1 < inSize) {
|
|
347
|
+
int d = tmp[t1] - p + (128 + 256);
|
|
348
|
+
p = tmp[t1];
|
|
349
|
+
tmp[t1] = (byte) d;
|
|
350
|
+
t1++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// We'll just jump from here to the wanted compress/decompress stuff if
|
|
355
|
+
// need be
|
|
356
|
+
switch (tp) {
|
|
357
|
+
case ZIP_COMPRESSION:
|
|
358
|
+
Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
|
|
359
|
+
def.setInput(tmp, 0, inSize);
|
|
360
|
+
def.finish();
|
|
361
|
+
ret = def.deflate(out);
|
|
362
|
+
return ret;
|
|
363
|
+
case RLE_COMPRESSION:
|
|
364
|
+
return rleCompress(tmp, inSize, out);
|
|
365
|
+
default:
|
|
366
|
+
return -1;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private static int rleCompress(byte[] in, int inLen, byte[] out) {
|
|
371
|
+
int runStart = 0, runEnd = 1, outWrite = 0;
|
|
372
|
+
while (runStart < inLen) {
|
|
373
|
+
while (runEnd < inLen && in[runStart] == in[runEnd] && (runEnd - runStart - 1) < RLE_MAX_RUN) {
|
|
374
|
+
runEnd++;
|
|
375
|
+
}
|
|
376
|
+
if (runEnd - runStart >= RLE_MIN_RUN) {
|
|
377
|
+
// Compressable run
|
|
378
|
+
out[outWrite++] = (byte) ((runEnd - runStart) - 1);
|
|
379
|
+
out[outWrite++] = in[runStart];
|
|
380
|
+
runStart = runEnd;
|
|
381
|
+
} else {
|
|
382
|
+
// Uncompressable run
|
|
383
|
+
while (runEnd < inLen && (((runEnd + 1) >= inLen || in[runEnd] != in[runEnd + 1]) || ((runEnd + 2) >= inLen || in[runEnd + 1] != in[runEnd + 2])) && (runEnd - runStart) < RLE_MAX_RUN) {
|
|
384
|
+
runEnd++;
|
|
385
|
+
}
|
|
386
|
+
out[outWrite++] = (byte) (runStart - runEnd);
|
|
387
|
+
while (runStart < runEnd) {
|
|
388
|
+
out[outWrite++] = in[runStart++];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
runEnd++;
|
|
392
|
+
}
|
|
393
|
+
return outWrite;
|
|
394
|
+
}
|
|
395
|
+
}
|