001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019/* 020* 021 022Licensed under the Apache License, Version 2.0 (the "License"); 023you may not use this file except in compliance with the License. 024You may obtain a copy of the License at 025 026 http://www.apache.org/licenses/LICENSE-2.0 027 028Unless required by applicable law or agreed to in writing, software 029distributed under the License is distributed on an "AS IS" BASIS, 030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 031See the License for the specific language governing permissions and 032limitations under the License. 033*/ 034 035package lucee.runtime.img.filter;import java.awt.Color; 036import java.util.Random; 037 038/** 039 * Some more useful math functions for image processing. 040 * These are becoming obsolete as we move to Java2D. Use MiscComposite instead. 041 */ 042public class PixelUtils { 043 044 public final static int REPLACE = 0; 045 public final static int NORMAL = 1; 046 public final static int MIN = 2; 047 public final static int MAX = 3; 048 public final static int ADD = 4; 049 public final static int SUBTRACT = 5; 050 public final static int DIFFERENCE = 6; 051 public final static int MULTIPLY = 7; 052 public final static int HUE = 8; 053 public final static int SATURATION = 9; 054 public final static int VALUE = 10; 055 public final static int COLOR = 11; 056 public final static int SCREEN = 12; 057 public final static int AVERAGE = 13; 058 public final static int OVERLAY = 14; 059 public final static int CLEAR = 15; 060 public final static int EXCHANGE = 16; 061 public final static int DISSOLVE = 17; 062 public final static int DST_IN = 18; 063 public final static int ALPHA = 19; 064 public final static int ALPHA_TO_GRAY = 20; 065 066 private static Random randomGenerator = new Random(); 067 068 /** 069 * Clamp a value to the range 0..255 070 */ 071 public static int clamp(int c) { 072 if (c < 0) 073 return 0; 074 if (c > 255) 075 return 255; 076 return c; 077 } 078 079 public static int interpolate(int v1, int v2, float f) { 080 return clamp((int)(v1+f*(v2-v1))); 081 } 082 083 public static int brightness(int rgb) { 084 int r = (rgb >> 16) & 0xff; 085 int g = (rgb >> 8) & 0xff; 086 int b = rgb & 0xff; 087 return (r+g+b)/3; 088 } 089 090 public static boolean nearColors(int rgb1, int rgb2, int tolerance) { 091 int r1 = (rgb1 >> 16) & 0xff; 092 int g1 = (rgb1 >> 8) & 0xff; 093 int b1 = rgb1 & 0xff; 094 int r2 = (rgb2 >> 16) & 0xff; 095 int g2 = (rgb2 >> 8) & 0xff; 096 int b2 = rgb2 & 0xff; 097 return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance; 098 } 099 100 private final static float hsb1[] = new float[3];//FIXME-not thread safe 101 private final static float hsb2[] = new float[3];//FIXME-not thread safe 102 103 // Return rgb1 painted onto rgb2 104 public static int combinePixels(int rgb1, int rgb2, int op) { 105 return combinePixels(rgb1, rgb2, op, 0xff); 106 } 107 108 public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha, int channelMask) { 109 return (rgb2 & ~channelMask) | combinePixels(rgb1 & channelMask, rgb2, op, extraAlpha); 110 } 111 112 public static int combinePixels(int rgb1, int rgb2, int op, int extraAlpha) { 113 if (op == REPLACE) 114 return rgb1; 115 int a1 = (rgb1 >> 24) & 0xff; 116 int r1 = (rgb1 >> 16) & 0xff; 117 int g1 = (rgb1 >> 8) & 0xff; 118 int b1 = rgb1 & 0xff; 119 int a2 = (rgb2 >> 24) & 0xff; 120 int r2 = (rgb2 >> 16) & 0xff; 121 int g2 = (rgb2 >> 8) & 0xff; 122 int b2 = rgb2 & 0xff; 123 124 switch (op) { 125 case NORMAL: 126 break; 127 case MIN: 128 r1 = Math.min(r1, r2); 129 g1 = Math.min(g1, g2); 130 b1 = Math.min(b1, b2); 131 break; 132 case MAX: 133 r1 = Math.max(r1, r2); 134 g1 = Math.max(g1, g2); 135 b1 = Math.max(b1, b2); 136 break; 137 case ADD: 138 r1 = clamp(r1+r2); 139 g1 = clamp(g1+g2); 140 b1 = clamp(b1+b2); 141 break; 142 case SUBTRACT: 143 r1 = clamp(r2-r1); 144 g1 = clamp(g2-g1); 145 b1 = clamp(b2-b1); 146 break; 147 case DIFFERENCE: 148 r1 = clamp(Math.abs(r1-r2)); 149 g1 = clamp(Math.abs(g1-g2)); 150 b1 = clamp(Math.abs(b1-b2)); 151 break; 152 case MULTIPLY: 153 r1 = clamp(r1*r2/255); 154 g1 = clamp(g1*g2/255); 155 b1 = clamp(b1*b2/255); 156 break; 157 case DISSOLVE: 158 if ((randomGenerator.nextInt() & 0xff) <= a1) { 159 r1 = r2; 160 g1 = g2; 161 b1 = b2; 162 } 163 break; 164 case AVERAGE: 165 r1 = (r1+r2)/2; 166 g1 = (g1+g2)/2; 167 b1 = (b1+b2)/2; 168 break; 169 case HUE: 170 case SATURATION: 171 case VALUE: 172 case COLOR: 173 Color.RGBtoHSB(r1, g1, b1, hsb1); 174 Color.RGBtoHSB(r2, g2, b2, hsb2); 175 switch (op) { 176 case HUE: 177 hsb2[0] = hsb1[0]; 178 break; 179 case SATURATION: 180 hsb2[1] = hsb1[1]; 181 break; 182 case VALUE: 183 hsb2[2] = hsb1[2]; 184 break; 185 case COLOR: 186 hsb2[0] = hsb1[0]; 187 hsb2[1] = hsb1[1]; 188 break; 189 } 190 rgb1 = Color.HSBtoRGB(hsb2[0], hsb2[1], hsb2[2]); 191 r1 = (rgb1 >> 16) & 0xff; 192 g1 = (rgb1 >> 8) & 0xff; 193 b1 = rgb1 & 0xff; 194 break; 195 case SCREEN: 196 r1 = 255 - ((255 - r1) * (255 - r2)) / 255; 197 g1 = 255 - ((255 - g1) * (255 - g2)) / 255; 198 b1 = 255 - ((255 - b1) * (255 - b2)) / 255; 199 break; 200 case OVERLAY: 201 int m, s; 202 s = 255 - ((255 - r1) * (255 - r2)) / 255; 203 m = r1 * r2 / 255; 204 r1 = (s * r1 + m * (255 - r1)) / 255; 205 s = 255 - ((255 - g1) * (255 - g2)) / 255; 206 m = g1 * g2 / 255; 207 g1 = (s * g1 + m * (255 - g1)) / 255; 208 s = 255 - ((255 - b1) * (255 - b2)) / 255; 209 m = b1 * b2 / 255; 210 b1 = (s * b1 + m * (255 - b1)) / 255; 211 break; 212 case CLEAR: 213 r1 = g1 = b1 = 0xff; 214 break; 215 case DST_IN: 216 r1 = clamp((r2*a1)/255); 217 g1 = clamp((g2*a1)/255); 218 b1 = clamp((b2*a1)/255); 219 a1 = clamp((a2*a1)/255); 220 return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 221 case ALPHA: 222 a1 = a1*a2/255; 223 return (a1 << 24) | (r2 << 16) | (g2 << 8) | b2; 224 case ALPHA_TO_GRAY: 225 int na = 255-a1; 226 return (a1 << 24) | (na << 16) | (na << 8) | na; 227 } 228 if (extraAlpha != 0xff || a1 != 0xff) { 229 a1 = a1*extraAlpha/255; 230 int a3 = (255-a1)*a2/255; 231 r1 = clamp((r1*a1+r2*a3)/255); 232 g1 = clamp((g1*a1+g2*a3)/255); 233 b1 = clamp((b1*a1+b2*a3)/255); 234 a1 = clamp(a1+a3); 235 } 236 return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 237 } 238 239}