blob: 7dd764faa32865e9a9f32745f69d35040765be5a [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.images.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.io.UnsyncByteArrayInputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
/**
* @author spleaner
*/
public class ImageInfoReader {
private static final Logger LOG = Logger.getInstance("#org.intellij.images.util.ImageInfoReader");
private ImageInfoReader() {
}
@Nullable
public static Info getInfo(@NotNull final String file) {
return read(file);
}
@Nullable
public static Info getInfo(@NotNull final byte[] data) {
return read(data);
}
@Nullable
private static Info read(@NotNull final String file) {
final RandomAccessFile raf;
try {
//noinspection HardCodedStringLiteral
raf = new RandomAccessFile(file, "r");
try {
return readFileData(raf);
}
finally {
try {
raf.close();
}
catch (IOException e) {
// nothing
}
}
}
catch (IOException e) {
return null;
}
}
@Nullable
private static Info read(@NotNull final byte[] data) {
final DataInputStream is = new DataInputStream(new UnsyncByteArrayInputStream(data));
try {
return readFileData(is);
}
catch (IOException e) {
return null;
}
finally {
try {
is.close();
}
catch (IOException e) {
// nothing
}
}
}
@Nullable
private static Info readFileData(@NotNull final DataInput di) throws IOException {
final int b1 = di.readUnsignedByte();
final int b2 = di.readUnsignedByte();
if (b1 == 0x47 && b2 == 0x49) {
return readGif(di);
}
if (b1 == 0x89 && b2 == 0x50) {
return readPng(di);
}
if (b1 == 0xff && b2 == 0xd8) {
return readJpeg(di);
}
//if (b1 == 0x42 && b2 == 0x4d) {
// return readBmp(raf);
//}
return null;
}
@Nullable
private static Info readGif(DataInput di) throws IOException {
final byte[] GIF_MAGIC_87A = {0x46, 0x38, 0x37, 0x61};
final byte[] GIF_MAGIC_89A = {0x46, 0x38, 0x39, 0x61};
byte[] a = new byte[11]; // 4 from the GIF signature + 7 from the global header
di.readFully(a);
if ((!eq(a, 0, GIF_MAGIC_89A, 0, 4)) && (!eq(a, 0, GIF_MAGIC_87A, 0, 4))) {
return null;
}
final int width = getShortLittleEndian(a, 4);
final int height = getShortLittleEndian(a, 6);
int flags = a[8] & 0xff;
final int bpp = ((flags >> 4) & 0x07) + 1;
return new Info(width, height, bpp);
}
private static Info readBmp(RandomAccessFile raf) throws IOException {
byte[] a = new byte[44];
if (raf.read(a) != a.length) {
return null;
}
final int width = getIntLittleEndian(a, 16);
final int height = getIntLittleEndian(a, 20);
if (width < 1 || height < 1) {
return null;
}
final int bpp = getShortLittleEndian(a, 26);
if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 & bpp != 32) {
return null;
}
return new Info(width, height, bpp);
}
@Nullable
private static Info readJpeg(DataInput di) throws IOException {
byte[] a = new byte[13];
while (true) {
di.readFully(a, 0, 4);
int marker = getShortBigEndian(a, 0);
final int size = getShortBigEndian(a, 2);
if ((marker & 0xff00) != 0xff00) {
return null;
}
if (marker == 0xffe0) {
if (size < 14) {
di.skipBytes(size - 2);
continue;
}
di.readFully(a, 0, 12);
di.skipBytes(size - 14);
}
else if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) {
di.readFully(a, 0, 6);
final int bpp = (a[0] & 0xff) * (a[5] & 0xff);
final int width = getShortBigEndian(a, 3);
final int height = getShortBigEndian(a, 1);
return new Info(width, height, bpp);
}
else {
di.skipBytes(size - 2);
}
}
}
@Nullable
private static Info readPng(DataInput di) throws IOException {
final byte[] PNG_MAGIC = {0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
byte[] a = new byte[27];
di.readFully(a);
if (!eq(a, 0, PNG_MAGIC, 0, 6)) {
return null;
}
final int width = getIntBigEndian(a, 14);
final int height = getIntBigEndian(a, 18);
int bpp = a[22] & 0xff;
int colorType = a[23] & 0xff;
if (colorType == 2 || colorType == 6) {
bpp *= 3;
}
return new Info(width, height, bpp);
}
private static int getShortBigEndian(byte[] a, int offset) {
return (a[offset] & 0xff) << 8 | (a[offset + 1] & 0xff);
}
private static boolean eq(byte[] a1, int offset1, byte[] a2, int offset2, int num) {
while (num-- > 0) {
if (a1[offset1++] != a2[offset2++]) {
return false;
}
}
return true;
}
private static int getIntBigEndian(byte[] a, int offset) {
return (a[offset] & 0xff) << 24 | (a[offset + 1] & 0xff) << 16 | (a[offset + 2] & 0xff) << 8 | a[offset + 3] & 0xff;
}
private static int getIntLittleEndian(byte[] a, int offset) {
return (a[offset + 3] & 0xff) << 24 | (a[offset + 2] & 0xff) << 16 | (a[offset + 1] & 0xff) << 8 | a[offset] & 0xff;
}
private static int getShortLittleEndian(byte[] a, int offset) {
return (a[offset] & 0xff) | (a[offset + 1] & 0xff) << 8;
}
public static class Info {
public int width;
public int height;
public int bpp;
public Info(int width, int height, int bpp) {
this.width = width;
this.height = height;
this.bpp = bpp;
}
}
}