Skip to content

Commit

Permalink
Code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaozhuai committed Jun 28, 2024
1 parent 3a22d56 commit 54988ea
Show file tree
Hide file tree
Showing 11 changed files with 866 additions and 431 deletions.
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = "io.github.xiaozhuai"
version = "1.1.6"
version = "1.2.0"

repositories {
mavenCentral()
Expand Down Expand Up @@ -44,6 +44,6 @@ tasks {
}

dependencies {
// !!! Do not forget to change QOIMetadata.QOI_LIBRARY_VERSION when upgrade qoi-java !!!
// !!! Do not forget to change QOIPluginConstants.QOI_VERSION when upgrade qoi-java !!!
implementation("me.saharnooby:qoi-java:1.2.1")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.github.xiaozhuai.jetbrains.qoi;

import org.jetbrains.annotations.NotNull;

import javax.imageio.ImageReadParam;
import javax.imageio.ImageWriteParam;
import java.awt.*;

final class ImageParamUtil {

private static final Point ZERO = new Point(0, 0);

static boolean isDefault(@NotNull ImageReadParam param) {
return param.getClass() == ImageReadParam.class &&
param.getSourceRegion() == null &&
param.getSourceXSubsampling() == 1 &&
param.getSourceYSubsampling() == 1 &&
param.getSubsamplingXOffset() == 0 &&
param.getSubsamplingYOffset() == 0 &&
param.getSourceBands() == null &&
param.getDestinationType() == null &&
param.getDestinationOffset().equals(ZERO) &&
param.getDestination() == null &&
param.getDestinationBands() == null;
}

static boolean isDefault(@NotNull ImageWriteParam param) {
return param.getClass() == ImageWriteParam.class &&
param.getSourceRegion() == null &&
param.getSourceXSubsampling() == 1 &&
param.getSourceYSubsampling() == 1 &&
param.getSubsamplingXOffset() == 0 &&
param.getSubsamplingYOffset() == 0 &&
param.getSourceBands() == null &&
param.getDestinationType() == null &&
param.getDestinationOffset().equals(ZERO);
}

}
284 changes: 284 additions & 0 deletions src/main/java/io/github/xiaozhuai/jetbrains/qoi/QOIImageReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
package io.github.xiaozhuai.jetbrains.qoi;

import org.jetbrains.annotations.NotNull;
import me.saharnooby.qoi.QOIColorSpace;
import me.saharnooby.qoi.QOIImage;
import me.saharnooby.qoi.QOIUtil;

import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.IOException;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;

public final class QOIImageReader extends ImageReader {

static final int[] OFFSETS_3 = {0, 1, 2};
static final int[] OFFSETS_4 = {0, 1, 2, 3};

private QOIImage image;

QOIImageReader(@NotNull ImageReaderSpi originatingProvider) {
super(originatingProvider);
}

private void readImage() throws IOException {
if (this.image != null) {
return;
}

if (this.input == null) {
throw new IllegalStateException("Input not set");
}

ImageInputStream input = (ImageInputStream) this.input;

// This assumes that no additional data is stored in the
// stream, otherwise buffering will corrupt the stream.
this.image = QOIUtil.readImage(new WrappedImageInputStream(input));
}

private void checkIndex(int imageIndex) {
if (imageIndex != 0) {
throw new IndexOutOfBoundsException();
}
}

@Override
public int getNumImages(boolean allowSearch) {
return 1;
}

@Override
public int getWidth(int imageIndex) throws IOException {
checkIndex(imageIndex);

readImage();

return this.image.getWidth();
}

@Override
public int getHeight(int imageIndex) throws IOException {
checkIndex(imageIndex);

readImage();

return this.image.getHeight();
}

@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
readImage();

boolean hasAlpha = this.image.getChannels() == 4;

ColorSpace colorSpace = getAwtColorSpace(this.image.getColorSpace());

ImageTypeSpecifier type = ImageTypeSpecifier.createInterleaved(
colorSpace,
hasAlpha ? OFFSETS_4 : OFFSETS_3,
DataBuffer.TYPE_BYTE,
hasAlpha,
false
);

return Collections.singletonList(type).iterator();
}

@Override
public IIOMetadata getStreamMetadata() {
// No metadata supported
return null;
}

@Override
public IIOMetadata getImageMetadata(int imageIndex) {
// No metadata supported
return null;
}

@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
checkIndex(imageIndex);

clearAbortRequest();

processImageStarted(imageIndex);

readImage();

int width = this.image.getWidth();
int height = this.image.getHeight();

BufferedImage source = convertToBufferedImage(this.image);

// Fast path
if (param == null || ImageParamUtil.isDefault(param)) {
processImageComplete();

return source;
}

Rectangle sourceRegion = getSourceRegion(param, width, height);

int sourceXSubsampling = param.getSourceXSubsampling();
int sourceYSubsampling = param.getSourceYSubsampling();
int[] sourceBands = param.getSourceBands();
int[] destinationBands = param.getDestinationBands();
Point destinationOffset = param.getDestinationOffset();

BufferedImage dest = getDestination(param, getImageTypes(0), width, height);

SampleModel destSampleModel = dest.getSampleModel();

int destBands = destSampleModel.getNumBands();

for (int band = 0; band < destBands; band++) {
if (destSampleModel.getSampleSize(band) != 8) {
throw new IIOException("Reading into images with band size != 8 bits is not supported");
}
}

checkReadParamBandSettings(param, this.image.getChannels(), destBands);

WritableRaster sourceRaster = source.getWritableTile(0, 0);
WritableRaster destRaster = dest.getWritableTile(0, 0);

if (sourceBands != null) {
sourceRaster = sourceRaster.createWritableChild(
0,
0,
width,
height,
0,
0,
sourceBands
);
}

if (destinationBands != null) {
destRaster = destRaster.createWritableChild(
0,
0,
destRaster.getWidth(),
destRaster.getHeight(),
0,
0,
destinationBands
);
}

int destMinX = destRaster.getMinX();
int destMaxX = destMinX + destRaster.getWidth() - 1;
int destMinY = destRaster.getMinY();
int destMaxY = destMinY + destRaster.getHeight() - 1;

int[] pixel = new int[sourceRaster.getNumBands()];

for (int y = sourceRegion.y; y < sourceRegion.y + sourceRegion.height; y += sourceYSubsampling) {
if (y < 0 || y >= height) {
continue;
}

int destY = destinationOffset.y + (y - sourceRegion.y) / sourceYSubsampling;

if (destY < destMinY || destY > destMaxY) {
continue;
}

for (int x = sourceRegion.x; x < sourceRegion.x + sourceRegion.width; x += sourceXSubsampling) {
if (x < 0 || x >= width) {
continue;
}

int destX = destinationOffset.x + (x - sourceRegion.x) / sourceXSubsampling;

if (destX < destMinX || destX > destMaxX) {
continue;
}

sourceRaster.getPixel(x, y, pixel);

destRaster.setPixel(destX, destY, pixel);
}

processImageProgress((y - sourceRegion.y) * 100F / sourceRegion.height);

if (abortRequested()) {
processReadAborted();

return dest;
}
}

processImageComplete();

return dest;
}

@Override
public void reset() {
super.reset();

this.image = null;
}

public static BufferedImage convertToBufferedImage(@NotNull QOIImage image) {
int width = image.getWidth();
int height = image.getHeight();
int channels = image.getChannels();

boolean hasAlpha = channels == 4;

DataBufferByte buffer = new DataBufferByte(image.getPixelData(), width * height * channels);

WritableRaster raster = Raster.createInterleavedRaster(
buffer,
width,
height,
channels * width,
channels,
hasAlpha ? OFFSETS_4 : OFFSETS_3,
new Point(0, 0)
);

ColorSpace awtColorSpace = getAwtColorSpace(image.getColorSpace());

ColorModel colorModel = new ComponentColorModel(
awtColorSpace,
hasAlpha,
false,
hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
DataBuffer.TYPE_BYTE
);

return new BufferedImage(
colorModel,
raster,
false,
new Hashtable<>()
);
}

private static ColorSpace getAwtColorSpace(@NotNull QOIColorSpace colorSpace) {
switch (colorSpace) {
case SRGB:
return ColorSpace.getInstance(ColorSpace.CS_sRGB);
case LINEAR:
return ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
default:
throw new RuntimeException();
}
}

}
Loading

0 comments on commit 54988ea

Please sign in to comment.