/* * Copyright © 2012 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.weatherapp.helpers; import javax.microedition.lcdui.Image; public class ImageHelper { private ImageHelper() { } public static Image scaleImage(Image original, float factor) { return scaleImage(original, (int) (factor * original.getWidth() + 0.5), (int) (factor * original.getHeight() + 0.5)); } public static Image scaleImage(Image original, int newWidth, int newHeight) { Image scaledImage = original; int originalWidth = original.getWidth(); if (originalWidth <= newWidth) { // Use pixel mixing for down scaling scaledImage = pixelMixing(original, newWidth, newHeight); } else if (originalWidth > newWidth) { // Use bilinear interpolation for up scaling scaledImage = bilinearInterpolation(original, newWidth, newHeight); } return scaledImage; } /** * Scales image simply by picking convenient pixels to the scaled image * @return Returns scaled image */ public static Image quickNDirty(Image original, int newWidth, int newHeight) { int[] rawInput = new int[original.getHeight() * original.getWidth()]; original.getRGB(rawInput, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight()); int[] rawOutput = new int[newWidth * newHeight]; int YD = (original.getHeight() / newHeight) * original.getWidth() - original.getWidth(); int YR = original.getHeight() % newHeight; int XD = original.getWidth() / newWidth; int XR = original.getWidth() % newWidth; int outOffset = 0; int inOffset = 0; for (int y = newHeight, YE = 0; y > 0; y--) { for (int x = newWidth, XE = 0; x > 0; x--) { rawOutput[outOffset++] = rawInput[inOffset]; inOffset += XD; XE += XR; if (XE >= newWidth) { XE -= newWidth; inOffset++; } } inOffset += YD; YE += YR; if (YE >= newHeight) { YE -= newHeight; inOffset += original.getWidth(); } } return Image.createRGBImage(rawOutput, newWidth, newHeight, true); } /** * Scales image by mixing pixels to the scaled image. Works best with down scaling. * @return Returns scaled image */ private static Image pixelMixing(Image original, int newWidth, int newHeight) { int[] rawInput = new int[original.getHeight() * original.getWidth()]; original.getRGB(rawInput, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight()); int[] rawOutput = new int[newWidth * newHeight]; int oWidth = original.getWidth(); int[] oX16 = new int[newWidth + 1]; for (int newX = 0; newX <= newWidth; newX++) { oX16[newX] = ((newX * oWidth) << 4) / newWidth; } int[] oXStartWidth = new int[newWidth]; int[] oXEndWidth = new int[newWidth]; for (int newX = 0; newX < newWidth; newX++) { oXStartWidth[newX] = 16 - (oX16[newX] % 16); oXEndWidth[newX] = oX16[newX + 1] % 16; } int oHeight = original.getHeight(); int[] oY16 = new int[newHeight + 1]; for (int newY = 0; newY <= newHeight; newY++) { oY16[newY] = ((newY * oHeight) << 4) / newHeight; } int oX16Start, oX16End, oY16Start, oY16End; int oYStartHeight, oYEndHeight; int oXStart, oXEnd, oYStart, oYEnd; int outArea, outColorArea, outAlpha, outRed, outGreen, outBlue; int areaHeight, areaWidth, area; int argb, a, r, g, b; for (int newY = 0; newY < newHeight; newY++) { oY16Start = oY16[newY]; oY16End = oY16[newY + 1]; oYStart = oY16Start >>> 4; oYEnd = oY16End >>> 4; oYStartHeight = 16 - (oY16Start % 16); oYEndHeight = oY16End % 16; for (int newX = 0; newX < newWidth; newX++) { oX16Start = oX16[newX]; oX16End = oX16[newX + 1]; oXStart = oX16Start >>> 4; oXEnd = oX16End >>> 4; outArea = 0; outColorArea = 0; outAlpha = 0; outRed = 0; outGreen = 0; outBlue = 0; for (int j = oYStart; j <= oYEnd; j++) { areaHeight = 16; if (oYStart == oYEnd) { areaHeight = oY16End - oY16Start; } else if (j == oYStart) { areaHeight = oYStartHeight; } else if (j == oYEnd) { areaHeight = oYEndHeight; } if (areaHeight == 0) { continue; } for (int i = oXStart; i <= oXEnd; i++) { areaWidth = 16; if (oXStart == oXEnd) { areaWidth = oX16End - oX16Start; } else if (i == oXStart) { areaWidth = oXStartWidth[newX]; } else if (i == oXEnd) { areaWidth = oXEndWidth[newX]; } if (areaWidth == 0) { continue; } area = areaWidth * areaHeight; outArea += area; argb = rawInput[i + j * original.getWidth()]; a = (argb >>> 24); if (a == 0) { continue; } area = a * area; outColorArea += area; r = (argb & 0x00ff0000) >>> 16; g = (argb & 0x0000ff00) >>> 8; b = argb & 0x000000ff; outRed += area * r; outGreen += area * g; outBlue += area * b; } } if (outColorArea > 0) { outAlpha = outColorArea / outArea; outRed = outRed / outColorArea; outGreen = outGreen / outColorArea; outBlue = outBlue / outColorArea; } rawOutput[newX + newY * newWidth] = (outAlpha << 24) | (outRed << 16) | (outGreen << 8) | outBlue; } } return Image.createRGBImage(rawOutput, newWidth, newHeight, true); } /** * Scales image using bilinear interpolation. Best suited for up scaling. * @return Returns scaled image */ private static Image bilinearInterpolation(Image original, int newWidth, int newHeight) { int[] rawInput = new int[original.getHeight() * original.getWidth()]; original.getRGB(rawInput, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight()); int[] rawOutput = new int[newWidth * newHeight]; int oWidth = original.getWidth(); int[] oX16 = new int[newWidth]; int max = (oWidth - 1) << 4; for (int newX = 0; newX < newWidth; newX++) { oX16[newX] = ((((newX << 1) + 1) * oWidth) << 3) / newWidth - 8; if (oX16[newX] < 0) { oX16[newX] = 0; } else if (oX16[newX] > max) { oX16[newX] = max; } } int oHeight = original.getHeight(); int[] oY16 = new int[newHeight]; max = (oHeight - 1) << 4; for (int newY = 0; newY < newHeight; newY++) { oY16[newY] = ((((newY << 1) + 1) * oHeight) << 3) / newHeight - 8; if (oY16[newY] < 0) { oY16[newY] = 0; } else if (oY16[newY] > max) { oY16[newY] = max; } } int[] oX = new int[2]; int[] oY = new int[2]; int[] wX = new int[2]; int[] wY = new int[2]; int outWeight, outColorWeight, outAlpha, outRed, outGreen, outBlue; int w, argb, a, r, g, b; for (int newY = 0; newY < newHeight; newY++) { oY[0] = oY16[newY] >>> 4; wY[1] = oY16[newY] & 0x0000000f; wY[0] = 16 - wY[1]; oY[1] = wY[1] == 0 ? oY[0] : oY[0] + 1; for (int newX = 0; newX < newWidth; newX++) { oX[0] = oX16[newX] >>> 4; wX[1] = oX16[newX] & 0x0000000f; wX[0] = 16 - wX[1]; oX[1] = wX[1] == 0 ? oX[0] : oX[0] + 1; outWeight = 0; outColorWeight = 0; outAlpha = 0; outRed = 0; outGreen = 0; outBlue = 0; for (int j = 0; j < 2; j++) { for (int i = 0; i < 2; i++) { if (wY[j] == 0 || wX[i] == 0) { continue; } w = wX[i] * wY[j]; outWeight += w; argb = rawInput[oX[i] + oY[j] * original.getWidth()]; a = (argb >>> 24); if (a == 0) { continue; } w = a * w; outColorWeight += w; r = (argb & 0x00ff0000) >>> 16; g = (argb & 0x0000ff00) >>> 8; b = argb & 0x000000ff; outRed += w * r; outGreen += w * g; outBlue += w * b; } } if (outColorWeight > 0) { outAlpha = outColorWeight / outWeight; outRed = outRed / outColorWeight; outGreen = outGreen / outColorWeight; outBlue = outBlue / outColorWeight; } rawOutput[newX + newY * newWidth] = (outAlpha << 24) | (outRed << 16) | (outGreen << 8) | outBlue; } } return Image.createRGBImage(rawOutput, newWidth, newHeight, true); } }