Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java
41161 views
1
/*
2
* Copyright (c) 2007, 2021, 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
23
* questions.
24
*/
25
26
package sun.java2d.cmm.lcms;
27
28
import java.awt.image.BufferedImage;
29
import java.awt.image.ColorModel;
30
import java.awt.image.ComponentColorModel;
31
import java.awt.image.ComponentSampleModel;
32
import java.awt.image.Raster;
33
34
import sun.awt.image.ByteComponentRaster;
35
import sun.awt.image.IntegerComponentRaster;
36
import sun.awt.image.ShortComponentRaster;
37
38
final class LCMSImageLayout {
39
40
public static int BYTES_SH(int x) {
41
return x;
42
}
43
44
public static int EXTRA_SH(int x) {
45
return x << 7;
46
}
47
48
public static int CHANNELS_SH(int x) {
49
return x << 3;
50
}
51
public static final int SWAPFIRST = 1 << 14;
52
public static final int DOSWAP = 1 << 10;
53
public static final int PT_RGB_8 =
54
CHANNELS_SH(3) | BYTES_SH(1);
55
public static final int PT_GRAY_8 =
56
CHANNELS_SH(1) | BYTES_SH(1);
57
public static final int PT_GRAY_16 =
58
CHANNELS_SH(1) | BYTES_SH(2);
59
public static final int PT_RGBA_8 =
60
EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
61
public static final int PT_ARGB_8 =
62
EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1) | SWAPFIRST;
63
public static final int PT_BGR_8 =
64
DOSWAP | CHANNELS_SH(3) | BYTES_SH(1);
65
public static final int PT_ABGR_8 =
66
DOSWAP | EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
67
public static final int PT_BGRA_8 = EXTRA_SH(1) | CHANNELS_SH(3)
68
| BYTES_SH(1) | DOSWAP | SWAPFIRST;
69
public static final int DT_BYTE = 0;
70
public static final int DT_SHORT = 1;
71
public static final int DT_INT = 2;
72
public static final int DT_DOUBLE = 3;
73
boolean isIntPacked = false;
74
int pixelType;
75
int dataType;
76
int width;
77
int height;
78
int nextRowOffset;
79
private int nextPixelOffset;
80
int offset;
81
82
/* This flag indicates whether the image can be processed
83
* at once by doTransfrom() native call. Otherwise, the
84
* image is processed scan by scan.
85
*/
86
private boolean imageAtOnce = false;
87
Object dataArray;
88
89
private int dataArrayLength; /* in bytes */
90
91
private LCMSImageLayout(int np, int pixelType, int pixelSize)
92
throws ImageLayoutException
93
{
94
this.pixelType = pixelType;
95
width = np;
96
height = 1;
97
nextPixelOffset = pixelSize;
98
nextRowOffset = safeMult(pixelSize, np);
99
offset = 0;
100
}
101
102
private LCMSImageLayout(int width, int height, int pixelType,
103
int pixelSize)
104
throws ImageLayoutException
105
{
106
this.pixelType = pixelType;
107
this.width = width;
108
this.height = height;
109
nextPixelOffset = pixelSize;
110
nextRowOffset = safeMult(pixelSize, width);
111
offset = 0;
112
}
113
114
115
public LCMSImageLayout(byte[] data, int np, int pixelType, int pixelSize)
116
throws ImageLayoutException
117
{
118
this(np, pixelType, pixelSize);
119
dataType = DT_BYTE;
120
dataArray = data;
121
dataArrayLength = data.length;
122
123
verify();
124
}
125
126
public LCMSImageLayout(short[] data, int np, int pixelType, int pixelSize)
127
throws ImageLayoutException
128
{
129
this(np, pixelType, pixelSize);
130
dataType = DT_SHORT;
131
dataArray = data;
132
dataArrayLength = 2 * data.length;
133
134
verify();
135
}
136
137
public LCMSImageLayout(int[] data, int np, int pixelType, int pixelSize)
138
throws ImageLayoutException
139
{
140
this(np, pixelType, pixelSize);
141
dataType = DT_INT;
142
dataArray = data;
143
dataArrayLength = 4 * data.length;
144
145
verify();
146
}
147
148
public LCMSImageLayout(double[] data, int np, int pixelType, int pixelSize)
149
throws ImageLayoutException
150
{
151
this(np, pixelType, pixelSize);
152
dataType = DT_DOUBLE;
153
dataArray = data;
154
dataArrayLength = 8 * data.length;
155
156
verify();
157
}
158
159
private LCMSImageLayout() {
160
}
161
162
/* This method creates a layout object for given image.
163
* Returns null if the image is not supported by current implementation.
164
*/
165
public static LCMSImageLayout createImageLayout(BufferedImage image) throws ImageLayoutException {
166
LCMSImageLayout l = new LCMSImageLayout();
167
168
switch (image.getType()) {
169
case BufferedImage.TYPE_INT_RGB:
170
l.pixelType = PT_ARGB_8;
171
l.isIntPacked = true;
172
break;
173
case BufferedImage.TYPE_INT_ARGB:
174
l.pixelType = PT_ARGB_8;
175
l.isIntPacked = true;
176
break;
177
case BufferedImage.TYPE_INT_BGR:
178
l.pixelType = PT_ABGR_8;
179
l.isIntPacked = true;
180
break;
181
case BufferedImage.TYPE_3BYTE_BGR:
182
l.pixelType = PT_BGR_8;
183
break;
184
case BufferedImage.TYPE_4BYTE_ABGR:
185
l.pixelType = PT_ABGR_8;
186
break;
187
case BufferedImage.TYPE_BYTE_GRAY:
188
l.pixelType = PT_GRAY_8;
189
break;
190
case BufferedImage.TYPE_USHORT_GRAY:
191
l.pixelType = PT_GRAY_16;
192
break;
193
default:
194
/* ColorConvertOp creates component images as
195
* default destination, so this kind of images
196
* has to be supported.
197
*/
198
ColorModel cm = image.getColorModel();
199
/* todo
200
* Our generic code for rasters does not support alpha channels,
201
* but it would be good to improve it when it is used from here.
202
* See "createImageLayout(image.getRaster())" below.
203
*/
204
if (!cm.hasAlpha() && cm instanceof ComponentColorModel) {
205
ComponentColorModel ccm = (ComponentColorModel) cm;
206
207
// verify whether the component size is fine
208
int[] cs = ccm.getComponentSize();
209
for (int s : cs) {
210
if (s != 8) {
211
return null;
212
}
213
}
214
215
return createImageLayout(image.getRaster());
216
217
}
218
return null;
219
}
220
221
l.width = image.getWidth();
222
l.height = image.getHeight();
223
224
switch (image.getType()) {
225
case BufferedImage.TYPE_INT_RGB:
226
case BufferedImage.TYPE_INT_ARGB:
227
case BufferedImage.TYPE_INT_BGR:
228
do {
229
IntegerComponentRaster intRaster = (IntegerComponentRaster)
230
image.getRaster();
231
l.nextRowOffset = safeMult(4, intRaster.getScanlineStride());
232
l.nextPixelOffset = safeMult(4, intRaster.getPixelStride());
233
l.offset = safeMult(4, intRaster.getDataOffset(0));
234
l.dataArray = intRaster.getDataStorage();
235
l.dataArrayLength = 4 * intRaster.getDataStorage().length;
236
l.dataType = DT_INT;
237
238
if (l.nextRowOffset == l.width * 4 * intRaster.getPixelStride()) {
239
l.imageAtOnce = true;
240
}
241
} while (false);
242
break;
243
244
case BufferedImage.TYPE_3BYTE_BGR:
245
case BufferedImage.TYPE_4BYTE_ABGR:
246
do {
247
ByteComponentRaster byteRaster = (ByteComponentRaster)
248
image.getRaster();
249
l.nextRowOffset = byteRaster.getScanlineStride();
250
l.nextPixelOffset = byteRaster.getPixelStride();
251
252
int firstBand = image.getSampleModel().getNumBands() - 1;
253
l.offset = byteRaster.getDataOffset(firstBand);
254
l.dataArray = byteRaster.getDataStorage();
255
l.dataArrayLength = byteRaster.getDataStorage().length;
256
l.dataType = DT_BYTE;
257
if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {
258
l.imageAtOnce = true;
259
}
260
} while (false);
261
break;
262
263
case BufferedImage.TYPE_BYTE_GRAY:
264
do {
265
ByteComponentRaster byteRaster = (ByteComponentRaster)
266
image.getRaster();
267
l.nextRowOffset = byteRaster.getScanlineStride();
268
l.nextPixelOffset = byteRaster.getPixelStride();
269
270
l.dataArrayLength = byteRaster.getDataStorage().length;
271
l.offset = byteRaster.getDataOffset(0);
272
l.dataArray = byteRaster.getDataStorage();
273
l.dataType = DT_BYTE;
274
275
if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {
276
l.imageAtOnce = true;
277
}
278
} while (false);
279
break;
280
281
case BufferedImage.TYPE_USHORT_GRAY:
282
do {
283
ShortComponentRaster shortRaster = (ShortComponentRaster)
284
image.getRaster();
285
l.nextRowOffset = safeMult(2, shortRaster.getScanlineStride());
286
l.nextPixelOffset = safeMult(2, shortRaster.getPixelStride());
287
288
l.offset = safeMult(2, shortRaster.getDataOffset(0));
289
l.dataArray = shortRaster.getDataStorage();
290
l.dataArrayLength = 2 * shortRaster.getDataStorage().length;
291
l.dataType = DT_SHORT;
292
293
if (l.nextRowOffset == l.width * 2 * shortRaster.getPixelStride()) {
294
l.imageAtOnce = true;
295
}
296
} while (false);
297
break;
298
default:
299
return null;
300
}
301
l.verify();
302
return l;
303
}
304
305
private static enum BandOrder {
306
DIRECT,
307
INVERTED,
308
ARBITRARY,
309
UNKNOWN;
310
311
public static BandOrder getBandOrder(int[] bandOffsets) {
312
BandOrder order = UNKNOWN;
313
314
int numBands = bandOffsets.length;
315
316
for (int i = 0; (order != ARBITRARY) && (i < bandOffsets.length); i++) {
317
switch (order) {
318
case UNKNOWN:
319
if (bandOffsets[i] == i) {
320
order = DIRECT;
321
} else if (bandOffsets[i] == (numBands - 1 - i)) {
322
order = INVERTED;
323
} else {
324
order = ARBITRARY;
325
}
326
break;
327
case DIRECT:
328
if (bandOffsets[i] != i) {
329
order = ARBITRARY;
330
}
331
break;
332
case INVERTED:
333
if (bandOffsets[i] != (numBands - 1 - i)) {
334
order = ARBITRARY;
335
}
336
break;
337
}
338
}
339
return order;
340
}
341
}
342
343
private void verify() throws ImageLayoutException {
344
345
if (offset < 0 || offset >= dataArrayLength) {
346
throw new ImageLayoutException("Invalid image layout");
347
}
348
349
if (nextPixelOffset != getBytesPerPixel(pixelType)) {
350
throw new ImageLayoutException("Invalid image layout");
351
}
352
353
int lastScanOffset = safeMult(nextRowOffset, (height - 1));
354
355
int lastPixelOffset = safeMult(nextPixelOffset, (width -1 ));
356
357
lastPixelOffset = safeAdd(lastPixelOffset, lastScanOffset);
358
359
int off = safeAdd(offset, lastPixelOffset);
360
361
if (off < 0 || off >= dataArrayLength) {
362
throw new ImageLayoutException("Invalid image layout");
363
}
364
}
365
366
static int safeAdd(int a, int b) throws ImageLayoutException {
367
long res = a;
368
res += b;
369
if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {
370
throw new ImageLayoutException("Invalid image layout");
371
}
372
return (int)res;
373
}
374
375
static int safeMult(int a, int b) throws ImageLayoutException {
376
long res = a;
377
res *= b;
378
if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {
379
throw new ImageLayoutException("Invalid image layout");
380
}
381
return (int)res;
382
}
383
384
@SuppressWarnings("serial") // JDK-implementation class
385
public static class ImageLayoutException extends Exception {
386
public ImageLayoutException(String message) {
387
super(message);
388
}
389
}
390
public static LCMSImageLayout createImageLayout(Raster r) {
391
LCMSImageLayout l = new LCMSImageLayout();
392
if (r instanceof ByteComponentRaster &&
393
r.getSampleModel() instanceof ComponentSampleModel) {
394
ByteComponentRaster br = (ByteComponentRaster)r;
395
396
ComponentSampleModel csm = (ComponentSampleModel)r.getSampleModel();
397
398
l.pixelType = CHANNELS_SH(br.getNumBands()) | BYTES_SH(1);
399
400
int[] bandOffsets = csm.getBandOffsets();
401
BandOrder order = BandOrder.getBandOrder(bandOffsets);
402
403
int firstBand = 0;
404
switch (order) {
405
case INVERTED:
406
l.pixelType |= DOSWAP;
407
firstBand = csm.getNumBands() - 1;
408
break;
409
case DIRECT:
410
// do nothing
411
break;
412
default:
413
// unable to create the image layout;
414
return null;
415
}
416
417
l.nextRowOffset = br.getScanlineStride();
418
l.nextPixelOffset = br.getPixelStride();
419
420
l.offset = br.getDataOffset(firstBand);
421
l.dataArray = br.getDataStorage();
422
l.dataType = DT_BYTE;
423
424
l.width = br.getWidth();
425
l.height = br.getHeight();
426
427
if (l.nextRowOffset == l.width * br.getPixelStride()) {
428
l.imageAtOnce = true;
429
}
430
return l;
431
}
432
return null;
433
}
434
435
/**
436
* Derives number of bytes per pixel from the pixel format.
437
* Following bit fields are used here:
438
* [0..2] - bytes per sample
439
* [3..6] - number of color samples per pixel
440
* [7..9] - number of non-color samples per pixel
441
*
442
* A complete description of the pixel format can be found
443
* here: lcms2.h, lines 651 - 667.
444
*
445
* @param pixelType pixel format in lcms2 notation.
446
* @return number of bytes per pixel for given pixel format.
447
*/
448
private static int getBytesPerPixel(int pixelType) {
449
int bytesPerSample = (0x7 & pixelType);
450
int colorSamplesPerPixel = 0xF & (pixelType >> 3);
451
int extraSamplesPerPixel = 0x7 & (pixelType >> 7);
452
453
return bytesPerSample * (colorSamplesPerPixel + extraSamplesPerPixel);
454
}
455
}
456
457