
com.idrsolutions.image.jpeg.JpegEncoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
An Open Source JavaFX PDF Viewer
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2015 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* JpegEncoder.java
* ---------------
*/
package com.idrsolutions.image.jpeg;
import java.awt.image.*;
import java.io.*;
/**
* Class writes buffered images as Baseline JPEGs
*
* Here is an example of how the code can be used:-
*
*
* JpegEncoder encoder = new JpegEncoder();
* encoder.write(image_to_save, outputStream);
*
*
*/
public class JpegEncoder {
private static final int[] sampleFrequency = {1, 1, 1};
private static final int[] TableNumber = {0, 1, 1};
/**
*
*/
public JpegEncoder() {
}
/**
* writes image as baseline jpegs
*
* This method does not close the provided OutputStream after the write
* operation has completed; it is the responsibility of the caller to close
* the stream,
*
*
* @param image BufferedImage to write image
* @param out The stream to write to
* @throws IOException if the image wasn't written
*/
public void write(BufferedImage image, OutputStream out) throws IOException {
int imageH = image.getHeight();
int imageW = image.getWidth();
int nComp = 3;
JpegHuffman huffman = new JpegHuffman();
WriteHeaders(out, huffman, imageW, imageH, nComp);
int[] frequencyH = new int[nComp];
int[] frequencyW = new int[nComp];
int maxF = 1;
for (int i = 0; i < sampleFrequency.length; i++) {
maxF = Math.max(maxF, sampleFrequency[i]);
}
for (int i = 0; i < nComp; i++) {
double hh = Math.ceil(1.0 * imageH * sampleFrequency[i] / maxF);
double ww = Math.ceil(1.0 * imageW * sampleFrequency[i] / maxF);
frequencyH[i] = (int) ((hh % 8 != 0) ? (Math.floor(hh / 8.0) + 1) * 8 : hh);
frequencyW[i] = (int) ((ww % 8 != 0) ? (Math.floor(ww / 8.0) + 1) * 8 : ww);
}
int cc = 0, rr, gg, bb;
int fw = frequencyW[0];
int fh = frequencyH[0];
int imageDiff = frequencyW[0] - imageW;
int freqDim = fw * fh;
byte[] r = new byte[freqDim];
byte[] g = new byte[freqDim];
byte[] b = new byte[freqDim];
switch (image.getType()) {
case BufferedImage.TYPE_INT_RGB:
case BufferedImage.TYPE_INT_ARGB:
int[] pixInts = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
int a = 0;
for (int i = 0; i < imageH; i++) {
for (int j = 0; j < imageW; j++) {
byte[] bgr = intToBytes(pixInts[a]);
r[cc] = bgr[1];
g[cc] = bgr[2];
b[cc] = bgr[3];
a++;
cc++;
}
cc += imageDiff;
}
break;
case BufferedImage.TYPE_INT_BGR:
int[] pixBGR = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
int aa = 0;
for (int i = 0; i < imageH; i++) {
for (int j = 0; j < imageW; j++) {
byte[] bgr = intToBytes(pixBGR[aa]);
r[cc] = bgr[3];
g[cc] = bgr[2];
b[cc] = bgr[1];
aa++;
cc++;
}
cc += imageDiff;
}
break;
case BufferedImage.TYPE_3BYTE_BGR:
byte[] pByte = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
int pb = 0;
for (int i = 0; i < imageH; i++) {
for (int j = 0; j < imageW; j++) {
r[cc] = pByte[pb + 2];
g[cc] = pByte[pb + 1];
b[cc] = pByte[pb];
pb += 3;
cc++;
}
cc += imageDiff;
}
// System.out.println("cc "+cc);
break;
case BufferedImage.TYPE_4BYTE_ABGR:
byte[] pBytes = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
int pb4 = 0;
for (int i = 0; i < imageH; i++) {
for (int j = 0; j < imageW; j++) {
r[cc] = pBytes[pb4 + 3];
g[cc] = pBytes[pb4 + 2];
b[cc] = pBytes[pb4 + 1];
pb4 += 4;
cc++;
}
cc += imageDiff;
}
break;
default:
BufferedImage bImage = new BufferedImage(imageW, imageH, BufferedImage.TYPE_INT_RGB);
bImage.createGraphics().drawImage(image, 0, 0, null);
int[] pixInt = ((DataBufferInt) bImage.getRaster().getDataBuffer()).getData();
int ii = 0;
for (int i = 0; i < imageH; i++) {
for (int j = 0; j < imageW; j++) {
byte[] bgr = intToBytes(pixInt[ii]);
r[cc] = bgr[1];
g[cc] = bgr[2];
b[cc] = bgr[3];
ii++;
cc++;
}
cc += imageDiff;
}
break;
}
cc = 0;
int y, u, v;
int dim = fw * fh;
for (int i = 0; i < dim; i++) {
rr = r[cc] & 0xff;
gg = g[cc] & 0xff;
bb = b[cc] & 0xff;
y = (128 + 76 * rr + 150 * gg + 29 * bb) >> 8;
u = (128 + 127 * bb - 84 * gg - 43 * rr) >> 8;
v = (128 + 127 * rr - 106 * gg - 21 * bb) >> 8;
y -= 128;
r[cc] = (byte) y;
g[cc] = (byte) u;
b[cc] = (byte) v;
cc++;
}
Object[] YCBCR = new Object[3];
YCBCR[0] = r;
YCBCR[1] = g;
YCBCR[2] = b;
WriteCompressedData(out, huffman, YCBCR, fh, fw, nComp);
WriteEOI(out);
}
private static void WriteCompressedData(OutputStream out, JpegHuffman huffman, Object[] YCBCR, int fh, int fw, int nComp) throws IOException {
int[] arrayDCT = new int[64];
int yBlocks = fh / 8;
int xBlocks = fw / 8;
int lastDCvalue[] = new int[nComp];
byte[] compBytes;
int idx, pos, a, b, x, y;
for (y = 0; y < yBlocks; y++) {
for (x = 0; x < xBlocks; x++) {
pos = (x + y * fw) * 8;
for (int comp = 0; comp < nComp; comp++) {
compBytes = (byte[]) YCBCR[comp];
idx = 0;
int ayfw = pos;
for (a = 0; a < 8; a++) {
for (b = 0; b < 8; b++) {
arrayDCT[idx++] = compBytes[ayfw + b];
}
ayfw += fw;
}
DCT.FDCTQ(arrayDCT, TableNumber[comp]);
huffman.encodeBlock(out, arrayDCT, lastDCvalue[comp], TableNumber[comp]);
lastDCvalue[comp] = arrayDCT[0];
}
}
}
huffman.end(out);
}
private static void WriteEOI(OutputStream out) throws IOException {
byte[] EOI = {(byte) 0xFF, (byte) 0xD9};
out.write(EOI);
}
private static void WriteHeaders(OutputStream out, JpegHuffman huffman, int imageWidth, int imageHeight, int nComp) throws IOException {
int index, offset;
writeMarker(out, Markers.SOI);
byte JFIF[] = new byte[18];
JFIF[0] = (byte) 0xff;
JFIF[1] = (byte) 0xe0;
JFIF[2] = (byte) 0x00;
JFIF[3] = (byte) 0x10;
JFIF[4] = (byte) 0x4a;
JFIF[5] = (byte) 0x46;
JFIF[6] = (byte) 0x49;
JFIF[7] = (byte) 0x46;
JFIF[8] = (byte) 0x00;
JFIF[9] = (byte) 0x01;
JFIF[10] = (byte) 0x00;
JFIF[11] = (byte) 0x00;
JFIF[12] = (byte) 0x00;
JFIF[13] = (byte) 0x01;
JFIF[14] = (byte) 0x00;
JFIF[15] = (byte) 0x01;
JFIF[16] = (byte) 0x00;
JFIF[17] = (byte) 0x00;
out.write(JFIF);
byte DQT[] = new byte[134];
DQT[0] = (byte) 0xFF;
DQT[1] = (byte) 0xDB;
DQT[2] = (byte) 0x00;
DQT[3] = (byte) 0x84;
offset = 4;
for (int i = 0; i < 2; i++) {
byte tempArray[];
DQT[offset++] = (byte) (i);
if (i == 0) {
tempArray = JpegLUT.QL;
for (int j = 0; j < 64; j++) {
DQT[offset++] = tempArray[JpegLUT.ZIGZAGORDER[j]];
}
} else {
tempArray = JpegLUT.QC;
for (int j = 0; j < 64; j++) {
DQT[offset++] = tempArray[JpegLUT.ZIGZAGORDER[j]];
}
}
}
out.write(DQT);
// Start of Frame Header
// int sampleFactor [] = new int[]{1,2,2};
writeMarker(out, Markers.SOF0);
writeLength(out, 17);
byte SOF[] = new byte[15];
SOF[0] = (byte) 8;
SOF[1] = (byte) ((imageHeight >> 8) & 0xFF);
SOF[2] = (byte) ((imageHeight) & 0xFF);
SOF[3] = (byte) ((imageWidth >> 8) & 0xFF);
SOF[4] = (byte) ((imageWidth) & 0xFF);
SOF[5] = (byte) nComp;
index = 6;
for (int i = 0; i < nComp; i++) {
SOF[index++] = (byte) (i + 1);
SOF[index++] = (byte) ((sampleFrequency[i] << 4) + sampleFrequency[i]);
SOF[index++] = (byte) TableNumber[i];
}
out.write(SOF);
// The DHT Header
byte DHT1[], DHT2[], DHT3[], DHT4[];
int bytes, temp, oldindex, intermediateindex;
index = 4;
oldindex = 4;
DHT1 = new byte[17];
DHT4 = new byte[4];
DHT4[0] = (byte) 0xFF;
DHT4[1] = (byte) 0xC4;
for (int i = 0; i < 4; i++) {
bytes = 0;
DHT1[index++ - oldindex] = (byte) huffman.bitList.get(i)[0];
for (int j = 1; j < 17; j++) {
temp = huffman.bitList.get(i)[j];
DHT1[index++ - oldindex] = (byte) temp;
bytes += temp;
}
intermediateindex = index;
DHT2 = new byte[bytes];
for (int j = 0; j < bytes; j++) {
DHT2[index++ - intermediateindex] = (byte) huffman.valList.get(i)[j];
}
DHT3 = new byte[index];
System.arraycopy(DHT4, 0, DHT3, 0, oldindex);
System.arraycopy(DHT1, 0, DHT3, oldindex, 17);
System.arraycopy(DHT2, 0, DHT3, oldindex + 17, bytes);
DHT4 = DHT3;
oldindex = index;
}
DHT4[2] = (byte) (((index - 2) >> 8) & 0xFF);
DHT4[3] = (byte) ((index - 2) & 0xFF);
out.write(DHT4);
// Start of Scan Header
byte SOS[] = new byte[14];
SOS[0] = (byte) 0xFF;
SOS[1] = (byte) 0xDA;
SOS[2] = (byte) 0x00;
SOS[3] = (byte) 12;
SOS[4] = (byte) nComp;
index = 5;
for (int i = 0; i < SOS[4]; i++) {
SOS[index++] = (byte) (i + 1);
SOS[index++] = (byte) ((TableNumber[i] << 4) + TableNumber[i]);
}
SOS[index++] = (byte) 0;
SOS[index++] = (byte) 63;
SOS[index++] = (byte) 0;
out.write(SOS);
}
private static void writeMarker(OutputStream out, int marker) throws IOException {
out.write((byte) marker >> 8);
out.write((byte) marker);
}
private static void writeLength(OutputStream out, int length) throws IOException {
out.write((byte) length >> 8);
out.write((byte) length);
}
private static byte[] intToBytes(int value) {
return new byte[]{(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value};
}
public static int[] rgbToYCBCR(int r, int g, int b) {
int y = (int) (0.299f * r + 0.587f * g + 0.114f * b);
int cb = (int) (128 - 0.169f * r - 0.331f * g + 0.5f * b);
int cr = (int) (128 + 0.5f * r - 0.419f * g - 0.081f * b);
return new int[]{y, cb, cr};
}
// public static void main(String[] args) throws Exception {
//
// java.io.File f = new java.io.File("C:\\Users\\suda\\Desktop\\testimages\\map.jpg");
// FileInputStream fis = new FileInputStream(f);
// byte[] bb = new byte[(int) f.length()];
// fis.read(bb);
// BufferedImage ii = javax.imageio.ImageIO.read(f);
//
// long start = System.currentTimeMillis();
// javax.imageio.ImageIO.write(ii, "jpg", new java.io.File("C:\\Users\\suda\\Desktop\\testimages\\imageIOWrite" + System.currentTimeMillis() + ".jpg"));
// System.out.println((System.currentTimeMillis() - start));
//
// start = System.currentTimeMillis();
// JpegEncoder enc = new JpegEncoder();
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("C:\\Users\\suda\\Desktop\\testimages\\sudaWrite" + System.currentTimeMillis() + ".jpg")));
// enc.write(ii, bos);
// bos.close();
//
// System.out.println((System.currentTimeMillis() - start));
//
// }
}
//
© 2015 - 2025 Weber Informatics LLC | Privacy Policy