/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * 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; * version 2.1 of the License. * * 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. */ package org.geotoolkit.display3d.utils; import org.geotoolkit.math.XMath; import org.jcodec.common.model.Picture; import org.jcodec.scale.Transform; /** * From the RgbToYuv420 Transform of JCodec Project * * @author Thomas Rouby (Geomatys) * */ public class TransformRGBtoYUV420 implements Transform { private final int upShift; private final int downShift; private final int downShiftChr; public TransformRGBtoYUV420(int upShift, int downShift) { this.upShift = upShift; this.downShift = downShift; // The +2 is to create an average of 4 pixels with chr transform this.downShiftChr = downShift + 2; } /** * Transform a Picture object with one channel RGB to a Picture with three channel YUV on format YUV420 * Format YUV420 * Channel 1 is int[width*height] for Y values * Channel 2 is int[width/2 * height/2] for U * Channel 3 is int[width/2 * height/2] for V * * each square of 2x2 Y correspond to a unique UV value * * This transform try to rescale src picture on dst size with linear method. * * @param src * @param dst must has a multiple of two for width and height. If not, last pixel forget. */ public void transform(Picture src, Picture dst) { final int[] srcData = src.getData()[0]; final int[][] dstData = dst.getData(); final int srcWidth = src.getWidth(); final int srcHeight = src.getHeight(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); for (int h=0; h<dstHeight-1; h+=2) { final int srcH0 = (int)(((double)h/(double)dstHeight)*srcHeight); final int srcH1 = (int)(((double)(h+1)/(double)dstHeight)*srcHeight); for (int w=0; w<dstWidth-1; w+=2) { final int srcW0 = (int)(((double)w/(double)dstWidth)*srcWidth); final int srcW1 = (int)(((double)(w+1)/(double)dstWidth)*srcWidth); // Compute array index for the pixels conversion final int chr = ind(w >> 1, h >> 1, dstWidth >> 1); final int ind00 = ind(srcW0, srcH0, srcWidth) * 3; final int luma00 = ind(w, h, dstWidth); final int ind01 = ind(srcW0, srcH1, srcWidth) * 3; final int luma01 = ind(w, h+1, dstWidth); final int ind10 = ind(srcW1, srcH0, srcWidth) * 3; final int luma10 = ind(w+1, h, dstWidth); final int ind11 = ind(srcW1, srcH1, srcWidth) * 3; final int luma11 = ind(w+1, h+1, dstWidth); rgb2yuv(srcData[ind00], srcData[ind00+1], srcData[ind00+2], dstData[0], luma00, dstData[1], chr, dstData[2], chr); dstData[0][luma00] = shiftLuma(dstData[0][luma00]); rgb2yuv(srcData[ind01], srcData[ind01+1], srcData[ind01+2], dstData[0], luma01, dstData[1], chr, dstData[2], chr); dstData[0][luma01] = shiftLuma(dstData[0][luma01]); rgb2yuv(srcData[ind10], srcData[ind10+1], srcData[ind10+2], dstData[0], luma10, dstData[1], chr, dstData[2], chr); dstData[0][luma10] = shiftLuma(dstData[0][luma10]); rgb2yuv(srcData[ind11], srcData[ind11+1], srcData[ind11+2], dstData[0], luma11, dstData[1], chr, dstData[2], chr); dstData[0][luma11] = shiftLuma(dstData[0][luma11]); dstData[1][chr] = shiftChroma(dstData[1][chr]); dstData[2][chr] = shiftChroma(dstData[2][chr]); } } } private int ind(int w, int h, int width){ return w + h*width; } private int shiftLuma(int luma) { return (luma << upShift) >> downShift; } private int shiftChroma(int chr) { return (chr << upShift) >> downShiftChr; } public static final void rgb2yuv(int r, int g, int b, int[] Y, int offY, int[] U, int offU, int[] V, int offV) { int y = 66 * r + 129 * g + 25 * b; int u = -38 * r - 74 * g + 112 * b; int v = 112 * r - 94 * g - 18 * b; y = (y + 128) >> 8; u = (u + 128) >> 8; v = (v + 128) >> 8; Y[offY] = XMath.clamp(y + 16, 0, 255); U[offU] += XMath.clamp(u + 128, 0, 255); V[offV] += XMath.clamp(v + 128, 0, 255); } }