126 // bound as rendering target by GraphicsContext |
128 // bound as rendering target by GraphicsContext |
127 private boolean isBound = false; |
129 private boolean isBound = false; |
128 // GraphicsContext bound to this image |
130 // GraphicsContext bound to this image |
129 private GraphicsContext boundingGc = null; |
131 private GraphicsContext boundingGc = null; |
130 |
132 |
|
133 // Image manages copies of itself for command buffering of drawImage and drawRegion. |
|
134 private Image commandBufferCopy; |
|
135 private int commandBufferCopyRefs; |
|
136 private boolean commandBufferCopyDirty; |
|
137 |
|
138 // A copy-constructed Image remembers what it's a copy of |
|
139 private Image whatAmIACopyOf; |
|
140 |
|
141 private Image() { |
|
142 } |
|
143 |
131 /** |
144 /** |
132 * Constructs new image with given native image handle. |
145 * Constructs new image with given native image handle. |
133 * |
146 * |
134 * @param imgHandle The handle of valid native image |
147 * @param imgHandle The handle of valid native image |
135 * @throws OutOfMemoryError if imgHandlde is invalid |
148 * @throws OutOfMemoryError if imgHandlde is invalid |
136 */ |
149 */ |
137 Image(int imgHandle) { |
150 Image(int imgHandle) { |
138 Utils.validateUiThread(); |
151 Utils.validateUiThread(); |
139 // validate handle |
152 // validate handle |
140 if(imgHandle == 0) { |
153 if(imgHandle == 0) { |
141 throw new OutOfMemoryError(); |
154 throw new OutOfMemoryError(); |
142 } |
155 } |
143 // store handle & get dimensions |
156 // store handle & get dimensions |
216 public Image(Image sourceImage, int x, int y, int width, int height) { |
229 public Image(Image sourceImage, int x, int y, int width, int height) { |
217 // validate sourceImage |
230 // validate sourceImage |
218 if (sourceImage == null) { |
231 if (sourceImage == null) { |
219 throw new NullPointerException("img is null"); |
232 throw new NullPointerException("img is null"); |
220 } |
233 } |
221 // Construct image in native side and store the handle |
234 createCopy(sourceImage, x, y, width, height); |
222 handle = OS.image_create(sourceImage.handle, x, y, width, height); // may throw outOfMemoryError |
|
223 // set dimensions |
|
224 updateSize(); |
|
225 } |
235 } |
226 |
236 |
227 /** |
237 /** |
228 * Creates a new image from given ImageData |
238 * Creates a new image from given ImageData |
229 * @param imageData |
239 * @param imageData |
267 * |
277 * |
268 * If this instance is being disposed before gc has released it, the |
278 * If this instance is being disposed before gc has released it, the |
269 * releaseTarget() of binding gc is called automatically. |
279 * releaseTarget() of binding gc is called automatically. |
270 */ |
280 */ |
271 public void dispose() { |
281 public void dispose() { |
272 Utils.validateUiThread(); |
282 Utils.validateUiThread(); |
273 if (handle != 0 || !disposed) { |
283 if (handle != 0 || !disposed) { |
274 // If this instance is being disposed while |
284 // If this instance is being disposed while |
275 // gc is still bound, release binding before disposing. |
285 // gc is still bound, release binding before disposing. |
276 if(isBound) { |
286 if(isBound) { |
277 isBound = false; |
287 isBound = false; |
591 * Returns native side handle. |
603 * Returns native side handle. |
592 * @return the native handle. |
604 * @return the native handle. |
593 */ |
605 */ |
594 public int getHandle() |
606 public int getHandle() |
595 { |
607 { |
596 checkState(); |
608 checkState(); |
597 return handle; |
609 return handle; |
598 } |
610 } |
599 |
611 |
600 /** |
612 /** |
601 * Returns native side QPixmap handle. |
613 * Returns native side QPixmap handle. |
602 * @return the native QPixmap handle. |
614 * @return the native QPixmap handle. |
603 */ |
615 */ |
604 synchronized public int getNativePixmapHandle() |
616 synchronized public int getNativePixmapHandle() |
605 { |
617 { |
606 checkState(); |
618 checkState(); |
607 if(pixmapHandle == 0) |
619 if(pixmapHandle == 0) |
608 { |
620 { |
609 // In the current implementation this will return |
621 // In the current implementation this will return |
610 // pointer to the already existing QPixmap (which |
622 // pointer to the already existing QPixmap (which |
611 // will be destroyed with the object), so the |
623 // will be destroyed with the object), so the |
619 /** |
631 /** |
620 * Creates <code>ImageData</code> objects |
632 * Creates <code>ImageData</code> objects |
621 * @return New object |
633 * @return New object |
622 */ |
634 */ |
623 public ImageData getImageData() { |
635 public ImageData getImageData() { |
624 checkState(); |
636 checkState(); |
625 return OS.image_getImageData(handle); |
637 return OS.image_getImageData(handle); |
626 } |
638 } |
627 |
639 |
628 /** |
640 /** |
629 * Private helper to check the state of the current instance. |
641 * Private helper to check the state of the current instance. |
630 */ |
642 */ |
631 private void checkState() { |
643 private void checkState() { |
632 Utils.validateUiThread(); |
644 Utils.validateUiThread(); |
633 if(disposed) { |
645 if(disposed) { |
634 throw new IllegalStateException("Image already disposed"); |
646 throw new IllegalStateException("Image already disposed"); |
635 } |
647 } |
636 } |
648 } |
637 |
649 |
655 throw new IllegalArgumentException("imageData is null"); |
667 throw new IllegalArgumentException("imageData is null"); |
656 } |
668 } |
657 int imageHandle = OS.image_create(imageData); |
669 int imageHandle = OS.image_create(imageData); |
658 return new Image(imageHandle); |
670 return new Image(imageHandle); |
659 } |
671 } |
|
672 |
|
673 /** |
|
674 * Constructs new image with given native QPixmap handle. |
|
675 * @param pixmapHandle Handle of native QPixmap. |
|
676 * @return Instance of loaded image. |
|
677 */ |
|
678 public static Image createImageFromPixmap(int pixmapHandle) { |
|
679 // input validation |
|
680 if(pixmapHandle <= 0) { |
|
681 throw new IllegalArgumentException("Invalid pixmap handle"); |
|
682 } |
|
683 // Construct an ge in native side and store the handle |
|
684 int handle = OS.image_create(pixmapHandle); |
|
685 return new Image(handle); |
|
686 } |
|
687 |
|
688 /** |
|
689 * Obtains a shallow copy of this Image to be placed in the command buffer. |
|
690 * The returned copy must be marked as free by calling freeCommandBufferCopy |
|
691 * when it's no longer needed. |
|
692 * |
|
693 * @return The copy |
|
694 */ |
|
695 Image getCommandBufferCopy() { |
|
696 if(commandBufferCopyDirty) { |
|
697 return copyInUIThread(); |
|
698 } |
|
699 if(commandBufferCopy == null) { |
|
700 commandBufferCopyDirty = false; |
|
701 commandBufferCopy = copyInUIThread(); |
|
702 commandBufferCopyRefs = 0; |
|
703 } |
|
704 commandBufferCopyRefs++; |
|
705 return commandBufferCopy; |
|
706 } |
|
707 |
|
708 /** |
|
709 * Marks a copy returned from getCommandBufferCopy as free. |
|
710 * |
|
711 * @param image |
|
712 * The image returned from getShallowCopy. |
|
713 */ |
|
714 void freeCommandBufferCopy() { |
|
715 if(disposed) { |
|
716 throw new RuntimeException("Image referenced by command buffer has been disposed"); |
|
717 } |
|
718 if(whatAmIACopyOf == null) { |
|
719 throw new RuntimeException("Image not a copy"); |
|
720 } |
|
721 whatAmIACopyOf.freeCommandBufferCopyOfMe(this); |
|
722 } |
|
723 |
|
724 /** |
|
725 * Tells to the Image that it has been modified and any copies returned from |
|
726 * getShallowCopy() have thus become deep copies. |
|
727 */ |
|
728 void pixelDataModified() { |
|
729 if(commandBufferCopy != null) { |
|
730 commandBufferCopyDirty = true; |
|
731 } |
|
732 } |
|
733 |
|
734 /* |
|
735 * Copy-construction |
|
736 */ |
|
737 private void createCopy(Image sourceImage, int x, int y, int width, int height) { |
|
738 // Construct image in native side and store the handle |
|
739 handle = OS.image_create(sourceImage.handle, x, y, width, height); // may throw outOfMemoryError |
|
740 // set dimensions |
|
741 updateSize(); |
|
742 whatAmIACopyOf = sourceImage; |
|
743 } |
|
744 |
|
745 /* |
|
746 * Called on the Image when a copy it has returned from getCommandBufferCopy |
|
747 * is being freed by a call to freeCommandBufferCopy. The Image may be |
|
748 * disposed at this point while the copy is not. |
|
749 */ |
|
750 private void freeCommandBufferCopyOfMe(Image copy) { |
|
751 if(copy != commandBufferCopy) { |
|
752 throw new RuntimeException("Copy doesn't exist, freed multiple times?"); |
|
753 } else { |
|
754 commandBufferCopyRefs--; |
|
755 if(commandBufferCopyRefs <= 0) { |
|
756 commandBufferCopy.dispose(); |
|
757 commandBufferCopy = null; |
|
758 } |
|
759 } |
|
760 } |
|
761 |
|
762 /* |
|
763 * Creates a shallow copy of the Image in the UI thread. |
|
764 */ |
|
765 private Image copyInUIThread() { |
|
766 Display d = Internal_PackageSupport.getDisplayInstance(); |
|
767 final Image copy = new Image(); |
|
768 if(d != null) { |
|
769 d.syncExec(new Runnable() { |
|
770 public void run() { |
|
771 copy.createCopy(Image.this, 0, 0, 0, 0); |
|
772 } |
|
773 }); |
|
774 } |
|
775 if(copy.handle == 0) { |
|
776 return null; |
|
777 } |
|
778 return copy; |
|
779 } |
660 } |
780 } |