mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-CR-63083: move webp from android to the platform. Create a new plugin, include it in IDEA and WS WEB-36384
(cherry picked from commit faae11591a4a39d61154d93a30eaaf824bfb64d5) GitOrigin-RevId: f849c100b551d4c3d06852ba31a9d585891b9948
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f95d4aef98
commit
c53f94d975
Generated
+1
@@ -810,6 +810,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/hg4idea/intellij.vcs.hg.iml" filepath="$PROJECT_DIR$/plugins/hg4idea/intellij.vcs.hg.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/svn4idea/intellij.vcs.svn.iml" filepath="$PROJECT_DIR$/plugins/svn4idea/intellij.vcs.svn.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/svn4idea/intellij.vcs.svn.tests.iml" filepath="$PROJECT_DIR$/plugins/svn4idea/intellij.vcs.svn.tests.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/webp/intellij.webp.iml" filepath="$PROJECT_DIR$/plugins/webp/intellij.webp.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/openapi/intellij.xml.iml" filepath="$PROJECT_DIR$/xml/openapi/intellij.xml.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/xml-analysis-api/intellij.xml.analysis.iml" filepath="$PROJECT_DIR$/xml/xml-analysis-api/intellij.xml.analysis.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/xml-analysis-impl/intellij.xml.analysis.impl.iml" filepath="$PROJECT_DIR$/xml/xml-analysis-impl/intellij.xml.analysis.impl.iml" />
|
||||
|
||||
@@ -68,6 +68,7 @@ abstract class BaseIdeaProperties extends JetBrainsProductProperties {
|
||||
"intellij.vcs.changeReminder",
|
||||
"intellij.filePrediction",
|
||||
"intellij.markdown",
|
||||
"intellij.webp",
|
||||
"intellij.grazie"
|
||||
]
|
||||
protected static final Map<String, String> CE_CLASS_VERSIONS = [
|
||||
|
||||
@@ -139,5 +139,6 @@
|
||||
<orderEntry type="module" module-name="intellij.statsCollector.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.filePrediction" scope="RUNTIME" />
|
||||
<orderEntry type="module" module-name="intellij.textmate" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.webp" scope="RUNTIME" />
|
||||
</component>
|
||||
</module>
|
||||
+5
-4
@@ -89,6 +89,11 @@ class CommunityRepositoryModules {
|
||||
plugin("intellij.laf.macos") {
|
||||
bundlingRestrictions.supportedOs = [OsFamily.MACOS]
|
||||
},
|
||||
plugin("intellij.webp"){
|
||||
withResource("lib/libwebp/linux", "lib/libwebp/linux")
|
||||
withResource("lib/libwebp/mac", "lib/libwebp/mac")
|
||||
withResource("lib/libwebp/win", "lib/libwebp/win")
|
||||
},
|
||||
plugin("intellij.laf.win10") {
|
||||
bundlingRestrictions.supportedOs = [OsFamily.WINDOWS]
|
||||
},
|
||||
@@ -360,10 +365,6 @@ class CommunityRepositoryModules {
|
||||
withResourceFromModule("intellij.android.core", "lib/sampleData", "lib/sampleData")
|
||||
withResourceArchive("../android/annotations", "lib/androidAnnotations.jar")
|
||||
|
||||
withResourceFromModule("intellij.android.adt.ui", "lib/libwebp/linux", "lib/libwebp/linux")
|
||||
withResourceFromModule("intellij.android.adt.ui", "lib/libwebp/mac", "lib/libwebp/mac")
|
||||
withResourceFromModule("intellij.android.adt.ui", "lib/libwebp/win", "lib/libwebp/win")
|
||||
|
||||
// here go some differences from original Android Studio layout
|
||||
def getSingleFile = { BuildContext context, String projectLibName ->
|
||||
List<File> libFiles = context.project.libraryCollection
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="intellij.xml" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/libwebp.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,6 @@
|
||||
# managed by go/iml_to_build
|
||||
java_import(
|
||||
name = "libwebp",
|
||||
jars = ["libwebp.jar"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
filegroup(
|
||||
name = "libwebp",
|
||||
srcs = glob([
|
||||
"mac/*.dylib",
|
||||
"win/*.dll",
|
||||
"linux/*.so",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,95 @@
|
||||
# Building webp jni library
|
||||
|
||||
The webp support requires a native library (`libwebp_jni`) and `libwebp.jar`.
|
||||
The native libraries are added to the `tools/adt/idea/` project in
|
||||
`adt-ui/lib/libwebp/[platform]`.
|
||||
|
||||
This describes how to build `libwebp_jni` for each of the platforms. The steps
|
||||
must be repeated for each platform - once for 32 bit and once for 64 bit
|
||||
architectures. __Make sure to do a clean build when switching architectures__:
|
||||
`git clean -dfX`.
|
||||
|
||||
`libwebp.jar` is copied directly from `<libwebp>/swig/` to `adt-ui/lib/`
|
||||
|
||||
## Windows:
|
||||
1. Install Visual Studio 2015 with the following components:
|
||||
- Visual C++ (including all sub components)
|
||||
- Universal Windows App Development Tools
|
||||
- Git for Windows
|
||||
1. Open "Git Bash", clone libwebp and checkout appropriate branch:
|
||||
```bat
|
||||
git clone https://chromium.googlesource.com/webm/libwebp
|
||||
```
|
||||
1. Open "Developer Command Prompt for VS2015"
|
||||
1. Make the webp_jni library as per the following commands:
|
||||
```bat
|
||||
# For 64 bit arch (not needed for 32 bit arch)
|
||||
cd "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC"
|
||||
vcvarsall amd64
|
||||
|
||||
cd \git\clone\location\libwebp
|
||||
# Make libwebp
|
||||
nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
|
||||
# Make jni binding now
|
||||
cd swig
|
||||
# For 32 bit
|
||||
cl /I..\src /I"%JDK%\include" ^
|
||||
/I"%JDK%\include\win32" ^
|
||||
libwebp_java_wrap.c /MT /LD /Fe:webp_jni.dll ^
|
||||
..\output\release-static\x86\lib\libwebp.lib
|
||||
# For 64 bit
|
||||
cl /I..\src /I"C:\Program Files (x86)\Java\jdk1.7.0_55\include" ^
|
||||
/I"C:\Program Files (x86)\Java\jdk1.7.0_55\include\win32" ^
|
||||
libwebp_java_wrap.c /MT /LD /Fe:webp_jni64.dll ^
|
||||
..\output\release-static\x64\lib\libwebp.lib
|
||||
```
|
||||
1. Note that unlike mac and linux, windows library is named `webp_jni` and not
|
||||
`libwebp_jni`.
|
||||
|
||||
## Mac/Linux
|
||||
1. Clone libwebp and checkout appropriate branch:
|
||||
```bash
|
||||
git clone https://chromium.googlesource.com/webm/libwebp
|
||||
```
|
||||
1. Edit makefile.unix:
|
||||
```diff
|
||||
--- a/makefile.unix
|
||||
+++ b/makefile.unix
|
||||
@@ -14,10 +14,11 @@
|
||||
# These flags assume you have libpng, libjpeg, libtiff and libgif installed. If
|
||||
# not, either follow the install instructions below or just comment out the next
|
||||
# four lines.
|
||||
-EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF
|
||||
-DWEBP_LIBS= -lpng -lz
|
||||
-CWEBP_LIBS= $(DWEBP_LIBS) -ljpeg -ltiff
|
||||
-GIF_LIBS = -lgif
|
||||
+EXTRA_FLAGS= -fPIC -m32
|
||||
+#-DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF
|
||||
+#DWEBP_LIBS= -lpng -lz
|
||||
+#CWEBP_LIBS= $(DWEBP_LIBS) -ljpeg -ltiff
|
||||
+#GIF_LIBS = -lgif
|
||||
|
||||
ifeq ($(strip $(shell uname)), Darwin)
|
||||
# Work around a problem linking tables marked as common symbols,
|
||||
```
|
||||
1. The above diff is for building for 32 bit arch. To build for 64 bit arch, do
|
||||
not add `-m32` to `EXTRA_FLAGS`.
|
||||
1. Make the libwebp_jni library as per the following commands
|
||||
```bash
|
||||
cd /git/clone/location/libwebp
|
||||
make -f makefile.unix
|
||||
cd swig
|
||||
gcc -shared -fPIC -fno-strict-aliasing -O2 \
|
||||
-m32 \
|
||||
-I/path/to/your/jdk/include \
|
||||
-I/path/to/your/jdk/include/[platform] \
|
||||
-I../src \
|
||||
-L../src \
|
||||
libwebp_java_wrap.c \
|
||||
-lwebp \
|
||||
-o libwebp_jni.[ext]
|
||||
```
|
||||
1. When building for 64 bit, use `-o libwebp_jni64.[ext]` instead and do not
|
||||
specify the `-m32` option.
|
||||
1. The `[ext]` for linux is `so` and for mac is `dylib`.
|
||||
1. No need to build the 32 bit variant for mac.
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<idea-plugin implementation-detail="true">
|
||||
<id>intellij.webp</id>
|
||||
<vendor>JetBrains</vendor>
|
||||
<depends>com.intellij.modules.lang</depends>
|
||||
<name>Webp Support</name>
|
||||
<description><![CDATA[
|
||||
<p>Provides support for webp images format.</p>
|
||||
]]></description>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<fileType name="Image" extensions="webp"/>
|
||||
<applicationInitializedListener implementation="com.android.tools.adtui.webp.WebpMetadata"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.tools.adtui.webp;
|
||||
|
||||
|
||||
import com.google.webp.libwebp;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Decoder for WebP. This needs the webp jni library loaded to function.
|
||||
*
|
||||
* @see IIORegistry
|
||||
*/
|
||||
public class WebpImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
private static final byte[] RIFF_HEADER = {'R', 'I', 'F', 'F'};
|
||||
private static final byte[] WEBP_HEADER = {'W', 'E', 'B', 'P'};
|
||||
|
||||
private static final int MAX_FILE_SIZE = 0x6400000; // 100 Megs
|
||||
|
||||
WebpImageReaderSpi() {
|
||||
vendorName = WebpMetadata.WEBP_VENDOR;
|
||||
version = WebpNativeLibHelper.getDecoderVersion();
|
||||
suffixes = WebpMetadata.WEBP_SUFFIXES;
|
||||
names = WebpMetadata.WEBP_FORMAT_NAMES;
|
||||
MIMETypes = WebpMetadata.WEBP_MIME_TYPES;
|
||||
pluginClassName = WebpReader.class.getName();
|
||||
inputTypes = new Class<?>[]{ImageInputStream.class};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(@NotNull Object source) throws IOException {
|
||||
assert source instanceof ImageInputStream;
|
||||
ImageInputStream stream = (ImageInputStream) source;
|
||||
long length = stream.length();
|
||||
// The length may be -1 for files of unknown size.
|
||||
// Accept them for now and if needed, throw an IOException later.
|
||||
if (length > MAX_FILE_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] header = new byte[12];
|
||||
int bytesRead = stream.read(header, 0, 12);
|
||||
return bytesRead == 12 &&
|
||||
arrayEquals(header, 0, RIFF_HEADER.length, RIFF_HEADER) &&
|
||||
arrayEquals(header, 8, WEBP_HEADER.length, WEBP_HEADER) &&
|
||||
WebpNativeLibHelper.loadNativeLibraryIfNeeded();
|
||||
} finally {
|
||||
try {
|
||||
stream.reset();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Logger.getInstance(WebpImageReaderSpi.class).error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean arrayEquals(@NotNull byte[] a1, int offset, int len, @NotNull byte[] a2) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (a1[offset + i] != a2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
WebpNativeLibHelper.requireNativeLibrary();
|
||||
return new WebpReader(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Webp Image Decoder";
|
||||
}
|
||||
|
||||
private static class WebpReader extends ImageReader {
|
||||
|
||||
private static final String UNABLE_TO_READ_WEBP_IMAGE = "Unable to read WebP image";
|
||||
|
||||
@Nullable
|
||||
private byte[] myInputBytes;
|
||||
private final int[] myWidthOut = new int[1];
|
||||
private final int[] myHeightOut = new int[1];
|
||||
private int myError;
|
||||
|
||||
public WebpReader(ImageReaderSpi originatingProvider) {
|
||||
super(originatingProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myInputBytes = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
|
||||
super.setInput(input, seekForwardOnly, ignoreMetadata);
|
||||
myInputBytes = null;
|
||||
myError = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(boolean allowSearch) throws IOException {
|
||||
return myInputBytes == null ? 0 : 1;
|
||||
}
|
||||
|
||||
private void loadInfoIfNeeded() throws IOException {
|
||||
if (input != null && myInputBytes == null) {
|
||||
myInputBytes = readStreamFully((ImageInputStream) input);
|
||||
myError = libwebp.WebPGetInfo(myInputBytes, myInputBytes.length, myWidthOut, myHeightOut);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static byte[] readStreamFully(@NotNull ImageInputStream stream) throws IOException {
|
||||
if (stream.length() != -1) {
|
||||
byte[] bytes = new byte[(int) stream.length()]; // Integer overflow prevented by canDecode check in reader spi above.
|
||||
|
||||
stream.readFully(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Unknown file size
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(0x100000); // initialize with 1 Meg to minimize reallocs.
|
||||
final int bufferSize = 0x4000; // 16k
|
||||
byte[] bytes = new byte[bufferSize];
|
||||
int idx;
|
||||
for (idx = 0; idx < MAX_FILE_SIZE / bufferSize; idx++) { // Just to make sure we don't exceed MAX_FILE_SIZE
|
||||
int read = stream.read(bytes, 0, bufferSize);
|
||||
buffer.write(bytes, 0, read);
|
||||
if (read != bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == MAX_FILE_SIZE / bufferSize) {
|
||||
throw new IOException("webp image too large");
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
loadInfoIfNeeded();
|
||||
if (myError != 0) {
|
||||
// Yes, the above check is correct - zero value means an error.
|
||||
return myWidthOut[0];
|
||||
}
|
||||
throw new IOException(UNABLE_TO_READ_WEBP_IMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
loadInfoIfNeeded();
|
||||
if (myError != 0) {
|
||||
// Yes, the above check is correct - zero value means an error.
|
||||
return myHeightOut[0];
|
||||
}
|
||||
throw new IOException(UNABLE_TO_READ_WEBP_IMAGE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IIOMetadata getStreamMetadata() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
loadInfoIfNeeded();
|
||||
if (myError == 0) {
|
||||
throw new IOException(UNABLE_TO_READ_WEBP_IMAGE);
|
||||
}
|
||||
assert myInputBytes != null;
|
||||
byte[] argb = libwebp.WebPDecodeARGB(myInputBytes, myInputBytes.length, myWidthOut, myHeightOut);
|
||||
@SuppressWarnings("UndesirableClassUsage")
|
||||
BufferedImage bi = new BufferedImage(myWidthOut[0], myHeightOut[0], BufferedImage.TYPE_INT_ARGB);
|
||||
// Copy the bytes read above to the image's data buffer.
|
||||
final int[] a = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
|
||||
IntBuffer intBuf = ByteBuffer.wrap(argb).asIntBuffer();
|
||||
assert a.length == intBuf.remaining();
|
||||
intBuf.get(a);
|
||||
return bi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.tools.adtui.webp;
|
||||
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import java.util.Locale;
|
||||
|
||||
class WebpImageWriteParam extends ImageWriteParam {
|
||||
WebpImageWriteParam(Locale locale, boolean compress, float quality) {
|
||||
super(locale);
|
||||
this.canWriteCompressed = compress;
|
||||
this.compressionQuality = quality;
|
||||
if (canWriteCompressed) {
|
||||
compressionMode = ImageWriteParam.MODE_EXPLICIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.tools.adtui.webp;
|
||||
|
||||
|
||||
import com.google.webp.libwebp;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Encoder for WebP. This needs the webp jni library loaded to function.
|
||||
*/
|
||||
public class WebpImageWriterSpi extends ImageWriterSpi {
|
||||
WebpImageWriterSpi() {
|
||||
vendorName = WebpMetadata.WEBP_VENDOR;
|
||||
version = WebpNativeLibHelper.getEncoderVersion();
|
||||
suffixes = WebpMetadata.WEBP_SUFFIXES;
|
||||
names = WebpMetadata.WEBP_FORMAT_NAMES;
|
||||
MIMETypes = WebpMetadata.WEBP_MIME_TYPES;
|
||||
pluginClassName = WebpWriter.class.getName();
|
||||
outputTypes = new Class<?>[]{ImageOutputStream.class};
|
||||
}
|
||||
|
||||
public static void writeImage(@NotNull BufferedImage image,
|
||||
@NotNull OutputStream outputStream,
|
||||
boolean lossless,
|
||||
int quality) throws IOException {
|
||||
// Instead of
|
||||
// ImageIO.write(image, WebpMetadata.WEBP_FORMAT_LOWER_CASE, stream);
|
||||
// which will pass null as the parameters to the ImageWriter, call through to the ImageWriter
|
||||
// more directly so we can set up the proper parameters:
|
||||
ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image);
|
||||
Iterator<ImageWriter> iterator = ImageIO.getImageWriters(type, WebpMetadata.WEBP_FORMAT_LOWER_CASE);
|
||||
if (iterator.hasNext()) {
|
||||
ImageWriter writer = iterator.next();
|
||||
try (ImageOutputStream stream = ImageIO.createImageOutputStream(outputStream)) {
|
||||
writer.setOutput(stream);
|
||||
try {
|
||||
ImageWriteParam param = new WebpImageWriteParam(writer.getLocale(), !lossless, quality / 100f);
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
}
|
||||
finally {
|
||||
writer.dispose();
|
||||
stream.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEncodeImage(ImageTypeSpecifier type) {
|
||||
return canWriteImage(type);
|
||||
}
|
||||
|
||||
public static boolean canWriteImage(ImageTypeSpecifier type) {
|
||||
SampleModel sm = type.getSampleModel();
|
||||
ColorModel cm = type.getColorModel();
|
||||
return (sm.getNumBands() >= 2 && sm.getNumBands() <= 4 || cm instanceof IndexColorModel) &&
|
||||
sm.getSampleSize(0) <= 8 &&
|
||||
sm.getWidth() <= 65535 &&
|
||||
sm.getHeight() <= 65535 &&
|
||||
(cm == null || cm.getComponentSize()[0] <= 8) &&
|
||||
WebpNativeLibHelper.loadNativeLibraryIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageWriter createWriterInstance(Object extension) throws IOException {
|
||||
WebpNativeLibHelper.requireNativeLibrary();
|
||||
return new WebpWriter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "WebP Image Encoder";
|
||||
}
|
||||
|
||||
private static class WebpWriter extends ImageWriter {
|
||||
public WebpWriter(ImageWriterSpi originatingProvider) {
|
||||
super(originatingProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
|
||||
return new WebpMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
return new WebpMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ImageOutputStream stream = null;
|
||||
|
||||
@Override
|
||||
public void setOutput(Object output) {
|
||||
super.setOutput(output);
|
||||
if (output != null) {
|
||||
if (!(output instanceof ImageOutputStream)) {
|
||||
throw new
|
||||
IllegalArgumentException("output is not an ImageOutputStream");
|
||||
}
|
||||
this.stream = (ImageOutputStream)output;
|
||||
this.stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else {
|
||||
this.stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
||||
if (stream == null) {
|
||||
throw new IllegalStateException("output == null!");
|
||||
}
|
||||
boolean lossless = WebpMetadata.DEFAULT_LOSSLESS;
|
||||
float quality = WebpMetadata.DEFAULT_ENCODING_QUALITY;
|
||||
if (param != null && param.canWriteCompressed()) {
|
||||
lossless = false;
|
||||
if (param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
|
||||
quality = param.getCompressionQuality();
|
||||
}
|
||||
}
|
||||
|
||||
Raster srcRas = getRaster(image);
|
||||
|
||||
int minX = srcRas.getMinX();
|
||||
int minY = srcRas.getMinY();
|
||||
int width = srcRas.getWidth();
|
||||
int height = srcRas.getHeight();
|
||||
int maxX = minX + width;
|
||||
int maxY = minY + height;
|
||||
|
||||
byte[] data = new byte[width * height * 4];
|
||||
// TODO: This code is the inefficient fallback dealing with any color/sample model.
|
||||
// However, we've restricted things in the canEncodeImage method so we might as well try
|
||||
// to do it more efficiently
|
||||
int index = 0;
|
||||
SampleModel sampleModel = srcRas.getSampleModel();
|
||||
int bands = sampleModel.getNumBands();
|
||||
if (bands == 1) {
|
||||
// Indexed image but not found in raster
|
||||
return;
|
||||
}
|
||||
assert bands >= 2 && bands <= 4; // enforced in canEncodeImage
|
||||
DataBuffer buffer = srcRas.getDataBuffer();
|
||||
if (bands >= 3) {
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int band = 0; band < bands; band++) {
|
||||
int sample = sampleModel.getSample(x, y, band, buffer);
|
||||
data[index++] = (byte)sample;
|
||||
}
|
||||
if (bands == 3) {
|
||||
data[index++] = (byte)255; // implicit alpha in an RGB (not RGBA) image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert bands == 2;
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
byte sample = (byte)sampleModel.getSample(x, y, 0, buffer);
|
||||
data[index++] = sample;
|
||||
data[index++] = sample;
|
||||
data[index++] = sample;
|
||||
byte alpha = (byte)sampleModel.getSample(x, y, 1, buffer);
|
||||
data[index++] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See https://developers.google.com/speed/webp/docs/api
|
||||
byte[] encoded;
|
||||
if (lossless) {
|
||||
encoded = libwebp.WebPEncodeLosslessRGBA(data, width, height, 4 * width);
|
||||
}
|
||||
else {
|
||||
encoded = libwebp.WebPEncodeRGBA(data, width, height, 4 * width, (int)(100 * quality));
|
||||
}
|
||||
stream.write(encoded);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Raster getRaster(@NotNull IIOImage image) {
|
||||
boolean rasterOnly = image.hasRaster();
|
||||
if (rasterOnly) {
|
||||
return image.getRaster();
|
||||
}
|
||||
else {
|
||||
RenderedImage renderedImage = image.getRenderedImage();
|
||||
if (renderedImage instanceof BufferedImage) {
|
||||
// Convert indexed to RGB
|
||||
BufferedImage bufferedImage = (BufferedImage)renderedImage;
|
||||
if (renderedImage.getColorModel() instanceof IndexColorModel) {
|
||||
//noinspection UndesirableClassUsage
|
||||
BufferedImage rgb = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics = rgb.createGraphics();
|
||||
graphics.drawImage(bufferedImage, 0, 0, null);
|
||||
graphics.dispose();
|
||||
return rgb.getRaster();
|
||||
}
|
||||
return bufferedImage.getRaster();
|
||||
}
|
||||
else if (renderedImage.getNumXTiles() == 1 &&
|
||||
renderedImage.getNumYTiles() == 1) {
|
||||
Raster raster = renderedImage.getTile(renderedImage.getMinTileX(),
|
||||
renderedImage.getMinTileY());
|
||||
if (raster.getWidth() != renderedImage.getWidth() ||
|
||||
raster.getHeight() != renderedImage.getHeight()) {
|
||||
raster = raster.createChild(raster.getMinX(),
|
||||
raster.getMinY(),
|
||||
renderedImage.getWidth(),
|
||||
renderedImage.getHeight(),
|
||||
raster.getMinX(),
|
||||
raster.getMinY(),
|
||||
null);
|
||||
}
|
||||
return raster;
|
||||
}
|
||||
else {
|
||||
return renderedImage.getData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.tools.adtui.webp;
|
||||
|
||||
import com.intellij.ide.ApplicationInitializedListener;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.metadata.IIOInvalidTreeException;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
|
||||
public class WebpMetadata extends IIOMetadata implements ApplicationInitializedListener {
|
||||
public static final String WEBP_FORMAT_LOWER_CASE = "webp";
|
||||
public static final String WEBP_FORMAT_UPPER_CASE = "WEBP";
|
||||
public static final String[] WEBP_FORMAT_NAMES = new String[] {WEBP_FORMAT_UPPER_CASE, WEBP_FORMAT_LOWER_CASE};
|
||||
public static final String EXT_WEBP = WEBP_FORMAT_LOWER_CASE;
|
||||
public static final String[] WEBP_SUFFIXES = new String[] { EXT_WEBP};
|
||||
public static final String[] WEBP_MIME_TYPES = new String[] {"image/webp"};
|
||||
public static final String WEBP_VENDOR = "Google LLC";
|
||||
public static final float DEFAULT_ENCODING_QUALITY = 0.75f;
|
||||
public static final boolean DEFAULT_LOSSLESS = true;
|
||||
|
||||
@Override
|
||||
public void componentsInitialized() {
|
||||
ensureWebpRegistered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that service providers are registered.
|
||||
*/
|
||||
public static void ensureWebpRegistered() {
|
||||
IIORegistry.getDefaultInstance().registerServiceProvider(new WebpImageReaderSpi(), ImageReaderSpi.class);
|
||||
IIORegistry.getDefaultInstance().registerServiceProvider(new WebpImageWriterSpi(), ImageWriterSpi.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getAsTree(String formatName) {
|
||||
return new IIOMetadataNode(nativeMetadataFormatName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeTree(String formatName, Node root) throws IIOInvalidTreeException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.tools.adtui.webp;
|
||||
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.application.PluginPathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WebpNativeLibHelper {
|
||||
|
||||
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private static boolean sJniLibLoaded;
|
||||
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private static boolean sJniLibLoadAttempted;
|
||||
|
||||
private WebpNativeLibHelper() {
|
||||
}
|
||||
|
||||
static void requireNativeLibrary() throws IOException {
|
||||
if (!loadNativeLibraryIfNeeded()) {
|
||||
throw new IOException("The WebP decoder library could not be loaded");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDecoderVersion() {
|
||||
// This is the current result of calling
|
||||
// libwebp.WebPGetEncoderVersion()
|
||||
// but we don't want to have to load the native library just to get this constant
|
||||
// (since it doesn't change until we change the bundled version of the library anyway).
|
||||
//
|
||||
// And more importantly, NOBODY looks at this version anyway.
|
||||
return "1280";
|
||||
}
|
||||
|
||||
public static String getEncoderVersion() {
|
||||
return getDecoderVersion();
|
||||
}
|
||||
|
||||
public static boolean loadNativeLibraryIfNeeded() {
|
||||
if (!sJniLibLoadAttempted) {
|
||||
try {
|
||||
loadNativeLibrary();
|
||||
}
|
||||
catch (UnsatisfiedLinkError e) {
|
||||
Logger.getInstance(WebpImageReaderSpi.class).warn(e);
|
||||
}
|
||||
}
|
||||
return sJniLibLoaded;
|
||||
}
|
||||
|
||||
private synchronized static void loadNativeLibrary() {
|
||||
if (sJniLibLoadAttempted) {
|
||||
// Already attempted to load, nothing to do here.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String libFileName = getLibName();
|
||||
File pluginPath = getLibLocation();
|
||||
File libPath = new File(pluginPath, libFileName);
|
||||
if (!libPath.exists()) {
|
||||
throw new UnsatisfiedLinkError(String.format("'%1$s' not found at '%2$s'", libFileName, libPath.getAbsolutePath()));
|
||||
}
|
||||
System.load(libPath.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
sJniLibLoadAttempted = true;
|
||||
}
|
||||
sJniLibLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from {@link com.intellij.util.lang.UrlClassLoader}.
|
||||
*/
|
||||
public static String getLibName() {
|
||||
String baseName = SystemInfo.is64Bit ? "webp_jni64" : "webp_jni";
|
||||
String fileName = System.mapLibraryName(baseName);
|
||||
if (SystemInfo.isMac) {
|
||||
fileName = fileName.replace(".jnilib", ".dylib");
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public static File getLibLocation() {
|
||||
// A terrible hack for dev environment.
|
||||
String libPath = "/lib/libwebp/" + getPlatformName();
|
||||
return new File(PluginPathManager.getPluginHomePath("webp") + libPath);
|
||||
}
|
||||
|
||||
private static String getPlatformName() {
|
||||
if (SystemInfo.isWindows) return "win";
|
||||
else if (SystemInfo.isMac) return "mac";
|
||||
else if (SystemInfo.isLinux) return "linux";
|
||||
else return "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user