Skip to content

Commit

Permalink
Implement fromLineStipple method in ImageSource
Browse files Browse the repository at this point in the history
  • Loading branch information
GuzulFromUkraine authored and ComBatVision committed Dec 10, 2023
1 parent 5af264e commit ba551c9
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ fun main() {
"Paths" to PathsTutorial(wwd.engine),
"Polygons" to PolygonsTutorial(wwd.engine),
"Ellipses" to EllipsesTutorial(wwd.engine),
// TODO Uncomment when ImageSource.fromLineStipple will be implemented
//"Dash and fill" to ShapeDashAndFillTutorial(wwd.engine),
"Dash and fill" to ShapesDashAndFillTutorial(wwd.engine),
"Labels" to LabelsTutorial(wwd.engine),
"Sight line" to SightlineTutorial(wwd.engine),
"Surface image" to SurfaceImageTutorial(wwd.engine),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package earth.worldwind.render.image

import android.graphics.Bitmap
import android.graphics.Color.argb
import android.graphics.Color
import androidx.annotation.DrawableRes
import dev.icerock.moko.resources.ImageResource
import earth.worldwind.util.AbstractSource
Expand Down Expand Up @@ -265,27 +265,20 @@ actual open class ImageSource protected constructor(source: Any): AbstractSource

protected open class LineStippleBitmapFactory(protected val factor: Int, protected val pattern: Short): BitmapFactory {
override suspend fun createBitmap(): Bitmap {
val transparent = argb(0, 0, 0, 0)
val white = argb(255, 255, 255, 255)
return if (factor <= 0) {
val width = 16
val pixels = IntArray(width)
pixels.fill(white)
val bitmap = Bitmap.createBitmap(width, 1 /*height*/, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0 /*offset*/, width /*stride*/, 0 /*x*/, 0 /*y*/, width, 1 /*height*/)
bitmap
val pixels = if (factor <= 0) {
IntArray(16) { Color.WHITE }
} else {
val width = factor * 16
val pixels = IntArray(width)
var pixel = 0
for (bi in 0..15) {
val bit = pattern.toInt() and (1 shl bi)
val color = if (bit == 0) transparent else white
for (fi in 0 until factor) pixels[pixel++] = color
IntArray(factor * 16).also { pixels ->
var pixel = 0
for (bi in 0..15) {
val bit = pattern.toInt() and (1 shl bi)
val color = if (bit == 0) Color.TRANSPARENT else Color.WHITE
for (fi in 0 until factor) pixels[pixel++] = color
}
}
val bitmap = Bitmap.createBitmap(width, 1 /*height*/, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0 /*offset*/, width /*stride*/, 0 /*x*/, 0 /*y*/, width, 1 /*height*/)
bitmap
}
return Bitmap.createBitmap(pixels.size, 1, Bitmap.Config.ARGB_8888).apply {
setPixels(pixels, 0, pixels.size, 0, 0, pixels.size, 1)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import earth.worldwind.util.kgl.*
import kotlinx.browser.window
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.khronos.webgl.TexImageSource
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.HTMLImageElement
import org.w3c.dom.Image
import org.w3c.dom.url.URL
import kotlin.time.Duration.Companion.seconds
Expand Down Expand Up @@ -59,14 +62,14 @@ actual open class RenderResourceCache(
when {
imageSource.isImage -> {
// Following type of image sources is already in memory, so a texture may be created and put into the cache immediately.
return createTexture(options, imageSource.asImage()).also { put(imageSource, it, it.byteCount) }
return createTexture(options, imageSource.asImage())?.also { put(imageSource, it, it.byteCount) }
}
imageSource.isImageFactory -> {
val factory = imageSource.asImageFactory()
if (factory.isRunBlocking) {
// Image factory makes easy operations, so a texture may be created and put into the cache immediately.
return factory.createImage()?.let { bitmap ->
createTexture(options, bitmap).also { put(imageSource, it, it.byteCount) }
return factory.createImage()?.let { image ->
createTexture(options, image)?.also { put(imageSource, it, it.byteCount) }
}
}
}
Expand Down Expand Up @@ -122,25 +125,31 @@ actual open class RenderResourceCache(
image.src = src
}

protected open fun createTexture(options: ImageOptions?, image: Image): Texture {
protected open fun createTexture(options: ImageOptions?, image: TexImageSource): Texture? {
var (width, height) = when (image) {
is HTMLImageElement -> image.width to image.height
is HTMLCanvasElement -> image.width to image.height
else -> return null
}

// Process initialWidth and initialHeight if specified
if (image.width == 0 || image.height == 0) {
if (width == 0 || height == 0) {
// If source image has dimensions, then resize it proportionally to fit initial size restrictions
val ratioW = if (options != null && options.initialWidth > 0) image.width / options.initialWidth else 0
val ratioH = if (options != null && options.initialHeight > 0) image.height / options.initialHeight else 0
val ratioW = if (options != null && options.initialWidth > 0) width / options.initialWidth else 0
val ratioH = if (options != null && options.initialHeight > 0) height / options.initialHeight else 0
val ratio = if (ratioH > ratioW) ratioH else ratioW
if (ratio > 0) {
image.width = image.width / ratio
image.height = image.height / ratio
width /= ratio
height /= ratio
}
} else if (options != null && options.initialWidth > 0 && options.initialHeight > 0) {
// If source image has no dimensions (e.g. SVG image), then set initial size of image
image.width = options.initialWidth
image.height = options.initialHeight
width = options.initialWidth
height = options.initialHeight
}

// Create image texture and apply texture parameters
val texture = ImageTexture(image)
val texture = ImageTexture(image, width, height)
if (options?.resamplingMode == ResamplingMode.NEAREST_NEIGHBOR) {
texture.setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST)
texture.setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST)
Expand All @@ -152,10 +161,9 @@ actual open class RenderResourceCache(
return texture
}

protected open fun retrievalSucceeded(source: ImageSource, options: ImageOptions?, image: Image) {
protected open fun retrievalSucceeded(source: ImageSource, options: ImageOptions?, image: TexImageSource) {
// Create texture and put it into cache.
val texture = createTexture(options, image)
put(source, texture, texture.byteCount)
createTexture(options, image)?.let { put(source, it, it.byteCount) }
currentRetrievals -= source
absentResourceList.unmarkResourceAbsent(source.hashCode())
WorldWind.requestRedraw()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package earth.worldwind.render

import earth.worldwind.geom.Vec2
import earth.worldwind.render.image.CanvasTexture
import earth.worldwind.render.image.ImageTexture
import earth.worldwind.shape.TextAttributes
import kotlinx.browser.document
import org.w3c.dom.*
Expand All @@ -18,7 +18,7 @@ actual open class TextRenderer actual constructor(protected val rc: RenderContex
* @returns A texture for the specified text string.
*/
actual fun renderText(text: String?, attributes: TextAttributes): Texture? =
if (text?.isNotEmpty() == true) CanvasTexture(drawText(text, attributes)) else null
if (text?.isNotEmpty() == true) ImageTexture(drawText(text, attributes)) else null

/**
* Creates a 2D Canvas for a specified text string while considering current TextRenderer state regarding outline
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import earth.worldwind.util.AbstractSource
import earth.worldwind.util.Logger.ERROR
import earth.worldwind.util.Logger.logMessage
import earth.worldwind.util.ResourcePostprocessor
import org.w3c.dom.Image
import org.w3c.dom.*
import org.w3c.dom.url.URL
import kotlinx.browser.document
import org.khronos.webgl.TexImageSource

/**
* Provides a mechanism for specifying images from a variety of sources. ImageSource retains the image source on behalf
Expand All @@ -23,6 +25,7 @@ import org.w3c.dom.url.URL
*/
actual open class ImageSource protected constructor(source: Any): AbstractSource(source) {
actual companion object {
protected val lineStippleFactories = mutableMapOf<Any, ImageFactory>()
/**
* Constructs an image source with a multi-platform resource identifier.
*
Expand Down Expand Up @@ -91,7 +94,13 @@ actual open class ImageSource protected constructor(source: Any): AbstractSource
* @return the new image source
*/
actual fun fromLineStipple(factor: Int, pattern: Short): ImageSource {
TODO("Not yet implemented")
val lFactor = factor.toLong() and 0xFFFFFFFFL
val lPattern = pattern.toLong() and 0xFFFFL
val key = lFactor shl 32 or lPattern
val factory = lineStippleFactories[key] ?: LineStippleImageFactory(factor, pattern).also {
lineStippleFactories[key] = it
}
return ImageSource(factory)
}

/**
Expand Down Expand Up @@ -182,6 +191,36 @@ actual open class ImageSource protected constructor(source: Any): AbstractSource
*
* @return the image associated with this factory
*/
fun createImage(): Image?
fun createImage(): TexImageSource?
}

protected open class LineStippleImageFactory(protected val factor: Int, protected val pattern: Short): ImageFactory {
override fun createImage(): TexImageSource {
val canvas = document.createElement("canvas") as HTMLCanvasElement
canvas.width = if (factor <= 0) 16 else factor * 16
canvas.height = 1
val ctx2D = canvas.getContext("2d") as CanvasRenderingContext2D
ctx2D.strokeStyle = "white"
ctx2D.beginPath()
if (factor <= 0) {
ctx2D.moveTo(0.0, 0.5)
ctx2D.lineTo(canvas.width.toDouble(), 0.5)
} else {
var offset = 0
for (bi in 0..15) {
val bit = pattern.toInt() and (1 shl bi)
if (bit != 0) {
ctx2D.moveTo(offset.toDouble(), 0.5)
ctx2D.lineTo((offset + factor).toDouble(), 0.5)
}
offset += factor
}
}
ctx2D.stroke()
return canvas
}

override fun toString() = "LineStippleCanvasFactory factor=$factor, pattern=" + (pattern.toInt() and 0xFFFF).toString(16).uppercase()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ import earth.worldwind.util.kgl.GL_TEXTURE_2D
import earth.worldwind.util.kgl.GL_UNSIGNED_BYTE
import earth.worldwind.util.kgl.WebKgl
import earth.worldwind.util.math.isPowerOfTwo
import org.khronos.webgl.TexImageSource
import org.khronos.webgl.WebGLRenderingContext.Companion.UNPACK_PREMULTIPLY_ALPHA_WEBGL
import org.w3c.dom.Image
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.HTMLImageElement

open class ImageTexture(image: Image) : Texture(image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE) {
protected var image: Image? = image
override val hasMipMap = isPowerOfTwo(image.width) && isPowerOfTwo(image.height)
open class ImageTexture(image: TexImageSource, width: Int, height: Int) : Texture(width, height, GL_RGBA, GL_UNSIGNED_BYTE) {
protected var image: TexImageSource? = image
override val hasMipMap = isPowerOfTwo(width) && isPowerOfTwo(height)

constructor(image: HTMLImageElement) : this(image, image.width, image.height)

constructor(image: HTMLCanvasElement) : this(image, image.width, image.height)

init {
coordTransform.setToVerticalFlip()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import earth.worldwind.util.AbstractSource
import earth.worldwind.util.Logger.ERROR
import earth.worldwind.util.Logger.logMessage
import earth.worldwind.util.ResourcePostprocessor
import java.awt.Color
import java.awt.image.BufferedImage
import java.io.File
import java.net.MalformedURLException
Expand All @@ -27,6 +28,8 @@ import java.net.URL
*/
actual open class ImageSource protected constructor(source: Any): AbstractSource(source) {
actual companion object {
protected val lineStippleFactories = mutableMapOf<Any, ImageFactory>()

/**
* Constructs an image source with a multi-platform resource identifier.
*
Expand Down Expand Up @@ -126,7 +129,13 @@ actual open class ImageSource protected constructor(source: Any): AbstractSource
*/
@JvmStatic
actual fun fromLineStipple(factor: Int, pattern: Short): ImageSource {
TODO("Not yet implemented")
val lFactor = factor.toLong() and 0xFFFFFFFFL
val lPattern = pattern.toLong() and 0xFFFFL
val key = lFactor shl 32 or lPattern
val factory = lineStippleFactories[key] ?: LineStippleImageFactory(factor, pattern).also {
lineStippleFactories[key] = it
}
return ImageSource(factory)
}

/**
Expand Down Expand Up @@ -229,4 +238,26 @@ actual open class ImageSource protected constructor(source: Any): AbstractSource
*/
suspend fun createImage(): BufferedImage?
}

protected open class LineStippleImageFactory(protected val factor: Int, protected val pattern: Short): ImageFactory {
override suspend fun createImage(): BufferedImage {
val pixels = if (factor <= 0) {
IntArray(16) { Color.WHITE.rgb }
} else {
IntArray(factor * 16).also { pixels ->
var pixel = 0
for (bi in 0..15) {
val bit = pattern.toInt() and (1 shl bi)
val color = if (bit == 0) 0 else Color.WHITE.rgb
for (fi in 0 until factor) pixels[pixel++] = color
}
}
}
return BufferedImage(pixels.size, 1, BufferedImage.TYPE_INT_ARGB).apply {
setRGB(0, 0, pixels.size, 1, pixels, 0, pixels.size)
}
}

override fun toString() = "LineStippleBitmapFactory factor=$factor, pattern=" + (pattern.toInt() and 0xFFFF).toString(16).uppercase()
}
}

0 comments on commit ba551c9

Please sign in to comment.