1 /*
2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
557 * @param sm The stream metadata or {@code null} if
558 * {@code writeHeader} is {@code false}.
559 * @param iioimage The image and image metadata.
560 * @param p The write parameters.
561 *
562 * @throws IllegalArgumentException if the number of bands is not 1.
563 * @throws IllegalArgumentException if the number of bits per sample is
564 * greater than 8.
565 * @throws IllegalArgumentException if the color component size is
566 * greater than 8.
567 * @throws IllegalArgumentException if {@code writeHeader} is
568 * {@code true} and {@code sm} is {@code null}.
569 * @throws IllegalArgumentException if {@code writeHeader} is
570 * {@code false} and a sequence is not being written.
571 */
572 private void write(boolean writeHeader,
573 boolean writeTrailer,
574 IIOMetadata sm,
575 IIOImage iioimage,
576 ImageWriteParam p) throws IOException {
577 clearAbortRequest();
578
579 RenderedImage image = iioimage.getRenderedImage();
580
581 // Check for ability to encode image.
582 if (needToCreateIndex(image)) {
583 image = PaletteBuilder.createIndexedImage(image);
584 iioimage.setRenderedImage(image);
585 }
586
587 ColorModel colorModel = image.getColorModel();
588 SampleModel sampleModel = image.getSampleModel();
589
590 // Determine source region and destination dimensions.
591 Rectangle sourceBounds = new Rectangle(image.getMinX(),
592 image.getMinY(),
593 image.getWidth(),
594 image.getHeight());
595 Dimension destSize = new Dimension();
596 computeRegions(sourceBounds, destSize, p);
597
812 // Write image data
813 writeRasterData(image, sourceBounds, destSize,
814 param, imageMetadata.interlaceFlag);
815 }
816
817 private void writeRows(RenderedImage image, LZWCompressor compressor,
818 int sx, int sdx, int sy, int sdy, int sw,
819 int dy, int ddy, int dw, int dh,
820 int numRowsWritten, int progressReportRowPeriod)
821 throws IOException {
822 if (DEBUG) System.out.println("Writing unoptimized");
823
824 int[] sbuf = new int[sw];
825 byte[] dbuf = new byte[dw];
826
827 Raster raster =
828 image.getNumXTiles() == 1 && image.getNumYTiles() == 1 ?
829 image.getTile(0, 0) : image.getData();
830 for (int y = dy; y < dh; y += ddy) {
831 if (numRowsWritten % progressReportRowPeriod == 0) {
832 if (abortRequested()) {
833 processWriteAborted();
834 return;
835 }
836 processImageProgress((numRowsWritten*100.0F)/dh);
837 }
838
839 raster.getSamples(sx, sy, sw, 1, 0, sbuf);
840 for (int i = 0, j = 0; i < dw; i++, j += sdx) {
841 dbuf[i] = (byte)sbuf[j];
842 }
843 compressor.compress(dbuf, 0, dw);
844 numRowsWritten++;
845 sy += sdy;
846 }
847 }
848
849 private void writeRowsOpt(byte[] data, int offset, int lineStride,
850 LZWCompressor compressor,
851 int dy, int ddy, int dw, int dh,
852 int numRowsWritten, int progressReportRowPeriod)
853 throws IOException {
854 if (DEBUG) System.out.println("Writing optimized");
855
856 offset += dy*lineStride;
857 lineStride *= ddy;
858 for (int y = dy; y < dh; y += ddy) {
859 if (numRowsWritten % progressReportRowPeriod == 0) {
860 if (abortRequested()) {
861 processWriteAborted();
862 return;
863 }
864 processImageProgress((numRowsWritten*100.0F)/dh);
865 }
866
867 compressor.compress(data, offset, dw);
868 numRowsWritten++;
869 offset += lineStride;
870 }
871 }
872
873 private void writeRasterData(RenderedImage image,
874 Rectangle sourceBounds,
875 Dimension destSize,
876 ImageWriteParam param,
877 boolean interlaceFlag) throws IOException {
878
879 int sourceXOffset = sourceBounds.x;
880 int sourceYOffset = sourceBounds.y;
881 int sourceWidth = sourceBounds.width;
882 int sourceHeight = sourceBounds.height;
883
884 int destWidth = destSize.width;
907 new LZWCompressor(stream, initCodeSize, false);
908
909 /* At this moment we know that input image is indexed image.
910 * We can directly copy data iff:
911 * - no subsampling required (periodX = 1, periodY = 0)
912 * - we can access data directly (image is non-tiled,
913 * i.e. image data are in single block)
914 * - we can calculate offset in data buffer (next 3 lines)
915 */
916 boolean isOptimizedCase =
917 periodX == 1 && periodY == 1 &&
918 image.getNumXTiles() == 1 && image.getNumYTiles() == 1 &&
919 sampleModel instanceof ComponentSampleModel &&
920 image.getTile(0, 0) instanceof ByteComponentRaster &&
921 image.getTile(0, 0).getDataBuffer() instanceof DataBufferByte;
922
923 int numRowsWritten = 0;
924
925 int progressReportRowPeriod = Math.max(destHeight/20, 1);
926
927 processImageStarted(imageIndex);
928
929 if (interlaceFlag) {
930 if (DEBUG) System.out.println("Writing interlaced");
931
932 if (isOptimizedCase) {
933 ByteComponentRaster tile =
934 (ByteComponentRaster)image.getTile(0, 0);
935 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
936 ComponentSampleModel csm =
937 (ComponentSampleModel)tile.getSampleModel();
938 int offset = csm.getOffset(sourceXOffset, sourceYOffset, 0);
939 // take into account the raster data offset
940 offset += tile.getDataOffset(0);
941 int lineStride = csm.getScanlineStride();
942
943 writeRowsOpt(data, offset, lineStride, compressor,
944 0, 8, destWidth, destHeight,
945 numRowsWritten, progressReportRowPeriod);
946
947 if (abortRequested()) {
956
957 if (abortRequested()) {
958 return;
959 }
960
961 numRowsWritten += (destHeight - 4)/8;
962
963 writeRowsOpt(data, offset, lineStride, compressor,
964 2, 4, destWidth, destHeight,
965 numRowsWritten, progressReportRowPeriod);
966
967 if (abortRequested()) {
968 return;
969 }
970
971 numRowsWritten += (destHeight - 2)/4;
972
973 writeRowsOpt(data, offset, lineStride, compressor,
974 1, 2, destWidth, destHeight,
975 numRowsWritten, progressReportRowPeriod);
976 } else {
977 writeRows(image, compressor,
978 sourceXOffset, periodX,
979 sourceYOffset, 8*periodY,
980 sourceWidth,
981 0, 8, destWidth, destHeight,
982 numRowsWritten, progressReportRowPeriod);
983
984 if (abortRequested()) {
985 return;
986 }
987
988 numRowsWritten += destHeight/8;
989
990 writeRows(image, compressor, sourceXOffset, periodX,
991 sourceYOffset + 4*periodY, 8*periodY,
992 sourceWidth,
993 4, 8, destWidth, destHeight,
994 numRowsWritten, progressReportRowPeriod);
995
999
1000 numRowsWritten += (destHeight - 4)/8;
1001
1002 writeRows(image, compressor, sourceXOffset, periodX,
1003 sourceYOffset + 2*periodY, 4*periodY,
1004 sourceWidth,
1005 2, 4, destWidth, destHeight,
1006 numRowsWritten, progressReportRowPeriod);
1007
1008 if (abortRequested()) {
1009 return;
1010 }
1011
1012 numRowsWritten += (destHeight - 2)/4;
1013
1014 writeRows(image, compressor, sourceXOffset, periodX,
1015 sourceYOffset + periodY, 2*periodY,
1016 sourceWidth,
1017 1, 2, destWidth, destHeight,
1018 numRowsWritten, progressReportRowPeriod);
1019 }
1020 } else {
1021 if (DEBUG) System.out.println("Writing non-interlaced");
1022
1023 if (isOptimizedCase) {
1024 Raster tile = image.getTile(0, 0);
1025 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
1026 ComponentSampleModel csm =
1027 (ComponentSampleModel)tile.getSampleModel();
1028 int offset = csm.getOffset(sourceXOffset, sourceYOffset, 0);
1029 int lineStride = csm.getScanlineStride();
1030
1031 writeRowsOpt(data, offset, lineStride, compressor,
1032 0, 1, destWidth, destHeight,
1033 numRowsWritten, progressReportRowPeriod);
1034 } else {
1035 writeRows(image, compressor,
1036 sourceXOffset, periodX,
1037 sourceYOffset, periodY,
1038 sourceWidth,
1039 0, 1, destWidth, destHeight,
1040 numRowsWritten, progressReportRowPeriod);
1041 }
1042 }
1043
1044 if (abortRequested()) {
1045 return;
1046 }
1047
1048 processImageProgress(100.0F);
1049
1050 compressor.flush();
1051
1052 stream.write(0x00);
1053
1054 processImageComplete();
1055 }
1056
1057 private void writeHeader(String version,
1058 int logicalScreenWidth,
1059 int logicalScreenHeight,
1060 int colorResolution,
1061 int pixelAspectRatio,
1062 int backgroundColorIndex,
1063 boolean sortFlag,
1064 int bitsPerPixel,
1065 byte[] globalColorTable) throws IOException {
1066 try {
1067 // Signature
1068 stream.writeBytes("GIF"+version);
|
1 /*
2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
557 * @param sm The stream metadata or {@code null} if
558 * {@code writeHeader} is {@code false}.
559 * @param iioimage The image and image metadata.
560 * @param p The write parameters.
561 *
562 * @throws IllegalArgumentException if the number of bands is not 1.
563 * @throws IllegalArgumentException if the number of bits per sample is
564 * greater than 8.
565 * @throws IllegalArgumentException if the color component size is
566 * greater than 8.
567 * @throws IllegalArgumentException if {@code writeHeader} is
568 * {@code true} and {@code sm} is {@code null}.
569 * @throws IllegalArgumentException if {@code writeHeader} is
570 * {@code false} and a sequence is not being written.
571 */
572 private void write(boolean writeHeader,
573 boolean writeTrailer,
574 IIOMetadata sm,
575 IIOImage iioimage,
576 ImageWriteParam p) throws IOException {
577
578 RenderedImage image = iioimage.getRenderedImage();
579
580 // Check for ability to encode image.
581 if (needToCreateIndex(image)) {
582 image = PaletteBuilder.createIndexedImage(image);
583 iioimage.setRenderedImage(image);
584 }
585
586 ColorModel colorModel = image.getColorModel();
587 SampleModel sampleModel = image.getSampleModel();
588
589 // Determine source region and destination dimensions.
590 Rectangle sourceBounds = new Rectangle(image.getMinX(),
591 image.getMinY(),
592 image.getWidth(),
593 image.getHeight());
594 Dimension destSize = new Dimension();
595 computeRegions(sourceBounds, destSize, p);
596
811 // Write image data
812 writeRasterData(image, sourceBounds, destSize,
813 param, imageMetadata.interlaceFlag);
814 }
815
816 private void writeRows(RenderedImage image, LZWCompressor compressor,
817 int sx, int sdx, int sy, int sdy, int sw,
818 int dy, int ddy, int dw, int dh,
819 int numRowsWritten, int progressReportRowPeriod)
820 throws IOException {
821 if (DEBUG) System.out.println("Writing unoptimized");
822
823 int[] sbuf = new int[sw];
824 byte[] dbuf = new byte[dw];
825
826 Raster raster =
827 image.getNumXTiles() == 1 && image.getNumYTiles() == 1 ?
828 image.getTile(0, 0) : image.getData();
829 for (int y = dy; y < dh; y += ddy) {
830 if (numRowsWritten % progressReportRowPeriod == 0) {
831 processImageProgress((numRowsWritten*100.0F)/dh);
832 if (abortRequested()) {
833 processWriteAborted();
834 return;
835 }
836 }
837
838 raster.getSamples(sx, sy, sw, 1, 0, sbuf);
839 for (int i = 0, j = 0; i < dw; i++, j += sdx) {
840 dbuf[i] = (byte)sbuf[j];
841 }
842 compressor.compress(dbuf, 0, dw);
843 numRowsWritten++;
844 sy += sdy;
845 }
846 }
847
848 private void writeRowsOpt(byte[] data, int offset, int lineStride,
849 LZWCompressor compressor,
850 int dy, int ddy, int dw, int dh,
851 int numRowsWritten, int progressReportRowPeriod)
852 throws IOException {
853 if (DEBUG) System.out.println("Writing optimized");
854
855 offset += dy*lineStride;
856 lineStride *= ddy;
857 for (int y = dy; y < dh; y += ddy) {
858 if (numRowsWritten % progressReportRowPeriod == 0) {
859 processImageProgress((numRowsWritten*100.0F)/dh);
860 if (abortRequested()) {
861 processWriteAborted();
862 return;
863 }
864 }
865
866 compressor.compress(data, offset, dw);
867 numRowsWritten++;
868 offset += lineStride;
869 }
870 }
871
872 private void writeRasterData(RenderedImage image,
873 Rectangle sourceBounds,
874 Dimension destSize,
875 ImageWriteParam param,
876 boolean interlaceFlag) throws IOException {
877
878 int sourceXOffset = sourceBounds.x;
879 int sourceYOffset = sourceBounds.y;
880 int sourceWidth = sourceBounds.width;
881 int sourceHeight = sourceBounds.height;
882
883 int destWidth = destSize.width;
906 new LZWCompressor(stream, initCodeSize, false);
907
908 /* At this moment we know that input image is indexed image.
909 * We can directly copy data iff:
910 * - no subsampling required (periodX = 1, periodY = 0)
911 * - we can access data directly (image is non-tiled,
912 * i.e. image data are in single block)
913 * - we can calculate offset in data buffer (next 3 lines)
914 */
915 boolean isOptimizedCase =
916 periodX == 1 && periodY == 1 &&
917 image.getNumXTiles() == 1 && image.getNumYTiles() == 1 &&
918 sampleModel instanceof ComponentSampleModel &&
919 image.getTile(0, 0) instanceof ByteComponentRaster &&
920 image.getTile(0, 0).getDataBuffer() instanceof DataBufferByte;
921
922 int numRowsWritten = 0;
923
924 int progressReportRowPeriod = Math.max(destHeight/20, 1);
925
926 clearAbortRequest();
927 processImageStarted(imageIndex);
928 if (abortRequested()) {
929 processWriteAborted();
930 return;
931 }
932
933 if (interlaceFlag) {
934 if (DEBUG) System.out.println("Writing interlaced");
935
936 if (isOptimizedCase) {
937 ByteComponentRaster tile =
938 (ByteComponentRaster)image.getTile(0, 0);
939 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
940 ComponentSampleModel csm =
941 (ComponentSampleModel)tile.getSampleModel();
942 int offset = csm.getOffset(sourceXOffset, sourceYOffset, 0);
943 // take into account the raster data offset
944 offset += tile.getDataOffset(0);
945 int lineStride = csm.getScanlineStride();
946
947 writeRowsOpt(data, offset, lineStride, compressor,
948 0, 8, destWidth, destHeight,
949 numRowsWritten, progressReportRowPeriod);
950
951 if (abortRequested()) {
960
961 if (abortRequested()) {
962 return;
963 }
964
965 numRowsWritten += (destHeight - 4)/8;
966
967 writeRowsOpt(data, offset, lineStride, compressor,
968 2, 4, destWidth, destHeight,
969 numRowsWritten, progressReportRowPeriod);
970
971 if (abortRequested()) {
972 return;
973 }
974
975 numRowsWritten += (destHeight - 2)/4;
976
977 writeRowsOpt(data, offset, lineStride, compressor,
978 1, 2, destWidth, destHeight,
979 numRowsWritten, progressReportRowPeriod);
980 if (abortRequested()) {
981 return;
982 }
983 } else {
984 writeRows(image, compressor,
985 sourceXOffset, periodX,
986 sourceYOffset, 8*periodY,
987 sourceWidth,
988 0, 8, destWidth, destHeight,
989 numRowsWritten, progressReportRowPeriod);
990
991 if (abortRequested()) {
992 return;
993 }
994
995 numRowsWritten += destHeight/8;
996
997 writeRows(image, compressor, sourceXOffset, periodX,
998 sourceYOffset + 4*periodY, 8*periodY,
999 sourceWidth,
1000 4, 8, destWidth, destHeight,
1001 numRowsWritten, progressReportRowPeriod);
1002
1006
1007 numRowsWritten += (destHeight - 4)/8;
1008
1009 writeRows(image, compressor, sourceXOffset, periodX,
1010 sourceYOffset + 2*periodY, 4*periodY,
1011 sourceWidth,
1012 2, 4, destWidth, destHeight,
1013 numRowsWritten, progressReportRowPeriod);
1014
1015 if (abortRequested()) {
1016 return;
1017 }
1018
1019 numRowsWritten += (destHeight - 2)/4;
1020
1021 writeRows(image, compressor, sourceXOffset, periodX,
1022 sourceYOffset + periodY, 2*periodY,
1023 sourceWidth,
1024 1, 2, destWidth, destHeight,
1025 numRowsWritten, progressReportRowPeriod);
1026 if (abortRequested()) {
1027 return;
1028 }
1029 }
1030 } else {
1031 if (DEBUG) System.out.println("Writing non-interlaced");
1032
1033 if (isOptimizedCase) {
1034 Raster tile = image.getTile(0, 0);
1035 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
1036 ComponentSampleModel csm =
1037 (ComponentSampleModel)tile.getSampleModel();
1038 int offset = csm.getOffset(sourceXOffset, sourceYOffset, 0);
1039 int lineStride = csm.getScanlineStride();
1040
1041 writeRowsOpt(data, offset, lineStride, compressor,
1042 0, 1, destWidth, destHeight,
1043 numRowsWritten, progressReportRowPeriod);
1044 if (abortRequested()) {
1045 return;
1046 }
1047 } else {
1048 writeRows(image, compressor,
1049 sourceXOffset, periodX,
1050 sourceYOffset, periodY,
1051 sourceWidth,
1052 0, 1, destWidth, destHeight,
1053 numRowsWritten, progressReportRowPeriod);
1054 if (abortRequested()) {
1055 return;
1056 }
1057 }
1058 }
1059
1060 compressor.flush();
1061
1062 stream.write(0x00);
1063
1064 processImageComplete();
1065 }
1066
1067 private void writeHeader(String version,
1068 int logicalScreenWidth,
1069 int logicalScreenHeight,
1070 int colorResolution,
1071 int pixelAspectRatio,
1072 int backgroundColorIndex,
1073 boolean sortFlag,
1074 int bitsPerPixel,
1075 byte[] globalColorTable) throws IOException {
1076 try {
1077 // Signature
1078 stream.writeBytes("GIF"+version);
|